Moved ProviderCollectionTest.cs from System assembly to System.Configuration.
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ToolTip.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. (http://www.novell.com)
21 //
22 // Authors:
23 //      Peter Bartok    pbartok@novell.com
24 //
25 //
26
27 using System.Collections;
28 using System.ComponentModel;
29 using System.Drawing;
30 using System.Drawing.Text;
31
32 namespace System.Windows.Forms {
33 #if NET_2_0
34         [DefaultEvent ("Popup")]
35 #endif
36         [ProvideProperty ("ToolTip", typeof(System.Windows.Forms.Control))]
37         [ToolboxItemFilter("System.Windows.Forms", ToolboxItemFilterType.Allow)]
38         public
39 #if !NET_2_0
40         sealed
41 #endif
42         class ToolTip : System.ComponentModel.Component, System.ComponentModel.IExtenderProvider {
43                 #region Local variables
44                 internal bool           is_active;
45                 internal int            automatic_delay;
46                 internal int            autopop_delay;
47                 internal int            initial_delay;
48                 internal int            re_show_delay;
49                 internal bool           show_always;
50
51                 internal Color          back_color;
52                 internal Color          fore_color;
53                 
54                 internal ToolTipWindow  tooltip_window;                 // The actual tooltip window
55                 internal Hashtable      tooltip_strings;                // List of strings for each control, indexed by control
56                 internal ArrayList      controls;
57                 internal Control        active_control;                 // Control for which the tooltip is currently displayed
58                 internal Control        last_control;                   // last control the mouse was in
59                 internal Timer          timer;                          // Used for the various intervals
60                 private Form            hooked_form;
61
62 #if NET_2_0
63                 private bool isBalloon;
64                 private bool owner_draw;
65                 private bool stripAmpersands;
66                 private ToolTipIcon tool_tip_icon;
67                 private string tool_tip_title;
68                 private bool useAnimation;
69                 private bool useFading;
70                 private object tag;
71 #endif
72
73                 #endregion      // Local variables
74
75                 #region ToolTipWindow Class
76                 internal class ToolTipWindow : Control {
77                         #region ToolTipWindow Class Local Variables
78                         private Control associated_control;
79                         #endregion      // ToolTipWindow Class Local Variables
80                         
81                         #region ToolTipWindow Class Constructor
82                         internal ToolTipWindow() {
83                                 Visible = false;
84                                 Size = new Size(100, 20);
85                                 ForeColor = ThemeEngine.Current.ColorInfoText;
86                                 BackColor = ThemeEngine.Current.ColorInfo;
87
88                                 VisibleChanged += new EventHandler(ToolTipWindow_VisibleChanged);
89
90 #if NET_2_0
91                                 // UIA Framework: Used to generate UnPopup
92                                 VisibleChanged += new EventHandler (OnUIAToolTip_VisibleChanged);
93 #endif
94
95                                 SetStyle (ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
96                                 SetStyle (ControlStyles.ResizeRedraw, true);
97                                 if (ThemeEngine.Current.ToolTipTransparentBackground) {
98                                         SetStyle (ControlStyles.SupportsTransparentBackColor, true);
99                                         BackColor = Color.Transparent;
100                                 } else
101                                         SetStyle (ControlStyles.Opaque, true);
102                         }
103
104                         #endregion      // ToolTipWindow Class Constructor
105
106                         #region ToolTipWindow Class Protected Instance Methods
107                         protected override void OnCreateControl() {
108                                 base.OnCreateControl ();
109                                 XplatUI.SetTopmost(this.window.Handle, true);
110                         }
111
112                         protected override CreateParams CreateParams {
113                                 get {
114                                         CreateParams cp;
115
116                                         cp = base.CreateParams;
117
118                                         cp.Style = (int)WindowStyles.WS_POPUP;
119                                         cp.Style |= (int)WindowStyles.WS_CLIPSIBLINGS;
120
121                                         cp.ExStyle = (int)(WindowExStyles.WS_EX_TOOLWINDOW | WindowExStyles.WS_EX_TOPMOST);
122
123                                         return cp;
124                                 }
125                         }
126
127                         protected override void OnPaint(PaintEventArgs pevent) {
128                                 // We don't do double-buffering on purpose:
129                                 // 1) we'd have to meddle with is_visible, it destroys the buffers if !visible
130                                 // 2) We don't draw much, no need to double buffer
131                                 base.OnPaint(pevent);
132
133                                 OnDraw (new DrawToolTipEventArgs (pevent.Graphics, associated_control, associated_control, ClientRectangle, this.Text, this.BackColor, this.ForeColor, this.Font));
134                         }
135
136                         protected override void OnTextChanged (EventArgs args)
137                         {
138                                 Invalidate ();
139                                 base.OnTextChanged (args); 
140                         }
141
142                         protected override void WndProc(ref Message m) {
143                                 if (m.Msg == (int)Msg.WM_SETFOCUS) {
144                                         if (m.WParam != IntPtr.Zero) {
145                                                 XplatUI.SetFocus(m.WParam);
146                                         }
147                                 }
148                                 base.WndProc (ref m);
149                         }
150
151
152                         #endregion      // ToolTipWindow Class Protected Instance Methods
153
154                         #region ToolTipWindow Class Private Methods
155                         internal virtual void OnDraw (DrawToolTipEventArgs e)
156                         {
157                                 DrawToolTipEventHandler eh = (DrawToolTipEventHandler)(Events[DrawEvent]);
158                                 if (eh != null)
159                                         eh (this, e);
160                                 else
161                                         ThemeEngine.Current.DrawToolTip (e.Graphics, e.Bounds, this);
162                         }
163                         
164                         internal virtual void OnPopup (PopupEventArgs e)
165                         {
166                                 PopupEventHandler eh = (PopupEventHandler)(Events[PopupEvent]);
167                                 if (eh != null)
168                                         eh (this, e);
169                                 else
170                                         e.ToolTipSize = ThemeEngine.Current.ToolTipSize (this, Text);
171                         }
172
173                         private void ToolTipWindow_VisibleChanged(object sender, EventArgs e) {
174                                 Control control = (Control)sender;
175
176                                 if (control.is_visible) {
177                                         XplatUI.SetTopmost(control.window.Handle, true);
178                                 } else {
179                                         XplatUI.SetTopmost(control.window.Handle, false);
180                                 }
181                         }
182 #if NET_2_0
183
184                         // UIA Framework
185                         private void OnUIAToolTip_VisibleChanged (object sender, EventArgs e)
186                         {
187                                 if (Visible == false) 
188                                         OnUnPopup (new PopupEventArgs (associated_control, associated_control, false, Size.Empty));
189                         }
190
191                         private void OnUnPopup (PopupEventArgs e)
192                         {
193                                 PopupEventHandler eh = (PopupEventHandler) (Events [UnPopupEvent]);
194                                 if (eh != null)
195                                         eh (this, e);
196                         }
197
198 #endif
199
200                         #endregion      // ToolTipWindow Class Protected Instance Methods
201
202                         #region Internal Properties
203                         internal override bool ActivateOnShow { get { return false; } }
204                         #endregion
205
206                         // This Present is used when we are using the expicit Show methods for 2.0.
207                         // It will not reposition the window.
208                         public void PresentModal (Control control, string text)
209                         {
210                                 if (IsDisposed)
211                                         return;
212
213                                 Size display_size;
214                                 XplatUI.GetDisplaySize (out display_size);
215
216                                 associated_control = control;
217
218                                 Text = text;
219
220                                 PopupEventArgs pea = new PopupEventArgs (control, control, false, Size.Empty);
221                                 OnPopup (pea);
222
223                                 if (pea.Cancel)
224                                         return;
225
226                                 Size = pea.ToolTipSize;
227
228                                 Visible = true;
229                         }
230                 
231                         public void Present (Control control, string text)
232                         {
233                                 if (IsDisposed)
234                                         return;
235
236                                 Size display_size;
237                                 XplatUI.GetDisplaySize (out display_size);
238
239 #if NET_2_0
240                                 associated_control = control;
241 #endif
242
243                                 Text = text;
244
245                                 PopupEventArgs pea = new PopupEventArgs (control, control, false, Size.Empty);
246                                 OnPopup (pea);
247                                 
248                                 if (pea.Cancel)
249                                         return;
250                                         
251                                 Size size = pea.ToolTipSize;
252
253                                 Width = size.Width;
254                                 Height = size.Height;
255
256                                 int cursor_w, cursor_h, hot_x, hot_y;
257                                 XplatUI.GetCursorInfo (control.Cursor.Handle, out cursor_w, out cursor_h, out hot_x, out hot_y);
258                                 Point loc = Control.MousePosition;
259                                 loc.Y += (cursor_h - hot_y);
260
261                                 if ((loc.X + Width) > display_size.Width)
262                                         loc.X = display_size.Width - Width;
263
264                                 if ((loc.Y + Height) > display_size.Height)
265                                         loc.Y = Control.MousePosition.Y - Height - hot_y;
266                                 
267                                 Location = loc;
268                                 Visible = true;
269                         }
270
271
272                         #region Internal Events
273                         static object DrawEvent = new object ();
274                         static object PopupEvent = new object ();
275         
276 #if NET_2_0
277                         // UIA Framework
278                         static object UnPopupEvent = new object ();
279 #endif
280
281                         public event DrawToolTipEventHandler Draw {
282                                 add { Events.AddHandler (DrawEvent, value); }
283                                 remove { Events.RemoveHandler (DrawEvent, value); }
284                         }
285
286                         public event PopupEventHandler Popup {
287                                 add { Events.AddHandler (PopupEvent, value); }
288                                 remove { Events.RemoveHandler (PopupEvent, value); }
289                         }
290
291 #if NET_2_0
292                         public event PopupEventHandler UnPopup {
293                                 add { Events.AddHandler (UnPopupEvent, value); }
294                                 remove { Events.RemoveHandler (UnPopupEvent, value); }
295                         }
296 #endif
297                         #endregion
298                 }
299                 #endregion      // ToolTipWindow Class
300
301                 #region Public Constructors & Destructors
302                 public ToolTip() {
303
304                         // Defaults from MS
305                         is_active = true;
306                         automatic_delay = 500;
307                         autopop_delay = 5000;
308                         initial_delay = 500;
309                         re_show_delay = 100;
310                         show_always = false;
311                         back_color = SystemColors.Info;
312                         fore_color = SystemColors.InfoText;
313                         
314 #if NET_2_0
315                         isBalloon = false;
316                         stripAmpersands = false;
317                         useAnimation = true;
318                         useFading = true;
319 #endif
320                         tooltip_strings = new Hashtable(5);
321                         controls = new ArrayList(5);
322
323                         tooltip_window = new ToolTipWindow();
324                         tooltip_window.MouseLeave += new EventHandler(control_MouseLeave);
325                         tooltip_window.Draw += new DrawToolTipEventHandler (tooltip_window_Draw);
326                         tooltip_window.Popup += new PopupEventHandler (tooltip_window_Popup);
327
328 #if NET_2_0
329                         // UIA Framework: Static event handlers
330                         tooltip_window.UnPopup += delegate (object sender, PopupEventArgs args) {
331                                 OnUnPopup (args);
332                         };
333                         UnPopup += new PopupEventHandler (OnUIAUnPopup);
334 #endif
335
336                         timer = new Timer();
337                         timer.Enabled = false;
338                         timer.Tick +=new EventHandler(timer_Tick);
339
340                 }
341
342
343                 #region UIA Framework: Events, Delegates and Methods
344 #if NET_2_0
345                 // NOTE: 
346                 //      We are using Reflection to add/remove internal events.
347                 //      Class ToolTipListener uses the events.
348                 //
349                 //      - UIAUnPopup. Event used to generate ChildRemoved in ToolTip
350                 //      - UIAToolTipHookUp. Event used to keep track of associated controls
351                 //      - UIAToolTipUnhookUp. Event used to remove track of associated controls
352                 static object UnPopupEvent = new object ();
353
354                 internal event PopupEventHandler UnPopup {
355                         add { Events.AddHandler (UnPopupEvent, value); }
356                         remove { Events.RemoveHandler (UnPopupEvent, value); }
357                 }
358
359                 internal static event PopupEventHandler UIAUnPopup;
360                 internal static event ControlEventHandler UIAToolTipHookUp;
361                 internal static event ControlEventHandler UIAToolTipUnhookUp;
362
363                 internal static void OnUIAUnPopup (object sender, PopupEventArgs args)
364                 {
365                         if (UIAUnPopup != null)
366                                 UIAUnPopup (sender, args);
367                 }
368
369                 internal static void OnUIAToolTipHookUp (object sender, ControlEventArgs args)
370                 {
371                         if (UIAToolTipHookUp != null)
372                                 UIAToolTipHookUp (sender, args);
373                 }
374
375                 internal static void OnUIAToolTipUnhookUp (object sender, ControlEventArgs args)
376                 {
377                         if (UIAToolTipUnhookUp != null)
378                                 UIAToolTipUnhookUp (sender, args);
379                 }
380
381 #endif 
382                 #endregion
383
384                 public ToolTip(System.ComponentModel.IContainer cont) : this() {
385                         cont.Add (this);
386                 }
387
388                 ~ToolTip() {
389                 }
390                 #endregion      // Public Constructors & Destructors
391
392                 #region Public Instance Properties
393                 [DefaultValue (true)]
394                 public bool Active {
395                         get {
396                                 return is_active;
397                         }
398
399                         set {
400                                 if (is_active != value) {
401                                         is_active = value;
402
403                                         if (tooltip_window.Visible) {
404                                                 tooltip_window.Visible = false;
405                                                 active_control = null;
406                                         }
407                                 }
408                         }
409                 }
410
411                 [DefaultValue (500)]
412                 [RefreshProperties (RefreshProperties.All)]
413                 public int AutomaticDelay {
414                         get {
415                                 return automatic_delay;
416                         }
417
418                         set {
419                                 if (automatic_delay != value) {
420                                         automatic_delay = value;
421                                         autopop_delay = automatic_delay * 10;
422                                         initial_delay = automatic_delay;
423                                         re_show_delay = automatic_delay / 5;
424                                 }
425                         }
426                 }
427
428                 [RefreshProperties (RefreshProperties.All)]
429                 public int AutoPopDelay {
430                         get {
431                                 return autopop_delay;
432                         }
433
434                         set {
435                                 if (autopop_delay != value) {
436                                         autopop_delay = value;
437                                 }
438                         }
439                 }
440
441 #if NET_2_0
442                 [DefaultValue ("Color [Info]")]
443                 public Color BackColor {
444                         get { return this.back_color; }
445                         set { this.back_color = value; tooltip_window.BackColor = value; }
446                 }
447
448                 [DefaultValue ("Color [InfoText]")]
449                 public Color ForeColor
450                 {
451                         get { return this.fore_color; }
452                         set { this.fore_color = value; tooltip_window.ForeColor = value; }
453                 }
454 #endif
455
456                 [RefreshProperties (RefreshProperties.All)]
457                 public int InitialDelay {
458                         get {
459                                 return initial_delay;
460                         }
461
462                         set {
463                                 if (initial_delay != value) {
464                                         initial_delay = value;
465                                 }
466                         }
467                 }
468
469 #if NET_2_0
470                 [DefaultValue (false)]
471                 public bool OwnerDraw {
472                         get { return this.owner_draw; }
473                         set { this.owner_draw = value; }
474                 }
475 #endif
476
477                 [RefreshProperties (RefreshProperties.All)]
478                 public int ReshowDelay {
479                         get {
480                                 return re_show_delay;
481                         }
482
483                         set {
484                                 if (re_show_delay != value) {
485                                         re_show_delay = value;
486                                 }
487                         }
488                 }
489
490                 [DefaultValue (false)]
491                 public bool ShowAlways {
492                         get {
493                                 return show_always;
494                         }
495
496                         set {
497                                 if (show_always != value) {
498                                         show_always = value;
499                                 }
500                         }
501                 }
502
503
504 #if NET_2_0
505                 [DefaultValue (false)]
506                 public bool IsBalloon {
507                         get { return isBalloon; }
508                         set { isBalloon = value; }
509                 }
510
511                 [Browsable (true)]
512                 [DefaultValue (false)]
513                 public bool StripAmpersands {
514                         get { return stripAmpersands; }
515                         set { stripAmpersands = value; }
516                 }
517
518                 [Localizable (false)]
519                 [Bindable (true)]
520                 [TypeConverter (typeof (StringConverter))]
521                 [DefaultValue (null)]
522                 public object Tag {
523                         get { return tag; }
524                         set { tag = value; }
525                 }
526
527                 [DefaultValue (ToolTipIcon.None)]
528                 public ToolTipIcon ToolTipIcon {
529                         get { return this.tool_tip_icon; }
530                         set { this.tool_tip_icon = value; }
531                 }
532                 
533                 [DefaultValue ("")]
534                 public string ToolTipTitle {
535                         get { return this.tool_tip_title; }
536                         set { this.tool_tip_title = value; }
537                 }
538                 
539                 [Browsable (true)]
540                 [DefaultValue (true)]
541                 public bool UseAnimation {
542                         get { return useAnimation; }
543                         set { useAnimation = value; }
544                 }
545
546                 [Browsable (true)]
547                 [DefaultValue (true)]
548                 public bool UseFading {
549                         get { return useFading; }
550                         set { useFading = value; }
551                 }
552 #endif
553
554                 #endregion      // Public Instance Properties
555
556                 #region Protected Properties
557 #if NET_2_0
558                 protected virtual CreateParams CreateParams
559                 {
560                         get
561                         {
562                                 CreateParams cp = new CreateParams ();
563
564                                 cp.Style = 2;
565
566                                 return cp;
567                         }
568                 }
569 #endif
570                 #endregion
571
572                 #region Public Instance Methods
573                 public bool CanExtend(object target) {
574                         return false;
575                 }
576
577 #if NET_2_0
578                 [Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
579                          "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
580 #endif
581                 [Localizable (true)]
582                 [DefaultValue ("")]
583                 public string GetToolTip (Control control)
584                 {
585                         string tooltip = (string)tooltip_strings[control];
586                         if (tooltip == null)
587                                 return "";
588                         return tooltip;
589                 }
590
591                 public void RemoveAll() {
592                         tooltip_strings.Clear();
593 #if NET_2_0
594                         //UIA Framework: ToolTip isn't associated anymore
595                         foreach (Control control in controls)
596                                 OnUIAToolTipUnhookUp (this, new ControlEventArgs (control));
597 #endif
598
599                         controls.Clear();
600                 }
601
602                 public void SetToolTip(Control control, string caption) {
603 #if NET_2_0
604                         // UIA Framework
605                         OnUIAToolTipHookUp (this, new ControlEventArgs (control));
606 #endif
607                         tooltip_strings[control] = caption;
608
609                         // no need for duplicates
610                         if (!controls.Contains(control)) {
611                                 control.MouseEnter += new EventHandler(control_MouseEnter);
612                                 control.MouseMove += new MouseEventHandler(control_MouseMove);
613                                 control.MouseLeave += new EventHandler(control_MouseLeave);
614                                 control.MouseDown += new MouseEventHandler (control_MouseDown);
615                                 controls.Add(control);
616                         }
617                         
618                         // if SetToolTip is called from a control and the mouse is currently over that control,
619                         // make sure that tooltip_window.Text gets updated if it's being shown,
620                         // or show the tooltip for it if is not
621                         if (active_control == control && caption != null && state == TipState.Show) {
622                                 Size size = ThemeEngine.Current.ToolTipSize(tooltip_window, caption);
623                                 tooltip_window.Width = size.Width;
624                                 tooltip_window.Height = size.Height;
625                                 tooltip_window.Text = caption;
626                                 timer.Stop ();
627                                 timer.Start ();
628                         } else if (control.IsHandleCreated && MouseInControl (control, false))
629                                 ShowTooltip (control);
630                 }
631
632                 public override string ToString() {
633                         return base.ToString() + " InitialDelay: " + initial_delay + ", ShowAlways: " + show_always;
634                 }
635
636 #if NET_2_0
637                 public void Show (string text, IWin32Window window)
638                 {
639                         Show (text, window, 0);
640                 }
641
642                 public void Show (string text, IWin32Window window, int duration)
643                 {
644                         if (window == null)
645                                 throw new ArgumentNullException ("window");
646                         if (duration < 0)
647                                 throw new ArgumentOutOfRangeException ("duration", "duration cannot be less than zero");
648
649                         if (!Active)
650                                 return;
651                                 
652                         timer.Stop ();
653                         
654                         Control c = (Control)window;
655
656                         XplatUI.SetOwner (tooltip_window.Handle, c.TopLevelControl.Handle);
657                         
658                         // If the mouse is in the requested window, use that position
659                         // Else, center in the requested window
660                         if (c.ClientRectangle.Contains (c.PointToClient (Control.MousePosition))) {
661                                 tooltip_window.Location = Control.MousePosition;
662                                 tooltip_strings[c] = text;
663                                 HookupControlEvents (c);
664                         }
665                         else
666                                 tooltip_window.Location = c.PointToScreen (new Point (c.Width / 2, c.Height / 2));
667                         
668                         // We need to hide our tooltip if the form loses focus, is closed, or is minimized
669                         HookupFormEvents ((Form)c.TopLevelControl);
670                         
671                         tooltip_window.PresentModal ((Control)window, text);
672                         
673                         state = TipState.Show;
674                         
675                         if (duration > 0) {
676                                 timer.Interval = duration;
677                                 timer.Start ();
678                         }
679                 }
680                 
681                 public void Show (string text, IWin32Window window, Point point)
682                 {
683                         Show (text, window, point, 0);
684                 }
685
686                 public void Show (string text, IWin32Window window, int x, int y)
687                 {
688                         Show (text, window, new Point (x, y), 0);
689                 }
690                 
691                 public void Show (string text, IWin32Window window, Point point, int duration)
692                 {
693                         if (window == null)
694                                 throw new ArgumentNullException ("window");
695                         if (duration < 0)
696                                 throw new ArgumentOutOfRangeException ("duration", "duration cannot be less than zero");
697
698                         if (!Active)
699                                 return;
700
701                         timer.Stop ();
702
703                         Control c = (Control)window;
704                         
705                         Point display_point = c.PointToScreen (Point.Empty);
706                         display_point.X += point.X;
707                         display_point.Y += point.Y;
708
709                         XplatUI.SetOwner (tooltip_window.Handle, c.TopLevelControl.Handle);
710
711                         // We need to hide our tooltip if the form loses focus, is closed, or is minimized
712                         HookupFormEvents ((Form)c.TopLevelControl);
713
714                         tooltip_window.Location = display_point;
715                         tooltip_window.PresentModal ((Control)window, text);
716
717                         state = TipState.Show;
718                         
719                         if (duration > 0) {
720                                 timer.Interval = duration;
721                                 timer.Start ();
722                         }
723                 }
724                 
725                 public void Show (string text, IWin32Window window, int x, int y, int duration)
726                 {
727                         Show (text, window, new Point (x, y), duration);
728                 }
729                 
730                 public
731 #else
732                 internal
733 #endif
734                 void Hide (IWin32Window win)
735                 {
736                         timer.Stop ();
737                         state = TipState.Initial;
738
739                         UnhookFormEvents ();
740                         tooltip_window.Visible = false;
741                 }
742                 #endregion      // Public Instance Methods
743
744                 #region Protected Instance Methods
745                 protected override void Dispose(bool disposing) {
746                         if (disposing) {
747                                 // Mop up the mess; or should we wait for the GC to kick in?
748                                 timer.Stop();
749                                 timer.Dispose();
750
751                                 // Not sure if we should clean up tooltip_window
752                                 tooltip_window.Dispose();
753
754                                 tooltip_strings.Clear();
755                                 
756 #if NET_2_0
757                                 //UIA Framework: ToolTip isn't associated anymore
758                                 foreach (Control control in controls)
759                                         OnUIAToolTipUnhookUp (this, new ControlEventArgs (control));
760 #endif
761                                 controls.Clear();
762                         }
763                 }
764
765 #if NET_2_0
766                 protected void StopTimer ()
767                 {
768                         timer.Stop ();
769                 }
770 #endif
771                 #endregion      // Protected Instance Methods
772
773                 enum TipState {
774                         Initial,
775                         Show,
776                         Down
777                 }
778
779                 TipState state = TipState.Initial;
780
781                 #region Private Methods
782
783 #if NET_2_0
784                 private void HookupFormEvents (Form form)
785                 {
786                         hooked_form = form;
787
788                         form.Deactivate += new EventHandler (Form_Deactivate);
789                         form.Closed += new EventHandler (Form_Closed);
790                         form.Resize += new EventHandler (Form_Resize);
791                 }
792
793                 private void HookupControlEvents (Control control)
794                 {
795                         if (!controls.Contains (control)) {
796                                 control.MouseEnter += new EventHandler (control_MouseEnter);
797                                 control.MouseMove += new MouseEventHandler (control_MouseMove);
798                                 control.MouseLeave += new EventHandler (control_MouseLeave);
799                                 control.MouseDown += new MouseEventHandler (control_MouseDown);
800                                 controls.Add (control);
801                         }
802                 }
803
804                 private void UnhookControlEvents (Control control)
805                 {
806                         control.MouseEnter -= new EventHandler (control_MouseEnter);
807                         control.MouseMove -= new MouseEventHandler (control_MouseMove);
808                         control.MouseLeave -= new EventHandler (control_MouseLeave);
809                         control.MouseDown -= new MouseEventHandler (control_MouseDown);
810                 }
811 #endif
812                 private void UnhookFormEvents ()
813                 {
814                         if (hooked_form == null)
815                                 return;
816
817                         hooked_form.Deactivate -= new EventHandler (Form_Deactivate);
818                         hooked_form.Closed -= new EventHandler (Form_Closed);
819                         hooked_form.Resize -= new EventHandler (Form_Resize);
820
821                         hooked_form = null;
822                 }
823
824
825                 private void Form_Resize (object sender, EventArgs e)
826                 {
827                         Form f = (Form)sender;
828
829                         if (f.WindowState == FormWindowState.Minimized)
830                                 tooltip_window.Visible = false;
831                 }
832
833                 private void Form_Closed (object sender, EventArgs e)
834                 {
835                         tooltip_window.Visible = false;
836                 }
837
838                 private void Form_Deactivate (object sender, EventArgs e)
839                 {
840                         tooltip_window.Visible = false;
841                 }
842
843                 internal void Present (Control control, string text)
844                 {
845                         tooltip_window.Present (control, text);
846                 }
847                 
848                 private void control_MouseEnter (object sender, EventArgs e) 
849                 {
850                         ShowTooltip (sender as Control);
851                 }
852
853                 private void ShowTooltip (Control control) 
854                 {
855                         last_control = control;
856
857                         // Whatever we're displaying right now, we don't want it anymore
858                         tooltip_window.Visible = false;
859                         timer.Stop();
860                         state = TipState.Initial;
861
862                         if (!is_active)
863                                 return;
864
865                         if (!show_always) {
866                                 IContainerControl cc = last_control.GetContainerControl ();
867                                 if ((cc == null) || (cc.ActiveControl == null)) {
868                                         return;
869                                 }
870                         }
871
872                         string text = (string)tooltip_strings[control];
873                         if (text != null && text.Length > 0) {
874                                 if (active_control == null) {
875                                         timer.Interval = initial_delay;
876                                 } else {
877                                         timer.Interval = Math.Max (re_show_delay, 1);
878                                 }
879
880                                 active_control = control;
881                                 timer.Start ();
882                         }
883                 }
884
885                 private void timer_Tick(object sender, EventArgs e) {
886                         timer.Stop();
887
888                         switch (state) {
889                         case TipState.Initial:
890                                 if (active_control == null)
891                                         return;
892                                 tooltip_window.Present (active_control, (string)tooltip_strings[active_control]);
893                                 state = TipState.Show;
894                                 timer.Interval = autopop_delay;
895                                 timer.Start();
896                                 break;
897
898                         case TipState.Show:
899                                 tooltip_window.Visible = false;
900                                 state = TipState.Down;
901                                 break;
902
903                         default:
904                                 throw new Exception ("Timer shouldn't be running in state: " + state);
905                         }
906                 }
907
908                 private void tooltip_window_Popup (object sender, PopupEventArgs e)
909                 {
910                         e.ToolTipSize = ThemeEngine.Current.ToolTipSize (tooltip_window, tooltip_window.Text);
911                         OnPopup (e);
912                 }
913
914                 private void tooltip_window_Draw (object sender, DrawToolTipEventArgs e)
915                 {
916 #if NET_2_0
917                         if (OwnerDraw)
918                                 OnDraw (e);
919                         else
920 #endif
921                                 ThemeEngine.Current.DrawToolTip (e.Graphics, e.Bounds, tooltip_window);
922                 }
923                 
924                 private bool MouseInControl (Control control, bool fuzzy) {
925                         Point   m;
926                         Point   c;
927                         Size    cw;
928
929                         if (control == null) {
930                                 return false;
931                         }
932
933                         m = Control.MousePosition;
934                         c = new Point(control.Bounds.X, control.Bounds.Y);
935                         if (control.Parent != null) {
936                                 c = control.Parent.PointToScreen(c);
937                         }
938                         cw = control.ClientSize;
939
940
941                         Rectangle rect = new Rectangle (c, cw);
942                         
943                         //
944                         // We won't get mouse move events on all platforms with the exact same
945                         // frequency, so cheat a bit.
946                         if (fuzzy)
947                                 rect.Inflate (2, 2);
948
949                         return rect.Contains (m);
950                 }
951
952                 private void control_MouseLeave(object sender, EventArgs e) 
953                 {
954                         timer.Stop ();
955
956                         active_control = null;
957                         tooltip_window.Visible = false;
958
959                         if (last_control == sender)
960                                 last_control = null;
961                 }
962
963
964                 void control_MouseDown (object sender, MouseEventArgs e)
965                 {
966                         timer.Stop();
967
968                         active_control = null;
969                         tooltip_window.Visible = false;
970                         
971                         if (last_control == sender)
972                                 last_control = null;
973                 }
974
975 #if NET_2_0
976 /*
977                 private void Hide (Control sender)
978                 {
979                         timer.Stop();
980
981                         if (!MouseInControl (tooltip_window, true) && !MouseInControl (active_control, true)) {
982                                 active_control = null;
983                                 tooltip_window.Visible = false;
984                         }
985                         
986                         if (last_control == sender)
987                                 last_control = null;
988                 }
989 */
990 #endif
991
992                 private void control_MouseMove(object sender, MouseEventArgs e) {
993                         if (state != TipState.Down) {
994                                 timer.Stop();
995                                 timer.Start();
996                         }
997                 }
998
999                 internal void OnDraw (DrawToolTipEventArgs e)
1000                 {
1001                         DrawToolTipEventHandler eh = (DrawToolTipEventHandler)(Events[DrawEvent]);
1002                         if (eh != null)
1003                                 eh (this, e);
1004                 }
1005
1006                 internal void OnPopup (PopupEventArgs e)
1007                 {
1008                         PopupEventHandler eh = (PopupEventHandler) (Events [PopupEvent]);
1009                         if (eh != null)
1010                                 eh (this, e);
1011                 }
1012
1013 #if NET_2_0
1014                 internal void OnUnPopup (PopupEventArgs e)
1015                 {
1016                         PopupEventHandler eh = (PopupEventHandler) (Events [UnPopupEvent]);
1017                         if (eh != null)
1018                                 eh (this, e);
1019                 }
1020 #endif
1021                 
1022                 internal bool Visible {
1023                         get { return tooltip_window.Visible; }
1024                 }
1025                 #endregion      // Private Methods
1026
1027                 #region Events
1028                 static object PopupEvent = new object ();
1029                 static object DrawEvent = new object ();
1030                 
1031 #if NET_2_0
1032                 public
1033 #endif          
1034                 event PopupEventHandler Popup {
1035                         add { Events.AddHandler (PopupEvent, value); }
1036                         remove { Events.RemoveHandler (PopupEvent, value); }
1037                 }
1038
1039 #if NET_2_0
1040                 public
1041 #endif
1042                 event DrawToolTipEventHandler Draw {
1043                         add { Events.AddHandler (DrawEvent, value); }
1044                         remove { Events.RemoveHandler (DrawEvent, value); }
1045                 }
1046                 #endregion
1047         }
1048 }