* ImageList.cs: When the image stream is set pull all the images
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / UpDownBase.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 // 
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 // 
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2004 Novell, Inc.
21 //
22 // Authors:
23 //      Miguel de Icaza (miguel@novell.com).
24 //
25 //
26 /*
27
28 TODO:
29
30         - Actually paint the border, could not get it to work.
31
32         - Implement ContextMenu property.
33         
34         - Force the size of the entry: it can not be resized vertically
35           ever, the size is set by the size of the font
36
37         - Add defaults with [DefautValue ]
38
39         - Audit every place where ChangingText is used, whose meaning is
40           `Me, the library is changing the text'.  Kind of hack to deal with
41           loops on events.
42
43         - Hook up the keyboard events.
44
45 */
46 using System;
47 using System.Drawing;
48 using System.ComponentModel;
49 using System.Runtime.InteropServices;
50
51 namespace System.Windows.Forms {
52         public abstract class UpDownBase : ContainerControl {
53
54                 internal class Spinner : Control, IDisposable {
55                         UpDownBase updownbase;
56                         Rectangle up, down, pressed;
57                         Timer timer;
58                         bool up_pressed, down_pressed;
59                         bool captured, mouse_in;
60
61                         //
62                         // Interval values
63                         //
64                         const int StartInterval = 1000;
65                         const int RepeatInterval = 400;
66                         const int ChangeInterval = 75;
67                         const int MinimumInterval = 100;
68                         
69                         internal Spinner (UpDownBase updownbase)
70                         {
71                                 this.updownbase = updownbase;
72                         }
73
74                         protected override void OnPaint (PaintEventArgs args)
75                         {
76                                 Draw (args.ClipRectangle);
77                                 args.Graphics.DrawImage (ImageBuffer, 0, 0);
78                         }
79
80                         protected override void OnLayout (LayoutEventArgs args)
81                         {
82                                 base.OnLayout (args);
83                                 Rectangle bounds = Bounds;
84
85                                 up = new Rectangle (0, 0, bounds.Width, bounds.Height/2);
86                                 down = new Rectangle (0, bounds.Height/2, bounds.Width, bounds.Height/2);
87                         }
88
89                         protected override void OnMouseDown (MouseEventArgs args)
90                         {
91                                 base.OnMouseDown (args);
92
93                                 if (args.Button != MouseButtons.Left)
94                                         return;
95
96                                 if (up.Contains (args.X, args.Y)){
97                                         up_pressed = true;
98                                         pressed = up;
99                                 } else if (down.Contains (args.X, args.Y)){
100                                         down_pressed = true;
101                                         pressed = down;
102                                 } else
103                                         return;
104
105                                 Click ();
106                                 Invalidate (pressed);
107                                 Capture = true;
108                                 InitTimer ();
109                                 
110                                 mouse_in = down_pressed | up_pressed;
111                         }
112
113                         protected override void OnMouseUp (MouseEventArgs args)
114                         {
115                                 if (Capture){
116                                         if (up_pressed){
117                                                 up_pressed = false;
118                                                 Invalidate (up);
119                                         }
120                                         if (down_pressed){
121                                                 down_pressed = false;
122                                                 Invalidate (down);
123                                         }
124                                 }
125                                 Capture = false;
126                                 timer.Enabled = false;
127                         }
128
129                         //
130                         // Sets up the auto-repeat timer, we give a one second
131                         // delay, and then we use the keyboard settings for auto-repeat.
132                         //
133                         void InitTimer ()
134                         {
135                                 if (timer != null){
136                                         timer.Interval = StartInterval;
137                                         timer.Enabled = true;
138                                         return;
139                                 }
140                                 
141                                 timer = new Timer ();
142                                 int kd = SystemInformation.KeyboardDelay;
143                                 kd = kd < 0 ? 0 : (kd > 4 ? 4 : kd);
144                                 timer.Interval = StartInterval;
145                                 timer.Tick += new EventHandler (ClockTick);
146                                 timer.Enabled = true;
147                         }
148
149                         void ClockTick (object o, EventArgs a)
150                         {
151                                 if (timer == null)
152                                         throw new Exception ("The timer that owns this callback is null!");
153                                 
154                                 int interval = timer.Interval;
155
156                                 if (interval == StartInterval)
157                                         interval = RepeatInterval;
158                                 else
159                                         interval -= ChangeInterval;
160
161                                 if (interval < MinimumInterval)
162                                         interval = MinimumInterval;
163                                 timer.Interval = interval;
164
165                                 Click ();
166                         }
167
168                         void Click ()
169                         {
170                                 if (up_pressed){
171                                         updownbase.UpButton ();
172                                 }
173                                 if (down_pressed)
174                                         updownbase.DownButton ();
175                         }
176
177                         protected override void OnMouseMove (MouseEventArgs args)
178                         {
179                                 base.OnMouseMove (args);
180                                 if (Capture){
181                                         bool old = mouse_in;
182
183                                         if (pressed.Contains (args.X, args.Y)){
184                                                 if (timer == null)
185                                                         InitTimer ();
186                                                 mouse_in = true;
187                                         } else {
188                                                 if (timer != null){
189                                                         timer.Enabled = false;
190                                                         timer.Dispose ();
191                                                         timer = null;
192                                                 }
193                                                 mouse_in = false;
194                                         }
195                                         if (mouse_in ^ old){
196                                                 Console.WriteLine ("STATE CHANGE");
197                                                 if (mouse_in)
198                                                         Click ();
199                                                 Invalidate (pressed);
200                                         }
201                                 }
202                         }
203                         
204                         void DrawUp ()
205                         {
206                                 ButtonState bs;
207
208                                 bs = mouse_in && up_pressed ? ButtonState.Pushed : ButtonState.Normal;
209                                 ThemeEngine.Current.CPDrawScrollButton (DeviceContext, up, ScrollButton.Up, bs);
210                         }
211                         
212                         void DrawDown ()
213                         {
214                                 ButtonState bs;
215
216                                 bs = mouse_in && down_pressed ? ButtonState.Pushed : ButtonState.Normal;
217                                 ThemeEngine.Current.CPDrawScrollButton (DeviceContext, down, ScrollButton.Down, bs);
218                         }
219
220                         void Draw (Rectangle clip)
221                         {
222                                 if (clip.Contains (up))
223                                         DrawUp ();
224                                 if (clip.Contains (down))
225                                         DrawDown ();
226                         }
227
228                         void IDisposable.Dispose ()
229                         {
230                                 if (timer != null){
231                                         timer.Stop ();
232                                         timer.Dispose ();
233                                 }
234                                 timer = null;
235                                 base.Dispose ();
236                         }
237                 }
238
239                 BorderStyle border_style = BorderStyle.Fixed3D;
240                 int desired_height = 0;
241                 TextBox entry;
242                 Spinner spinner;
243                 int border;
244                 int scrollbar_button_size = ThemeEngine.Current.ScrollBarButtonSize;
245                 LeftRightAlignment updown_align = LeftRightAlignment.Right;
246                 bool changing_text = false;
247                 bool user_edit = false;
248                 bool intercept = true;
249                 
250                 public UpDownBase () : base ()
251                 {
252                         SuspendLayout ();
253
254                         entry = new TextBox ();
255                         entry.Font = Font;
256                         entry.Size = new Size (120, Font.Height);
257                         entry.LostFocus += new EventHandler (EntryOnLostFocus);
258                         entry.TextChanged += new EventHandler (OnTextBoxTextChanged);
259                         entry.KeyDown += new KeyEventHandler (OnTextBoxKeyDown);
260                         entry.KeyPress += new KeyPressEventHandler (OnTextBoxKeyPress);
261                         entry.LostFocus += new EventHandler (OnTextBoxLostFocus);
262                         entry.Resize += new EventHandler (OnTextBoxResize);
263                         entry.TextChanged += new EventHandler (OnTextBoxTextChanged);
264                         SetStyle (ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
265                         
266                         entry.ReadOnly = false;
267                         Controls.Add (entry);
268
269                         spinner = new Spinner (this);
270                         Controls.Add (spinner);
271
272                         ComputeSizeAndLocation ();
273                         ResumeLayout ();
274                 }
275
276                 void EntryOnLostFocus (object sender, EventArgs e)
277                 {
278                         OnLostFocus (e);
279                 }
280
281                 void ComputeSizeAndLocation ()
282                 {
283                         if (BorderStyle == BorderStyle.Fixed3D)
284                                 border = 2;
285                         else if (BorderStyle == BorderStyle.FixedSingle)
286                                 border = 1;
287                         else
288                                 border = 0;
289                                 
290                         Size = (entry.Size + spinner.Size + new Size (border, border));
291                         
292                         entry.Location = new Point (border, border);
293                 }
294                 
295
296 #region UpDownBase overwritten methods
297                 
298                 protected override void OnMouseWheel (MouseEventArgs args)
299                 {
300                         base.OnMouseWheel (args);
301
302                         if (args.Delta > 0)
303                                 UpButton ();
304                         else if (args.Delta < 0)
305                                 DownButton ();
306                 }
307
308                 protected virtual void OnChanged (object source, EventArgs e)
309                 {
310                         // Not clear, the docs state that this will raise the
311                         // Changed event, but that event is not listed anywhere.
312                 }
313
314                 protected override void OnFontChanged (EventArgs e)
315                 {
316                         base.OnFontChanged (e);
317
318                         entry.Font = Font;
319                         desired_height = entry.Height;
320                         Height = desired_height;
321                 }
322
323                 protected override void OnHandleCreated (EventArgs e)
324                 {
325                         base.OnHandleCreated (e);
326                         desired_height = entry.Height;
327                 }
328                                 
329                 protected override void OnLayout (LayoutEventArgs args)
330                 {
331                         base.OnLayout (args);
332
333                         Rectangle bounds = Bounds;
334                         int entry_width = bounds.Right - scrollbar_button_size - 1;
335
336                         entry.SetBounds (bounds.X, bounds.Y, entry_width, bounds.Height);
337                         spinner.SetBounds (entry_width + 1, bounds.Y, scrollbar_button_size, bounds.Height);
338                 }
339
340 #if NET_2_0
341                 protected override void OnPaint (PaintEventArgs e)
342                 {
343                         base.OnPaint (e);
344                         ThemeEngine.Current.CPDrawBorderStyle (e.Graphics, ClientRectangle, BorderStyle.Fixed3D);
345                         e.Graphics.DrawImage (ImageBuffer, 0, 0);
346                 }
347
348                 protected override void SetVisibleCore (bool state)
349                 {
350                         base.SetVisibleCore (state);
351                 }
352 #endif
353
354                 [EditorBrowsable(EditorBrowsableState.Advanced)]
355                 protected override void WndProc (ref Message m)
356                 {
357                         base.WndProc (ref m);
358                 }
359
360                 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
361                 {
362                         //
363                         // Force the size to be our height.
364                         //
365                         base.SetBoundsCore (x, y, width, desired_height, specified);
366                 }
367                 
368                 protected override void Dispose (bool disposing)
369                 {
370                         if (spinner != null){
371                                 if (disposing){
372                                         spinner.Dispose ();
373                                         entry.Dispose ();
374                                 }
375                         }
376                         spinner = null;
377                         entry = null;
378                         base.Dispose (true);
379                 }
380                 
381 #endregion
382                 
383 #region UpDownBase virtual methods
384                 //
385                 // These are hooked up to the various events from the Entry line that
386                 // we do not have yet, and implement the keyboard behavior (use a different
387                 // widget to test)
388                 //
389                 protected virtual void OnTextBoxKeyDown (object source, KeyEventArgs e)
390                 {
391                         OnKeyDown(e);
392                 }
393                 
394                 protected virtual void OnTextBoxKeyPress (object source, KeyPressEventArgs e)
395                 {
396                         OnKeyPress (e);
397                 }
398
399                 protected virtual void OnTextBoxLostFocus (object source, EventArgs e)
400                 {
401                         OnLostFocus (e);
402                 }
403
404                 protected virtual void OnTextBoxResize (object source, EventArgs e)
405                 {
406                         OnResize (e);
407                 }
408
409                 protected virtual void OnTextBoxTextChanged (object source, EventArgs e)
410                 {
411                         OnTextChanged (e);
412 #if false
413                         if (changing_text)
414                                 return;
415                         changing_text = false;
416                         user_edit = true;
417                         OnChanged (source, e);
418 #endif
419                 }
420
421 #endregion
422
423 #region UpDownBase Properties
424
425                 /* FIXME: Do not know what Autoscroll should do */
426                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
427                 [EditorBrowsable(EditorBrowsableState.Never)]
428                 [Browsable(false)]
429                 public virtual bool AutoScroll {
430                         get {
431                                 return base.AutoScroll;
432                         }
433
434                         set {
435                                 base.AutoScroll = value;
436                         }
437                 }
438
439                 /* FIXME: Do not know what AutoscrollMargin does */
440                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
441                 [EditorBrowsable(EditorBrowsableState.Never)]
442                 [Browsable(false)]
443                 public new Size AutoScrollMargin {
444                         get {
445                                 return base.AutoScrollMargin;
446                         }
447
448                         set {
449                                 base.AutoScrollMargin = value;
450                         }
451                 }
452
453                 /* FIXME: Do not know what AutoscrollMinSize does */
454                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
455                 [EditorBrowsable(EditorBrowsableState.Never)]
456                 [Browsable(false)]
457                 public new Size AutoScrollMinSize {
458                         get {
459                                 return base.AutoScrollMinSize;
460                         }
461
462                         set {
463                                 base.AutoScrollMinSize = value;
464                         }
465                 }
466
467                 public override Color BackColor {
468                         get {
469                                 return base.BackColor;
470                         }
471
472                         set {
473                                 entry.BackColor = value;
474                         }
475                 }
476
477                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
478                 [EditorBrowsable(EditorBrowsableState.Never)]
479                 [Browsable(false)]
480                 public override Image BackgroundImage {
481                         get {
482                                 return entry.BackgroundImage;
483                         }
484
485                         set {
486                                 entry.BackgroundImage = value;
487                         }
488                 }
489
490                 [DefaultValue(BorderStyle.Fixed3D)]
491                 [DispId(-504)]
492                 public BorderStyle BorderStyle {
493                         get {
494                                 return border_style;
495                         }
496
497                         set {
498                                 border_style = value;
499
500                                 SuspendLayout ();
501                                 ComputeSizeAndLocation ();
502                                 ResumeLayout ();
503                         }
504                 }
505
506                 //
507                 // Used internally to flag when the derivative classes are changing
508                 // the Text property as opposed to the user
509                 //
510                 protected bool ChangingText {
511                         get {
512                                 return changing_text;
513                         }
514
515                         set {
516                                 changing_text = value;
517                         }
518                 }
519
520                 // TODO: What should this do?
521                 public override ContextMenu ContextMenu {
522                         get {
523                                 return null;
524                         }
525
526                         set {
527                                 /* */
528                         }
529                 }
530
531                 protected override CreateParams CreateParams {
532                         get {
533                                 return base.CreateParams;
534                         }
535                 }
536
537                 protected bool UserEdit {
538                         get {
539                                 return user_edit;
540                         }
541
542                         set {
543                                 user_edit = value;
544                         }
545                 }
546
547                 public override string Text {
548                         get {
549                                 if (entry == null)
550                                         return String.Empty;
551                                 
552                                 return entry.Text;
553                         }
554
555                         set {
556                                 //
557                                 // The documentation is conflicts with itself, we can
558                                 // not call UpdateEditText, as this will call Text to
559                                 // set the value.
560                                 //
561                                 entry.Text = value;
562                                 
563                                 if (UserEdit)
564                                         ValidateEditText ();
565                                 
566                                 if (ChangingText)
567                                         ChangingText = false;
568                         }
569                 }
570
571                 public LeftRightAlignment UpDownAlign
572                 {
573                         get {
574                                 return updown_align;
575                         }
576                         
577                         set {
578                                 updown_align = value;
579                         }
580                 }
581
582                 [DefaultValue(false)]
583                 public bool ReadOnly {
584                         get {
585                                 return entry.ReadOnly;
586                         }
587
588                         set {
589                                 entry.ReadOnly = value;
590                         }
591                 }
592
593                 public override bool Focused {
594                         get {
595                                 return entry.Focused;
596                         }
597                 }
598
599                 public override Color ForeColor {
600                         get {
601                                 return base.ForeColor;
602                         }
603
604                         set {
605                                 base.ForeColor = value;
606                                 entry.ForeColor = value;
607                         }
608                         
609                 }
610
611                 public bool InterceptArrowKeys {
612                         get {
613                                 return intercept;
614                         }
615
616                         set {
617                                 intercept = value;
618                         }
619                 }
620 #endregion
621                 
622 #region UpDownBase standard methods
623                 public void Select (int start, int length)
624                 {
625                         entry.Select (start, length);
626                 }
627
628                 protected virtual void ValidateEditText ()
629                 {
630                         // 
631                 }
632 #endregion
633
634 #region Events
635                 //
636                 // All these events are just a proxy to the base class,
637                 // we must overwrite them for API compatibility
638                 //
639                 
640                 public new event EventHandler MouseEnter {
641                         add {
642                                 base.MouseEnter += value;
643                         }
644
645                         remove {
646                                 base.MouseEnter -= value;
647                         }
648                 }
649
650                 public new event EventHandler MouseHover {
651                         add {
652                                 base.MouseHover += value;
653                         }
654
655                         remove {
656                                 base.MouseHover -= value;
657                         }
658                 }
659
660                 public new event EventHandler MouseLeave {
661                         add {
662                                 base.MouseLeave += value;
663                         }
664
665                         remove {
666                                 base.MouseLeave -= value;
667                         }
668                 }
669
670                 public new event EventHandler BackgroundImageChanged {
671                         add {
672                                 base.BackgroundImageChanged += value;
673                         }
674
675                         remove {
676                                 base.BackgroundImageChanged -= value;
677                         }
678                 }
679 #endregion
680                 
681 #region Abstract methods
682                 public abstract void DownButton ();
683                 public abstract void UpButton ();
684                 public abstract void UpdateEditText ();
685 #endregion
686         }
687 }