# HG changeset patch
# User moel.mich
# Date 1342293844 0
# Node ID daa9590e1beed69c4bf109f7272e7a9e8bb3f153
# Parent  1dfe9dac1651fdc5dfad9d7fe44e8c8326905958
Fixed Issue 269.

diff -r 1dfe9dac1651 -r daa9590e1bee GUI/NotifyIconAdv.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GUI/NotifyIconAdv.cs	Sat Jul 14 19:24:04 2012 +0000
@@ -0,0 +1,502 @@
+/*
+ 
+  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 <mmoeller@openhardwaremonitor.org>
+	
+*/
+
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Runtime.InteropServices;
+using System.Reflection;
+using System.Windows.Forms;
+
+namespace OpenHardwareMonitor.GUI {
+  public class NotifyIconAdv : 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 NotifyIconAdv() {
+      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 = ++NotifyIconAdv.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 == NotifyIconAdv.WM_TASKBARCREATED) {
+        lock (syncObj) {
+          created = false;
+        }
+        UpdateNotifyIcon(visible);
+      }
+
+      window.DefWndProc(ref message);
+    }
+
+    private class NotifyIconNativeWindow : NativeWindow {
+      private NotifyIconAdv reference;
+      private GCHandle referenceHandle;
+
+      internal NotifyIconNativeWindow(NotifyIconAdv 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);
+    }
+  }
+}
diff -r 1dfe9dac1651 -r daa9590e1bee GUI/SensorNotifyIcon.cs
--- a/GUI/SensorNotifyIcon.cs	Thu Jul 12 10:17:18 2012 +0000
+++ b/GUI/SensorNotifyIcon.cs	Sat Jul 14 19:24:04 2012 +0000
@@ -24,7 +24,7 @@
     private UnitManager unitManager;
 
     private ISensor sensor;
-    private NotifyIcon notifyIcon;
+    private NotifyIconAdv notifyIcon;
     private Bitmap bitmap;
     private Graphics graphics;
     private Color color;
@@ -40,7 +40,7 @@
     {
       this.unitManager = unitManager;
       this.sensor = sensor;
-      this.notifyIcon = new NotifyIcon();
+      this.notifyIcon = new NotifyIconAdv();
 
       Color defaultColor = Color.Black;
       if (sensor.SensorType == SensorType.Load ||
diff -r 1dfe9dac1651 -r daa9590e1bee GUI/SystemTray.cs
--- a/GUI/SystemTray.cs	Thu Jul 12 10:17:18 2012 +0000
+++ b/GUI/SystemTray.cs	Sat Jul 14 19:24:04 2012 +0000
@@ -23,7 +23,7 @@
     private UnitManager unitManager;
     private List<SensorNotifyIcon> list = new List<SensorNotifyIcon>();
     private bool mainIconEnabled = false;
-    private NotifyIcon mainIcon;
+    private NotifyIconAdv mainIcon;
 
     public SystemTray(IComputer computer, PersistentSettings settings,
       UnitManager unitManager) 
@@ -34,7 +34,7 @@
       computer.HardwareAdded += new HardwareEventHandler(HardwareAdded);
       computer.HardwareRemoved += new HardwareEventHandler(HardwareRemoved);
 
-      this.mainIcon = new NotifyIcon();
+      this.mainIcon = new NotifyIconAdv();
 
       ContextMenu contextMenu = new ContextMenu();
       MenuItem hideShowItem = new MenuItem("Hide/Show");
diff -r 1dfe9dac1651 -r daa9590e1bee OpenHardwareMonitor.csproj
--- a/OpenHardwareMonitor.csproj	Thu Jul 12 10:17:18 2012 +0000
+++ b/OpenHardwareMonitor.csproj	Sat Jul 14 19:24:04 2012 +0000
@@ -73,6 +73,9 @@
     <Compile Include="GUI\GadgetWindow.cs" />
     <Compile Include="GUI\Gadget.cs" />
     <Compile Include="GUI\HardwareTypeImage.cs" />
+    <Compile Include="GUI\NotifyIconAdv.cs">
+      <SubType>Component</SubType>
+    </Compile>
     <Compile Include="GUI\PlotPanel.cs">
       <SubType>UserControl</SubType>
     </Compile>