Fix bug #395
[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         [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                         }
260
261
262                         #region Internal Events
263                         static object DrawEvent = new object ();
264                         static object PopupEvent = new object ();
265         
266                         // UIA Framework
267                         static object UnPopupEvent = new object ();
268
269                         public event DrawToolTipEventHandler Draw {
270                                 add { Events.AddHandler (DrawEvent, value); }
271                                 remove { Events.RemoveHandler (DrawEvent, value); }
272                         }
273
274                         public event PopupEventHandler Popup {
275                                 add { Events.AddHandler (PopupEvent, value); }
276                                 remove { Events.RemoveHandler (PopupEvent, value); }
277                         }
278
279                         internal event PopupEventHandler UnPopup {
280                                 add { Events.AddHandler (UnPopupEvent, value); }
281                                 remove { Events.RemoveHandler (UnPopupEvent, value); }
282                         }
283                         #endregion
284                 }
285                 #endregion      // ToolTipWindow Class
286
287                 #region Public Constructors & Destructors
288                 public ToolTip() {
289
290                         // Defaults from MS
291                         is_active = true;
292                         automatic_delay = 500;
293                         autopop_delay = 5000;
294                         initial_delay = 500;
295                         re_show_delay = 100;
296                         show_always = false;
297                         back_color = SystemColors.Info;
298                         fore_color = SystemColors.InfoText;
299                         
300                         isBalloon = false;
301                         stripAmpersands = false;
302                         useAnimation = true;
303                         useFading = true;
304                         tooltip_strings = new Hashtable(5);
305                         controls = new ArrayList(5);
306
307                         tooltip_window = new ToolTipWindow();
308                         tooltip_window.MouseLeave += new EventHandler(control_MouseLeave);
309                         tooltip_window.Draw += new DrawToolTipEventHandler (tooltip_window_Draw);
310                         tooltip_window.Popup += new PopupEventHandler (tooltip_window_Popup);
311
312                         // UIA Framework: Static event handlers
313                         tooltip_window.UnPopup += delegate (object sender, PopupEventArgs args) {
314                                 OnUnPopup (args);
315                         };
316                         UnPopup += new PopupEventHandler (OnUIAUnPopup);
317
318                         timer = new Timer();
319                         timer.Enabled = false;
320                         timer.Tick +=new EventHandler(timer_Tick);
321
322                 }
323
324
325                 #region UIA Framework: Events, Delegates and Methods
326                 // NOTE: 
327                 //      We are using Reflection to add/remove internal events.
328                 //      Class ToolTipListener uses the events.
329                 //
330                 //      - UIAUnPopup. Event used to generate ChildRemoved in ToolTip
331                 //      - UIAToolTipHookUp. Event used to keep track of associated controls
332                 //      - UIAToolTipUnhookUp. Event used to remove track of associated controls
333                 static object UnPopupEvent = new object ();
334
335                 internal event PopupEventHandler UnPopup {
336                         add { Events.AddHandler (UnPopupEvent, value); }
337                         remove { Events.RemoveHandler (UnPopupEvent, value); }
338                 }
339
340                 internal static event PopupEventHandler UIAUnPopup;
341                 internal static event ControlEventHandler UIAToolTipHookUp;
342                 internal static event ControlEventHandler UIAToolTipUnhookUp;
343
344                 internal Rectangle UIAToolTipRectangle {
345                         get { return tooltip_window.Bounds; }
346                 }
347
348                 internal static void OnUIAUnPopup (object sender, PopupEventArgs args)
349                 {
350                         if (UIAUnPopup != null)
351                                 UIAUnPopup (sender, args);
352                 }
353
354                 internal static void OnUIAToolTipHookUp (object sender, ControlEventArgs args)
355                 {
356                         if (UIAToolTipHookUp != null)
357                                 UIAToolTipHookUp (sender, args);
358                 }
359
360                 internal static void OnUIAToolTipUnhookUp (object sender, ControlEventArgs args)
361                 {
362                         if (UIAToolTipUnhookUp != null)
363                                 UIAToolTipUnhookUp (sender, args);
364                 }
365
366                 #endregion
367
368                 public ToolTip(System.ComponentModel.IContainer cont) : this() {
369                         cont.Add (this);
370                 }
371
372                 ~ToolTip() {
373                 }
374                 #endregion      // Public Constructors & Destructors
375
376                 #region Public Instance Properties
377                 [DefaultValue (true)]
378                 public bool Active {
379                         get {
380                                 return is_active;
381                         }
382
383                         set {
384                                 if (is_active != value) {
385                                         is_active = value;
386
387                                         if (tooltip_window.Visible) {
388                                                 tooltip_window.Visible = false;
389                                                 active_control = null;
390                                         }
391                                 }
392                         }
393                 }
394
395                 [DefaultValue (500)]
396                 [RefreshProperties (RefreshProperties.All)]
397                 public int AutomaticDelay {
398                         get {
399                                 return automatic_delay;
400                         }
401
402                         set {
403                                 if (automatic_delay != value) {
404                                         automatic_delay = value;
405                                         autopop_delay = automatic_delay * 10;
406                                         initial_delay = automatic_delay;
407                                         re_show_delay = automatic_delay / 5;
408                                 }
409                         }
410                 }
411
412                 [RefreshProperties (RefreshProperties.All)]
413                 public int AutoPopDelay {
414                         get {
415                                 return autopop_delay;
416                         }
417
418                         set {
419                                 if (autopop_delay != value) {
420                                         autopop_delay = value;
421                                 }
422                         }
423                 }
424
425                 [DefaultValue ("Color [Info]")]
426                 public Color BackColor {
427                         get { return this.back_color; }
428                         set { this.back_color = value; tooltip_window.BackColor = value; }
429                 }
430
431                 [DefaultValue ("Color [InfoText]")]
432                 public Color ForeColor
433                 {
434                         get { return this.fore_color; }
435                         set { this.fore_color = value; tooltip_window.ForeColor = value; }
436                 }
437
438                 [RefreshProperties (RefreshProperties.All)]
439                 public int InitialDelay {
440                         get {
441                                 return initial_delay;
442                         }
443
444                         set {
445                                 if (initial_delay != value) {
446                                         initial_delay = value;
447                                 }
448                         }
449                 }
450
451                 [DefaultValue (false)]
452                 public bool OwnerDraw {
453                         get { return this.owner_draw; }
454                         set { this.owner_draw = value; }
455                 }
456
457                 [RefreshProperties (RefreshProperties.All)]
458                 public int ReshowDelay {
459                         get {
460                                 return re_show_delay;
461                         }
462
463                         set {
464                                 if (re_show_delay != value) {
465                                         re_show_delay = value;
466                                 }
467                         }
468                 }
469
470                 [DefaultValue (false)]
471                 public bool ShowAlways {
472                         get {
473                                 return show_always;
474                         }
475
476                         set {
477                                 if (show_always != value) {
478                                         show_always = value;
479                                 }
480                         }
481                 }
482
483
484                 [DefaultValue (false)]
485                 public bool IsBalloon {
486                         get { return isBalloon; }
487                         set { isBalloon = value; }
488                 }
489
490                 [Browsable (true)]
491                 [DefaultValue (false)]
492                 public bool StripAmpersands {
493                         get { return stripAmpersands; }
494                         set { stripAmpersands = value; }
495                 }
496
497                 [Localizable (false)]
498                 [Bindable (true)]
499                 [TypeConverter (typeof (StringConverter))]
500                 [DefaultValue (null)]
501                 public object Tag {
502                         get { return tag; }
503                         set { tag = value; }
504                 }
505
506                 [DefaultValue (ToolTipIcon.None)]
507                 public ToolTipIcon ToolTipIcon {
508                         get { return this.tool_tip_icon; }
509                         set {
510                                 switch (value) {
511                                         case ToolTipIcon.None:
512                                                 tooltip_window.icon = null;
513                                                 break;
514                                         case ToolTipIcon.Error:
515                                                 tooltip_window.icon = SystemIcons.Error;
516                                                 break;
517                                         case ToolTipIcon.Warning:
518                                                 tooltip_window.icon = SystemIcons.Warning;
519                                                 break;
520                                         case ToolTipIcon.Info:
521                                                 tooltip_window.icon = SystemIcons.Information;
522                                                 break;
523                                 }
524
525                                 tool_tip_icon = value;
526                         }
527                 }
528                 
529                 [DefaultValue ("")]
530                 public string ToolTipTitle {
531                         get { return tooltip_window.title; }
532                         set {
533                                if (value == null)
534                                        value = String.Empty;
535                                
536                                tooltip_window.title = value; 
537                         }
538                 }
539                 
540                 [Browsable (true)]
541                 [DefaultValue (true)]
542                 public bool UseAnimation {
543                         get { return useAnimation; }
544                         set { useAnimation = value; }
545                 }
546
547                 [Browsable (true)]
548                 [DefaultValue (true)]
549                 public bool UseFading {
550                         get { return useFading; }
551                         set { useFading = value; }
552                 }
553
554                 #endregion      // Public Instance Properties
555
556                 #region Protected Properties
557                 protected virtual CreateParams CreateParams
558                 {
559                         get
560                         {
561                                 CreateParams cp = new CreateParams ();
562
563                                 cp.Style = 2;
564
565                                 return cp;
566                         }
567                 }
568                 #endregion
569
570                 #region Public Instance Methods
571                 public bool CanExtend(object target) {
572                         return false;
573                 }
574
575                 [Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
576                          "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
577                 [Localizable (true)]
578                 [DefaultValue ("")]
579                 public string GetToolTip (Control control)
580                 {
581                         string tooltip = (string)tooltip_strings[control];
582                         if (tooltip == null)
583                                 return "";
584                         return tooltip;
585                 }
586
587                 public void RemoveAll() {
588                         tooltip_strings.Clear();
589                         //UIA Framework: ToolTip isn't associated anymore
590                         foreach (Control control in controls)
591                                 OnUIAToolTipUnhookUp (this, new ControlEventArgs (control));
592
593                         controls.Clear();
594                 }
595
596                 public void SetToolTip(Control control, string caption) {
597                         // UIA Framework
598                         OnUIAToolTipHookUp (this, new ControlEventArgs (control));
599                         tooltip_strings[control] = caption;
600
601                         // no need for duplicates
602                         if (!controls.Contains(control)) {
603                                 control.MouseEnter += new EventHandler(control_MouseEnter);
604                                 control.MouseMove += new MouseEventHandler(control_MouseMove);
605                                 control.MouseLeave += new EventHandler(control_MouseLeave);
606                                 control.MouseDown += new MouseEventHandler (control_MouseDown);
607                                 controls.Add(control);
608                         }
609                         
610                         // if SetToolTip is called from a control and the mouse is currently over that control,
611                         // make sure that tooltip_window.Text gets updated if it's being shown,
612                         // or show the tooltip for it if is not
613                         if (active_control == control && caption != null && state == TipState.Show) {
614                                 Size size = ThemeEngine.Current.ToolTipSize(tooltip_window, caption);
615                                 tooltip_window.Width = size.Width;
616                                 tooltip_window.Height = size.Height;
617                                 tooltip_window.Text = caption;
618                                 timer.Stop ();
619                                 timer.Start ();
620                         } else if (control.IsHandleCreated && MouseInControl (control, false))
621                                 ShowTooltip (control);
622                 }
623
624                 public override string ToString() {
625                         return base.ToString() + " InitialDelay: " + initial_delay + ", ShowAlways: " + show_always;
626                 }
627
628                 public void Show (string text, IWin32Window window)
629                 {
630                         Show (text, window, 0);
631                 }
632
633                 public void Show (string text, IWin32Window window, int duration)
634                 {
635                         if (window == null)
636                                 throw new ArgumentNullException ("window");
637                         if (duration < 0)
638                                 throw new ArgumentOutOfRangeException ("duration", "duration cannot be less than zero");
639
640                         if (!Active)
641                                 return;
642                                 
643                         timer.Stop ();
644                         
645                         Control c = (Control)window;
646
647                         XplatUI.SetOwner (tooltip_window.Handle, c.TopLevelControl.Handle);
648                         
649                         // If the mouse is in the requested window, use that position
650                         // Else, center in the requested window
651                         if (c.ClientRectangle.Contains (c.PointToClient (Control.MousePosition))) {
652                                 tooltip_window.Location = Control.MousePosition;
653                                 tooltip_strings[c] = text;
654                                 HookupControlEvents (c);
655                         }
656                         else
657                                 tooltip_window.Location = c.PointToScreen (new Point (c.Width / 2, c.Height / 2));
658                         
659                         // We need to hide our tooltip if the form loses focus, is closed, or is minimized
660                         HookupFormEvents ((Form)c.TopLevelControl);
661                         
662                         tooltip_window.PresentModal ((Control)window, text);
663                         
664                         state = TipState.Show;
665                         
666                         if (duration > 0) {
667                                 timer.Interval = duration;
668                                 timer.Start ();
669                         }
670                 }
671                 
672                 public void Show (string text, IWin32Window window, Point point)
673                 {
674                         Show (text, window, point, 0);
675                 }
676
677                 public void Show (string text, IWin32Window window, int x, int y)
678                 {
679                         Show (text, window, new Point (x, y), 0);
680                 }
681                 
682                 public void Show (string text, IWin32Window window, Point point, int duration)
683                 {
684                         if (window == null)
685                                 throw new ArgumentNullException ("window");
686                         if (duration < 0)
687                                 throw new ArgumentOutOfRangeException ("duration", "duration cannot be less than zero");
688
689                         if (!Active)
690                                 return;
691
692                         timer.Stop ();
693
694                         Control c = (Control)window;
695                         
696                         Point display_point = c.PointToScreen (Point.Empty);
697                         display_point.X += point.X;
698                         display_point.Y += point.Y;
699
700                         XplatUI.SetOwner (tooltip_window.Handle, c.TopLevelControl.Handle);
701
702                         // We need to hide our tooltip if the form loses focus, is closed, or is minimized
703                         HookupFormEvents ((Form)c.TopLevelControl);
704
705                         tooltip_window.Location = display_point;
706                         tooltip_window.PresentModal ((Control)window, text);
707
708                         state = TipState.Show;
709                         
710                         if (duration > 0) {
711                                 timer.Interval = duration;
712                                 timer.Start ();
713                         }
714                 }
715                 
716                 public void Show (string text, IWin32Window window, int x, int y, int duration)
717                 {
718                         Show (text, window, new Point (x, y), duration);
719                 }
720                 
721                 public void Hide (IWin32Window win)
722                 {
723                         timer.Stop ();
724                         state = TipState.Initial;
725
726                         UnhookFormEvents ();
727                         tooltip_window.Visible = false;
728                 }
729                 #endregion      // Public Instance Methods
730
731                 #region Protected Instance Methods
732                 protected override void Dispose(bool disposing) {
733                         // call the base impl first to avoid conflicts with any parent's events
734                         base.Dispose (disposing);
735
736                         if (disposing) {
737                                 // Mop up the mess; or should we wait for the GC to kick in?
738                                 timer.Stop();
739                                 timer.Dispose();
740
741                                 // Not sure if we should clean up tooltip_window
742                                 tooltip_window.Dispose();
743
744                                 tooltip_strings.Clear();
745                                 
746                                 //UIA Framework: ToolTip isn't associated anymore
747                                 foreach (Control control in controls)
748                                         OnUIAToolTipUnhookUp (this, new ControlEventArgs (control));
749                                 controls.Clear();
750                         }
751                 }
752
753                 protected void StopTimer ()
754                 {
755                         timer.Stop ();
756                 }
757                 #endregion      // Protected Instance Methods
758
759                 internal enum TipState {
760                         Initial,
761                         Show,
762                         Down
763                 }
764
765                 TipState state = TipState.Initial;
766
767                 #region Private Methods
768
769                 private void HookupFormEvents (Form form)
770                 {
771                         hooked_form = form;
772
773                         form.Deactivate += new EventHandler (Form_Deactivate);
774                         form.Closed += new EventHandler (Form_Closed);
775                         form.Resize += new EventHandler (Form_Resize);
776                 }
777
778                 private void HookupControlEvents (Control control)
779                 {
780                         if (!controls.Contains (control)) {
781                                 control.MouseEnter += new EventHandler (control_MouseEnter);
782                                 control.MouseMove += new MouseEventHandler (control_MouseMove);
783                                 control.MouseLeave += new EventHandler (control_MouseLeave);
784                                 control.MouseDown += new MouseEventHandler (control_MouseDown);
785                                 controls.Add (control);
786                         }
787                 }
788
789                 private void UnhookControlEvents (Control control)
790                 {
791                         control.MouseEnter -= new EventHandler (control_MouseEnter);
792                         control.MouseMove -= new MouseEventHandler (control_MouseMove);
793                         control.MouseLeave -= new EventHandler (control_MouseLeave);
794                         control.MouseDown -= new MouseEventHandler (control_MouseDown);
795                 }
796                 private void UnhookFormEvents ()
797                 {
798                         if (hooked_form == null)
799                                 return;
800
801                         hooked_form.Deactivate -= new EventHandler (Form_Deactivate);
802                         hooked_form.Closed -= new EventHandler (Form_Closed);
803                         hooked_form.Resize -= new EventHandler (Form_Resize);
804
805                         hooked_form = null;
806                 }
807
808
809                 private void Form_Resize (object sender, EventArgs e)
810                 {
811                         Form f = (Form)sender;
812
813                         if (f.WindowState == FormWindowState.Minimized)
814                                 tooltip_window.Visible = false;
815                 }
816
817                 private void Form_Closed (object sender, EventArgs e)
818                 {
819                         tooltip_window.Visible = false;
820                 }
821
822                 private void Form_Deactivate (object sender, EventArgs e)
823                 {
824                         tooltip_window.Visible = false;
825                 }
826
827                 internal void Present (Control control, string text)
828                 {
829                         tooltip_window.Present (control, text);
830                 }
831                 
832                 private void control_MouseEnter (object sender, EventArgs e) 
833                 {
834                         ShowTooltip (sender as Control);
835                 }
836
837                 private void ShowTooltip (Control control) 
838                 {
839                         last_control = control;
840
841                         // Whatever we're displaying right now, we don't want it anymore
842                         tooltip_window.Visible = false;
843                         timer.Stop();
844                         state = TipState.Initial;
845
846                         if (!is_active)
847                                 return;
848
849                         // ShowAlways controls whether the controls in non-active forms
850                         // can display its tooltips, even if they are not current active control.
851                         if (!show_always && control.FindForm () != Form.ActiveForm)
852                                 return;
853
854                         string text = (string)tooltip_strings[control];
855                         if (text != null && text.Length > 0) {
856                                 if (active_control == null) {
857                                         timer.Interval = Math.Max (initial_delay, 1);
858                                 } else {
859                                         timer.Interval = Math.Max (re_show_delay, 1);
860                                 }
861
862                                 active_control = control;
863                                 timer.Start ();
864                         }
865                 }
866
867                 private void timer_Tick(object sender, EventArgs e) {
868                         timer.Stop();
869
870                         switch (state) {
871                         case TipState.Initial:
872                                 if (active_control == null)
873                                         return;
874                                 tooltip_window.Present (active_control, (string)tooltip_strings[active_control]);
875                                 state = TipState.Show;
876                                 timer.Interval = autopop_delay;
877                                 timer.Start();
878                                 break;
879
880                         case TipState.Show:
881                                 tooltip_window.Visible = false;
882                                 state = TipState.Down;
883                                 break;
884
885                         default:
886                                 throw new Exception ("Timer shouldn't be running in state: " + state);
887                         }
888                 }
889
890                 private void tooltip_window_Popup (object sender, PopupEventArgs e)
891                 {
892                         e.ToolTipSize = ThemeEngine.Current.ToolTipSize (tooltip_window, tooltip_window.Text);
893                         OnPopup (e);
894                 }
895
896                 private void tooltip_window_Draw (object sender, DrawToolTipEventArgs e)
897                 {
898                         if (OwnerDraw)
899                                 OnDraw (e);
900                         else
901                                 ThemeEngine.Current.DrawToolTip (e.Graphics, e.Bounds, tooltip_window);
902                 }
903                 
904                 private bool MouseInControl (Control control, bool fuzzy) {
905                         Point   m;
906                         Point   c;
907                         Size    cw;
908
909                         if (control == null) {
910                                 return false;
911                         }
912
913                         m = Control.MousePosition;
914                         c = new Point(control.Bounds.X, control.Bounds.Y);
915                         if (control.Parent != null) {
916                                 c = control.Parent.PointToScreen(c);
917                         }
918                         cw = control.ClientSize;
919
920
921                         Rectangle rect = new Rectangle (c, cw);
922                         
923                         //
924                         // We won't get mouse move events on all platforms with the exact same
925                         // frequency, so cheat a bit.
926                         if (fuzzy)
927                                 rect.Inflate (2, 2);
928
929                         return rect.Contains (m);
930                 }
931
932                 private void control_MouseLeave(object sender, EventArgs e) 
933                 {
934                         timer.Stop ();
935
936                         active_control = null;
937                         tooltip_window.Visible = false;
938
939                         if (last_control == sender)
940                                 last_control = null;
941                 }
942
943
944                 void control_MouseDown (object sender, MouseEventArgs e)
945                 {
946                         timer.Stop();
947
948                         active_control = null;
949                         tooltip_window.Visible = false;
950                         
951                         if (last_control == sender)
952                                 last_control = null;
953                 }
954
955                 private void control_MouseMove(object sender, MouseEventArgs e) {
956                         if (state != TipState.Down) {
957                                 timer.Stop();
958                                 timer.Start();
959                         }
960                 }
961
962                 internal void OnDraw (DrawToolTipEventArgs e)
963                 {
964                         DrawToolTipEventHandler eh = (DrawToolTipEventHandler)(Events[DrawEvent]);
965                         if (eh != null)
966                                 eh (this, e);
967                 }
968
969                 internal void OnPopup (PopupEventArgs e)
970                 {
971                         PopupEventHandler eh = (PopupEventHandler) (Events [PopupEvent]);
972                         if (eh != null)
973                                 eh (this, e);
974                 }
975
976                 internal void OnUnPopup (PopupEventArgs e)
977                 {
978                         PopupEventHandler eh = (PopupEventHandler) (Events [UnPopupEvent]);
979                         if (eh != null)
980                                 eh (this, e);
981                 }
982                 
983                 internal bool Visible {
984                         get { return tooltip_window.Visible; }
985                 }
986                 #endregion      // Private Methods
987
988                 #region Events
989                 static object PopupEvent = new object ();
990                 static object DrawEvent = new object ();
991                 
992                 public event PopupEventHandler Popup {
993                         add { Events.AddHandler (PopupEvent, value); }
994                         remove { Events.RemoveHandler (PopupEvent, value); }
995                 }
996
997                 public event DrawToolTipEventHandler Draw {
998                         add { Events.AddHandler (DrawEvent, value); }
999                         remove { Events.RemoveHandler (DrawEvent, value); }
1000                 }
1001                 #endregion
1002         }
1003 }