* ImageList.cs: When the image stream is set pull all the images
[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
28 // COMPLETE
29
30 using System.Collections;
31 using System.ComponentModel;
32 using System.Drawing;
33
34 namespace System.Windows.Forms {
35         [ProvideProperty ("ToolTip", typeof(System.Windows.Forms.Control))]
36         [ToolboxItemFilter("System.Windows.Forms", ToolboxItemFilterType.Allow)]
37         public sealed class ToolTip : System.ComponentModel.Component, System.ComponentModel.IExtenderProvider {
38                 #region Local variables
39                 internal bool           is_active;
40                 internal int            automatic_delay;
41                 internal int            autopop_delay;
42                 internal int            initial_delay;
43                 internal int            re_show_delay;
44                 internal bool           show_always;
45
46                 internal ToolTipWindow  tooltip_window;                 // The actual tooltip window
47                 internal Hashtable      tooltip_strings;                // List of strings for each control, indexed by control
48                 internal Control        active_control;                 // Control for which the tooltip is currently displayed
49                 internal Control        last_control;                   // last control the mouse was in; null if the last control did not have a tooltip
50                 internal Size           display_size;                   // Size of the screen
51                 internal Timer          timer;                          // Used for the various intervals
52                 #endregion      // Local variables
53
54                 #region ToolTipWindow Class
55                 internal class ToolTipWindow : Control {
56                         #region ToolTipWindow Class Local Variables
57                         internal StringFormat   string_format;
58                         internal ToolTip        owner;
59                         #endregion      // ToolTipWindow Class Local Variables
60
61                         #region ToolTipWindow Class Constructor
62                         internal ToolTipWindow(ToolTip owner) : base() {
63                                 this.owner = owner;
64
65                                 string_format = new StringFormat();
66                                 string_format.LineAlignment = StringAlignment.Center;
67                                 string_format.Alignment = StringAlignment.Center;
68                                 string_format.FormatFlags = StringFormatFlags.NoWrap;
69
70                                 Visible = false;
71                                 Size = new Size(100, 20);
72                                 ForeColor = ThemeEngine.Current.ColorInfoText;
73                                 BackColor = ThemeEngine.Current.ColorInfoWindow;
74
75                                 VisibleChanged += new EventHandler(ToolTipWindow_VisibleChanged);
76
77                                 SetStyle (ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
78                                 SetStyle (ControlStyles.ResizeRedraw | ControlStyles.Opaque, true);
79                         }
80
81                         #endregion      // ToolTipWindow Class Constructor
82 \r
83                         #region ToolTipWindow Class Protected Instance Methods\r
84                         protected override void OnCreateControl() {\r
85                                 base.OnCreateControl ();\r
86                                 XplatUI.SetTopmost(this.window.Handle, IntPtr.Zero, true);
87                         }\r
88
89                         protected override CreateParams CreateParams {\r
90                                 get {\r
91                                         CreateParams cp;\r
92 \r
93                                         cp = base.CreateParams;\r
94 \r
95                                         cp.Style = (int)WindowStyles.WS_POPUP;\r
96                                         cp.Style |= (int)WindowStyles.WS_CLIPSIBLINGS;\r
97 \r
98                                         cp.ExStyle = (int)(WindowStyles.WS_EX_TOOLWINDOW | WindowStyles.WS_EX_TOPMOST);
99 \r
100                                         return cp;\r
101                                 }\r
102                         }\r
103 \r
104                         protected override void OnPaint(PaintEventArgs pevent) {\r
105                                 // We don't do double-buffering on purpose:\r
106                                 // 1) we'd have to meddle with is_visible, it destroys the buffers if !visible\r
107                                 // 2) We don't draw much, no need to double buffer\r
108                                 ThemeEngine.Current.DrawToolTip(pevent.Graphics, ClientRectangle, owner);
109                         }\r
110 \r
111                         protected override void Dispose(bool disposing) {\r
112                                 if (disposing) {\r
113                                         this.string_format.Dispose();\r
114                                 }\r
115                                 base.Dispose (disposing);\r
116                         }\r
117 \r
118                         protected override void WndProc(ref Message m) {\r
119                                 if (m.Msg == (int)Msg.WM_SETFOCUS) {\r
120                                         if (m.WParam != IntPtr.Zero) {\r
121                                                 XplatUI.SetFocus(m.WParam);\r
122                                         }\r
123                                 }\r
124                                 base.WndProc (ref m);\r
125                         }\r
126 \r
127 \r
128                         #endregion      // ToolTipWindow Class Protected Instance Methods\r
129 \r
130                         #region ToolTipWindow Class Private Methods\r
131                         private void ToolTipWindow_VisibleChanged(object sender, EventArgs e) {\r
132                                 Control control = (Control)sender;\r
133 \r
134                                 if (control.is_visible) {\r
135                                         XplatUI.SetTopmost(control.window.Handle, IntPtr.Zero, true);
136                                 } else {\r
137                                         XplatUI.SetTopmost(control.window.Handle, IntPtr.Zero, false);
138                                 }\r
139                         }\r
140                         #endregion      // ToolTipWindow Class Protected Instance Methods\r
141                 }
142                 #endregion      // ToolTipWindow Class
143
144                 #region Public Constructors & Destructors
145                 public ToolTip() {
146                         XplatUI.GetDisplaySize(out display_size);
147
148                         // Defaults from MS
149                         is_active = true;
150                         automatic_delay = 500;
151                         autopop_delay = 5000;
152                         initial_delay = 500;
153                         re_show_delay = 100;
154                         show_always = false;
155
156                         tooltip_strings = new Hashtable(5);
157
158                         tooltip_window = new ToolTipWindow(this);
159                         tooltip_window.MouseLeave += new EventHandler(control_MouseLeave);
160
161                         timer = new Timer();
162                         timer.Enabled = false;
163                         timer.Tick +=new EventHandler(timer_Tick);\r
164                 }
165
166                 public ToolTip(System.ComponentModel.IContainer cont) : this() {\r
167                         // Dunno why I'd need the container\r
168                 }
169
170                 ~ToolTip() {
171                 }
172                 #endregion      // Public Constructors & Destructors
173
174                 #region Public Instance Properties
175                 [DefaultValue (true)]
176                 public bool Active {
177                         get {
178                                 return is_active;
179                         }
180
181                         set {
182                                 if (is_active != value) {
183                                         is_active = value;
184
185                                         if (tooltip_window.Visible) {
186                                                 tooltip_window.Visible = false;
187                                                 active_control = null;
188                                         }
189                                 }
190                         }
191                 }
192
193                 [DefaultValue (500)]
194                 [RefreshProperties (RefreshProperties.All)]
195                 public int AutomaticDelay {
196                         get {
197                                 return automatic_delay;
198                         }
199
200                         set {
201                                 if (automatic_delay != value) {
202                                         automatic_delay = value;
203                                         autopop_delay = automatic_delay * 10;
204                                         initial_delay = automatic_delay;
205                                         re_show_delay = automatic_delay / 5;
206                                 }
207                         }
208                 }
209
210                 [RefreshProperties (RefreshProperties.All)]
211                 public int AutoPopDelay {
212                         get {
213                                 return autopop_delay;
214                         }
215
216                         set {
217                                 if (autopop_delay != value) {
218                                         autopop_delay = value;
219                                 }
220                         }
221                 }
222
223                 [RefreshProperties (RefreshProperties.All)]
224                 public int InitialDelay {
225                         get {
226                                 return initial_delay;
227                         }
228
229                         set {
230                                 if (initial_delay != value) {
231                                         initial_delay = value;
232                                 }
233                         }
234                 }
235
236                 [RefreshProperties (RefreshProperties.All)]
237                 public int ReshowDelay {
238                         get {
239                                 return re_show_delay;
240                         }
241
242                         set {
243                                 if (re_show_delay != value) {
244                                         re_show_delay = value;
245                                 }
246                         }
247                 }
248
249                 [DefaultValue (false)]
250                 public bool ShowAlways {
251                         get {
252                                 return show_always;
253                         }
254
255                         set {
256                                 if (show_always != value) {
257                                         show_always = value;
258                                 }
259                         }
260                 }
261                 #endregion      // Public Instance Properties
262
263                 #region Public Instance Methods
264                 public bool CanExtend(object target) {
265                         return false;
266                 }
267
268                 [Localizable (true)]
269                 [DefaultValue ("")]
270                 public string GetToolTip(Control control) {
271                         return (string)tooltip_strings[control];
272                 }
273
274                 public void RemoveAll() {
275                         tooltip_strings.Clear();
276                 }
277
278                 public void SetToolTip(Control control, string caption) {
279                         tooltip_strings[control] = caption;
280
281                         control.MouseEnter += new EventHandler(control_MouseEnter);
282                         control.MouseMove += new MouseEventHandler(control_MouseMove);
283                         control.MouseLeave += new EventHandler(control_MouseLeave);
284                 }
285
286                 public override string ToString() {\r
287                         return base.ToString() + " InitialDelay: " + initial_delay + ", ShowAlways: " + show_always;\r
288                 }\r
289                 #endregion      // Public Instance Methods
290
291                 #region Protected Instance Methods
292                 protected override void Dispose(bool disposing) {
293                         if (disposing) {
294                                 // Mop up the mess; or should we wait for the GC to kick in?
295                                 timer.Stop();
296                                 timer.Dispose();
297
298                                 // Not sure if we should clean up tooltip_window
299                                 tooltip_window.Dispose();
300
301                                 tooltip_strings.Clear();
302                         }
303                 }
304                 #endregion      // Protected Instance Methods\r
305 \r
306                 #region Private Methods\r
307                 private void control_MouseEnter(object sender, EventArgs e) {\r
308                         string  text;\r
309 \r
310                         // Whatever we're displaying right now, we don't want it anymore\r
311                         tooltip_window.Visible = false;\r
312                         timer.Stop();\r
313 \r
314                         // if we're in the same control as before (how'd that happen?) or if we're not active, leave\r
315                         if (!is_active || (active_control == (Control)sender)) {\r
316                                 return;\r
317                         }\r
318 \r
319                         // As of this writing, our MWF implementation had no clue what an active control was :-(\r
320                         if (!show_always) {\r
321                                 if (((Control)sender).GetContainerControl().ActiveControl == null) {\r
322                                         return;\r
323                                 }\r
324                         }\r
325 \r
326                         text = (string)tooltip_strings[sender];\r
327                         if (text != null) {\r
328                                 Size size;
329 \r
330                                 size = ThemeEngine.Current.ToolTipSize(this, text);\r
331                                 tooltip_window.Width = size.Width;\r
332                                 tooltip_window.Height = size.Height;\r
333                                 tooltip_window.Text = text;\r
334 \r
335                                 // FIXME - this needs to be improved; the tooltip will show up under the mouse, which is annoying; use cursor size once implemented\r
336 \r
337                                 if ((Control.MousePosition.X+1+tooltip_window.Width) < display_size.Width) {\r
338                                         tooltip_window.Left = Control.MousePosition.X+1;\r
339                                 } else {\r
340                                         tooltip_window.Left = display_size.Width-tooltip_window.Width;\r
341                                 }\r
342 \r
343                                 if ((Control.MousePosition.Y+tooltip_window.Height)<display_size.Height) {\r
344                                         tooltip_window.Top = Control.MousePosition.Y;\r
345                                 } else {\r
346                                         tooltip_window.Top = Control.MousePosition.Y-tooltip_window.Height;\r
347                                 }\r
348 \r
349                                 // Since we get the mouse enter before the mouse leave, active_control will still be non-null if we were in a \r
350                                 // tooltip'd control; should prolly check on X11 too, and make sure that driver behaves the same way\r
351                                 if (active_control == null) {\r
352                                         timer.Interval = initial_delay;\r
353                                 } else {\r
354                                         timer.Interval = re_show_delay;\r
355                                 }\r
356 \r
357                                 active_control = (Control)sender;\r
358 \r
359                                 // We're all set, lets wake the timer (which will then make us visible)\r
360                                 timer.Enabled = true;\r
361                         }\r
362                 }\r
363 \r
364                 private void timer_Tick(object sender, EventArgs e) {\r
365                         // Show our pretty selves\r
366                         timer.Stop();\r
367 \r
368                         // FIXME - Should not need this check../\r
369                         if (tooltip_window.IsDisposed) {\r
370                                 return;\r
371                         }\r
372 \r
373                         if (!tooltip_window.Visible) {\r
374                                 // The initial_delay timer kicked in\r
375                                 tooltip_window.Visible = true;\r
376                                 timer.Interval = autopop_delay;\r
377                                 timer.Start();\r
378                         } else {\r
379                                 // The autopop_delay timer happened\r
380                                 tooltip_window.Visible = false;\r
381                         }\r
382                 }\r
383 \r
384 \r
385                 private bool MouseInControl(Control control) {\r
386                         Point   m;\r
387                         Point   c;\r
388                         Size    cw;\r
389 \r
390                         if (control == null) {\r
391                                 return false;\r
392                         }\r
393 \r
394                         m = Control.MousePosition;\r
395                         c = new Point(control.Bounds.X, control.Bounds.Y);\r
396                         if (control.parent != null) {
397                                 c = control.parent.PointToScreen(c);
398                         }
399                         cw = control.ClientSize;\r
400 \r
401                         if (c.X<=m.X && m.X<(c.X+cw.Width) &&\r
402                                 c.Y<=m.Y && m.Y<(c.Y+cw.Height)) {\r
403                                 return true;\r
404                         }\r
405                         return false;\r
406                 }\r
407 \r
408                 private void control_MouseLeave(object sender, EventArgs e) {\r
409                         // In case the timer is still running, stop it\r
410                         timer.Stop();\r
411 \r
412                         if (!MouseInControl(tooltip_window) && !MouseInControl(active_control)) {\r
413                                 active_control = null;\r
414                                 tooltip_window.Visible = false;\r
415                         }\r
416                 }\r
417 \r
418                 private void control_MouseMove(object sender, MouseEventArgs e) {\r
419                         // Restart the interval, the mouse moved\r
420                         timer.Stop();\r
421                         timer.Start();\r
422 \r
423                 }\r
424                 #endregion      // Private Methods\r
425         }
426 }