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")]
36 [DefaultEvent("MouseDoubleClick")]
38 [DefaultEvent("MouseDown")]
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;
46 private Bitmap icon_bitmap;
49 private NotifyIconWindow window;
50 private bool systray_active;
51 private ToolTip tooltip;
53 private string balloon_text;
54 private string balloon_title;
55 private ToolTipIcon balloon_icon;
56 private ContextMenuStrip context_menu_strip;
59 #endregion // Local Variables
61 #region NotifyIconWindow Class
62 internal class NotifyIconWindow : Form {
66 public NotifyIconWindow(NotifyIcon owner) {
69 rect = new Rectangle(0, 0, 1, 1);
71 FormBorderStyle = FormBorderStyle.None;
75 SizeChanged += new EventHandler(HandleSizeChanged);
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;
85 ContextMenuStrip = owner.context_menu_strip;
89 protected override CreateParams CreateParams {
93 cp = base.CreateParams;
95 cp.Parent = IntPtr.Zero;
96 cp.Style = (int)WindowStyles.WS_POPUP;
97 cp.Style |= (int)WindowStyles.WS_CLIPSIBLINGS;
99 cp.ExStyle = (int)(WindowExStyles.WS_EX_TOOLWINDOW);
105 protected override void WndProc(ref Message m) {
108 // NotifyIcon does CONTEXTMENU on mouse up, not down
109 // so we swallow the message here, and handle it on our own
111 case Msg.WM_CONTEXTMENU:
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));
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);
125 owner.OnMouseClick (new MouseEventArgs (MouseButtons.Left, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
130 case Msg.WM_LBUTTONDBLCLK: {
131 owner.OnDoubleClick (EventArgs.Empty);
133 owner.OnMouseDoubleClick (new MouseEventArgs (MouseButtons.Left, 2, Control.MousePosition.X, Control.MousePosition.Y, 0));
138 case Msg.WM_MOUSEMOVE: {
139 owner.OnMouseMove (new MouseEventArgs(MouseButtons.None, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
143 case Msg.WM_RBUTTONDOWN: {
144 owner.OnMouseDown (new MouseEventArgs(MouseButtons.Right, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
148 case Msg.WM_RBUTTONUP: {
149 owner.OnMouseUp (new MouseEventArgs(MouseButtons.Right, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
150 owner.OnClick (EventArgs.Empty);
152 owner.OnMouseClick (new MouseEventArgs (MouseButtons.Right, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
157 case Msg.WM_RBUTTONDBLCLK: {
158 owner.OnDoubleClick (EventArgs.Empty);
160 owner.OnMouseDoubleClick (new MouseEventArgs (MouseButtons.Left, 2, Control.MousePosition.X, Control.MousePosition.Y, 0));
165 case Msg.NIN_BALLOONUSERCLICK: {
166 owner.OnBalloonTipClicked (EventArgs.Empty);
170 case Msg.NIN_BALLOONSHOW: {
171 owner.OnBalloonTipShown (EventArgs.Empty);
175 case Msg.NIN_BALLOONHIDE:
176 case Msg.NIN_BALLOONTIMEOUT: {
177 owner.OnBalloonTipClosed (EventArgs.Empty);
185 base.WndProc (ref m);
188 internal void CalculateIconRect() {
193 // Icons are always square. Try to center them in the window
194 if (ClientRectangle.Width < ClientRectangle.Height) {
195 size = ClientRectangle.Width;
197 size = ClientRectangle.Height;
199 x = this.ClientRectangle.Width / 2 - size / 2;
200 y = this.ClientRectangle.Height / 2 - size / 2;
201 rect = new Rectangle(x, y, size, size);
203 Bounds = new Rectangle (0, 0, size, size);
206 internal override void OnPaintInternal (PaintEventArgs e) {
207 if (owner.icon != null) {
208 e.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(SystemColors.Window), rect);
209 e.Graphics.DrawImage(owner.icon_bitmap,
211 new Rectangle (0, 0, owner.icon_bitmap.Width, owner.icon_bitmap.Height),
217 internal void InternalRecreateHandle () {
218 base.RecreateHandle ();
222 private void HandleSizeChanged(object sender, EventArgs e) {
223 owner.Recalculate ();
226 private void HandleClick (object sender, EventArgs e)
230 owner.OnMouseClick (new MouseEventArgs (MouseButtons.Left, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
234 private void HandleDoubleClick (object sender, EventArgs e)
236 owner.OnDoubleClick (e);
238 owner.OnMouseDoubleClick (new MouseEventArgs (MouseButtons.Left, 2, Control.MousePosition.X, Control.MousePosition.Y, 0));
242 private void HandleMouseDown (object sender, MouseEventArgs e)
244 owner.OnMouseDown (e);
247 private void HandleMouseUp (object sender, MouseEventArgs e)
252 private void HandleMouseMove (object sender, MouseEventArgs e)
254 owner.OnMouseMove (e);
257 #endregion // NotifyIconWindow Class
259 #region NotifyIconBalloonWindow Class
261 internal class BalloonWindow : Form
263 private IntPtr owner;
266 private string title;
268 private ToolTipIcon icon;
270 public BalloonWindow (IntPtr owner)
274 StartPosition = FormStartPosition.Manual;
275 FormBorderStyle = FormBorderStyle.None;
277 MouseDown += new MouseEventHandler (HandleMouseDown);
279 timer = new Timer ();
280 timer.Enabled = false;
281 timer.Tick += new EventHandler (HandleTimer);
284 protected override void Dispose (bool disposing)
290 base.Dispose (disposing);
293 protected override CreateParams CreateParams {
297 cp = base.CreateParams;
299 cp.Style = (int)WindowStyles.WS_POPUP;
300 cp.Style |= (int)WindowStyles.WS_CLIPSIBLINGS;
302 cp.ExStyle = (int)(WindowExStyles.WS_EX_TOOLWINDOW | WindowExStyles.WS_EX_TOPMOST);
308 public new void Close () {
310 XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONHIDE);
313 protected override void OnShown (EventArgs e)
319 protected override void OnPaint (PaintEventArgs e)
321 ThemeEngine.Current.DrawBalloonWindow (e.Graphics, ClientRectangle, this);
325 private void Recalculate ()
327 Rectangle rect = ThemeEngine.Current.BalloonWindowRect (this);
332 Height = rect.Height;
335 // To be used when we have a "close button" inside balloon.
336 //private void HandleClick (object sender, EventArgs e)
341 private void HandleMouseDown (object sender, MouseEventArgs e)
343 XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONUSERCLICK);
347 private void HandleTimer (object sender, EventArgs e)
350 XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONTIMEOUT);
354 internal StringFormat Format {
356 StringFormat format = new StringFormat ();
357 format.Alignment = StringAlignment.Near;
358 format.HotkeyPrefix = HotkeyPrefix.Hide;
364 public new ToolTipIcon Icon {
365 get { return this.icon; }
367 if (value == this.icon)
375 public string Title {
376 get { return this.title; }
378 if (value == this.title)
386 public override string Text {
387 get { return this.text; }
389 if (value == this.text)
398 get { return timer.Interval; }
400 // Some systems theres a limitiation in timeout, WinXP is between 10k and 30k.
402 timer.Interval = 10000;
403 else if (value > 30000)
404 timer.Interval = 30000;
406 timer.Interval = value;
411 #endregion // NotifyIconBalloonWindow Class
413 #region Public Constructors
414 public NotifyIcon() {
415 window = new NotifyIconWindow(this);
416 systray_active = false;
424 public NotifyIcon(System.ComponentModel.IContainer container) : this() {
426 #endregion // Public Constructors
428 #region Public Methods
430 public void ShowBalloonTip (int timeout)
432 ShowBalloonTip(timeout, balloon_title, balloon_text, balloon_icon);
435 public void ShowBalloonTip(int timeout, string tipTitle, string tipText, ToolTipIcon tipIcon)
437 XplatUI.SystrayBalloon(window.Handle, timeout, tipTitle, tipText, tipIcon);
440 #endregion Public Methods
442 #region Private Methods
444 private void OnBalloonTipClicked (EventArgs e)
446 EventHandler eh = (EventHandler)(Events [BalloonTipClickedEvent]);
451 private void OnBalloonTipClosed (EventArgs e)
453 EventHandler eh = (EventHandler)(Events [BalloonTipClosedEvent]);
458 private void OnBalloonTipShown (EventArgs e)
460 EventHandler eh = (EventHandler)(Events [BalloonTipShownEvent]);
465 private void OnClick (EventArgs e)
467 EventHandler eh = (EventHandler)(Events [ClickEvent]);
472 private void OnDoubleClick (EventArgs e)
474 EventHandler eh = (EventHandler)(Events [DoubleClickEvent]);
480 private void OnMouseClick (MouseEventArgs e)
482 MouseEventHandler eh = (MouseEventHandler)(Events[MouseClickEvent]);
487 private void OnMouseDoubleClick (MouseEventArgs e)
489 MouseEventHandler eh = (MouseEventHandler)(Events[MouseDoubleClickEvent]);
495 private void OnMouseDown (MouseEventArgs e)
497 MouseEventHandler eh = (MouseEventHandler)(Events [MouseDownEvent]);
502 private void OnMouseUp (MouseEventArgs e)
504 if ((e.Button & MouseButtons.Right) == MouseButtons.Right && context_menu != null)
505 context_menu.Show (window, new Point(e.X, e.Y));
507 else if ((e.Button & MouseButtons.Right) == MouseButtons.Right && context_menu_strip != null)
508 context_menu_strip.Show (window, new Point (e.X, e.Y), ToolStripDropDownDirection.AboveLeft);
511 MouseEventHandler eh = (MouseEventHandler)(Events [MouseUpEvent]);
516 private void OnMouseMove (MouseEventArgs e)
518 MouseEventHandler eh = (MouseEventHandler)(Events [MouseMoveEvent]);
523 private void Recalculate ()
525 window.CalculateIconRect ();
527 if (!Visible || (text == string.Empty && icon == null)) {
538 private void ShowSystray()
543 icon_bitmap = icon.ToBitmap();
545 systray_active = true;
546 XplatUI.SystrayAdd(window.Handle, text, icon, out tooltip);
549 private void HideSystray()
551 if (!systray_active) {
555 systray_active = false;
556 XplatUI.SystrayRemove(window.Handle, ref tooltip);
559 private void UpdateSystray()
561 if (icon_bitmap != null) {
562 icon_bitmap.Dispose();
566 icon_bitmap = icon.ToBitmap();
570 XplatUI.SystrayChange(window.Handle, text, icon, ref tooltip);
572 #endregion // Private Methods
574 #region Public Instance Properties
576 [DefaultValue ("None")]
577 public ToolTipIcon BalloonTipIcon {
578 get { return this.balloon_icon; }
580 if (value == this.balloon_icon)
583 this.balloon_icon = value;
589 [Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
590 "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
591 public string BalloonTipText {
592 get { return this.balloon_text; }
594 if (value == this.balloon_text)
597 this.balloon_text = value;
603 public string BalloonTipTitle {
604 get { return this.balloon_title; }
606 if (value == this.balloon_title)
609 this.balloon_title = value;
618 public ContextMenu ContextMenu {
624 if (context_menu != value) {
625 context_menu = value;
626 window.ContextMenu = value;
632 [DefaultValue (null)]
633 public ContextMenuStrip ContextMenuStrip {
634 get { return this.context_menu_strip; }
636 if (this.context_menu_strip != value) {
637 this.context_menu_strip = value;
638 window.ContextMenuStrip = value;
660 [Localizable (false)]
662 [TypeConverter (typeof (StringConverter))]
663 [DefaultValue (null)]
665 get { return this.tag; }
666 set { this.tag = value; }
670 [Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
671 typeof (System.Drawing.Design.UITypeEditor))]
681 if (value.Length >= 64) {
682 throw new ArgumentException("ToolTip length must be less than 64 characters long", "Text");
691 [DefaultValue(false)]
692 public bool Visible {
698 if (visible != value) {
701 // Let our control know, too
702 window.is_visible = value;
712 #endregion // Public Instance Properties
714 #region Protected Instance Methods
715 protected override void Dispose(bool disposing) {
719 if (icon_bitmap != null) {
720 icon_bitmap.Dispose();
726 base.Dispose (disposing);
729 #endregion // Protected Instance Methods
732 static object ClickEvent = new object ();
733 static object DoubleClickEvent = new object ();
734 static object MouseDownEvent = new object ();
735 static object MouseMoveEvent = new object ();
736 static object MouseUpEvent = new object ();
739 static object BalloonTipClickedEvent = new object ();
740 static object BalloonTipClosedEvent = new object ();
741 static object BalloonTipShownEvent = new object ();
742 static object MouseClickEvent = new object ();
743 static object MouseDoubleClickEvent = new object ();
745 [MWFCategory("Action")]
746 public event EventHandler BalloonTipClicked {
747 add { Events.AddHandler (BalloonTipClickedEvent, value); }
748 remove { Events.RemoveHandler (BalloonTipClickedEvent, value); }
751 [MWFCategory("Action")]
752 public event EventHandler BalloonTipClosed {
753 add { Events.AddHandler (BalloonTipClosedEvent, value); }
754 remove { Events.RemoveHandler (BalloonTipClosedEvent, value); }
757 [MWFCategory("Action")]
758 public event EventHandler BalloonTipShown {
759 add { Events.AddHandler (BalloonTipShownEvent, value); }
760 remove { Events.RemoveHandler (BalloonTipShownEvent, value); }
763 [MWFCategory("Action")]
764 public event MouseEventHandler MouseClick {
765 add { Events.AddHandler (MouseClickEvent, value); }
766 remove { Events.RemoveHandler (MouseClickEvent, value); }
769 [MWFCategory ("Action")]
770 public event MouseEventHandler MouseDoubleClick {
771 add { Events.AddHandler (MouseDoubleClickEvent, value); }
772 remove { Events.RemoveHandler (MouseDoubleClickEvent, value); }
777 [MWFCategory("Action")]
781 public event EventHandler Click {
782 add { Events.AddHandler (ClickEvent, value); }
783 remove { Events.RemoveHandler (ClickEvent, value); }
787 [MWFCategory("Action")]
791 public event EventHandler DoubleClick {
792 add { Events.AddHandler (DoubleClickEvent, value); }
793 remove { Events.RemoveHandler (DoubleClickEvent, value); }
796 public event MouseEventHandler MouseDown {
797 add { Events.AddHandler (MouseDownEvent, value); }
798 remove { Events.RemoveHandler (MouseDownEvent, value); }
801 public event MouseEventHandler MouseMove {
802 add { Events.AddHandler (MouseMoveEvent, value); }
803 remove { Events.RemoveHandler (MouseMoveEvent, value); }
806 public event MouseEventHandler MouseUp {
807 add { Events.AddHandler (MouseUpEvent, value); }
808 remove { Events.RemoveHandler (MouseUpEvent, value); }