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;
52 private bool double_click;
54 private string balloon_text;
55 private string balloon_title;
56 private ToolTipIcon balloon_icon;
57 private ContextMenuStrip context_menu_strip;
60 #endregion // Local Variables
62 #region NotifyIconWindow Class
63 internal class NotifyIconWindow : Form {
67 public NotifyIconWindow(NotifyIcon owner) {
70 rect = new Rectangle(0, 0, 1, 1);
72 FormBorderStyle = FormBorderStyle.None;
76 SizeChanged += new EventHandler(HandleSizeChanged);
78 // Events that need to be sent to our parent
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));
126 case Msg.WM_LBUTTONDBLCLK: {
127 owner.OnDoubleClick (EventArgs.Empty);
129 owner.OnMouseDoubleClick (new MouseEventArgs (MouseButtons.Left, 2, Control.MousePosition.X, Control.MousePosition.Y, 0));
134 case Msg.WM_MOUSEMOVE: {
135 owner.OnMouseMove (new MouseEventArgs(MouseButtons.None, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
139 case Msg.WM_RBUTTONDOWN: {
140 owner.OnMouseDown (new MouseEventArgs(MouseButtons.Right, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
144 case Msg.WM_RBUTTONUP: {
145 owner.OnMouseUp (new MouseEventArgs(MouseButtons.Right, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
149 case Msg.WM_RBUTTONDBLCLK: {
150 owner.OnDoubleClick (EventArgs.Empty);
152 owner.OnMouseDoubleClick (new MouseEventArgs (MouseButtons.Left, 2, Control.MousePosition.X, Control.MousePosition.Y, 0));
157 case Msg.NIN_BALLOONUSERCLICK: {
158 owner.OnBalloonTipClicked (EventArgs.Empty);
162 case Msg.NIN_BALLOONSHOW: {
163 owner.OnBalloonTipShown (EventArgs.Empty);
167 case Msg.NIN_BALLOONHIDE:
168 case Msg.NIN_BALLOONTIMEOUT: {
169 owner.OnBalloonTipClosed (EventArgs.Empty);
177 base.WndProc (ref m);
180 internal void CalculateIconRect() {
185 // Icons are always square. Try to center them in the window
186 if (ClientRectangle.Width < ClientRectangle.Height) {
187 size = ClientRectangle.Width;
189 size = ClientRectangle.Height;
191 x = this.ClientRectangle.Width / 2 - size / 2;
192 y = this.ClientRectangle.Height / 2 - size / 2;
193 rect = new Rectangle(x, y, size, size);
195 Bounds = new Rectangle (0, 0, size, size);
198 internal override void OnPaintInternal (PaintEventArgs e) {
199 if (owner.icon != null) {
200 e.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(SystemColors.Window), rect);
201 e.Graphics.DrawImage(owner.icon_bitmap,
203 new Rectangle (0, 0, owner.icon_bitmap.Width, owner.icon_bitmap.Height),
209 internal void InternalRecreateHandle () {
210 base.RecreateHandle ();
213 private void HandleSizeChanged(object sender, EventArgs e) {
214 owner.Recalculate ();
217 private void HandleDoubleClick (object sender, EventArgs e)
219 owner.OnDoubleClick (e);
221 owner.OnMouseDoubleClick (new MouseEventArgs (MouseButtons.Left, 2, Control.MousePosition.X, Control.MousePosition.Y, 0));
225 private void HandleMouseDown (object sender, MouseEventArgs e)
227 owner.OnMouseDown (e);
230 private void HandleMouseUp (object sender, MouseEventArgs e)
235 private void HandleMouseMove (object sender, MouseEventArgs e)
237 owner.OnMouseMove (e);
240 #endregion // NotifyIconWindow Class
242 #region NotifyIconBalloonWindow Class
244 internal class BalloonWindow : Form
246 private IntPtr owner;
249 private string title;
251 private ToolTipIcon icon;
253 public BalloonWindow (IntPtr owner)
257 StartPosition = FormStartPosition.Manual;
258 FormBorderStyle = FormBorderStyle.None;
260 MouseDown += new MouseEventHandler (HandleMouseDown);
262 timer = new Timer ();
263 timer.Enabled = false;
264 timer.Tick += new EventHandler (HandleTimer);
267 protected override void Dispose (bool disposing)
273 base.Dispose (disposing);
276 protected override CreateParams CreateParams {
280 cp = base.CreateParams;
282 cp.Style = (int)WindowStyles.WS_POPUP;
283 cp.Style |= (int)WindowStyles.WS_CLIPSIBLINGS;
285 cp.ExStyle = (int)(WindowExStyles.WS_EX_TOOLWINDOW | WindowExStyles.WS_EX_TOPMOST);
291 public new void Close () {
293 XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONHIDE);
296 protected override void OnShown (EventArgs e)
302 protected override void OnPaint (PaintEventArgs e)
304 ThemeEngine.Current.DrawBalloonWindow (e.Graphics, ClientRectangle, this);
308 private void Recalculate ()
310 Rectangle rect = ThemeEngine.Current.BalloonWindowRect (this);
315 Height = rect.Height;
318 // To be used when we have a "close button" inside balloon.
319 //private void HandleClick (object sender, EventArgs e)
324 private void HandleMouseDown (object sender, MouseEventArgs e)
326 XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONUSERCLICK);
330 private void HandleTimer (object sender, EventArgs e)
333 XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONTIMEOUT);
337 internal StringFormat Format {
339 StringFormat format = new StringFormat ();
340 format.Alignment = StringAlignment.Near;
341 format.HotkeyPrefix = HotkeyPrefix.Hide;
347 public new ToolTipIcon Icon {
348 get { return this.icon; }
350 if (value == this.icon)
358 public string Title {
359 get { return this.title; }
361 if (value == this.title)
369 public override string Text {
370 get { return this.text; }
372 if (value == this.text)
381 get { return timer.Interval; }
383 // Some systems theres a limitiation in timeout, WinXP is between 10k and 30k.
385 timer.Interval = 10000;
386 else if (value > 30000)
387 timer.Interval = 30000;
389 timer.Interval = value;
394 #endregion // NotifyIconBalloonWindow Class
396 #region Public Constructors
397 public NotifyIcon() {
398 window = new NotifyIconWindow(this);
399 systray_active = false;
407 public NotifyIcon(System.ComponentModel.IContainer container) : this() {
409 #endregion // Public Constructors
411 #region Public Methods
413 public void ShowBalloonTip (int timeout)
415 ShowBalloonTip(timeout, balloon_title, balloon_text, balloon_icon);
418 public void ShowBalloonTip(int timeout, string tipTitle, string tipText, ToolTipIcon tipIcon)
420 XplatUI.SystrayBalloon(window.Handle, timeout, tipTitle, tipText, tipIcon);
423 #endregion Public Methods
425 #region Private Methods
427 private void OnBalloonTipClicked (EventArgs e)
429 EventHandler eh = (EventHandler)(Events [BalloonTipClickedEvent]);
434 private void OnBalloonTipClosed (EventArgs e)
436 EventHandler eh = (EventHandler)(Events [BalloonTipClosedEvent]);
441 private void OnBalloonTipShown (EventArgs e)
443 EventHandler eh = (EventHandler)(Events [BalloonTipShownEvent]);
448 private void OnClick (EventArgs e)
450 EventHandler eh = (EventHandler)(Events [ClickEvent]);
455 private void OnDoubleClick (EventArgs e)
458 EventHandler eh = (EventHandler)(Events [DoubleClickEvent]);
464 private void OnMouseClick (MouseEventArgs e)
466 MouseEventHandler eh = (MouseEventHandler)(Events[MouseClickEvent]);
471 private void OnMouseDoubleClick (MouseEventArgs e)
473 MouseEventHandler eh = (MouseEventHandler)(Events[MouseDoubleClickEvent]);
479 private void OnMouseDown (MouseEventArgs e)
481 MouseEventHandler eh = (MouseEventHandler)(Events [MouseDownEvent]);
486 private void OnMouseUp (MouseEventArgs e)
488 if ((e.Button & MouseButtons.Right) == MouseButtons.Right) {
489 if (context_menu != null) {
490 XplatUI.SetForegroundWindow (window.Handle);
491 context_menu.Show (window, new Point(e.X, e.Y));
494 else if (context_menu_strip != null) {
495 XplatUI.SetForegroundWindow (window.Handle);
496 context_menu_strip.Show (window, new Point (e.X, e.Y), ToolStripDropDownDirection.AboveLeft);
501 MouseEventHandler eh = (MouseEventHandler)(Events [MouseUpEvent]);
506 OnClick (EventArgs.Empty);
510 double_click = false;
514 private void OnMouseMove (MouseEventArgs e)
516 MouseEventHandler eh = (MouseEventHandler)(Events [MouseMoveEvent]);
521 private void Recalculate ()
523 window.CalculateIconRect ();
525 if (!Visible || (text == string.Empty && icon == null)) {
536 private void ShowSystray()
541 icon_bitmap = icon.ToBitmap();
543 systray_active = true;
544 XplatUI.SystrayAdd(window.Handle, text, icon, out tooltip);
547 private void HideSystray()
549 if (!systray_active) {
553 systray_active = false;
554 XplatUI.SystrayRemove(window.Handle, ref tooltip);
557 private void UpdateSystray()
559 if (icon_bitmap != null) {
560 icon_bitmap.Dispose();
564 icon_bitmap = icon.ToBitmap();
568 XplatUI.SystrayChange(window.Handle, text, icon, ref tooltip);
570 #endregion // Private Methods
572 #region Public Instance Properties
574 [DefaultValue ("None")]
575 public ToolTipIcon BalloonTipIcon {
576 get { return this.balloon_icon; }
578 if (value == this.balloon_icon)
581 this.balloon_icon = value;
587 [Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
588 "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
589 public string BalloonTipText {
590 get { return this.balloon_text; }
592 if (value == this.balloon_text)
595 this.balloon_text = value;
601 public string BalloonTipTitle {
602 get { return this.balloon_title; }
604 if (value == this.balloon_title)
607 this.balloon_title = value;
616 public ContextMenu ContextMenu {
622 if (context_menu != value) {
623 context_menu = value;
624 window.ContextMenu = value;
630 [DefaultValue (null)]
631 public ContextMenuStrip ContextMenuStrip {
632 get { return this.context_menu_strip; }
634 if (this.context_menu_strip != value) {
635 this.context_menu_strip = value;
636 window.ContextMenuStrip = value;
658 [Localizable (false)]
660 [TypeConverter (typeof (StringConverter))]
661 [DefaultValue (null)]
663 get { return this.tag; }
664 set { this.tag = value; }
668 [Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
669 typeof (System.Drawing.Design.UITypeEditor))]
679 if (value.Length >= 64) {
680 throw new ArgumentException("ToolTip length must be less than 64 characters long", "Text");
689 [DefaultValue(false)]
690 public bool Visible {
696 if (visible != value) {
699 // Let our control know, too
700 window.is_visible = value;
710 #endregion // Public Instance Properties
712 #region Protected Instance Methods
713 protected override void Dispose(bool disposing) {
717 if (icon_bitmap != null) {
718 icon_bitmap.Dispose();
724 base.Dispose (disposing);
727 #endregion // Protected Instance Methods
730 static object ClickEvent = new object ();
731 static object DoubleClickEvent = new object ();
732 static object MouseDownEvent = new object ();
733 static object MouseMoveEvent = new object ();
734 static object MouseUpEvent = new object ();
737 static object BalloonTipClickedEvent = new object ();
738 static object BalloonTipClosedEvent = new object ();
739 static object BalloonTipShownEvent = new object ();
740 static object MouseClickEvent = new object ();
741 static object MouseDoubleClickEvent = new object ();
743 [MWFCategory("Action")]
744 public event EventHandler BalloonTipClicked {
745 add { Events.AddHandler (BalloonTipClickedEvent, value); }
746 remove { Events.RemoveHandler (BalloonTipClickedEvent, value); }
749 [MWFCategory("Action")]
750 public event EventHandler BalloonTipClosed {
751 add { Events.AddHandler (BalloonTipClosedEvent, value); }
752 remove { Events.RemoveHandler (BalloonTipClosedEvent, value); }
755 [MWFCategory("Action")]
756 public event EventHandler BalloonTipShown {
757 add { Events.AddHandler (BalloonTipShownEvent, value); }
758 remove { Events.RemoveHandler (BalloonTipShownEvent, value); }
761 [MWFCategory("Action")]
762 public event MouseEventHandler MouseClick {
763 add { Events.AddHandler (MouseClickEvent, value); }
764 remove { Events.RemoveHandler (MouseClickEvent, value); }
767 [MWFCategory ("Action")]
768 public event MouseEventHandler MouseDoubleClick {
769 add { Events.AddHandler (MouseDoubleClickEvent, value); }
770 remove { Events.RemoveHandler (MouseDoubleClickEvent, value); }
775 [MWFCategory("Action")]
779 public event EventHandler Click {
780 add { Events.AddHandler (ClickEvent, value); }
781 remove { Events.RemoveHandler (ClickEvent, value); }
785 [MWFCategory("Action")]
789 public event EventHandler DoubleClick {
790 add { Events.AddHandler (DoubleClickEvent, value); }
791 remove { Events.RemoveHandler (DoubleClickEvent, value); }
794 public event MouseEventHandler MouseDown {
795 add { Events.AddHandler (MouseDownEvent, value); }
796 remove { Events.RemoveHandler (MouseDownEvent, value); }
799 public event MouseEventHandler MouseMove {
800 add { Events.AddHandler (MouseMoveEvent, value); }
801 remove { Events.RemoveHandler (MouseMoveEvent, value); }
804 public event MouseEventHandler MouseUp {
805 add { Events.AddHandler (MouseUpEvent, value); }
806 remove { Events.RemoveHandler (MouseUpEvent, value); }