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