* X11Keyboard.cs: Detect and use the num lock mask.
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / TrackBar.cs
1 //
2 // System.Windows.Forms.TrackBar.cs
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 //
23 // Autors:
24 //              Jordi Mas i Hernandez, jordi@ximian.com
25 //
26 // TODO:
27 //              - The AutoSize functionality seems quite broken for vertical controls in .Net 1.1. Not
28 //              sure if we are implementing it the right way.
29 //
30 // Copyright (C) Novell Inc., 2004
31 //
32 //
33 // $Revision: 1.15 $
34 // $Modtime: $
35 // $Log: TrackBar.cs,v $
36 // Revision 1.15  2004/10/05 04:56:12  jackson
37 // Let the base Control handle the buffers, derived classes should not have to CreateBuffers themselves.
38 //
39 // Revision 1.14  2004/09/28 18:44:25  pbartok
40 // - Streamlined Theme interfaces:
41 //   * Each DrawXXX method for a control now is passed the object for the
42 //     control to be drawn in order to allow accessing any state the theme
43 //     might require
44 //
45 //   * ControlPaint methods for the theme now have a CP prefix to avoid
46 //     name clashes with the Draw methods for controls
47 //
48 //   * Every control now retrieves it's DefaultSize from the current theme
49 //
50 // Revision 1.13  2004/08/23 20:10:03  jordi
51 // fixes properties and methods
52 //
53 // Revision 1.12  2004/08/21 20:21:48  pbartok
54 // - Replaced direct XplatUI calls with their Control counterpart
55 //
56 // Revision 1.11  2004/08/20 19:45:50  jordi
57 // fixes timer, new properties and methods
58 //
59 // Revision 1.10  2004/08/13 20:55:20  jordi
60 // change from wndproc to events
61 //
62 // Revision 1.9  2004/08/13 18:46:26  jordi
63 // adds timer and grap window
64 //
65 // Revision 1.8  2004/08/12 20:29:01  jordi
66 // Trackbar enhancement, fix mouse problems, highli thumb, etc
67 //
68 // Revision 1.7  2004/08/10 23:27:12  jordi
69 // add missing methods, properties, and restructure to hide extra ones
70 //
71 // Revision 1.6  2004/08/10 15:47:11  jackson
72 // Allow control to handle buffering
73 //
74 // Revision 1.5  2004/08/07 23:32:26  jordi
75 // throw exceptions of invalid enums values
76 //
77 // Revision 1.4  2004/08/06 23:18:06  pbartok
78 // - Fixed some rounding issues with float/int
79 //
80 // Revision 1.3  2004/07/27 15:53:02  jordi
81 // fixes trackbar events, def classname, methods signature
82 //
83 // Revision 1.2  2004/07/26 17:42:03  jordi
84 // Theme support
85 //
86 // Revision 1.1  2004/07/15 09:38:02  jordi
87 // Horizontal and Vertical TrackBar control implementation
88 //
89 //
90
91 // NOT COMPLETE
92
93 using System.ComponentModel;
94 using System.Drawing;
95 using System.Drawing.Imaging;
96 using System.Drawing.Drawing2D;
97 using System.Timers;
98
99 namespace System.Windows.Forms
100 {       
101
102         [DefaultEvent ("Scroll")]
103         public class TrackBar : Control, ISupportInitialize
104         {
105                 private int minimum;
106                 private int maximum;
107                 internal int tickFrequency;
108                 private bool autosize;
109                 private int position;
110                 private int smallChange;
111                 private int largeChange;
112                 private Orientation orientation;
113                 private TickStyle tickStyle;
114                 internal Rectangle paint_area = new Rectangle ();
115                 private Rectangle thumb_pos = new Rectangle ();  /* Current position and size of the thumb */
116                 private Rectangle thumb_area = new Rectangle (); /* Area where the thumb can scroll */
117                 internal bool thumb_pressed = false;             
118                 private System.Timers.Timer holdclick_timer = new System.Timers.Timer ();
119                 internal int thumb_mouseclick;          
120                 private bool mouse_clickmove;
121
122                 #region Events
123                 public event EventHandler Scroll;
124                 public event EventHandler ValueChanged;         
125                 public new event EventHandler ImeModeChanged;
126                 public new event EventHandler ForeColorChanged;
127                 public new event EventHandler TextChanged;
128                 public new event EventHandler BackgroundImageChanged;
129                 #endregion // Events
130
131                 public TrackBar ()
132                 {
133                         orientation = Orientation.Horizontal;
134                         minimum = 0;
135                         maximum = 10;
136                         tickFrequency = 1;
137                         autosize = true;
138                         position = 0;
139                         tickStyle = TickStyle.BottomRight;
140                         smallChange = 1;
141                         largeChange = 5;
142                         Scroll = null;
143                         ValueChanged  = null;           
144                         mouse_clickmove = false;
145                         SizeChanged += new System.EventHandler (OnResizeTB);
146                         MouseDown += new MouseEventHandler (OnMouseDownTB); 
147                         MouseUp += new MouseEventHandler (OnMouseUpTB); 
148                         MouseMove += new MouseEventHandler (OnMouseMoveTB);
149                         holdclick_timer.Elapsed += new ElapsedEventHandler (OnFirstClickTimer);
150
151                         SetStyle (ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
152                         SetStyle (ControlStyles.ResizeRedraw | ControlStyles.Opaque, true);                     
153                 }
154
155                 #region Private & Internal Properties
156                 internal Rectangle ThumbPos {
157                         get {
158                                 return thumb_pos;
159                         }
160
161                         set {
162                                 thumb_pos = value;
163                         }
164                 }
165
166                 internal Rectangle ThumbArea {
167                         get {
168                                 return thumb_area;
169                         }
170
171                         set {
172                                 thumb_area = value;
173                         }
174                 }
175                 #endregion      // Private & Internal Properties
176
177                 #region Public Properties
178
179                 [DefaultValue (true)]
180                 public bool AutoSize {
181                         get { return autosize; }
182                         set { autosize = value;}
183                 }
184
185                 [EditorBrowsable (EditorBrowsableState.Never)]
186                 public override Image BackgroundImage {
187                         get { return base.BackgroundImage; }
188                         set { 
189                                 if (base.BackgroundImage == value)
190                                         return;
191
192                                 if (BackgroundImageChanged != null)
193                                         BackgroundImageChanged (this, EventArgs.Empty);
194
195                                 base.BackgroundImage = value; 
196                         }
197                 }
198
199                 protected override CreateParams CreateParams {
200                         get {
201                                 CreateParams createParams = base.CreateParams;
202                                 createParams.ClassName = XplatUI.DefaultClassName;
203
204                                 createParams.Style = (int) (
205                                         WindowStyles.WS_CHILD |
206                                         WindowStyles.WS_VISIBLE);
207
208                                 return createParams;
209                         }
210                 }
211
212                 protected override ImeMode DefaultImeMode {
213                         get {return ImeMode.Disable; }
214                 }
215
216                 protected override Size DefaultSize {
217                         get { return ThemeEngine.Current.TrackBarDefaultSize; }
218                 }       
219                 
220                 [EditorBrowsable (EditorBrowsableState.Never)]   
221                 public override Font Font {
222                         get { return base.Font; }
223                         set { base.Font = value; }
224                 }
225
226                 [EditorBrowsable (EditorBrowsableState.Never)]  
227                 public override Color ForeColor {
228                         get { return base.ForeColor; }
229                         set {
230                                 if (value == base.ForeColor)
231                                         return;
232
233                                 if (ForeColorChanged != null)
234                                         ForeColorChanged (this, EventArgs.Empty);
235
236                                 Refresh ();
237                         }
238                 }               
239
240                 [EditorBrowsable (EditorBrowsableState.Never)]  
241                 public new ImeMode ImeMode {
242                         get { return base.ImeMode; }
243                         set {
244                                 if (value == base.ImeMode)
245                                         return;
246
247                                 base.ImeMode = value;
248                                 if (ImeModeChanged != null)
249                                         ImeModeChanged (this, EventArgs.Empty);
250                         }
251                 }
252                 
253                 [DefaultValue (5)]
254                 public int LargeChange \r
255                 {
256                         get { return largeChange; }
257                         set {
258                                 if (value < 0)
259                                         throw new Exception( string.Format("Value '{0}' must be greater than or equal to 0.", value));
260
261                                 largeChange = value;
262                                 Refresh ();
263                         }
264                 }
265
266                 [DefaultValue (10)]
267                 [RefreshProperties (RefreshProperties.All)]             
268                 public int Maximum {
269                         get { return maximum; }
270                         set {
271                                 if (maximum != value)  {
272                                         maximum = value;
273
274                                         if (maximum < minimum)
275                                                 minimum = maximum;
276
277                                         Refresh ();
278                                 }
279                         }
280                 }
281
282                 [DefaultValue (0)]
283                 [RefreshProperties (RefreshProperties.All)]             
284                 public int Minimum {
285                         get { return minimum; }
286                         set {
287
288                                 if (Minimum != value) {
289                                         minimum = value;
290
291                                         if (minimum > maximum)
292                                                 maximum = minimum;
293
294                                         Refresh ();
295                                 }
296                         }
297                 }
298
299                 [DefaultValue (Orientation.Horizontal)]
300                 [Localizable (true)]
301                 public Orientation Orientation {
302                         get { return orientation; }
303                         set {
304                                 if (!Enum.IsDefined (typeof (Orientation), value))
305                                         throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for Orientation", value));
306
307                                 /* Orientation can be changed once the control has been created */
308                                 if (orientation != value) {
309                                         orientation = value;
310                                 
311                                         int old_witdh = Width;
312                                         Width = Height;
313                                         Height = old_witdh;
314                                         Refresh (); 
315                                 }
316                         }
317                 }
318
319                 [DefaultValue (1)]
320                 public int SmallChange {
321                         get { return smallChange;}
322                         set {
323                                 if ( value < 0 )
324                                         throw new Exception( string.Format("Value '{0}' must be greater than or equal to 0.", value));
325
326                                 if (smallChange != value) {
327                                         smallChange = value;
328                                         Refresh ();
329                                 }
330                         }
331                 }
332
333                 [EditorBrowsable (EditorBrowsableState.Never)]
334                 [Bindable (false)]
335                 public override string Text {
336                         get {   return base.Text; }                     
337                         set {
338                                 if (value == base.Text)
339                                         return;
340
341                                 if (TextChanged != null)
342                                         TextChanged (this, EventArgs.Empty);
343
344                                 Refresh ();
345                         }
346                 }
347
348                 [DefaultValue (1)]
349                 public int TickFrequency {
350                         get { return tickFrequency; }
351                         set {
352                                 if ( value > 0 ) {
353                                         tickFrequency = value;
354                                         Refresh ();
355                                 }
356                         }
357                 }
358
359                 [DefaultValue (TickStyle.BottomRight)]
360                 public TickStyle TickStyle {
361                         get { return tickStyle; }
362                         set {                           
363                                 if (!Enum.IsDefined (typeof (TickStyle), value))
364                                         throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for TickStyle", value));
365                                 
366                                 if (tickStyle != value) {
367                                         tickStyle = value;
368                                         Refresh ();
369                                 }
370                         }
371                 }
372                 
373                 [DefaultValue (0)]
374                 [Bindable (false)]
375                 public int Value {
376                         get { return position; }
377                         set {
378                                 if (value < Minimum || value > Maximum)
379                                         throw new ArgumentException(
380                                                 string.Format("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
381                                 
382                                 if (position != value) {                                                                                                        
383                                         position = value;                                       
384                                         
385                                         if (ValueChanged != null)                               
386                                                 ValueChanged (this, new EventArgs ());
387                                                 
388                                         Refresh ();
389                                 }                               
390                         }
391                 }
392
393                 #endregion //Public Properties
394
395                 #region Public Methods
396
397                 public virtual void BeginInit ()                
398                 {
399
400                 }
401
402                 protected override void CreateHandle ()
403                 {
404                         base.CreateHandle ();
405                 }
406
407
408                 public virtual void EndInit ()          
409                 {
410
411                 }
412
413                 protected override bool IsInputKey (Keys keyData)
414                 {
415                         return false;
416                 }
417
418                 protected override void OnBackColorChanged (EventArgs e)
419                 {
420
421                 }
422
423                 protected override void OnHandleCreated (EventArgs e)
424                 {                       
425                         if (AutoSize)
426                                 if (Orientation == Orientation.Horizontal)
427                                         Size = new Size (Width, 40);
428                                 else
429                                         Size = new Size (50, Height);
430
431                         UpdateArea ();
432                         UpdatePos (Value, true);                        
433                 }
434         
435                 [EditorBrowsable (EditorBrowsableState.Advanced)]
436                 protected override void OnMouseWheel (MouseEventArgs e)
437                 {
438                         if (!Enabled) return;
439                         
440                         if (e.Delta > 0)
441                                 SmallDecrement ();
442                         else
443                                 SmallIncrement ();
444
445                         base.OnMouseWheel (e);
446                                         
447                 }
448
449                 protected virtual void OnScroll (EventArgs e) 
450                 {
451                         if (Scroll != null) 
452                                 Scroll (this, e);
453                 }
454
455                 protected virtual void OnValueChanged (EventArgs e) 
456                 {
457                         if (ValueChanged != null) 
458                                 ValueChanged (this, e);
459                 }
460
461                 public void SetRange (int minValue, int maxValue)
462                 {
463                         Minimum = minValue;
464                         Maximum = maxValue;
465
466                         Refresh ();
467                 }
468
469                 public override string ToString()
470                 {
471                         return string.Format("System.Windows.Forms.Trackbar, Minimum: {0}, Maximum: {1}, Value: {2}",
472                                                 Minimum, Maximum, Value);
473                 }
474                                                         
475
476                 protected override void WndProc (ref Message m)
477                 {
478                         switch ((Msg) m.Msg) {
479         
480                         case Msg.WM_PAINT: {                            
481                                 PaintEventArgs  paint_event;
482
483                                 paint_event = XplatUI.PaintEventStart (Handle);
484                                 OnPaintTB (paint_event);
485                                 XplatUI.PaintEventEnd (Handle);
486                                 return;
487                         }               
488
489                         case Msg.WM_KEYDOWN: 
490                                 OnKeyDownTB (new KeyEventArgs ((Keys)m.WParam.ToInt32 ()));
491                                 return;                 
492                                 
493                         case Msg.WM_ERASEBKGND:
494                                 m.Result = (IntPtr) 1; /* Disable background painting to avoid flickering */
495                                 return;
496                                 
497                         default:
498                                 break;
499                         }
500
501                         base.WndProc (ref m);
502                 }
503                 
504                 #endregion Public Methods
505
506                 #region Private Methods
507
508                 private void UpdateArea ()
509                 {
510                         paint_area.X = paint_area.Y = 0;
511                         paint_area.Width = Width;
512                         paint_area.Height = Height;                     
513                 }
514
515                 private void UpdatePos (int newPos, bool update_trumbpos)
516                 {
517                         int old = position;
518
519                         if (newPos < minimum)
520                                 Value = minimum;
521                         else
522                                 if (newPos > maximum)
523                                 Value = maximum;
524                         else
525                                 Value = newPos;                         
526                 }
527                 
528                 private void LargeIncrement ()
529                 {                       
530                         UpdatePos (position + LargeChange, true);
531                         Refresh ();
532                         OnScroll (new EventArgs ());
533                 }
534
535                 private void LargeDecrement ()
536                 {
537                         UpdatePos (position - LargeChange, true);
538                         Refresh ();
539                         OnScroll (new EventArgs ());
540                 }
541
542                 private void SmallIncrement ()
543                 {                       
544                         UpdatePos (position + SmallChange, true);
545                         Refresh ();
546                         OnScroll (new EventArgs ());
547                 }
548
549                 private void SmallDecrement ()
550                 {
551                         UpdatePos (position - SmallChange, true);
552                         Refresh ();
553                         OnScroll (new EventArgs ());    
554                 }
555                 
556                 private void Draw ()
557                 {                                       
558                         float ticks = (Maximum - Minimum) / tickFrequency; /* N of ticks draw*/                        
559         
560                         ThemeEngine.Current.DrawTrackBar(DeviceContext, this.ClientRectangle, this);
561                 }               
562
563                 private void OnMouseUpTB (object sender, MouseEventArgs e)
564                 {       
565                         if (!Enabled) return;                   
566
567                         if (thumb_pressed == true || mouse_clickmove == true) { 
568                                 thumb_pressed = false;
569                                 holdclick_timer.Enabled = false;
570                                 this.Capture = false;
571                                 Refresh ();
572                         }
573                 }
574
575                 private void OnMouseDownTB (object sender, MouseEventArgs e)
576                 {
577                         if (!Enabled) return;                                           
578
579                         bool fire_timer = false;
580                         
581                         Point point = new Point (e.X, e.Y);
582
583                         if (orientation == Orientation.Horizontal) {
584                                 
585                                 if (thumb_pos.Contains (point)) {
586                                         this.Capture = true;
587                                         thumb_pressed = true;
588                                         thumb_mouseclick = e.X;
589                                         Refresh ();                                     
590                                 }
591                                 else {
592                                         if (paint_area.Contains (point)) {
593                                                 if (e.X > thumb_pos.X + thumb_pos.Width)
594                                                         LargeIncrement ();
595                                                 else
596                                                         LargeDecrement ();
597
598                                                 Refresh ();
599                                                 fire_timer = true;
600                                                 mouse_clickmove = true;
601                                         }
602                                 }
603                         }
604                         else {
605                                 if (thumb_pos.Contains (point)) {
606                                         this.Capture = true;
607                                         thumb_pressed = true;
608                                         thumb_mouseclick = e.Y;
609                                         Refresh ();
610                                         
611                                 }
612                                 else {
613                                         if (paint_area.Contains (point)) {
614                                                 if (e.Y > thumb_pos.Y + thumb_pos.Height)
615                                                         LargeIncrement ();
616                                                 else
617                                                         LargeDecrement ();
618
619                                                 Refresh ();
620                                                 fire_timer = true;
621                                                 mouse_clickmove = true;
622                                         }
623                                 }
624                         }
625
626                         if (fire_timer) {                               \r
627                                 holdclick_timer.Interval = 300;\r
628                                 holdclick_timer.Enabled = true;                         
629                         }                       
630                 }
631
632                 private void OnMouseMoveTB (object sender, MouseEventArgs e)
633                 {                       
634                         if (!Enabled) return;
635                 
636                         Point pnt = new Point (e.X, e.Y);
637
638                         /* Moving the thumb */
639                         if (thumb_pressed) {
640                                                                                                 
641                                 if (orientation == Orientation.Horizontal){
642                                         if (paint_area.Contains (e.X, thumb_pos.Y))
643                                                 thumb_mouseclick = e.X; 
644                                 }
645                                 else {
646                                         if (paint_area.Contains (thumb_pos.X, e.Y))
647                                                 thumb_mouseclick = e.Y;
648                                 }
649
650                                 Refresh ();
651                                 OnScroll (new EventArgs ());
652                         }
653                 }
654
655                 private void OnResizeTB (object sender, System.EventArgs e)
656                 {                       
657                         if (Width <= 0 || Height <= 0)
658                                 return;
659
660                         UpdateArea ();
661                 }               
662
663                 private void OnPaintTB (PaintEventArgs pevent)
664                 {               
665                         if (Width <= 0 || Height <=  0 || Visible == false)
666                                 return;         
667
668                         /* Copies memory drawing buffer to screen*/
669                         UpdateArea ();
670                         Draw ();
671                         pevent.Graphics.DrawImage (ImageBuffer, 0, 0);
672                 }  
673
674                 private void OnKeyDownTB (KeyEventArgs e) \r
675                 {                       
676                         switch (e.KeyCode) {                    \r
677                         case Keys.Up:
678                         case Keys.Right:
679                                 SmallIncrement ();
680                                 break;
681
682                         case Keys.Down:
683                         case Keys.Left:
684                                 SmallDecrement ();
685                                 break;
686                         
687                         default:
688                                 break;
689                         }
690                 }
691
692                 private void OnFirstClickTimer (Object source, ElapsedEventArgs e)\r
693                 {                                               \r
694                         Point pnt;\r
695                         pnt = PointToClient (MousePosition);                    \r
696 \r
697                         if (thumb_area.Contains (pnt))  {\r
698                                 if (orientation == Orientation.Horizontal) {\r
699                                         if (pnt.X > thumb_pos.X + thumb_pos.Width)
700                                                 LargeIncrement ();
701
702                                         if (pnt.X < thumb_pos.X)
703                                                 LargeDecrement ();                                              \r
704                                 }\r
705                                 else                            {\r
706                                         if (pnt.Y > thumb_pos.Y + thumb_pos.Height)
707                                                 LargeIncrement ();
708
709                                         if (pnt.Y < thumb_pos.Y)
710                                                 LargeDecrement ();
711                                 }
712
713                                 Refresh ();
714 \r
715                         }                       \r
716                 }                                       
717
718                 protected override void SetBoundsCore (int x, int y,int width, int height, BoundsSpecified specified)
719                 {
720                         base.SetBoundsCore (x, y,width, height, specified);
721                 }
722
723                 
724                 #endregion // Private Methods
725         }
726 }
727