773 lines
19 KiB
C#
Raw Normal View History

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Copyright (c) 2005 Novell, Inc. (http://www.novell.com)
//
// Authors:
// Peter Bartok pbartok@novell.com
//
//
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Text;
namespace System.Windows.Forms {
[DefaultProperty("Text")]
[DefaultEvent("MouseDoubleClick")]
[Designer ("System.Windows.Forms.Design.NotifyIconDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
[ToolboxItemFilter("System.Windows.Forms", ToolboxItemFilterType.Allow)]
public sealed class NotifyIcon : Component {
#region Local Variables
private ContextMenu context_menu;
private Icon icon;
private Bitmap icon_bitmap;
private string text;
private bool visible;
private NotifyIconWindow window;
private bool systray_active;
private ToolTip tooltip;
private bool double_click;
private string balloon_text;
private string balloon_title;
private ToolTipIcon balloon_icon;
private ContextMenuStrip context_menu_strip;
private object tag;
#endregion // Local Variables
#region NotifyIconWindow Class
internal class NotifyIconWindow : Form {
NotifyIcon owner;
Rectangle rect;
public NotifyIconWindow(NotifyIcon owner) {
this.owner = owner;
is_visible = false;
rect = new Rectangle(0, 0, 1, 1);
FormBorderStyle = FormBorderStyle.None;
//CreateControl();
SizeChanged += new EventHandler(HandleSizeChanged);
// Events that need to be sent to our parent
DoubleClick += new EventHandler(HandleDoubleClick);
MouseDown +=new MouseEventHandler(HandleMouseDown);
MouseUp +=new MouseEventHandler(HandleMouseUp);
MouseMove +=new MouseEventHandler(HandleMouseMove);
ContextMenu = owner.context_menu;
ContextMenuStrip = owner.context_menu_strip;
}
protected override CreateParams CreateParams {
get {
CreateParams cp;
cp = base.CreateParams;
cp.Parent = IntPtr.Zero;
cp.Style = (int)WindowStyles.WS_POPUP;
cp.Style |= (int)WindowStyles.WS_CLIPSIBLINGS;
cp.ExStyle = (int)(WindowExStyles.WS_EX_TOOLWINDOW);
return cp;
}
}
protected override void WndProc(ref Message m) {
switch((Msg)m.Msg) {
//
// NotifyIcon does CONTEXTMENU on mouse up, not down
// so we swallow the message here, and handle it on our own
//
case Msg.WM_CONTEXTMENU:
return;
case Msg.WM_USER: {
switch ((Msg)m.LParam.ToInt32()) {
case Msg.WM_LBUTTONDOWN: {
owner.OnMouseDown (new MouseEventArgs(MouseButtons.Left, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
return;
}
case Msg.WM_LBUTTONUP: {
owner.OnMouseUp (new MouseEventArgs(MouseButtons.Left, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
return;
}
case Msg.WM_LBUTTONDBLCLK: {
owner.OnDoubleClick (EventArgs.Empty);
owner.OnMouseDoubleClick (new MouseEventArgs (MouseButtons.Left, 2, Control.MousePosition.X, Control.MousePosition.Y, 0));
return;
}
case Msg.WM_MOUSEMOVE: {
owner.OnMouseMove (new MouseEventArgs(MouseButtons.None, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
return;
}
case Msg.WM_RBUTTONDOWN: {
owner.OnMouseDown (new MouseEventArgs(MouseButtons.Right, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
return;
}
case Msg.WM_RBUTTONUP: {
owner.OnMouseUp (new MouseEventArgs(MouseButtons.Right, 1, Control.MousePosition.X, Control.MousePosition.Y, 0));
return;
}
case Msg.WM_RBUTTONDBLCLK: {
owner.OnDoubleClick (EventArgs.Empty);
owner.OnMouseDoubleClick (new MouseEventArgs (MouseButtons.Left, 2, Control.MousePosition.X, Control.MousePosition.Y, 0));
return;
}
case Msg.NIN_BALLOONUSERCLICK: {
owner.OnBalloonTipClicked (EventArgs.Empty);
return;
}
case Msg.NIN_BALLOONSHOW: {
owner.OnBalloonTipShown (EventArgs.Empty);
return;
}
case Msg.NIN_BALLOONHIDE:
case Msg.NIN_BALLOONTIMEOUT: {
owner.OnBalloonTipClosed (EventArgs.Empty);
return;
}
}
return;
}
}
base.WndProc (ref m);
}
internal void CalculateIconRect() {
int x;
int y;
int size;
// Icons are always square. Try to center them in the window
if (ClientRectangle.Width < ClientRectangle.Height) {
size = ClientRectangle.Width;
} else {
size = ClientRectangle.Height;
}
x = this.ClientRectangle.Width / 2 - size / 2;
y = this.ClientRectangle.Height / 2 - size / 2;
rect = new Rectangle(x, y, size, size);
Bounds = new Rectangle (0, 0, size, size);
}
internal override void OnPaintInternal (PaintEventArgs e) {
if (owner.icon != null) {
// At least in Gnome, the background of the panel is the same as the Menu, so we go for it
// instead of (most of the time) plain white.
e.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(SystemColors.Menu), rect);
e.Graphics.DrawImage(owner.icon_bitmap,
rect,
new Rectangle (0, 0, owner.icon_bitmap.Width, owner.icon_bitmap.Height),
GraphicsUnit.Pixel);
}
}
internal void InternalRecreateHandle () {
base.RecreateHandle ();
}
private void HandleSizeChanged(object sender, EventArgs e) {
owner.Recalculate ();
}
private void HandleDoubleClick (object sender, EventArgs e)
{
owner.OnDoubleClick (e);
owner.OnMouseDoubleClick (new MouseEventArgs (MouseButtons.Left, 2, Control.MousePosition.X, Control.MousePosition.Y, 0));
}
private void HandleMouseDown (object sender, MouseEventArgs e)
{
owner.OnMouseDown (e);
}
private void HandleMouseUp (object sender, MouseEventArgs e)
{
owner.OnMouseUp (e);
}
private void HandleMouseMove (object sender, MouseEventArgs e)
{
owner.OnMouseMove (e);
}
}
#endregion // NotifyIconWindow Class
#region NotifyIconBalloonWindow Class
internal class BalloonWindow : Form
{
private IntPtr owner;
private Timer timer;
private string title;
private string text;
private ToolTipIcon icon;
public BalloonWindow (IntPtr owner)
{
this.owner = owner;
StartPosition = FormStartPosition.Manual;
FormBorderStyle = FormBorderStyle.None;
MouseDown += new MouseEventHandler (HandleMouseDown);
timer = new Timer ();
timer.Enabled = false;
timer.Tick += new EventHandler (HandleTimer);
}
public IntPtr OwnerHandle {
get {
return owner;
}
}
protected override void Dispose (bool disposing)
{
if (disposing) {
timer.Stop();
timer.Dispose();
}
base.Dispose (disposing);
}
protected override CreateParams CreateParams {
get {
CreateParams cp;
cp = base.CreateParams;
cp.Style = (int)WindowStyles.WS_POPUP;
cp.Style |= (int)WindowStyles.WS_CLIPSIBLINGS;
cp.ExStyle = (int)(WindowExStyles.WS_EX_TOOLWINDOW | WindowExStyles.WS_EX_TOPMOST);
return cp;
}
}
public new void Close () {
base.Close ();
XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONHIDE);
}
protected override void OnShown (EventArgs e)
{
base.OnShown (e);
timer.Start ();
}
protected override void OnPaint (PaintEventArgs e)
{
ThemeEngine.Current.DrawBalloonWindow (e.Graphics, ClientRectangle, this);
base.OnPaint (e);
}
private void Recalculate ()
{
Rectangle rect = ThemeEngine.Current.BalloonWindowRect (this);
Left = rect.Left;
Top = rect.Top;
Width = rect.Width;
Height = rect.Height;
}
// To be used when we have a "close button" inside balloon.
//private void HandleClick (object sender, EventArgs e)
//{
// Close ();
//}
private void HandleMouseDown (object sender, MouseEventArgs e)
{
XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONUSERCLICK);
base.Close ();
}
private void HandleTimer (object sender, EventArgs e)
{
timer.Stop ();
XplatUI.SendMessage (owner, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONTIMEOUT);
base.Close ();
}
internal StringFormat Format {
get {
StringFormat format = new StringFormat ();
format.Alignment = StringAlignment.Near;
format.HotkeyPrefix = HotkeyPrefix.Hide;
return format;
}
}
public new ToolTipIcon Icon {
get { return this.icon; }
set {
if (value == this.icon)
return;
this.icon = value;
Recalculate ();
}
}
public string Title {
get { return this.title; }
set {
if (value == this.title)
return;
this.title = value;
Recalculate ();
}
}
public override string Text {
get { return this.text; }
set {
if (value == this.text)
return;
this.text = value;
Recalculate ();
}
}
public int Timeout {
get { return timer.Interval; }
set {
// Some systems theres a limitiation in timeout, WinXP is between 10k and 30k.
if (value < 10000)
timer.Interval = 10000;
else if (value > 30000)
timer.Interval = 30000;
else
timer.Interval = value;
}
}
}
#endregion // NotifyIconBalloonWindow Class
#region Public Constructors
public NotifyIcon() {
window = new NotifyIconWindow(this);
systray_active = false;
balloon_title = "";
balloon_text = "";
}
public NotifyIcon(System.ComponentModel.IContainer container) : this() {
}
#endregion // Public Constructors
#region Public Methods
public void ShowBalloonTip (int timeout)
{
ShowBalloonTip(timeout, balloon_title, balloon_text, balloon_icon);
}
public void ShowBalloonTip(int timeout, string tipTitle, string tipText, ToolTipIcon tipIcon)
{
XplatUI.SystrayBalloon(window.Handle, timeout, tipTitle, tipText, tipIcon);
}
#endregion Public Methods
#region Private Methods
private void OnBalloonTipClicked (EventArgs e)
{
EventHandler eh = (EventHandler)(Events [BalloonTipClickedEvent]);
if (eh != null)
eh (this, e);
}
private void OnBalloonTipClosed (EventArgs e)
{
EventHandler eh = (EventHandler)(Events [BalloonTipClosedEvent]);
if (eh != null)
eh (this, e);
}
private void OnBalloonTipShown (EventArgs e)
{
EventHandler eh = (EventHandler)(Events [BalloonTipShownEvent]);
if (eh != null)
eh (this, e);
}
private void OnClick (EventArgs e)
{
EventHandler eh = (EventHandler)(Events [ClickEvent]);
if (eh != null)
eh (this, e);
}
private void OnDoubleClick (EventArgs e)
{
double_click = true;
EventHandler eh = (EventHandler)(Events [DoubleClickEvent]);
if (eh != null)
eh (this, e);
}
private void OnMouseClick (MouseEventArgs e)
{
MouseEventHandler eh = (MouseEventHandler)(Events[MouseClickEvent]);
if (eh != null)
eh (this, e);
}
private void OnMouseDoubleClick (MouseEventArgs e)
{
MouseEventHandler eh = (MouseEventHandler)(Events[MouseDoubleClickEvent]);
if (eh != null)
eh (this, e);
}
private void OnMouseDown (MouseEventArgs e)
{
MouseEventHandler eh = (MouseEventHandler)(Events [MouseDownEvent]);
if (eh != null)
eh (this, e);
}
private void OnMouseUp (MouseEventArgs e)
{
if ((e.Button & MouseButtons.Right) == MouseButtons.Right) {
if (context_menu != null) {
XplatUI.SetForegroundWindow (window.Handle);
context_menu.Show (window, new Point(e.X, e.Y));
}
else if (context_menu_strip != null) {
XplatUI.SetForegroundWindow (window.Handle);
context_menu_strip.Show (window, new Point (e.X, e.Y), ToolStripDropDownDirection.AboveLeft);
}
}
MouseEventHandler eh = (MouseEventHandler)(Events [MouseUpEvent]);
if (eh != null)
eh (this, e);
if (!double_click) {
OnClick (EventArgs.Empty);
OnMouseClick (e);
double_click = false;
}
}
private void OnMouseMove (MouseEventArgs e)
{
MouseEventHandler eh = (MouseEventHandler)(Events [MouseMoveEvent]);
if (eh != null)
eh (this, e);
}
private void Recalculate ()
{
window.CalculateIconRect ();
if (!Visible || (text == string.Empty && icon == null)) {
HideSystray ();
} else {
if (systray_active)
UpdateSystray ();
else
ShowSystray ();
}
}
private void ShowSystray()
{
if (icon == null)
return;
icon_bitmap = icon.ToBitmap();
systray_active = true;
XplatUI.SystrayAdd(window.Handle, text, icon, out tooltip);
}
private void HideSystray()
{
if (!systray_active) {
return;
}
systray_active = false;
XplatUI.SystrayRemove(window.Handle, ref tooltip);
}
private void UpdateSystray()
{
if (icon_bitmap != null) {
icon_bitmap.Dispose();
}
if (icon != null) {
icon_bitmap = icon.ToBitmap();
}
window.Invalidate();
XplatUI.SystrayChange(window.Handle, text, icon, ref tooltip);
}
#endregion // Private Methods
#region Public Instance Properties
[DefaultValue ("None")]
public ToolTipIcon BalloonTipIcon {
get { return this.balloon_icon; }
set {
if (value == this.balloon_icon)
return;
this.balloon_icon = value;
}
}
[Localizable(true)]
[DefaultValue ("")]
[Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
"System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
public string BalloonTipText {
get { return this.balloon_text; }
set {
if (value == this.balloon_text)
return;
this.balloon_text = value;
}
}
[Localizable(true)]
[DefaultValue ("")]
public string BalloonTipTitle {
get { return this.balloon_title; }
set {
if (value == this.balloon_title)
return;
this.balloon_title = value;
}
}
[DefaultValue(null)]
[Browsable (false)]
public ContextMenu ContextMenu {
get {
return context_menu;
}
set {
if (context_menu != value) {
context_menu = value;
window.ContextMenu = value;
}
}
}
[DefaultValue (null)]
public ContextMenuStrip ContextMenuStrip {
get { return this.context_menu_strip; }
set {
if (this.context_menu_strip != value) {
this.context_menu_strip = value;
window.ContextMenuStrip = value;
}
}
}
[Localizable(true)]
[DefaultValue(null)]
public Icon Icon {
get {
return icon;
}
set {
if (icon != value) {
icon = value;
Recalculate ();
}
}
}
[Localizable (false)]
[Bindable (true)]
[TypeConverter (typeof (StringConverter))]
[DefaultValue (null)]
public object Tag {
get { return this.tag; }
set { this.tag = value; }
}
[DefaultValue ("")]
[Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design,
typeof (System.Drawing.Design.UITypeEditor))]
[Localizable (true)]
public string Text {
get {
return text;
}
set {
if (text != value) {
if (value.Length >= 64) {
throw new ArgumentException("ToolTip length must be less than 64 characters long", "Text");
}
text = value;
Recalculate ();
}
}
}
[Localizable(true)]
[DefaultValue(false)]
public bool Visible {
get {
return visible;
}
set {
if (visible != value) {
visible = value;
// Let our control know, too
window.is_visible = value;
if (visible) {
ShowSystray ();
} else {
HideSystray();
}
}
}
}
#endregion // Public Instance Properties
#region Protected Instance Methods
protected override void Dispose(bool disposing) {
if (visible)
HideSystray();
if (icon_bitmap != null) {
icon_bitmap.Dispose();
}
if (disposing)
icon = null;
base.Dispose (disposing);
}
#endregion // Protected Instance Methods
#region Events
static object ClickEvent = new object ();
static object DoubleClickEvent = new object ();
static object MouseDownEvent = new object ();
static object MouseMoveEvent = new object ();
static object MouseUpEvent = new object ();
static object BalloonTipClickedEvent = new object ();
static object BalloonTipClosedEvent = new object ();
static object BalloonTipShownEvent = new object ();
static object MouseClickEvent = new object ();
static object MouseDoubleClickEvent = new object ();
[MWFCategory("Action")]
public event EventHandler BalloonTipClicked {
add { Events.AddHandler (BalloonTipClickedEvent, value); }
remove { Events.RemoveHandler (BalloonTipClickedEvent, value); }
}
[MWFCategory("Action")]
public event EventHandler BalloonTipClosed {
add { Events.AddHandler (BalloonTipClosedEvent, value); }
remove { Events.RemoveHandler (BalloonTipClosedEvent, value); }
}
[MWFCategory("Action")]
public event EventHandler BalloonTipShown {
add { Events.AddHandler (BalloonTipShownEvent, value); }
remove { Events.RemoveHandler (BalloonTipShownEvent, value); }
}
[MWFCategory("Action")]
public event MouseEventHandler MouseClick {
add { Events.AddHandler (MouseClickEvent, value); }
remove { Events.RemoveHandler (MouseClickEvent, value); }
}
[MWFCategory ("Action")]
public event MouseEventHandler MouseDoubleClick {
add { Events.AddHandler (MouseDoubleClickEvent, value); }
remove { Events.RemoveHandler (MouseDoubleClickEvent, value); }
}
[MWFCategory("Action")]
public event EventHandler Click {
add { Events.AddHandler (ClickEvent, value); }
remove { Events.RemoveHandler (ClickEvent, value); }
}
[MWFCategory("Action")]
public event EventHandler DoubleClick {
add { Events.AddHandler (DoubleClickEvent, value); }
remove { Events.RemoveHandler (DoubleClickEvent, value); }
}
public event MouseEventHandler MouseDown {
add { Events.AddHandler (MouseDownEvent, value); }
remove { Events.RemoveHandler (MouseDownEvent, value); }
}
public event MouseEventHandler MouseMove {
add { Events.AddHandler (MouseMoveEvent, value); }
remove { Events.RemoveHandler (MouseMoveEvent, value); }
}
public event MouseEventHandler MouseUp {
add { Events.AddHandler (MouseUpEvent, value); }
remove { Events.RemoveHandler (MouseUpEvent, value); }
}
#endregion // Events
}
}