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