In System.Windows.Forms:
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / NotifyIcon.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) 2005 Novell, Inc. (http://www.novell.com)
21 //
22 // Authors:
23 //      Peter Bartok    pbartok@novell.com
24 //
25 //
26
27 using System;
28 using System.ComponentModel;
29 using System.ComponentModel.Design;
30 using System.Drawing;
31 using System.Drawing.Text;
32
33 namespace System.Windows.Forms {
34         [DefaultProperty("Text")]
35 #if NET_2_0
36         [DefaultEvent("MouseDoubleClick")]
37 #else
38         [DefaultEvent("MouseDown")]
39 #endif
40         [Designer ("System.Windows.Forms.Design.NotifyIconDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
41         [ToolboxItemFilter("System.Windows.Forms", ToolboxItemFilterType.Allow)]
42         public sealed class NotifyIcon : Component {
43                 #region Local Variables
44                 private ContextMenu             context_menu;
45                 private Icon                    icon;
46                 private Bitmap                  icon_bitmap;
47                 private string                  text;
48                 private bool                    visible;
49                 private NotifyIconWindow        window;
50                 private bool                    systray_active;
51                 private ToolTip                 tooltip;
52 #if NET_2_0
53                 private string balloon_text;
54                 private string balloon_title;
55                 private ToolTipIcon balloon_icon;
56                 private ContextMenuStrip        context_menu_strip;
57                 private object                  tag;
58 #endif
59                 #endregion      // Local Variables
60
61                 #region NotifyIconWindow Class
62                 internal class NotifyIconWindow : Form {
63                         NotifyIcon      owner;
64                         Rectangle       rect;
65
66                         public NotifyIconWindow(NotifyIcon owner) {
67                                 this.owner = owner;
68                                 is_visible = false;
69                                 rect = new Rectangle(0, 0, 1, 1);
70
71                                 FormBorderStyle = FormBorderStyle.None;
72
73                                 //CreateControl();
74
75                                 SizeChanged += new EventHandler(HandleSizeChanged);
76
77                                 // Events that need to be sent to our parent
78                                 Click += new EventHandler(HandleClick);
79                                 DoubleClick += new EventHandler(HandleDoubleClick);
80                                 MouseDown +=new MouseEventHandler(HandleMouseDown);
81                                 MouseUp +=new MouseEventHandler(HandleMouseUp);
82                                 MouseMove +=new MouseEventHandler(HandleMouseMove);
83                                 ContextMenu = owner.context_menu;
84 #if NET_2_0
85                                 ContextMenuStrip = owner.context_menu_strip;
86 #endif
87                         }
88
89                         protected override CreateParams CreateParams {
90                                 get {
91                                         CreateParams cp;
92
93                                         cp = base.CreateParams;
94
95                                         cp.Parent = IntPtr.Zero;
96                                         cp.Style = (int)WindowStyles.WS_POPUP;
97                                         cp.Style |= (int)WindowStyles.WS_CLIPSIBLINGS;
98
99                                         cp.ExStyle = (int)(WindowExStyles.WS_EX_TOOLWINDOW);
100
101                                         return cp;
102                                 }
103                         }
104
105                         protected override void WndProc(ref Message m) {
106                                 switch((Msg)m.Msg) {
107                                                 //
108                                                 //  NotifyIcon does CONTEXTMENU on mouse up, not down
109                                                 //  so we swallow the message here, and handle it on our own
110                                                 // 
111                                         case Msg.WM_CONTEXTMENU:
112                                                 return;
113
114                                         case Msg.WM_USER: {
115                                                 switch ((Msg)m.LParam.ToInt32()) {
116                                                         case Msg.WM_LBUTTONDOWN: {
117                                                                 owner.OnMouseDown (new MouseEventArgs(MouseButtons.Left, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
118                                                                 return;
119                                                         }
120
121                                                         case Msg.WM_LBUTTONUP: {
122                                                                 owner.OnMouseUp (new MouseEventArgs(MouseButtons.Left, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
123                                                                 owner.OnClick (EventArgs.Empty);
124                                                                 return;
125                                                         }
126
127                                                         case Msg.WM_LBUTTONDBLCLK: {
128                                                                 owner.OnDoubleClick (EventArgs.Empty);
129                                                                 return;
130                                                         }
131
132                                                         case Msg.WM_MOUSEMOVE: {
133                                                                 owner.OnMouseMove (new MouseEventArgs(MouseButtons.None, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
134                                                                 return;
135                                                         }
136
137                                                         case Msg.WM_RBUTTONDOWN: {
138                                                                 owner.OnMouseDown (new MouseEventArgs(MouseButtons.Right, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
139                                                                 return;
140                                                         }
141
142                                                         case Msg.WM_RBUTTONUP: {
143                                                                 owner.OnMouseUp (new MouseEventArgs(MouseButtons.Right, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
144                                                                 owner.OnClick (EventArgs.Empty);
145                                                                 return;
146                                                         }
147
148                                                         case Msg.WM_RBUTTONDBLCLK: {
149                                                                 owner.OnDoubleClick (EventArgs.Empty);
150                                                                 return;
151                                                         }
152 #if NET_2_0                                                     
153                                                         case Msg.NIN_BALLOONUSERCLICK: {
154                                                                 owner.OnBalloonTipClicked (EventArgs.Empty);
155                                                                 return;
156                                                         }
157
158                                                         case Msg.NIN_BALLOONSHOW: {
159                                                                 owner.OnBalloonTipShown (EventArgs.Empty);
160                                                                 return;
161                                                         }
162
163                                                         case Msg.NIN_BALLOONHIDE:
164                                                         case Msg.NIN_BALLOONTIMEOUT: {
165                                                                 owner.OnBalloonTipClosed (EventArgs.Empty);
166                                                                 return;
167                                                         }
168 #endif
169                                                 }
170                                                 return;
171                                         }
172                                 }
173                                 base.WndProc (ref m);
174                         }
175
176                         internal void CalculateIconRect() {
177                                 int             x;
178                                 int             y;
179                                 int             size;
180
181                                 // Icons are always square. Try to center them in the window
182                                 if (ClientRectangle.Width < ClientRectangle.Height) {
183                                         size = ClientRectangle.Width;
184                                 } else {
185                                         size = ClientRectangle.Height;
186                                 }
187                                 x = this.ClientRectangle.Width / 2 - size / 2;
188                                 y = this.ClientRectangle.Height / 2 - size / 2;
189                                 rect = new Rectangle(x, y, size, size);
190
191                                 Bounds = new Rectangle (0, 0, size, size);
192                         }
193
194                         internal override void OnPaintInternal (PaintEventArgs e) {
195                                 if (owner.icon != null) {
196                                         e.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(SystemColors.Window), rect);
197                                         e.Graphics.DrawImage(owner.icon_bitmap,
198                                                              rect,
199                                                              new Rectangle (0, 0, owner.icon_bitmap.Width, owner.icon_bitmap.Height),
200                                                              GraphicsUnit.Pixel);
201
202                                 }
203                         }
204
205                         internal void InternalRecreateHandle () {
206                                 base.RecreateHandle ();
207                         }
208
209
210                         private void HandleSizeChanged(object sender, EventArgs e) {
211                                 owner.Recalculate ();
212                         }
213
214                         private void HandleClick (object sender, EventArgs e)
215                         {
216                                 owner.OnClick (e);
217                         }
218
219                         private void HandleDoubleClick (object sender, EventArgs e)
220                         {
221                                 owner.OnDoubleClick (e);
222                         }
223
224                         private void HandleMouseDown (object sender, MouseEventArgs e)
225                         {
226                                 owner.OnMouseDown (e);
227                         }
228
229                         private void HandleMouseUp (object sender, MouseEventArgs e)
230                         {
231                                 owner.OnMouseUp (e);
232                         }
233
234                         private void HandleMouseMove (object sender, MouseEventArgs e)
235                         {
236                                 owner.OnMouseMove (e);
237                         }
238                 }
239                 #endregion      // NotifyIconWindow Class
240                 
241                 #region NotifyIconBalloonWindow Class
242 #if NET_2_0
243                 internal class BalloonWindow : Form 
244                 {
245                         private IntPtr owner;
246                         private Timer timer;
247                         
248                         private string title;
249                         private string text;
250                         private ToolTipIcon icon;
251
252                         public BalloonWindow (IntPtr owner)
253                         {
254                                 this.owner = owner;
255                                 
256                                 StartPosition = FormStartPosition.Manual;
257                                 FormBorderStyle = FormBorderStyle.None;
258                                 TopMost = true;
259
260                                 MouseDown += new MouseEventHandler (HandleMouseDown);
261                                 
262                                 timer = new Timer ();
263                                 timer.Enabled = false;
264                                 timer.Tick += new EventHandler (HandleTimer);
265                         }
266
267                         protected override void OnShown (EventArgs e)
268                         {
269                                 base.OnShown (e);
270                                 timer.Start ();
271                         }
272                         
273                         protected override void OnPaint (PaintEventArgs e) 
274                         {
275                                 ThemeEngine.Current.DrawBalloonWindow (e.Graphics, ClientRectangle, this);
276                                 base.OnPaint (e);
277                         }
278
279                         private void Recalculate () 
280                         {
281                                 Rectangle rect = ThemeEngine.Current.BalloonWindowRect (this);
282                                 
283                                 Left = rect.Left;
284                                 Top = rect.Top;
285                                 Width = rect.Width;
286                                 Height = rect.Height;
287                         }
288
289                         // To be used when we have a "close button" inside balloon.
290                         //private void HandleClick (object sender, EventArgs e)
291                         //{
292                         //      XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONHIDE);
293                         //      Close ();
294                         //}
295
296                         private void HandleMouseDown (object sender, MouseEventArgs e)
297                         {
298                                 XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONUSERCLICK);
299                                 Close ();
300                         }
301
302                         private void HandleTimer (object sender, EventArgs e)
303                         {
304                                 timer.Stop ();
305                                 XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONTIMEOUT);
306                                 Close ();
307                         }
308                         
309                         internal StringFormat Format {
310                                 get {
311                                         StringFormat format = new StringFormat ();
312                                         format.Alignment = StringAlignment.Near;
313                                         format.HotkeyPrefix = HotkeyPrefix.Hide;
314
315                                         return format;
316                                 }
317                         }
318
319                         public string Title {
320                                 get { return this.title; }
321                                 set { 
322                                         if (value == this.title)
323                                                 return;
324
325                                         this.title = value;
326                                         Recalculate ();
327                                 }
328                         }
329
330                         public string Text {
331                                 get { return this.text; }
332                                 set { 
333                                         if (value == this.text)
334                                                 return;
335
336                                         this.text = value;
337                                         Recalculate ();
338                                 }
339                         }
340                         
341                         public int Timeout {
342                                 get { return timer.Interval; }
343                                 set {
344                                         // Some systems theres a limitiation in timeout, WinXP is between 10k and 30k.
345                                         if (value < 10000)
346                                                 timer.Interval = 10000;
347                                         else if (value > 30000)
348                                                 timer.Interval = 30000;
349                                         else
350                                                 timer.Interval = value;
351                                 }
352                         }
353                 }
354 #endif
355                 #endregion  // NotifyIconBalloonWindow Class
356
357                 #region Public Constructors
358                 public NotifyIcon() {
359                         window = new NotifyIconWindow(this);
360                         systray_active = false;
361
362 #if NET_2_0                     
363                         balloon_title = "";
364                         balloon_text = "";
365 #endif
366                 }
367
368                 public NotifyIcon(System.ComponentModel.IContainer container) : this() {
369                 }
370                 #endregion      // Public Constructors
371
372                 #region Public Methods
373 #if NET_2_0
374                 public void ShowBalloonTip (int timeout)
375                 {
376                         ShowBalloonTip(timeout, balloon_title, balloon_text, balloon_icon);
377                 }
378
379                 public void ShowBalloonTip(int timeout, string title, string text, ToolTipIcon icon)
380                 {
381                         XplatUI.SystrayBalloon(window.Handle, timeout, title, text, icon);
382                 }
383 #endif
384                 #endregion Public Methods
385                 
386                 #region Private Methods
387 #if NET_2_0
388                 private void OnBalloonTipClicked (EventArgs e)
389                 {
390                         EventHandler eh = (EventHandler)(Events [BalloonTipClickedEvent]);
391                         if (eh != null)
392                                 eh (this, e);
393                 }
394
395                 private void OnBalloonTipClosed (EventArgs e)
396                 {
397                         EventHandler eh = (EventHandler)(Events [BalloonTipClosedEvent]);
398                         if (eh != null)
399                                 eh (this, e);
400                 }
401
402                 private void OnBalloonTipShown (EventArgs e)
403                 {
404                         EventHandler eh = (EventHandler)(Events [BalloonTipShownEvent]);
405                         if (eh != null)
406                                 eh (this, e);
407                 }
408 #endif
409                 private void OnClick (EventArgs e)
410                 {
411                         EventHandler eh = (EventHandler)(Events [ClickEvent]);
412                         if (eh != null)
413                                 eh (this, e);
414                 }
415
416                 private void OnDoubleClick (EventArgs e)
417                 {
418                         EventHandler eh = (EventHandler)(Events [DoubleClickEvent]);
419                         if (eh != null)
420                                 eh (this, e);
421                 }
422
423                 private void OnMouseDown (MouseEventArgs e)
424                 {
425                         MouseEventHandler eh = (MouseEventHandler)(Events [MouseDownEvent]);
426                         if (eh != null)
427                                 eh (this, e);
428                 }
429
430                 private void OnMouseUp (MouseEventArgs e)
431                 {
432                         if ((e.Button & MouseButtons.Right) == MouseButtons.Right && context_menu != null)
433                                 context_menu.Show (window, new Point(e.X, e.Y));
434 #if NET_2_0
435                         else if ((e.Button & MouseButtons.Right) == MouseButtons.Right && context_menu_strip != null)
436                                 context_menu_strip.Show (window, new Point (e.X, e.Y), ToolStripDropDownDirection.AboveLeft);
437 #endif
438                         
439                         MouseEventHandler eh = (MouseEventHandler)(Events [MouseUpEvent]);
440                         if (eh != null)
441                                 eh (this, e);
442                 }
443
444                 private void OnMouseMove (MouseEventArgs e)
445                 {
446                         MouseEventHandler eh = (MouseEventHandler)(Events [MouseMoveEvent]);
447                         if (eh != null)
448                                 eh (this, e);
449                 }
450
451                 private void Recalculate () 
452                 {
453                         window.CalculateIconRect ();
454
455                         if (systray_active)
456                                 UpdateSystray ();
457                 }
458
459                 private void ShowSystray()
460                 {
461                         systray_active = true;
462
463                         if (icon == null)
464                                 return;
465
466                         icon_bitmap = icon.ToBitmap();
467
468                         XplatUI.SystrayAdd(window.Handle, text, icon, out tooltip);
469                 }
470
471                 private void HideSystray()
472                 {
473                         if (!systray_active) {
474                                 return;
475                         }
476
477                         systray_active = false;
478                         XplatUI.SystrayRemove(window.Handle, ref tooltip);
479                 }
480
481                 private void UpdateSystray()
482                 {
483                         if (icon_bitmap != null) {
484                                 icon_bitmap.Dispose();
485                         }
486
487                         if (icon != null) {
488                                 icon_bitmap = icon.ToBitmap();
489                         }
490
491                         XplatUI.SystrayChange(window.Handle, text, icon, ref tooltip);
492                         window.Invalidate();
493                 }
494                 #endregion      // Private Methods
495
496                 #region Public Instance Properties
497 #if NET_2_0
498                 public ToolTipIcon BalloonTipIcon {
499                         get { return this.balloon_icon; }
500                         set {
501                                 if (value == this.balloon_icon)
502                                         return;
503                 
504                 this.balloon_icon = value;
505                         }
506                 }
507
508                 [Localizable(true)]
509                 public string BalloonTipText {
510                         get { return this.balloon_text; }
511                         set {
512                                 if (value == this.balloon_text)
513                                         return;
514                                 
515                                 this.balloon_text = value;
516                         }
517                 }
518                 
519                 [Localizable(true)]
520                 public string BalloonTipTitle {
521                         get { return this.balloon_title; }
522                         set {
523                                 if (value == this.balloon_title)
524                                         return;
525         
526                                 this.balloon_title = value;
527                         }
528                 }
529 #endif
530                 
531                 [DefaultValue(null)]
532 #if NET_2_0
533                 [Browsable (false)]
534 #endif
535                 public ContextMenu ContextMenu {
536                         get {
537                                 return context_menu;
538                         }
539
540                         set {
541                                 if (context_menu != value) {
542                                         context_menu = value;
543                                         window.ContextMenu = value;
544                                 }
545                         }
546                 }
547
548 #if NET_2_0
549                 [DefaultValue (null)]
550                 public ContextMenuStrip ContextMenuStrip {
551                         get { return this.context_menu_strip; }
552                         set {
553                                 if (this.context_menu_strip != value) {
554                                         this.context_menu_strip = value;
555                                         window.ContextMenuStrip = value;
556                                 }
557                         }
558                 }
559 #endif
560
561                 [Localizable(true)]
562                 [DefaultValue(null)]
563                 public Icon Icon {
564                         get {
565                                 return icon;
566                         }
567
568                         set {
569                                 if (icon != value) {
570                                         icon = value;
571                                         if (text == string.Empty && icon == null) {
572                                                 HideSystray ();
573                                         }
574                                         else {
575                                                 Recalculate ();
576                                         }
577                                 }
578                         }
579                 }
580
581 #if NET_2_0
582                 [Localizable (false)]
583                 [Bindable (true)]
584                 [TypeConverter (typeof (StringConverter))]
585                 [DefaultValue (null)]
586                 public object Tag {
587                         get { return this.tag; }
588                         set { this.tag = value; }
589                 }
590
591                 [DefaultValue ("")]
592                 [Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
593                          typeof (System.Drawing.Design.UITypeEditor))]
594 #endif
595                 [Localizable (true)]
596                 public string Text {
597                         get {
598                                 return text;
599                         }
600
601                         set {
602                                 if (text != value) {
603                                         if (value.Length >= 64) {
604                                                 throw new ArgumentException("ToolTip length must be less than 64 characters long", "Text");
605                                         }
606                                         text = value;
607                                         if (text == string.Empty && icon == null) {
608                                                 HideSystray();
609                                         } else {
610                                                 Recalculate ();
611                                         }
612                                 }
613                         }
614                 }
615
616                 [Localizable(true)]
617                 [DefaultValue(false)]
618                 public bool Visible {
619                         get {
620                                 return visible;
621                         }
622
623                         set {
624                                 if (visible != value) {
625                                         visible = value;
626
627                                         // Let our control know, too
628                                         window.is_visible = value;
629
630                                         if (visible) {
631                                                 ShowSystray ();
632                                         } else {
633                                                 HideSystray();
634                                         }
635                                 }
636                         }
637                 }
638                 #endregion      // Public Instance Properties
639
640                 #region Protected Instance Methods
641                 protected override void Dispose(bool disposing) {
642                         if (visible)
643                                 HideSystray();
644
645                         if (icon_bitmap != null) {
646                                 icon_bitmap.Dispose();
647                         }
648
649                         if (disposing)
650                                 icon = null;
651
652                         base.Dispose (disposing);
653                 }
654
655                 #endregion      // Protected Instance Methods
656
657                 #region Events
658                 static object ClickEvent = new object ();
659                 static object DoubleClickEvent = new object ();
660                 static object MouseDownEvent = new object ();
661                 static object MouseMoveEvent = new object ();
662                 static object MouseUpEvent = new object ();
663
664 #if NET_2_0
665                 static object BalloonTipClickedEvent = new object ();
666                 static object BalloonTipClosedEvent = new object ();
667                 static object BalloonTipShownEvent = new object ();
668
669                 [MWFCategory("Action")]
670                 public event EventHandler BalloonTipClicked {
671                         add { Events.AddHandler (BalloonTipClickedEvent, value); }
672                         remove { Events.RemoveHandler (BalloonTipClickedEvent, value); }
673                 }
674
675                 [MWFCategory("Action")]
676                 public event EventHandler BalloonTipClosed {
677                         add { Events.AddHandler (BalloonTipClosedEvent, value); }
678                         remove { Events.RemoveHandler (BalloonTipClosedEvent, value); }
679                 }
680
681                 [MWFCategory("Action")]
682                 public event EventHandler BalloonTipShown {
683                         add { Events.AddHandler (BalloonTipShownEvent, value); }
684                         remove { Events.RemoveHandler (BalloonTipShownEvent, value); }
685                 }
686 #endif
687
688 #if NET_2_0
689                 [MWFCategory("Action")]
690 #else
691                 [Category("Action")]
692 #endif
693                 public event EventHandler Click {
694                         add { Events.AddHandler (ClickEvent, value); }
695                         remove { Events.RemoveHandler (ClickEvent, value); }
696                 }
697
698 #if NET_2_0
699                 [MWFCategory("Action")]
700 #else
701                 [Category("Action")]
702 #endif
703                 public event EventHandler DoubleClick {
704                         add { Events.AddHandler (DoubleClickEvent, value); }
705                         remove { Events.RemoveHandler (DoubleClickEvent, value); }
706                 }
707
708                 public event MouseEventHandler MouseDown {
709                         add { Events.AddHandler (MouseDownEvent, value); }
710                         remove { Events.RemoveHandler (MouseDownEvent, value); }
711                 }
712
713                 public event MouseEventHandler MouseMove {
714                         add { Events.AddHandler (MouseMoveEvent, value); }
715                         remove { Events.RemoveHandler (MouseMoveEvent, value); }
716                 }
717
718                 public event MouseEventHandler MouseUp {
719                         add { Events.AddHandler (MouseUpEvent, value); }
720                         remove { Events.RemoveHandler (MouseUpEvent, value); }
721                 }
722
723                 #endregion      // Events
724         }
725 }