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