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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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.
20 // Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
23 // Peter Bartok pbartok@novell.com
28 using System.ComponentModel;
29 using System.ComponentModel.Design;
31 using System.Drawing.Text;
33 namespace System.Windows.Forms {
34 [DefaultProperty("Text")]
35 [DefaultEvent("MouseDoubleClick")]
36 [Designer ("System.Windows.Forms.Design.NotifyIconDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
37 [ToolboxItemFilter("System.Windows.Forms", ToolboxItemFilterType.Allow)]
38 public sealed class NotifyIcon : Component {
39 #region Local Variables
40 private ContextMenu context_menu;
42 private Bitmap icon_bitmap;
45 private NotifyIconWindow window;
46 private bool systray_active;
47 private ToolTip tooltip;
48 private bool double_click;
49 private string balloon_text;
50 private string balloon_title;
51 private ToolTipIcon balloon_icon;
52 private ContextMenuStrip context_menu_strip;
54 #endregion // Local Variables
56 #region NotifyIconWindow Class
57 internal class NotifyIconWindow : Form {
61 public NotifyIconWindow(NotifyIcon owner) {
64 rect = new Rectangle(0, 0, 1, 1);
66 FormBorderStyle = FormBorderStyle.None;
70 SizeChanged += new EventHandler(HandleSizeChanged);
72 // Events that need to be sent to our parent
73 DoubleClick += new EventHandler(HandleDoubleClick);
74 MouseDown +=new MouseEventHandler(HandleMouseDown);
75 MouseUp +=new MouseEventHandler(HandleMouseUp);
76 MouseMove +=new MouseEventHandler(HandleMouseMove);
77 ContextMenu = owner.context_menu;
78 ContextMenuStrip = owner.context_menu_strip;
81 protected override CreateParams CreateParams {
85 cp = base.CreateParams;
87 cp.Parent = IntPtr.Zero;
88 cp.Style = (int)WindowStyles.WS_POPUP;
89 cp.Style |= (int)WindowStyles.WS_CLIPSIBLINGS;
91 cp.ExStyle = (int)(WindowExStyles.WS_EX_TOOLWINDOW);
97 protected override void WndProc(ref Message m) {
100 // NotifyIcon does CONTEXTMENU on mouse up, not down
101 // so we swallow the message here, and handle it on our own
103 case Msg.WM_CONTEXTMENU:
107 switch ((Msg)m.LParam.ToInt32()) {
108 case Msg.WM_LBUTTONDOWN: {
109 owner.OnMouseDown (new MouseEventArgs(MouseButtons.Left, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
113 case Msg.WM_LBUTTONUP: {
114 owner.OnMouseUp (new MouseEventArgs(MouseButtons.Left, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
118 case Msg.WM_LBUTTONDBLCLK: {
119 owner.OnDoubleClick (EventArgs.Empty);
120 owner.OnMouseDoubleClick (new MouseEventArgs (MouseButtons.Left, 2, Control.MousePosition.X, Control.MousePosition.Y, 0));
124 case Msg.WM_MOUSEMOVE: {
125 owner.OnMouseMove (new MouseEventArgs(MouseButtons.None, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
129 case Msg.WM_RBUTTONDOWN: {
130 owner.OnMouseDown (new MouseEventArgs(MouseButtons.Right, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
134 case Msg.WM_RBUTTONUP: {
135 owner.OnMouseUp (new MouseEventArgs(MouseButtons.Right, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
139 case Msg.WM_RBUTTONDBLCLK: {
140 owner.OnDoubleClick (EventArgs.Empty);
141 owner.OnMouseDoubleClick (new MouseEventArgs (MouseButtons.Left, 2, Control.MousePosition.X, Control.MousePosition.Y, 0));
145 case Msg.NIN_BALLOONUSERCLICK: {
146 owner.OnBalloonTipClicked (EventArgs.Empty);
150 case Msg.NIN_BALLOONSHOW: {
151 owner.OnBalloonTipShown (EventArgs.Empty);
155 case Msg.NIN_BALLOONHIDE:
156 case Msg.NIN_BALLOONTIMEOUT: {
157 owner.OnBalloonTipClosed (EventArgs.Empty);
164 base.WndProc (ref m);
167 internal void CalculateIconRect() {
172 // Icons are always square. Try to center them in the window
173 if (ClientRectangle.Width < ClientRectangle.Height) {
174 size = ClientRectangle.Width;
176 size = ClientRectangle.Height;
178 x = this.ClientRectangle.Width / 2 - size / 2;
179 y = this.ClientRectangle.Height / 2 - size / 2;
180 rect = new Rectangle(x, y, size, size);
182 Bounds = new Rectangle (0, 0, size, size);
185 internal override void OnPaintInternal (PaintEventArgs e) {
186 if (owner.icon != null) {
187 // At least in Gnome, the background of the panel is the same as the Menu, so we go for it
188 // instead of (most of the time) plain white.
189 e.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(SystemColors.Menu), rect);
190 e.Graphics.DrawImage(owner.icon_bitmap,
192 new Rectangle (0, 0, owner.icon_bitmap.Width, owner.icon_bitmap.Height),
198 internal void InternalRecreateHandle () {
199 base.RecreateHandle ();
202 private void HandleSizeChanged(object sender, EventArgs e) {
203 owner.Recalculate ();
206 private void HandleDoubleClick (object sender, EventArgs e)
208 owner.OnDoubleClick (e);
209 owner.OnMouseDoubleClick (new MouseEventArgs (MouseButtons.Left, 2, Control.MousePosition.X, Control.MousePosition.Y, 0));
212 private void HandleMouseDown (object sender, MouseEventArgs e)
214 owner.OnMouseDown (e);
217 private void HandleMouseUp (object sender, MouseEventArgs e)
222 private void HandleMouseMove (object sender, MouseEventArgs e)
224 owner.OnMouseMove (e);
227 #endregion // NotifyIconWindow Class
229 #region NotifyIconBalloonWindow Class
230 internal class BalloonWindow : Form
232 private IntPtr owner;
235 private string title;
237 private ToolTipIcon icon;
239 public BalloonWindow (IntPtr owner)
243 StartPosition = FormStartPosition.Manual;
244 FormBorderStyle = FormBorderStyle.None;
246 MouseDown += new MouseEventHandler (HandleMouseDown);
248 timer = new Timer ();
249 timer.Enabled = false;
250 timer.Tick += new EventHandler (HandleTimer);
253 public IntPtr OwnerHandle {
259 protected override void Dispose (bool disposing)
265 base.Dispose (disposing);
268 protected override CreateParams CreateParams {
272 cp = base.CreateParams;
274 cp.Style = (int)WindowStyles.WS_POPUP;
275 cp.Style |= (int)WindowStyles.WS_CLIPSIBLINGS;
277 cp.ExStyle = (int)(WindowExStyles.WS_EX_TOOLWINDOW | WindowExStyles.WS_EX_TOPMOST);
283 public new void Close () {
285 XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONHIDE);
288 protected override void OnShown (EventArgs e)
294 protected override void OnPaint (PaintEventArgs e)
296 ThemeEngine.Current.DrawBalloonWindow (e.Graphics, ClientRectangle, this);
300 private void Recalculate ()
302 Rectangle rect = ThemeEngine.Current.BalloonWindowRect (this);
307 Height = rect.Height;
310 // To be used when we have a "close button" inside balloon.
311 //private void HandleClick (object sender, EventArgs e)
316 private void HandleMouseDown (object sender, MouseEventArgs e)
318 XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONUSERCLICK);
322 private void HandleTimer (object sender, EventArgs e)
325 XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONTIMEOUT);
329 internal StringFormat Format {
331 StringFormat format = new StringFormat ();
332 format.Alignment = StringAlignment.Near;
333 format.HotkeyPrefix = HotkeyPrefix.Hide;
339 public new ToolTipIcon Icon {
340 get { return this.icon; }
342 if (value == this.icon)
350 public string Title {
351 get { return this.title; }
353 if (value == this.title)
361 public override string Text {
362 get { return this.text; }
364 if (value == this.text)
373 get { return timer.Interval; }
375 // Some systems theres a limitiation in timeout, WinXP is between 10k and 30k.
377 timer.Interval = 10000;
378 else if (value > 30000)
379 timer.Interval = 30000;
381 timer.Interval = value;
385 #endregion // NotifyIconBalloonWindow Class
387 #region Public Constructors
388 public NotifyIcon() {
389 window = new NotifyIconWindow(this);
390 systray_active = false;
396 public NotifyIcon(System.ComponentModel.IContainer container) : this() {
398 #endregion // Public Constructors
400 #region Public Methods
401 public void ShowBalloonTip (int timeout)
403 ShowBalloonTip(timeout, balloon_title, balloon_text, balloon_icon);
406 public void ShowBalloonTip(int timeout, string tipTitle, string tipText, ToolTipIcon tipIcon)
408 XplatUI.SystrayBalloon(window.Handle, timeout, tipTitle, tipText, tipIcon);
410 #endregion Public Methods
412 #region Private Methods
413 private void OnBalloonTipClicked (EventArgs e)
415 EventHandler eh = (EventHandler)(Events [BalloonTipClickedEvent]);
420 private void OnBalloonTipClosed (EventArgs e)
422 EventHandler eh = (EventHandler)(Events [BalloonTipClosedEvent]);
427 private void OnBalloonTipShown (EventArgs e)
429 EventHandler eh = (EventHandler)(Events [BalloonTipShownEvent]);
434 private void OnClick (EventArgs e)
436 EventHandler eh = (EventHandler)(Events [ClickEvent]);
441 private void OnDoubleClick (EventArgs e)
444 EventHandler eh = (EventHandler)(Events [DoubleClickEvent]);
449 private void OnMouseClick (MouseEventArgs e)
451 MouseEventHandler eh = (MouseEventHandler)(Events[MouseClickEvent]);
456 private void OnMouseDoubleClick (MouseEventArgs e)
458 MouseEventHandler eh = (MouseEventHandler)(Events[MouseDoubleClickEvent]);
463 private void OnMouseDown (MouseEventArgs e)
465 MouseEventHandler eh = (MouseEventHandler)(Events [MouseDownEvent]);
470 private void OnMouseUp (MouseEventArgs e)
472 if ((e.Button & MouseButtons.Right) == MouseButtons.Right) {
473 if (context_menu != null) {
474 XplatUI.SetForegroundWindow (window.Handle);
475 context_menu.Show (window, new Point(e.X, e.Y));
477 else if (context_menu_strip != null) {
478 XplatUI.SetForegroundWindow (window.Handle);
479 context_menu_strip.Show (window, new Point (e.X, e.Y), ToolStripDropDownDirection.AboveLeft);
483 MouseEventHandler eh = (MouseEventHandler)(Events [MouseUpEvent]);
488 OnClick (EventArgs.Empty);
490 double_click = false;
494 private void OnMouseMove (MouseEventArgs e)
496 MouseEventHandler eh = (MouseEventHandler)(Events [MouseMoveEvent]);
501 private void Recalculate ()
503 window.CalculateIconRect ();
505 if (!Visible || (text == string.Empty && icon == null)) {
516 private void ShowSystray()
521 icon_bitmap = icon.ToBitmap();
523 systray_active = true;
524 XplatUI.SystrayAdd(window.Handle, text, icon, out tooltip);
527 private void HideSystray()
529 if (!systray_active) {
533 systray_active = false;
534 XplatUI.SystrayRemove(window.Handle, ref tooltip);
537 private void UpdateSystray()
539 if (icon_bitmap != null) {
540 icon_bitmap.Dispose();
544 icon_bitmap = icon.ToBitmap();
548 XplatUI.SystrayChange(window.Handle, text, icon, ref tooltip);
550 #endregion // Private Methods
552 #region Public Instance Properties
553 [DefaultValue ("None")]
554 public ToolTipIcon BalloonTipIcon {
555 get { return this.balloon_icon; }
557 if (value == this.balloon_icon)
560 this.balloon_icon = value;
566 [Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
567 "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
568 public string BalloonTipText {
569 get { return this.balloon_text; }
571 if (value == this.balloon_text)
574 this.balloon_text = value;
580 public string BalloonTipTitle {
581 get { return this.balloon_title; }
583 if (value == this.balloon_title)
586 this.balloon_title = value;
592 public ContextMenu ContextMenu {
598 if (context_menu != value) {
599 context_menu = value;
600 window.ContextMenu = value;
605 [DefaultValue (null)]
606 public ContextMenuStrip ContextMenuStrip {
607 get { return this.context_menu_strip; }
609 if (this.context_menu_strip != value) {
610 this.context_menu_strip = value;
611 window.ContextMenuStrip = value;
631 [Localizable (false)]
633 [TypeConverter (typeof (StringConverter))]
634 [DefaultValue (null)]
636 get { return this.tag; }
637 set { this.tag = value; }
641 [Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
642 typeof (System.Drawing.Design.UITypeEditor))]
651 if (value.Length >= 64) {
652 throw new ArgumentException("ToolTip length must be less than 64 characters long", "Text");
661 [DefaultValue(false)]
662 public bool Visible {
668 if (visible != value) {
671 // Let our control know, too
672 window.is_visible = value;
682 #endregion // Public Instance Properties
684 #region Protected Instance Methods
685 protected override void Dispose(bool disposing) {
689 if (icon_bitmap != null) {
690 icon_bitmap.Dispose();
696 base.Dispose (disposing);
699 #endregion // Protected Instance Methods
702 static object ClickEvent = new object ();
703 static object DoubleClickEvent = new object ();
704 static object MouseDownEvent = new object ();
705 static object MouseMoveEvent = new object ();
706 static object MouseUpEvent = new object ();
707 static object BalloonTipClickedEvent = new object ();
708 static object BalloonTipClosedEvent = new object ();
709 static object BalloonTipShownEvent = new object ();
710 static object MouseClickEvent = new object ();
711 static object MouseDoubleClickEvent = new object ();
713 [MWFCategory("Action")]
714 public event EventHandler BalloonTipClicked {
715 add { Events.AddHandler (BalloonTipClickedEvent, value); }
716 remove { Events.RemoveHandler (BalloonTipClickedEvent, value); }
719 [MWFCategory("Action")]
720 public event EventHandler BalloonTipClosed {
721 add { Events.AddHandler (BalloonTipClosedEvent, value); }
722 remove { Events.RemoveHandler (BalloonTipClosedEvent, value); }
725 [MWFCategory("Action")]
726 public event EventHandler BalloonTipShown {
727 add { Events.AddHandler (BalloonTipShownEvent, value); }
728 remove { Events.RemoveHandler (BalloonTipShownEvent, value); }
731 [MWFCategory("Action")]
732 public event MouseEventHandler MouseClick {
733 add { Events.AddHandler (MouseClickEvent, value); }
734 remove { Events.RemoveHandler (MouseClickEvent, value); }
737 [MWFCategory ("Action")]
738 public event MouseEventHandler MouseDoubleClick {
739 add { Events.AddHandler (MouseDoubleClickEvent, value); }
740 remove { Events.RemoveHandler (MouseDoubleClickEvent, value); }
743 [MWFCategory("Action")]
744 public event EventHandler Click {
745 add { Events.AddHandler (ClickEvent, value); }
746 remove { Events.RemoveHandler (ClickEvent, value); }
749 [MWFCategory("Action")]
750 public event EventHandler DoubleClick {
751 add { Events.AddHandler (DoubleClickEvent, value); }
752 remove { Events.RemoveHandler (DoubleClickEvent, value); }
755 public event MouseEventHandler MouseDown {
756 add { Events.AddHandler (MouseDownEvent, value); }
757 remove { Events.RemoveHandler (MouseDownEvent, value); }
760 public event MouseEventHandler MouseMove {
761 add { Events.AddHandler (MouseMoveEvent, value); }
762 remove { Events.RemoveHandler (MouseMoveEvent, value); }
765 public event MouseEventHandler MouseUp {
766 add { Events.AddHandler (MouseUpEvent, value); }
767 remove { Events.RemoveHandler (MouseUpEvent, value); }