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 public IntPtr OwnerHandle {
273 protected override void Dispose (bool disposing)
279 base.Dispose (disposing);
282 protected override CreateParams CreateParams {
286 cp = base.CreateParams;
288 cp.Style = (int)WindowStyles.WS_POPUP;
289 cp.Style |= (int)WindowStyles.WS_CLIPSIBLINGS;
291 cp.ExStyle = (int)(WindowExStyles.WS_EX_TOOLWINDOW | WindowExStyles.WS_EX_TOPMOST);
297 public new void Close () {
299 XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONHIDE);
302 protected override void OnShown (EventArgs e)
308 protected override void OnPaint (PaintEventArgs e)
310 ThemeEngine.Current.DrawBalloonWindow (e.Graphics, ClientRectangle, this);
314 private void Recalculate ()
316 Rectangle rect = ThemeEngine.Current.BalloonWindowRect (this);
321 Height = rect.Height;
324 // To be used when we have a "close button" inside balloon.
325 //private void HandleClick (object sender, EventArgs e)
330 private void HandleMouseDown (object sender, MouseEventArgs e)
332 XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONUSERCLICK);
336 private void HandleTimer (object sender, EventArgs e)
339 XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONTIMEOUT);
343 internal StringFormat Format {
345 StringFormat format = new StringFormat ();
346 format.Alignment = StringAlignment.Near;
347 format.HotkeyPrefix = HotkeyPrefix.Hide;
353 public new ToolTipIcon Icon {
354 get { return this.icon; }
356 if (value == this.icon)
364 public string Title {
365 get { return this.title; }
367 if (value == this.title)
375 public override string Text {
376 get { return this.text; }
378 if (value == this.text)
387 get { return timer.Interval; }
389 // Some systems theres a limitiation in timeout, WinXP is between 10k and 30k.
391 timer.Interval = 10000;
392 else if (value > 30000)
393 timer.Interval = 30000;
395 timer.Interval = value;
400 #endregion // NotifyIconBalloonWindow Class
402 #region Public Constructors
403 public NotifyIcon() {
404 window = new NotifyIconWindow(this);
405 systray_active = false;
413 public NotifyIcon(System.ComponentModel.IContainer container) : this() {
415 #endregion // Public Constructors
417 #region Public Methods
419 public void ShowBalloonTip (int timeout)
421 ShowBalloonTip(timeout, balloon_title, balloon_text, balloon_icon);
424 public void ShowBalloonTip(int timeout, string tipTitle, string tipText, ToolTipIcon tipIcon)
426 XplatUI.SystrayBalloon(window.Handle, timeout, tipTitle, tipText, tipIcon);
429 #endregion Public Methods
431 #region Private Methods
433 private void OnBalloonTipClicked (EventArgs e)
435 EventHandler eh = (EventHandler)(Events [BalloonTipClickedEvent]);
440 private void OnBalloonTipClosed (EventArgs e)
442 EventHandler eh = (EventHandler)(Events [BalloonTipClosedEvent]);
447 private void OnBalloonTipShown (EventArgs e)
449 EventHandler eh = (EventHandler)(Events [BalloonTipShownEvent]);
454 private void OnClick (EventArgs e)
456 EventHandler eh = (EventHandler)(Events [ClickEvent]);
461 private void OnDoubleClick (EventArgs e)
464 EventHandler eh = (EventHandler)(Events [DoubleClickEvent]);
470 private void OnMouseClick (MouseEventArgs e)
472 MouseEventHandler eh = (MouseEventHandler)(Events[MouseClickEvent]);
477 private void OnMouseDoubleClick (MouseEventArgs e)
479 MouseEventHandler eh = (MouseEventHandler)(Events[MouseDoubleClickEvent]);
485 private void OnMouseDown (MouseEventArgs e)
487 MouseEventHandler eh = (MouseEventHandler)(Events [MouseDownEvent]);
492 private void OnMouseUp (MouseEventArgs e)
494 if ((e.Button & MouseButtons.Right) == MouseButtons.Right) {
495 if (context_menu != null) {
496 XplatUI.SetForegroundWindow (window.Handle);
497 context_menu.Show (window, new Point(e.X, e.Y));
500 else if (context_menu_strip != null) {
501 XplatUI.SetForegroundWindow (window.Handle);
502 context_menu_strip.Show (window, new Point (e.X, e.Y), ToolStripDropDownDirection.AboveLeft);
507 MouseEventHandler eh = (MouseEventHandler)(Events [MouseUpEvent]);
512 OnClick (EventArgs.Empty);
516 double_click = false;
520 private void OnMouseMove (MouseEventArgs e)
522 MouseEventHandler eh = (MouseEventHandler)(Events [MouseMoveEvent]);
527 private void Recalculate ()
529 window.CalculateIconRect ();
531 if (!Visible || (text == string.Empty && icon == null)) {
542 private void ShowSystray()
547 icon_bitmap = icon.ToBitmap();
549 systray_active = true;
550 XplatUI.SystrayAdd(window.Handle, text, icon, out tooltip);
553 private void HideSystray()
555 if (!systray_active) {
559 systray_active = false;
560 XplatUI.SystrayRemove(window.Handle, ref tooltip);
563 private void UpdateSystray()
565 if (icon_bitmap != null) {
566 icon_bitmap.Dispose();
570 icon_bitmap = icon.ToBitmap();
574 XplatUI.SystrayChange(window.Handle, text, icon, ref tooltip);
576 #endregion // Private Methods
578 #region Public Instance Properties
580 [DefaultValue ("None")]
581 public ToolTipIcon BalloonTipIcon {
582 get { return this.balloon_icon; }
584 if (value == this.balloon_icon)
587 this.balloon_icon = value;
593 [Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
594 "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
595 public string BalloonTipText {
596 get { return this.balloon_text; }
598 if (value == this.balloon_text)
601 this.balloon_text = value;
607 public string BalloonTipTitle {
608 get { return this.balloon_title; }
610 if (value == this.balloon_title)
613 this.balloon_title = value;
622 public ContextMenu ContextMenu {
628 if (context_menu != value) {
629 context_menu = value;
630 window.ContextMenu = value;
636 [DefaultValue (null)]
637 public ContextMenuStrip ContextMenuStrip {
638 get { return this.context_menu_strip; }
640 if (this.context_menu_strip != value) {
641 this.context_menu_strip = value;
642 window.ContextMenuStrip = value;
664 [Localizable (false)]
666 [TypeConverter (typeof (StringConverter))]
667 [DefaultValue (null)]
669 get { return this.tag; }
670 set { this.tag = value; }
674 [Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
675 typeof (System.Drawing.Design.UITypeEditor))]
685 if (value.Length >= 64) {
686 throw new ArgumentException("ToolTip length must be less than 64 characters long", "Text");
695 [DefaultValue(false)]
696 public bool Visible {
702 if (visible != value) {
705 // Let our control know, too
706 window.is_visible = value;
716 #endregion // Public Instance Properties
718 #region Protected Instance Methods
719 protected override void Dispose(bool disposing) {
723 if (icon_bitmap != null) {
724 icon_bitmap.Dispose();
730 base.Dispose (disposing);
733 #endregion // Protected Instance Methods
736 static object ClickEvent = new object ();
737 static object DoubleClickEvent = new object ();
738 static object MouseDownEvent = new object ();
739 static object MouseMoveEvent = new object ();
740 static object MouseUpEvent = new object ();
743 static object BalloonTipClickedEvent = new object ();
744 static object BalloonTipClosedEvent = new object ();
745 static object BalloonTipShownEvent = new object ();
746 static object MouseClickEvent = new object ();
747 static object MouseDoubleClickEvent = new object ();
749 [MWFCategory("Action")]
750 public event EventHandler BalloonTipClicked {
751 add { Events.AddHandler (BalloonTipClickedEvent, value); }
752 remove { Events.RemoveHandler (BalloonTipClickedEvent, value); }
755 [MWFCategory("Action")]
756 public event EventHandler BalloonTipClosed {
757 add { Events.AddHandler (BalloonTipClosedEvent, value); }
758 remove { Events.RemoveHandler (BalloonTipClosedEvent, value); }
761 [MWFCategory("Action")]
762 public event EventHandler BalloonTipShown {
763 add { Events.AddHandler (BalloonTipShownEvent, value); }
764 remove { Events.RemoveHandler (BalloonTipShownEvent, value); }
767 [MWFCategory("Action")]
768 public event MouseEventHandler MouseClick {
769 add { Events.AddHandler (MouseClickEvent, value); }
770 remove { Events.RemoveHandler (MouseClickEvent, value); }
773 [MWFCategory ("Action")]
774 public event MouseEventHandler MouseDoubleClick {
775 add { Events.AddHandler (MouseDoubleClickEvent, value); }
776 remove { Events.RemoveHandler (MouseDoubleClickEvent, value); }
781 [MWFCategory("Action")]
785 public event EventHandler Click {
786 add { Events.AddHandler (ClickEvent, value); }
787 remove { Events.RemoveHandler (ClickEvent, value); }
791 [MWFCategory("Action")]
795 public event EventHandler DoubleClick {
796 add { Events.AddHandler (DoubleClickEvent, value); }
797 remove { Events.RemoveHandler (DoubleClickEvent, value); }
800 public event MouseEventHandler MouseDown {
801 add { Events.AddHandler (MouseDownEvent, value); }
802 remove { Events.RemoveHandler (MouseDownEvent, value); }
805 public event MouseEventHandler MouseMove {
806 add { Events.AddHandler (MouseMoveEvent, value); }
807 remove { Events.RemoveHandler (MouseMoveEvent, value); }
810 public event MouseEventHandler MouseUp {
811 add { Events.AddHandler (MouseUpEvent, value); }
812 remove { Events.RemoveHandler (MouseUpEvent, value); }