diff -r f0015e909f41 -r fe939a729030 Server/NotifyIconAdv.cs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Server/NotifyIconAdv.cs Sun Jan 18 18:11:32 2015 +0100 @@ -0,0 +1,969 @@ +/* + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + + Copyright (C) 2012 Michael Möller + +*/ + +using System; +using System.ComponentModel; +using System.Drawing; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace SharpDisplayManager +{ + public class NotifyIconAdv : IDisposable + { + private NotifyIcon genericNotifyIcon; + private NotifyIconWindowsImplementation windowsNotifyIcon; + + public NotifyIconAdv() + { + int p = (int)Environment.OSVersion.Platform; + if ((p == 4) || (p == 128)) + { // Unix + genericNotifyIcon = new NotifyIcon(); + } + else + { // Windows + windowsNotifyIcon = new NotifyIconWindowsImplementation(); + } + } + + public event EventHandler BalloonTipClicked + { + add + { + if (genericNotifyIcon != null) + genericNotifyIcon.BalloonTipClicked += value; + else + windowsNotifyIcon.BalloonTipClicked += value; + } + remove + { + if (genericNotifyIcon != null) + genericNotifyIcon.BalloonTipClicked -= value; + else + windowsNotifyIcon.BalloonTipClicked -= value; + } + } + + public event EventHandler BalloonTipClosed + { + add + { + if (genericNotifyIcon != null) + genericNotifyIcon.BalloonTipClosed += value; + else + windowsNotifyIcon.BalloonTipClosed += value; + } + remove + { + if (genericNotifyIcon != null) + genericNotifyIcon.BalloonTipClosed -= value; + else + windowsNotifyIcon.BalloonTipClosed -= value; + } + } + + public event EventHandler BalloonTipShown + { + add + { + if (genericNotifyIcon != null) + genericNotifyIcon.BalloonTipShown += value; + else + windowsNotifyIcon.BalloonTipShown += value; + } + remove + { + if (genericNotifyIcon != null) + genericNotifyIcon.BalloonTipShown -= value; + else + windowsNotifyIcon.BalloonTipShown -= value; + } + } + + public event EventHandler Click + { + add + { + if (genericNotifyIcon != null) + genericNotifyIcon.Click += value; + else + windowsNotifyIcon.Click += value; + } + remove + { + if (genericNotifyIcon != null) + genericNotifyIcon.Click -= value; + else + windowsNotifyIcon.Click -= value; + } + } + + public event EventHandler DoubleClick + { + add + { + if (genericNotifyIcon != null) + genericNotifyIcon.DoubleClick += value; + else + windowsNotifyIcon.DoubleClick += value; + } + remove + { + if (genericNotifyIcon != null) + genericNotifyIcon.DoubleClick -= value; + else + windowsNotifyIcon.DoubleClick -= value; + } + } + + public event MouseEventHandler MouseClick + { + add + { + if (genericNotifyIcon != null) + genericNotifyIcon.MouseClick += value; + else + windowsNotifyIcon.MouseClick += value; + } + remove + { + if (genericNotifyIcon != null) + genericNotifyIcon.MouseClick -= value; + else + windowsNotifyIcon.MouseClick -= value; + } + } + + public event MouseEventHandler MouseDoubleClick + { + add + { + if (genericNotifyIcon != null) + genericNotifyIcon.MouseDoubleClick += value; + else + windowsNotifyIcon.MouseDoubleClick += value; + } + remove + { + if (genericNotifyIcon != null) + genericNotifyIcon.MouseDoubleClick -= value; + else + windowsNotifyIcon.MouseDoubleClick -= value; + } + } + + public event MouseEventHandler MouseDown + { + add + { + if (genericNotifyIcon != null) + genericNotifyIcon.MouseDown += value; + else + windowsNotifyIcon.MouseDown += value; + } + remove + { + if (genericNotifyIcon != null) + genericNotifyIcon.MouseDown -= value; + else + windowsNotifyIcon.MouseDown -= value; + } + } + + public event MouseEventHandler MouseMove + { + add + { + if (genericNotifyIcon != null) + genericNotifyIcon.MouseMove += value; + else + windowsNotifyIcon.MouseMove += value; + } + remove + { + if (genericNotifyIcon != null) + genericNotifyIcon.MouseMove -= value; + else + windowsNotifyIcon.MouseMove -= value; + } + } + + public event MouseEventHandler MouseUp + { + add + { + if (genericNotifyIcon != null) + genericNotifyIcon.MouseUp += value; + else + windowsNotifyIcon.MouseUp += value; + } + remove + { + if (genericNotifyIcon != null) + genericNotifyIcon.MouseUp -= value; + else + windowsNotifyIcon.MouseUp -= value; + } + } + + public string BalloonTipText + { + get + { + if (genericNotifyIcon != null) + return genericNotifyIcon.BalloonTipText; + else + return windowsNotifyIcon.BalloonTipText; + } + set + { + if (genericNotifyIcon != null) + genericNotifyIcon.BalloonTipText = value; + else + windowsNotifyIcon.BalloonTipText = value; + } + } + + public ToolTipIcon BalloonTipIcon + { + get + { + if (genericNotifyIcon != null) + return genericNotifyIcon.BalloonTipIcon; + else + return windowsNotifyIcon.BalloonTipIcon; + } + set + { + if (genericNotifyIcon != null) + genericNotifyIcon.BalloonTipIcon = value; + else + windowsNotifyIcon.BalloonTipIcon = value; + } + } + + public string BalloonTipTitle + { + get + { + if (genericNotifyIcon != null) + return genericNotifyIcon.BalloonTipTitle; + else + return windowsNotifyIcon.BalloonTipTitle; + } + set + { + if (genericNotifyIcon != null) + genericNotifyIcon.BalloonTipTitle = value; + else + windowsNotifyIcon.BalloonTipTitle = value; + } + } + + public ContextMenu ContextMenu + { + get + { + if (genericNotifyIcon != null) + return genericNotifyIcon.ContextMenu; + else + return windowsNotifyIcon.ContextMenu; + } + set + { + if (genericNotifyIcon != null) + genericNotifyIcon.ContextMenu = value; + else + windowsNotifyIcon.ContextMenu = value; + } + } + + public ContextMenuStrip ContextMenuStrip + { + get + { + if (genericNotifyIcon != null) + return genericNotifyIcon.ContextMenuStrip; + else + return windowsNotifyIcon.ContextMenuStrip; + } + set + { + if (genericNotifyIcon != null) + genericNotifyIcon.ContextMenuStrip = value; + else + windowsNotifyIcon.ContextMenuStrip = value; + } + } + + public object Tag { get; set; } + + public Icon Icon + { + get + { + if (genericNotifyIcon != null) + return genericNotifyIcon.Icon; + else + return windowsNotifyIcon.Icon; + } + set + { + if (genericNotifyIcon != null) + genericNotifyIcon.Icon = value; + else + windowsNotifyIcon.Icon = value; + } + } + + public string Text + { + get + { + if (genericNotifyIcon != null) + return genericNotifyIcon.Text; + else + return windowsNotifyIcon.Text; + } + set + { + if (genericNotifyIcon != null) + genericNotifyIcon.Text = value; + else + windowsNotifyIcon.Text = value; + } + } + + public bool Visible + { + get + { + if (genericNotifyIcon != null) + return genericNotifyIcon.Visible; + else + return windowsNotifyIcon.Visible; + } + set + { + if (genericNotifyIcon != null) + genericNotifyIcon.Visible = value; + else + windowsNotifyIcon.Visible = value; + } + } + + public void Dispose() + { + if (genericNotifyIcon != null) + genericNotifyIcon.Dispose(); + else + windowsNotifyIcon.Dispose(); + } + + public void ShowBalloonTip(int timeout) + { + ShowBalloonTip(timeout, BalloonTipTitle, BalloonTipText, BalloonTipIcon); + } + + public void ShowBalloonTip(int timeout, string tipTitle, string tipText, + ToolTipIcon tipIcon) + { + if (genericNotifyIcon != null) + genericNotifyIcon.ShowBalloonTip(timeout, tipTitle, tipText, tipIcon); + else + windowsNotifyIcon.ShowBalloonTip(timeout, tipTitle, tipText, tipIcon); + } + + private class NotifyIconWindowsImplementation : Component + { + private static int nextId = 0; + + private object syncObj = new object(); + private Icon icon; + private string text = ""; + private int id; + private bool created; + private NotifyIconNativeWindow window; + private bool doubleClickDown; + private bool visible; + private MethodInfo commandDispatch; + + public event EventHandler BalloonTipClicked; + + public event EventHandler BalloonTipClosed; + + public event EventHandler BalloonTipShown; + + public event EventHandler Click; + + public event EventHandler DoubleClick; + + public event MouseEventHandler MouseClick; + + public event MouseEventHandler MouseDoubleClick; + + public event MouseEventHandler MouseDown; + + public event MouseEventHandler MouseMove; + + public event MouseEventHandler MouseUp; + + public string BalloonTipText { get; set; } + + public ToolTipIcon BalloonTipIcon { get; set; } + + public string BalloonTipTitle { get; set; } + + public ContextMenu ContextMenu { get; set; } + + public ContextMenuStrip ContextMenuStrip { get; set; } + + public object Tag { get; set; } + + public Icon Icon + { + get + { + return icon; + } + set + { + if (icon != value) + { + icon = value; + UpdateNotifyIcon(visible); + } + } + } + + public string Text + { + get + { + return text; + } + set + { + if (value == null) + value = ""; + + if (value.Length > 63) + throw new ArgumentOutOfRangeException(); + + if (!value.Equals(text)) + { + text = value; + + if (visible) + UpdateNotifyIcon(visible); + } + } + } + + public bool Visible + { + get + { + return visible; + } + set + { + if (visible != value) + { + visible = value; + UpdateNotifyIcon(visible); + } + } + } + + public NotifyIconWindowsImplementation() + { + BalloonTipText = ""; + BalloonTipTitle = ""; + + commandDispatch = typeof(Form).Assembly. + GetType("System.Windows.Forms.Command").GetMethod("DispatchID", + BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, + null, new Type[] { typeof(int) }, null); + + id = ++NotifyIconWindowsImplementation.nextId; + window = new NotifyIconNativeWindow(this); + UpdateNotifyIcon(visible); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (window != null) + { + icon = null; + text = ""; + UpdateNotifyIcon(false); + window.DestroyHandle(); + window = null; + ContextMenu = null; + ContextMenuStrip = null; + } + } + else + { + if (window != null && window.Handle != IntPtr.Zero) + { + NativeMethods.PostMessage( + new HandleRef(window, window.Handle), WM_CLOSE, 0, 0); + window.ReleaseHandle(); + } + } + base.Dispose(disposing); + } + + public void ShowBalloonTip(int timeout) + { + ShowBalloonTip(timeout, BalloonTipTitle, BalloonTipText, BalloonTipIcon); + } + + public void ShowBalloonTip(int timeout, string tipTitle, string tipText, + ToolTipIcon tipIcon) + { + if (timeout < 0) + throw new ArgumentOutOfRangeException("timeout"); + + if (string.IsNullOrEmpty(tipText)) + throw new ArgumentException("tipText"); + + if (DesignMode) + return; + + if (created) + { + NativeMethods.NotifyIconData data = new NativeMethods.NotifyIconData(); + if (window.Handle == IntPtr.Zero) + window.CreateHandle(new CreateParams()); + + data.Window = window.Handle; + data.ID = id; + data.Flags = NativeMethods.NotifyIconDataFlags.Info; + data.TimeoutOrVersion = timeout; + data.InfoTitle = tipTitle; + data.Info = tipText; + data.InfoFlags = (int)tipIcon; + + NativeMethods.Shell_NotifyIcon( + NativeMethods.NotifyIconMessage.Modify, data); + } + } + + private void ShowContextMenu() + { + if (ContextMenu == null && ContextMenuStrip == null) + return; + + NativeMethods.Point p = new NativeMethods.Point(); + NativeMethods.GetCursorPos(ref p); + NativeMethods.SetForegroundWindow( + new HandleRef(window, window.Handle)); + + if (ContextMenu != null) + { + ContextMenu.GetType().InvokeMember("OnPopup", + BindingFlags.NonPublic | BindingFlags.InvokeMethod | + BindingFlags.Instance, null, ContextMenu, + new Object[] { System.EventArgs.Empty }); + + NativeMethods.TrackPopupMenuEx( + new HandleRef(ContextMenu, ContextMenu.Handle), 72, + p.x, p.y, new HandleRef(window, window.Handle), + IntPtr.Zero); + + NativeMethods.PostMessage( + new HandleRef(window, window.Handle), WM_NULL, 0, 0); + return; + } + + if (ContextMenuStrip != null) + ContextMenuStrip.GetType().InvokeMember("ShowInTaskbar", + BindingFlags.NonPublic | BindingFlags.InvokeMethod | + BindingFlags.Instance, null, ContextMenuStrip, + new Object[] { p.x, p.y }); + } + + private void UpdateNotifyIcon(bool showNotifyIcon) + { + if (DesignMode) + return; + + lock (syncObj) + { + window.LockReference(showNotifyIcon); + + NativeMethods.NotifyIconData data = new NativeMethods.NotifyIconData(); + data.CallbackMessage = WM_TRAYMOUSEMESSAGE; + data.Flags = NativeMethods.NotifyIconDataFlags.Message; + + if (showNotifyIcon && window.Handle == IntPtr.Zero) + window.CreateHandle(new CreateParams()); + + data.Window = window.Handle; + data.ID = id; + + if (icon != null) + { + data.Flags |= NativeMethods.NotifyIconDataFlags.Icon; + data.Icon = icon.Handle; + } + + data.Flags |= NativeMethods.NotifyIconDataFlags.Tip; + data.Tip = text; + + if (showNotifyIcon && icon != null) + { + if (!created) + { + int i = 0; + do + { + created = NativeMethods.Shell_NotifyIcon( + NativeMethods.NotifyIconMessage.Add, data); + if (!created) + { + System.Threading.Thread.Sleep(200); + i++; + } + } while (!created && i < 40); + } + else + { + NativeMethods.Shell_NotifyIcon( + NativeMethods.NotifyIconMessage.Modify, data); + } + } + else + { + if (created) + { + int i = 0; + bool deleted = false; + do + { + deleted = NativeMethods.Shell_NotifyIcon( + NativeMethods.NotifyIconMessage.Delete, data); + if (!deleted) + { + System.Threading.Thread.Sleep(200); + i++; + } + } while (!deleted && i < 40); + created = false; + } + } + } + } + + private void ProcessMouseDown(ref Message message, MouseButtons button, + bool doubleClick) + { + if (doubleClick) + { + if (DoubleClick != null) + DoubleClick(this, new MouseEventArgs(button, 2, 0, 0, 0)); + + if (MouseDoubleClick != null) + MouseDoubleClick(this, new MouseEventArgs(button, 2, 0, 0, 0)); + + doubleClickDown = true; + } + + if (MouseDown != null) + MouseDown(this, + new MouseEventArgs(button, doubleClick ? 2 : 1, 0, 0, 0)); + } + + private void ProcessMouseUp(ref Message message, MouseButtons button) + { + if (MouseUp != null) + MouseUp(this, new MouseEventArgs(button, 0, 0, 0, 0)); + + if (!doubleClickDown) + { + if (Click != null) + Click(this, new MouseEventArgs(button, 0, 0, 0, 0)); + + if (MouseClick != null) + MouseClick(this, new MouseEventArgs(button, 0, 0, 0, 0)); + } + doubleClickDown = false; + } + + private void ProcessInitMenuPopup(ref Message message) + { + if (ContextMenu != null && + (bool)ContextMenu.GetType().InvokeMember("ProcessInitMenuPopup", + BindingFlags.NonPublic | BindingFlags.InvokeMethod | + BindingFlags.Instance, null, ContextMenu, + new Object[] { message.WParam })) + { + return; + } + window.DefWndProc(ref message); + } + + private void WndProc(ref Message message) + { + switch (message.Msg) + { + case WM_DESTROY: + UpdateNotifyIcon(false); + return; + + case WM_COMMAND: + if (message.LParam != IntPtr.Zero) + { + window.DefWndProc(ref message); + return; + } + commandDispatch.Invoke(null, new object[] { + message.WParam.ToInt32() & 0xFFFF }); + return; + + case WM_INITMENUPOPUP: + ProcessInitMenuPopup(ref message); + return; + + case WM_TRAYMOUSEMESSAGE: + switch ((int)message.LParam) + { + case WM_MOUSEMOVE: + if (MouseMove != null) + MouseMove(this, + new MouseEventArgs(Control.MouseButtons, 0, 0, 0, 0)); + return; + + case WM_LBUTTONDOWN: + ProcessMouseDown(ref message, MouseButtons.Left, false); + return; + + case WM_LBUTTONUP: + ProcessMouseUp(ref message, MouseButtons.Left); + return; + + case WM_LBUTTONDBLCLK: + ProcessMouseDown(ref message, MouseButtons.Left, true); + return; + + case WM_RBUTTONDOWN: + ProcessMouseDown(ref message, MouseButtons.Right, false); + return; + + case WM_RBUTTONUP: + if (ContextMenu != null || ContextMenuStrip != null) + ShowContextMenu(); + ProcessMouseUp(ref message, MouseButtons.Right); + return; + + case WM_RBUTTONDBLCLK: + ProcessMouseDown(ref message, MouseButtons.Right, true); + return; + + case WM_MBUTTONDOWN: + ProcessMouseDown(ref message, MouseButtons.Middle, false); + return; + + case WM_MBUTTONUP: + ProcessMouseUp(ref message, MouseButtons.Middle); + return; + + case WM_MBUTTONDBLCLK: + ProcessMouseDown(ref message, MouseButtons.Middle, true); + return; + + case NIN_BALLOONSHOW: + if (BalloonTipShown != null) + BalloonTipShown(this, EventArgs.Empty); + return; + + case NIN_BALLOONHIDE: + case NIN_BALLOONTIMEOUT: + if (BalloonTipClosed != null) + BalloonTipClosed(this, EventArgs.Empty); + return; + + case NIN_BALLOONUSERCLICK: + if (BalloonTipClicked != null) + BalloonTipClicked(this, EventArgs.Empty); + return; + + default: + return; + } + } + + if (message.Msg == NotifyIconWindowsImplementation.WM_TASKBARCREATED) + { + lock (syncObj) + { + created = false; + } + UpdateNotifyIcon(visible); + } + + window.DefWndProc(ref message); + } + + private class NotifyIconNativeWindow : NativeWindow + { + private NotifyIconWindowsImplementation reference; + private GCHandle referenceHandle; + + internal NotifyIconNativeWindow(NotifyIconWindowsImplementation component) + { + this.reference = component; + } + + ~NotifyIconNativeWindow() + { + if (base.Handle != IntPtr.Zero) + NativeMethods.PostMessage( + new HandleRef(this, base.Handle), WM_CLOSE, 0, 0); + } + + public void LockReference(bool locked) + { + if (locked) + { + if (!referenceHandle.IsAllocated) + { + referenceHandle = GCHandle.Alloc(reference, GCHandleType.Normal); + return; + } + } + else + { + if (referenceHandle.IsAllocated) + referenceHandle.Free(); + } + } + + protected override void OnThreadException(Exception e) + { + Application.OnThreadException(e); + } + + protected override void WndProc(ref Message m) + { + reference.WndProc(ref m); + } + } + + private const int WM_NULL = 0x00; + private const int WM_DESTROY = 0x02; + private const int WM_CLOSE = 0x10; + private const int WM_COMMAND = 0x111; + private const int WM_INITMENUPOPUP = 0x117; + private const int WM_MOUSEMOVE = 0x200; + private const int WM_LBUTTONDOWN = 0x201; + private const int WM_LBUTTONUP = 0x202; + private const int WM_LBUTTONDBLCLK = 0x203; + private const int WM_RBUTTONDOWN = 0x204; + private const int WM_RBUTTONUP = 0x205; + private const int WM_RBUTTONDBLCLK = 0x206; + private const int WM_MBUTTONDOWN = 0x207; + private const int WM_MBUTTONUP = 0x208; + private const int WM_MBUTTONDBLCLK = 0x209; + private const int WM_TRAYMOUSEMESSAGE = 0x800; + + private const int NIN_BALLOONSHOW = 0x402; + private const int NIN_BALLOONHIDE = 0x403; + private const int NIN_BALLOONTIMEOUT = 0x404; + private const int NIN_BALLOONUSERCLICK = 0x405; + + private static int WM_TASKBARCREATED = + NativeMethods.RegisterWindowMessage("TaskbarCreated"); + + private static class NativeMethods + { + [DllImport("user32.dll", CharSet = CharSet.Auto)] + public static extern IntPtr PostMessage(HandleRef hwnd, int msg, + int wparam, int lparam); + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + public static extern int RegisterWindowMessage(string msg); + + [Flags] + public enum NotifyIconDataFlags : int + { + Message = 0x1, + Icon = 0x2, + Tip = 0x4, + State = 0x8, + Info = 0x10 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public class NotifyIconData + { + private int Size = Marshal.SizeOf(typeof(NotifyIconData)); + public IntPtr Window; + public int ID; + public NotifyIconDataFlags Flags; + public int CallbackMessage; + public IntPtr Icon; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] + public string Tip; + + public int State; + public int StateMask; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string Info; + + public int TimeoutOrVersion; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] + public string InfoTitle; + + public int InfoFlags; + } + + public enum NotifyIconMessage : int + { + Add = 0x0, + Modify = 0x1, + Delete = 0x2 + } + + [DllImport("shell32.dll", CharSet = CharSet.Auto)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool Shell_NotifyIcon(NotifyIconMessage message, + NotifyIconData pnid); + + [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] + public static extern bool TrackPopupMenuEx(HandleRef hmenu, int fuFlags, + int x, int y, HandleRef hwnd, IntPtr tpm); + + [StructLayout(LayoutKind.Sequential)] + public struct Point + { + public int x; + public int y; + } + + [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] + public static extern bool GetCursorPos(ref Point point); + + [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] + public static extern bool SetForegroundWindow(HandleRef hWnd); + } + } + } +} \ No newline at end of file