# HG changeset patch
# User moel.mich
# Date 1283802793 0
# Node ID c16fd81b520ab323f96346c8ea1f2f9c0fd63b01
# Parent  e4ee19d583bdfe84212e90ffcf35c7a02309d8cf
Added a desktop gadget implementation.

diff -r e4ee19d583bd -r c16fd81b520a GUI/Gadget.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GUI/Gadget.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -0,0 +1,172 @@
+/*
+  
+  Version: MPL 1.1/GPL 2.0/LGPL 2.1
+
+  The contents of this file are subject to the Mozilla Public License Version
+  1.1 (the "License"); you may not use this file except in compliance with
+  the License. You may obtain a copy of the License at
+ 
+  http://www.mozilla.org/MPL/
+
+  Software distributed under the License is distributed on an "AS IS" basis,
+  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+  for the specific language governing rights and limitations under the License.
+
+  The Original Code is the Open Hardware Monitor code.
+
+  The Initial Developer of the Original Code is 
+  Michael Möller <m.moeller@gmx.ch>.
+  Portions created by the Initial Developer are Copyright (C) 2010
+  the Initial Developer. All Rights Reserved.
+
+  Contributor(s):
+
+  Alternatively, the contents of this file may be used under the terms of
+  either the GNU General Public License Version 2 or later (the "GPL"), or
+  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+  in which case the provisions of the GPL or the LGPL are applicable instead
+  of those above. If you wish to allow use of your version of this file only
+  under the terms of either the GPL or the LGPL, and not to allow others to
+  use your version of this file under the terms of the MPL, indicate your
+  decision by deleting the provisions above and replace them with the notice
+  and other provisions required by the GPL or the LGPL. If you do not delete
+  the provisions above, a recipient may use your version of this file under
+  the terms of any one of the MPL, the GPL or the LGPL.
+ 
+*/
+
+using System;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.Drawing.Text;
+using System.Windows.Forms;
+
+namespace OpenHardwareMonitor.GUI {
+  public abstract class Gadget : IDisposable {
+
+    private GadgetWindow window;
+    private Bitmap buffer;
+    private Graphics graphics;
+
+    public Gadget() {
+      this.window = new GadgetWindow();
+      CreateBuffer();
+    }
+
+    public void Dispose() {
+      this.graphics.Dispose();
+      this.buffer.Dispose();
+    }
+
+    public Point Location {
+      get {
+        return window.Location;
+      }
+      set {
+        window.Location = value;
+      }
+    }
+
+    public event EventHandler LocationChanged {
+      add {
+        window.LocationChanged += value;
+      }
+      remove {
+        window.LocationChanged -= value;
+      }
+    }
+
+    protected virtual Size Size {
+      get {
+        return window.Size; 
+      }
+      set {
+        if (window.Size != value) {
+          DisposeBuffer();
+          this.window.Size = value;          
+          CreateBuffer();
+        }
+      }
+    }
+
+    public byte Opacity {
+      get {
+        return window.Opacity;
+      }
+      set {
+        window.Opacity = value;
+      }
+    }
+
+    public bool LockPosition {
+      get {
+        return window.LockPosition;
+      }
+      set {
+        window.LockPosition = value;
+      }
+    }
+
+    public bool AlwaysOnTop {
+      get {
+        return window.AlwaysOnTop;
+      }
+      set {
+        window.AlwaysOnTop = value;
+      }
+    }
+
+    public ContextMenu ContextMenu {
+      get {
+        return window.ContextMenu;
+      }
+      set {
+        window.ContextMenu = value;
+      }
+    }
+
+    private void CreateBuffer() {
+      this.buffer = new Bitmap(window.Size.Width, window.Size.Height, 
+        PixelFormat.Format32bppArgb);
+      this.graphics = Graphics.FromImage(this.buffer);
+      if (Environment.OSVersion.Version.Major > 5) {
+        this.graphics.TextRenderingHint = TextRenderingHint.SystemDefault;
+        this.graphics.SmoothingMode = SmoothingMode.HighQuality;
+      }
+    }
+
+    private void DisposeBuffer() {
+      if (buffer != null) {
+        this.buffer.Dispose();
+        this.buffer = null;
+      }
+      if (graphics != null) {
+        this.graphics.Dispose();
+        this.graphics = null;
+      }
+    }
+
+    public bool Visible {
+      get {
+        return window.Visible;
+      }
+      set {
+        if (value != window.Visible) {
+          if (value)
+            Redraw();
+          window.Visible = value;
+        }
+      }
+    }
+
+    public void Redraw() {
+      OnPaint(new PaintEventArgs(graphics, 
+        new Rectangle(Point.Empty, window.Size)));
+      window.Update(buffer);
+    }
+
+    protected abstract void OnPaint(PaintEventArgs e);
+  
+  }
+}
diff -r e4ee19d583bd -r c16fd81b520a GUI/GadgetWindow.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GUI/GadgetWindow.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -0,0 +1,423 @@
+/*
+  
+  Version: MPL 1.1/GPL 2.0/LGPL 2.1
+
+  The contents of this file are subject to the Mozilla Public License Version
+  1.1 (the "License"); you may not use this file except in compliance with
+  the License. You may obtain a copy of the License at
+ 
+  http://www.mozilla.org/MPL/
+
+  Software distributed under the License is distributed on an "AS IS" basis,
+  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+  for the specific language governing rights and limitations under the License.
+
+  The Original Code is the Open Hardware Monitor code.
+
+  The Initial Developer of the Original Code is 
+  Michael Möller <m.moeller@gmx.ch>.
+  Portions created by the Initial Developer are Copyright (C) 2010
+  the Initial Developer. All Rights Reserved.
+
+  Contributor(s):
+
+  Alternatively, the contents of this file may be used under the terms of
+  either the GNU General Public License Version 2 or later (the "GPL"), or
+  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+  in which case the provisions of the GPL or the LGPL are applicable instead
+  of those above. If you wish to allow use of your version of this file only
+  under the terms of either the GPL or the LGPL, and not to allow others to
+  use your version of this file under the terms of the MPL, indicate your
+  decision by deleting the provisions above and replace them with the notice
+  and other provisions required by the GPL or the LGPL. If you do not delete
+  the provisions above, a recipient may use your version of this file under
+  the terms of any one of the MPL, the GPL or the LGPL.
+ 
+*/
+
+using System;
+using System.Drawing;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace OpenHardwareMonitor.GUI {
+  public class GadgetWindow : NativeWindow {
+
+    private bool visible = false;
+    private bool lockPosition = false;
+    private bool alwaysOnTop = false;
+    private byte opacity = 255;
+    private Point location = new Point(100, 100);
+    private Size size = new Size(130, 84);
+    private ContextMenu contextMenu = null;
+    private MethodInfo commandDispatch;
+
+    public GadgetWindow() {
+      Type commandType = 
+        typeof(Form).Assembly.GetType("System.Windows.Forms.Command");
+      commandDispatch = commandType.GetMethod("DispatchID", 
+        BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public, 
+        null, new Type[]{ typeof(int) }, null);
+
+      this.CreateHandle(CreateParams);
+
+      // move window to the bottom
+      MoveToBottom(Handle);
+
+      // prevent window from fading to a glass sheet when peek is invoked
+      try {
+        bool value = true;
+        int r = NativeMethods.DwmSetWindowAttribute(Handle,
+          WindowAttribute.DWMWA_EXCLUDED_FROM_PEEK, ref value,
+          Marshal.SizeOf(value));
+      } catch (DllNotFoundException) { } catch (EntryPointNotFoundException) { }
+    }
+
+    private void ShowDesktopChanged(bool showDesktop) {
+      if (showDesktop) {
+        MoveToTopMost(Handle);
+      } else {
+        MoveToBottom(Handle);
+      }
+    }
+
+    private void MoveToBottom(IntPtr handle) {
+      NativeMethods.SetWindowPos(handle, HWND_BOTTOM, 0, 0, 0, 0,
+        SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
+    }
+
+    private void MoveToTopMost(IntPtr handle) {
+      NativeMethods.SetWindowPos(handle, HWND_TOPMOST, 0, 0, 0, 0,
+        SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
+    }
+
+    private void ShowContextMenu(Point position) {
+      NativeMethods.TrackPopupMenuEx(contextMenu.Handle, 
+        TPM_RIGHTBUTTON | TPM_VERTICAL, position.X,
+        position.Y, Handle, IntPtr.Zero);
+    }
+
+    protected virtual CreateParams CreateParams {
+      get {
+        CreateParams cp = new CreateParams();
+        cp.Width = size.Width;
+        cp.Height = size.Height;
+        cp.X = location.X;
+        cp.Y = location.Y;
+        cp.ExStyle = WS_EX_LAYERED | WS_EX_TOOLWINDOW;
+        return cp;
+      }
+    }
+
+    protected override void WndProc(ref Message message) {
+      switch (message.Msg) {
+        case WM_COMMAND:
+          // need to dispatch the message for the context menu
+          if (message.LParam == IntPtr.Zero) 
+            commandDispatch.Invoke(null, new object[] { 
+              message.WParam.ToInt32() & 0xFFFF });          
+          break;
+        case WM_NCHITTEST:           
+          // all pixels of the form belong to the caption
+          message.Result = HTCAPTION; 
+          break;
+        case WM_NCLBUTTONDBLCLK:  
+          message.Result = IntPtr.Zero; break;
+        case WM_NCRBUTTONDOWN:
+          message.Result = IntPtr.Zero; break;
+        case WM_NCRBUTTONUP:
+          if (contextMenu != null)
+            ShowContextMenu(new Point(
+              (int)((uint)message.LParam & 0xFFFF), 
+              (int)(((uint)message.LParam >>16) & 0xFFFF)));          
+          message.Result = IntPtr.Zero; 
+          break;
+        case WM_WINDOWPOSCHANGING:         
+          WindowPos wp = (WindowPos)Marshal.PtrToStructure(
+            message.LParam, typeof(WindowPos));
+
+          // add the nomove flag if position is locked
+          if (lockPosition)
+            wp.flags |= SWP_NOMOVE;
+
+          // prevent the window from leaving the screen
+          if ((wp.flags & SWP_NOMOVE) == 0) {            
+            Rectangle rect = Screen.GetWorkingArea(new Point(wp.x, wp.y));
+            const int margin = 20;
+            wp.x = Math.Max(wp.x, rect.Left - wp.cx + margin);
+            wp.x = Math.Min(wp.x, rect.Right - margin);
+            wp.y = Math.Max(wp.y, rect.Top - wp.cy + margin);
+            wp.y = Math.Min(wp.y, rect.Bottom - margin);
+
+            // raise the event if location changed
+            if (location.X != wp.x || location.Y != wp.y) {
+              location = new Point(wp.x, wp.y);
+              if (LocationChanged != null)
+                LocationChanged(this, EventArgs.Empty);
+            }
+          }          
+
+          Marshal.StructureToPtr(wp, message.LParam, false);
+          message.Result = IntPtr.Zero;
+          break;           
+        default:
+          base.WndProc(ref message); break;
+      }      
+    }
+
+    private BlendFunction CreateBlendFunction() {
+      BlendFunction blend = new BlendFunction();
+      blend.BlendOp = AC_SRC_OVER;
+      blend.BlendFlags = 0;
+      blend.SourceConstantAlpha = opacity;
+      blend.AlphaFormat = AC_SRC_ALPHA;
+      return blend;
+    }
+
+    public void Update(Bitmap bitmap) {
+      IntPtr screen = NativeMethods.GetDC(IntPtr.Zero);
+      IntPtr memory = NativeMethods.CreateCompatibleDC(screen);
+      IntPtr newHBitmap = IntPtr.Zero;
+      IntPtr oldHBitmap = IntPtr.Zero;
+
+      try {
+        newHBitmap = bitmap.GetHbitmap(Color.Black);
+        oldHBitmap = NativeMethods.SelectObject(memory, newHBitmap);
+
+        Size size = bitmap.Size;
+        Point pointSource = Point.Empty;
+        Point topPos = Location;
+
+        BlendFunction blend = CreateBlendFunction();
+        NativeMethods.UpdateLayeredWindow(Handle, screen, ref topPos,
+          ref size, memory, ref pointSource, 0, ref blend, ULW_ALPHA);
+      } finally {
+        NativeMethods.ReleaseDC(IntPtr.Zero, screen);
+        if (newHBitmap != IntPtr.Zero) {
+          NativeMethods.SelectObject(memory, oldHBitmap);
+          NativeMethods.DeleteObject(newHBitmap);
+        }
+        NativeMethods.DeleteDC(memory);
+      }
+    }
+
+    public byte Opacity {
+      get {
+        return opacity;
+      }
+      set {
+        if (opacity != value) {
+          opacity = value;
+          BlendFunction blend = CreateBlendFunction();
+          NativeMethods.UpdateLayeredWindow(Handle, IntPtr.Zero, IntPtr.Zero,
+            IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, 0, ref blend, ULW_ALPHA);
+        }
+      }
+    }
+
+    public bool Visible {
+      get {
+        return visible;
+      }
+      set {
+        if (visible != value) {
+          visible = value;
+          NativeMethods.SetWindowPos(Handle, IntPtr.Zero, 0, 0, 0, 0,
+            SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER |
+            (value ? SWP_SHOWWINDOW : SWP_HIDEWINDOW));
+          if (value)
+            ShowDesktop.Instance.ShowDesktopChanged += ShowDesktopChanged;
+          else
+            ShowDesktop.Instance.ShowDesktopChanged -= ShowDesktopChanged;         
+        }
+      }
+    }
+
+    // if locked, the window can not be moved
+    public bool LockPosition {
+      get {
+        return lockPosition;
+      }
+      set {
+        lockPosition = value;
+      }
+    }
+
+    public bool AlwaysOnTop {
+      get {
+        return alwaysOnTop;
+      }
+      set {
+        if (value != alwaysOnTop) {
+          alwaysOnTop = value;
+          if (alwaysOnTop) {
+            ShowDesktop.Instance.ShowDesktopChanged -= ShowDesktopChanged;
+            MoveToTopMost(Handle);            
+          } else {
+            MoveToBottom(Handle);
+            ShowDesktop.Instance.ShowDesktopChanged += ShowDesktopChanged;
+          }
+        }
+      }
+    }
+
+    public Size Size {
+      get {
+        return size; 
+      }
+      set {
+        if (size != value) {
+          size = value;
+          NativeMethods.SetWindowPos(Handle, IntPtr.Zero, 0, 0, size.Width,
+            size.Height, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | 
+            SWP_NOSENDCHANGING);
+        }
+      }
+    }
+
+    public Point Location {
+      get {
+        return location;
+      }
+      set {
+        NativeMethods.SetWindowPos(Handle, IntPtr.Zero, value.X, value.Y, 0,
+          0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING);
+        location = value;
+        if (LocationChanged != null)
+          LocationChanged(this, EventArgs.Empty);
+      }
+    }
+
+    public event EventHandler LocationChanged;
+
+    public ContextMenu ContextMenu {
+      get {
+        return contextMenu;
+      }
+      set {
+        this.contextMenu = value;
+      }
+    }
+
+    [StructLayout(LayoutKind.Sequential, Pack = 1)]
+    private struct BlendFunction {
+      public byte BlendOp;
+      public byte BlendFlags;
+      public byte SourceConstantAlpha;
+      public byte AlphaFormat;
+    }
+
+    [StructLayout(LayoutKind.Sequential, Pack = 1)]
+    private struct WindowPos {
+      public IntPtr hwnd;
+      public IntPtr hwndInsertAfter;
+      public int x;
+      public int y;
+      public int cx;
+      public int cy;
+      public uint flags;
+    }
+
+    public static readonly IntPtr HWND_BOTTOM = (IntPtr)1;
+    public static readonly IntPtr HWND_TOPMOST = (IntPtr)(-1);
+
+    public const int WS_EX_LAYERED = 0x00080000;
+    public const int WS_EX_TOOLWINDOW = 0x00000080;
+
+    public const uint SWP_NOSIZE = 0x0001;
+    public const uint SWP_NOMOVE = 0x0002;
+    public const uint SWP_NOACTIVATE = 0x0010;
+    public const uint SWP_HIDEWINDOW = 0x0080;
+    public const uint SWP_SHOWWINDOW = 0x0040;
+    public const uint SWP_NOZORDER = 0x0004;
+    public const uint SWP_NOSENDCHANGING = 0x0400;
+
+    public const int ULW_COLORKEY = 0x00000001;
+    public const int ULW_ALPHA = 0x00000002;
+    public const int ULW_OPAQUE = 0x00000004;
+
+    public const byte AC_SRC_OVER = 0x00;
+    public const byte AC_SRC_ALPHA = 0x01;
+
+    public const int WM_NCHITTEST = 0x0084;
+    public const int WM_NCLBUTTONDBLCLK = 0x00A3;
+    public const int WM_NCLBUTTONDOWN = 0x00A1;
+    public const int WM_NCLBUTTONUP = 0x00A2;
+    public const int WM_NCRBUTTONDOWN = 0x00A4;
+    public const int WM_NCRBUTTONUP = 0x00A5;
+    public const int WM_WINDOWPOSCHANGING = 0x0046;
+    public const int WM_COMMAND = 0x0111;
+
+    public const int TPM_RIGHTBUTTON = 0x0002;
+    public const int TPM_VERTICAL = 0x0040;
+
+    public readonly IntPtr HTCAPTION = (IntPtr)2;
+
+    private enum WindowAttribute : int {
+      DWMWA_NCRENDERING_ENABLED = 1,
+      DWMWA_NCRENDERING_POLICY,
+      DWMWA_TRANSITIONS_FORCEDISABLED,
+      DWMWA_ALLOW_NCPAINT,
+      DWMWA_CAPTION_BUTTON_BOUNDS,
+      DWMWA_NONCLIENT_RTL_LAYOUT,
+      DWMWA_FORCE_ICONIC_REPRESENTATION,
+      DWMWA_FLIP3D_POLICY,
+      DWMWA_EXTENDED_FRAME_BOUNDS,
+      DWMWA_HAS_ICONIC_BITMAP,
+      DWMWA_DISALLOW_PEEK,
+      DWMWA_EXCLUDED_FROM_PEEK,
+      DWMWA_LAST
+    }
+
+    private static class NativeMethods {
+      private const string USER = "user32.dll";
+      private const string GDI = "gdi32.dll";
+      public const string DWMAPI = "dwmapi.dll";
+
+      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
+      [return: MarshalAs(UnmanagedType.Bool)]
+      public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst, 
+        ref Point pptDst, ref Size psize, IntPtr hdcSrc, ref Point pprSrc, 
+        int crKey, ref BlendFunction pblend, int dwFlags);
+
+      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
+      [return: MarshalAs(UnmanagedType.Bool)]
+      public static extern bool UpdateLayeredWindow(IntPtr hwnd, IntPtr hdcDst,
+        IntPtr pptDst, IntPtr psize, IntPtr hdcSrc, IntPtr pprSrc,
+        int crKey, ref BlendFunction pblend, int dwFlags);  
+
+      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
+      public static extern IntPtr GetDC(IntPtr hWnd);
+
+      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
+      public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
+
+      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
+      public static extern bool SetWindowPos(IntPtr hWnd,
+        IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
+
+      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
+      public static extern bool TrackPopupMenuEx(IntPtr hMenu, uint uFlags, 
+        int x, int y, IntPtr hWnd, IntPtr tpmParams);
+
+      [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
+      public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
+
+      [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
+      [return: MarshalAs(UnmanagedType.Bool)]
+      public static extern bool DeleteDC(IntPtr hdc);
+      
+      [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
+      public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
+
+      [DllImport(GDI, CallingConvention = CallingConvention.Winapi)]
+      [return: MarshalAs(UnmanagedType.Bool)]
+      public static extern bool DeleteObject(IntPtr hObject);
+
+      [DllImport(DWMAPI, CallingConvention = CallingConvention.Winapi)]
+      public static extern int DwmSetWindowAttribute(IntPtr hwnd,
+        WindowAttribute dwAttribute, ref bool pvAttribute, int cbAttribute);
+    }    
+  }
+}
diff -r e4ee19d583bd -r c16fd81b520a GUI/HardwareNode.cs
--- a/GUI/HardwareNode.cs	Tue Aug 24 22:11:10 2010 +0000
+++ b/GUI/HardwareNode.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -55,32 +55,7 @@
       this.settings = settings;
       this.unitManager = unitManager;
       this.hardware = hardware;
-      switch (hardware.HardwareType) {
-        case HardwareType.CPU: 
-          this.Image = Utilities.EmbeddedResources.GetImage("cpu.png");
-          break;
-        case HardwareType.GPU:
-          if (hardware.Identifier.ToString().Contains("nvidia"))
-            this.Image = Utilities.EmbeddedResources.GetImage("nvidia.png");
-          else
-            this.Image = Utilities.EmbeddedResources.GetImage("ati.png");
-          break;
-        case HardwareType.HDD: 
-          this.Image = Utilities.EmbeddedResources.GetImage("hdd.png");
-          break;
-        case HardwareType.Heatmaster:
-          this.Image = Utilities.EmbeddedResources.GetImage("bigng.png");
-          break;
-        case HardwareType.Mainboard: 
-          this.Image = Utilities.EmbeddedResources.GetImage("mainboard.png");
-          break;
-        case HardwareType.SuperIO: 
-          this.Image = Utilities.EmbeddedResources.GetImage("chip.png");
-          break;
-        case HardwareType.TBalancer: 
-          this.Image = Utilities.EmbeddedResources.GetImage("bigng.png");
-          break;
-      }
+      this.Image = HardwareTypeImage.Instance.GetImage(hardware.HardwareType);
 
       typeNodes.Add(new TypeNode(SensorType.Voltage));
       typeNodes.Add(new TypeNode(SensorType.Clock));      
diff -r e4ee19d583bd -r c16fd81b520a GUI/HardwareTypeImage.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GUI/HardwareTypeImage.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -0,0 +1,95 @@
+/*
+  
+  Version: MPL 1.1/GPL 2.0/LGPL 2.1
+
+  The contents of this file are subject to the Mozilla Public License Version
+  1.1 (the "License"); you may not use this file except in compliance with
+  the License. You may obtain a copy of the License at
+ 
+  http://www.mozilla.org/MPL/
+
+  Software distributed under the License is distributed on an "AS IS" basis,
+  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+  for the specific language governing rights and limitations under the License.
+
+  The Original Code is the Open Hardware Monitor code.
+
+  The Initial Developer of the Original Code is 
+  Michael Möller <m.moeller@gmx.ch>.
+  Portions created by the Initial Developer are Copyright (C) 2010
+  the Initial Developer. All Rights Reserved.
+
+  Contributor(s):
+
+  Alternatively, the contents of this file may be used under the terms of
+  either the GNU General Public License Version 2 or later (the "GPL"), or
+  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+  in which case the provisions of the GPL or the LGPL are applicable instead
+  of those above. If you wish to allow use of your version of this file only
+  under the terms of either the GPL or the LGPL, and not to allow others to
+  use your version of this file under the terms of the MPL, indicate your
+  decision by deleting the provisions above and replace them with the notice
+  and other provisions required by the GPL or the LGPL. If you do not delete
+  the provisions above, a recipient may use your version of this file under
+  the terms of any one of the MPL, the GPL or the LGPL.
+ 
+*/
+
+using System;
+using System.Drawing;
+using System.Collections.Generic;
+using OpenHardwareMonitor.Hardware;
+
+namespace OpenHardwareMonitor.GUI {
+  public class HardwareTypeImage {
+    private static HardwareTypeImage instance = new HardwareTypeImage();
+
+    private IDictionary<HardwareType, Image> images = 
+      new Dictionary<HardwareType, Image>();
+
+    private HardwareTypeImage() { }
+
+    public static HardwareTypeImage Instance {
+      get { return instance; }
+    }
+
+    public Image GetImage(HardwareType hardwareType) {
+      Image image;
+      if (images.TryGetValue(hardwareType, out image)) {
+        return image;
+      } else {
+        switch (hardwareType) {
+          case HardwareType.CPU:
+            image = Utilities.EmbeddedResources.GetImage("cpu.png");
+            break;
+          case HardwareType.GpuNvidia:
+            image = Utilities.EmbeddedResources.GetImage("nvidia.png");
+            break;
+          case HardwareType.GpuAti:
+            image = Utilities.EmbeddedResources.GetImage("ati.png");
+            break;
+          case HardwareType.HDD:
+            image = Utilities.EmbeddedResources.GetImage("hdd.png");
+            break;
+          case HardwareType.Heatmaster:
+            image = Utilities.EmbeddedResources.GetImage("bigng.png");
+            break;
+          case HardwareType.Mainboard:
+            image = Utilities.EmbeddedResources.GetImage("mainboard.png");
+            break;
+          case HardwareType.SuperIO:
+            image = Utilities.EmbeddedResources.GetImage("chip.png");
+            break;
+          case HardwareType.TBalancer:
+            image = Utilities.EmbeddedResources.GetImage("bigng.png");
+            break;
+          default:
+            image = new Bitmap(1, 1);
+            break;
+        }
+        images.Add(hardwareType, image);
+        return image;
+      }
+    }
+  }
+}
diff -r e4ee19d583bd -r c16fd81b520a GUI/MainForm.Designer.cs
--- a/GUI/MainForm.Designer.cs	Tue Aug 24 22:11:10 2010 +0000
+++ b/GUI/MainForm.Designer.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -88,6 +88,7 @@
       this.valueMenuItem = new System.Windows.Forms.MenuItem();
       this.minMenuItem = new System.Windows.Forms.MenuItem();
       this.maxMenuItem = new System.Windows.Forms.MenuItem();
+      this.gadgetMenuItem = new System.Windows.Forms.MenuItem();
       this.optionsMenuItem = new System.Windows.Forms.MenuItem();
       this.startMinMenuItem = new System.Windows.Forms.MenuItem();
       this.minTrayMenuItem = new System.Windows.Forms.MenuItem();
@@ -231,6 +232,7 @@
             this.MenuItem3,
             this.hiddenMenuItem,
             this.plotMenuItem,
+            this.gadgetMenuItem,
             this.MenuItem1,
             this.columnsMenuItem});
       this.viewMenuItem.Text = "View";
@@ -258,12 +260,12 @@
       // 
       // MenuItem1
       // 
-      this.MenuItem1.Index = 4;
+      this.MenuItem1.Index = 5;
       this.MenuItem1.Text = "-";
       // 
       // columnsMenuItem
       // 
-      this.columnsMenuItem.Index = 5;
+      this.columnsMenuItem.Index = 6;
       this.columnsMenuItem.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
             this.valueMenuItem,
             this.minMenuItem,
@@ -285,6 +287,11 @@
       this.maxMenuItem.Index = 2;
       this.maxMenuItem.Text = "Max";
       // 
+      // gadgetMenuItem
+      // 
+      this.gadgetMenuItem.Index = 4;
+      this.gadgetMenuItem.Text = "Show Gadget";
+      // 
       // optionsMenuItem
       // 
       this.optionsMenuItem.Index = 2;
@@ -501,6 +508,7 @@
     private System.Windows.Forms.MenuItem MenuItem2;
     private System.Windows.Forms.MenuItem resetMinMaxMenuItem;
     private System.Windows.Forms.MenuItem MenuItem3;
+    private System.Windows.Forms.MenuItem gadgetMenuItem;
   }
 }
 
diff -r e4ee19d583bd -r c16fd81b520a GUI/MainForm.cs
--- a/GUI/MainForm.cs	Tue Aug 24 22:11:10 2010 +0000
+++ b/GUI/MainForm.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -62,6 +62,7 @@
     private SystemTray systemTray;    
     private StartupManager startupManager = new StartupManager();
     private UpdateVisitor updateVisitor = new UpdateVisitor();
+    private SensorGadget gadget;
 
     private UserOption showHiddenSensors;
     private UserOption showPlot;
@@ -72,6 +73,7 @@
     private UserOption minimizeToTray;
     private UserOption autoStart;
     private UserOption readHddSensors;
+    private UserOption showGadget;
 
     public MainForm() {      
       InitializeComponent();
@@ -138,6 +140,8 @@
       systemTray.HideShowCommand += hideShowClick;
       systemTray.ExitCommand += exitClick;
 
+      gadget = new SensorGadget(computer, settings, unitManager);
+
       computer.HardwareAdded += new HardwareEventHandler(HardwareAdded);
       computer.HardwareRemoved += new HardwareEventHandler(HardwareRemoved);
       computer.Open();
@@ -194,7 +198,7 @@
 
       autoStart = new UserOption(null, startupManager.Startup, startupMenuItem, settings);
       autoStart.Changed += delegate(object sender, EventArgs e) {
-        startupManager.Startup = autoStart.Value; ;
+        startupManager.Startup = autoStart.Value; 
       };
 
       readHddSensors = new UserOption("hddMenuItem", true, hddMenuItem, settings);
@@ -203,6 +207,11 @@
         UpdatePlotSelection(null, null);
       };
 
+      showGadget = new UserOption("gadgetMenuItem", false, gadgetMenuItem, settings);
+      showGadget.Changed += delegate(object sender, EventArgs e) {
+        gadget.Visible = showGadget.Value;
+      };
+
       celciusMenuItem.Checked = 
         unitManager.TemperatureUnit == TemperatureUnit.Celcius;
       fahrenheitMenuItem.Checked = !celciusMenuItem.Checked;
@@ -225,7 +234,7 @@
       Microsoft.Win32.SystemEvents.SessionEnded +=
         delegate(object sender, Microsoft.Win32.SessionEndedEventArgs e) {
           SaveConfiguration();
-        };
+        };  
     }
     
     private void SubHardwareAdded(IHardware hardware, Node node) {
@@ -313,6 +322,7 @@
       treeView.Invalidate();
       plotPanel.Invalidate();
       systemTray.Redraw();
+      gadget.Redraw();
     }
 
     private void SaveConfiguration() {
@@ -369,7 +379,7 @@
               nodeTextBoxText.BeginEdit();
             };
             sensorContextMenu.MenuItems.Add(item);
-          }          
+          }
           if (node.IsVisible) {
             MenuItem item = new MenuItem("Hide");
             item.Click += delegate(object obj, EventArgs args) {
@@ -382,20 +392,30 @@
               node.IsVisible = true;
             };
             sensorContextMenu.MenuItems.Add(item);
-          }         
-          if (systemTray.Contains(node.Sensor)) {
-            MenuItem item = new MenuItem("Remove From Tray");
-            item.Click += delegate(object obj, EventArgs args) {
+          }
+          sensorContextMenu.MenuItems.Add(new MenuItem("-"));
+
+          MenuItem menuItem = new MenuItem("Show in Tray");
+          menuItem.Checked = systemTray.Contains(node.Sensor);
+          menuItem.Click += delegate(object obj, EventArgs args) {
+            if (menuItem.Checked)
               systemTray.Remove(node.Sensor);
-            };
-            sensorContextMenu.MenuItems.Add(item);
-          } else {
-            MenuItem item = new MenuItem("Add To Tray");
-            item.Click += delegate(object obj, EventArgs args) {
+            else
               systemTray.Add(node.Sensor, true);
-            };
-            sensorContextMenu.MenuItems.Add(item);
-          }
+          };
+          sensorContextMenu.MenuItems.Add(menuItem);
+
+          menuItem = new MenuItem("Show in Gadget");
+          menuItem.Checked = gadget.Contains(node.Sensor);
+          menuItem.Click += delegate(object obj, EventArgs args) {
+            if (menuItem.Checked) {
+              gadget.Remove(node.Sensor);
+            } else {
+              gadget.Add(node.Sensor);
+            }
+          };
+          sensorContextMenu.MenuItems.Add(menuItem);
+
           sensorContextMenu.Show(treeView, new Point(m.X, m.Y));
         }
       }
diff -r e4ee19d583bd -r c16fd81b520a GUI/SensorGadget.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GUI/SensorGadget.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -0,0 +1,361 @@
+/*
+  
+  Version: MPL 1.1/GPL 2.0/LGPL 2.1
+
+  The contents of this file are subject to the Mozilla Public License Version
+  1.1 (the "License"); you may not use this file except in compliance with
+  the License. You may obtain a copy of the License at
+ 
+  http://www.mozilla.org/MPL/
+
+  Software distributed under the License is distributed on an "AS IS" basis,
+  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+  for the specific language governing rights and limitations under the License.
+
+  The Original Code is the Open Hardware Monitor code.
+
+  The Initial Developer of the Original Code is 
+  Michael Möller <m.moeller@gmx.ch>.
+  Portions created by the Initial Developer are Copyright (C) 2010
+  the Initial Developer. All Rights Reserved.
+
+  Contributor(s):
+
+  Alternatively, the contents of this file may be used under the terms of
+  either the GNU General Public License Version 2 or later (the "GPL"), or
+  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+  in which case the provisions of the GPL or the LGPL are applicable instead
+  of those above. If you wish to allow use of your version of this file only
+  under the terms of either the GPL or the LGPL, and not to allow others to
+  use your version of this file under the terms of the MPL, indicate your
+  decision by deleting the provisions above and replace them with the notice
+  and other provisions required by the GPL or the LGPL. If you do not delete
+  the provisions above, a recipient may use your version of this file under
+  the terms of any one of the MPL, the GPL or the LGPL.
+ 
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+using OpenHardwareMonitor.Hardware;
+
+namespace OpenHardwareMonitor.GUI {
+  public class SensorGadget : Gadget {
+
+    private UnitManager unitManager;
+
+    private Image back = Utilities.EmbeddedResources.GetImage("gadget.png");
+    private Image barBack = Utilities.EmbeddedResources.GetImage("barback.png");
+    private Image barblue = Utilities.EmbeddedResources.GetImage("barblue.png");
+    private const int topBorder = 4;
+    private const int bottomBorder = 6;
+    private const int leftBorder = 6;
+    private const int rightBorder = 6;
+    private const int iconSize = 11;
+    private const int hardwareLineHeight = 13;
+    private const int sensorLineHeight = 11;
+
+    private IDictionary<IHardware, IList<ISensor>> sensors =
+      new SortedDictionary<IHardware, IList<ISensor>>(new HardwareComparer());
+
+    private PersistentSettings settings;
+    private UserOption alwaysOnTop;
+    private UserOption lockPosition;
+
+    private Font largeFont;
+    private Font smallFont;
+    private Brush darkWhite = new SolidBrush(Color.FromArgb(0xF0, 0xF0, 0xF0));
+
+    public SensorGadget(IComputer computer, PersistentSettings settings, 
+      UnitManager unitManager) 
+    {
+      this.unitManager = unitManager;
+      this.settings = settings;
+      computer.HardwareAdded += new HardwareEventHandler(HardwareAdded);
+      computer.HardwareRemoved += new HardwareEventHandler(HardwareRemoved);
+
+      this.largeFont = new Font(SystemFonts.MessageBoxFont.FontFamily, 7.5f, 
+        FontStyle.Bold); 
+      this.smallFont = new Font(SystemFonts.MessageBoxFont.FontFamily, 6.5f);      
+
+      this.Location = new Point(
+        settings.GetValue("sensorGadget.Location.X", 100),
+        settings.GetValue("sensorGadget.Location.Y", 100)); 
+      LocationChanged += delegate(object sender, EventArgs e) {
+        settings.SetValue("sensorGadget.Location.X", Location.X);
+        settings.SetValue("sensorGadget.Location.Y", Location.Y);
+      };
+      
+      ContextMenu contextMenu = new ContextMenu();
+      MenuItem lockItem = new MenuItem("Lock Position");
+      contextMenu.MenuItems.Add(lockItem);
+      contextMenu.MenuItems.Add(new MenuItem("-"));
+      MenuItem alwaysOnTopItem = new MenuItem("Always on Top");
+      contextMenu.MenuItems.Add(alwaysOnTopItem);
+      MenuItem opacityMenu = new MenuItem("Opacity");
+      contextMenu.MenuItems.Add(opacityMenu);
+      Opacity = (byte)settings.GetValue("sensorGadget.Opacity", 255);      
+      for (int i = 0; i < 5; i++) {
+        MenuItem item = new MenuItem((20 * (i + 1)).ToString() + " %");
+        byte o = (byte)(51 * (i + 1));
+        item.Tag = o;
+        item.Checked = Opacity == o;
+        item.Click += delegate(object sender, EventArgs e) {
+          Opacity = (byte)item.Tag;
+          settings.SetValue("sensorGadget.Opacity", Opacity);
+          foreach (MenuItem mi in opacityMenu.MenuItems)
+            mi.Checked = (byte)mi.Tag == Opacity;          
+        };
+        opacityMenu.MenuItems.Add(item);
+      }
+      this.ContextMenu = contextMenu;
+
+      alwaysOnTop = new UserOption("sensorGadget.AlwaysOnTop", false, 
+        alwaysOnTopItem, settings);
+      alwaysOnTop.Changed += delegate(object sender, EventArgs e) {
+        this.AlwaysOnTop = alwaysOnTop.Value;
+      };
+      lockPosition = new UserOption("sensorGadget.LockPosition", false,
+        lockItem, settings);
+      lockPosition.Changed += delegate(object sender, EventArgs e) {
+        this.LockPosition = lockPosition.Value;
+      };
+
+      Resize();
+    }
+
+    private void HardwareRemoved(IHardware hardware) {
+      hardware.SensorAdded -= new SensorEventHandler(SensorAdded);
+      hardware.SensorRemoved -= new SensorEventHandler(SensorRemoved);
+      foreach (ISensor sensor in hardware.Sensors)
+        SensorRemoved(sensor);
+      foreach (IHardware subHardware in hardware.SubHardware)
+        HardwareRemoved(subHardware);
+    }
+
+    private void HardwareAdded(IHardware hardware) {
+      foreach (ISensor sensor in hardware.Sensors)
+        SensorAdded(sensor);
+      hardware.SensorAdded += new SensorEventHandler(SensorAdded);
+      hardware.SensorRemoved += new SensorEventHandler(SensorRemoved);
+      foreach (IHardware subHardware in hardware.SubHardware)
+        HardwareAdded(subHardware);
+    }
+
+    private void SensorAdded(ISensor sensor) {
+      if (settings.GetValue(new Identifier(sensor.Identifier,
+        "gadget").ToString(), false)) 
+        Add(sensor);
+    }
+
+    private void SensorRemoved(ISensor sensor) {
+      if (Contains(sensor))
+        Remove(sensor, false);
+    }
+
+    public bool Contains(ISensor sensor) {
+      foreach (IList<ISensor> list in sensors.Values)
+        if (list.Contains(sensor))
+          return true;
+      return false;
+    }
+
+    public void Add(ISensor sensor) {
+      if (Contains(sensor)) {
+        return;
+      } else {
+        // get the right hardware
+        IHardware hardware = sensor.Hardware;
+        while (hardware.Parent != null)
+          hardware = hardware.Parent;
+
+        // get the sensor list associated with the hardware
+        IList<ISensor> list;
+        if (!sensors.TryGetValue(hardware, out list)) {
+          list = new List<ISensor>();
+          sensors.Add(hardware, list);
+        }
+
+        // insert the sensor at the right position
+        int i = 0;
+        while (i < list.Count && (list[i].SensorType < sensor.SensorType || 
+          (list[i].SensorType == sensor.SensorType && 
+           list[i].Index < sensor.Index))) i++;
+        list.Insert(i, sensor);
+
+        settings.SetValue(
+          new Identifier(sensor.Identifier, "gadget").ToString(), true);
+        
+        Resize();
+        Redraw();
+      }
+    }
+
+    public void Remove(ISensor sensor) {
+      Remove(sensor, true);
+    }
+
+    private void Remove(ISensor sensor, bool deleteConfig) {
+      if (deleteConfig) 
+        settings.Remove(new Identifier(sensor.Identifier, "gadget").ToString());
+
+      foreach (KeyValuePair<IHardware, IList<ISensor>> keyValue in sensors)
+        if (keyValue.Value.Contains(sensor)) {
+          keyValue.Value.Remove(sensor);          
+          if (keyValue.Value.Count == 0) {
+            sensors.Remove(keyValue.Key);
+            break;
+          }
+        }
+      Resize();
+      Redraw();
+    }
+
+    private void Resize() {
+      int y = topBorder + 1;
+      foreach (KeyValuePair<IHardware, IList<ISensor>> pair in sensors) {
+        y += hardwareLineHeight;
+        y += pair.Value.Count * sensorLineHeight;
+      }
+      y += bottomBorder + 2;
+      y = Math.Max(y, topBorder + bottomBorder + 10);
+      this.Size = new Size(130, y);
+    }
+
+    private void DrawBackground(Graphics g) {
+      int w = Size.Width;
+      int h = Size.Height;
+      int t = topBorder;
+      int b = bottomBorder;
+      int l = leftBorder;
+      int r = rightBorder;
+      GraphicsUnit u = GraphicsUnit.Pixel;
+
+      g.DrawImage(back, new Rectangle(0, 0, l, t),
+        new Rectangle(0, 0, l, t), u);
+      g.DrawImage(back, new Rectangle(l, 0, w - l - r, t),
+        new Rectangle(l, 0, back.Width - l - r, t), u);
+      g.DrawImage(back, new Rectangle(w - r, 0, r, t),
+        new Rectangle(back.Width - r, 0, r, t), u);
+
+      g.DrawImage(back, new Rectangle(0, t, l, h - t - b),
+        new Rectangle(0, t, l, back.Height - t - b), u);
+      g.DrawImage(back, new Rectangle(l, t, w - l - r, h - t - b),
+        new Rectangle(l, t, back.Width - l - r, back.Height - t - b), u);
+      g.DrawImage(back, new Rectangle(w - r, t, r, h - t - b),
+        new Rectangle(back.Width - r, t, r, back.Height - t - b), u);
+
+      g.DrawImage(back, new Rectangle(0, h - b, l, b),
+        new Rectangle(0, back.Height - b, l, b), u);
+      g.DrawImage(back, new Rectangle(l, h - b, w - l - r, b),
+        new Rectangle(l, back.Height - b, back.Width - l - r, b), u);
+      g.DrawImage(back, new Rectangle(w - r, h - b, r, b),
+        new Rectangle(back.Width - r, back.Height - b, r, b), u);
+    }
+
+    private void DrawProgress(Graphics g, int x, int y, int width, int height,
+      float progress) 
+    {
+      g.DrawImage(barBack, 
+        new RectangleF(x + width * progress, y, width * (1 - progress), height), 
+        new RectangleF(barBack.Width * progress, 0, 
+          (1 - progress) * barBack.Width, barBack.Height), 
+        GraphicsUnit.Pixel);
+      g.DrawImage(barblue,
+        new RectangleF(x, y, width * progress, height),
+        new RectangleF(0, 0, progress * barblue.Width, barblue.Height),
+        GraphicsUnit.Pixel);
+    }
+
+    protected override void OnPaint(PaintEventArgs e) {
+      Graphics g = e.Graphics;
+      int w = Size.Width;
+      int h = Size.Height;
+
+      g.Clear(Color.Transparent);
+
+      DrawBackground(g);
+
+      StringFormat stringFormat = new StringFormat();
+      stringFormat.Alignment = StringAlignment.Far;
+
+      int x;
+      int y = topBorder + 1;
+      foreach (KeyValuePair<IHardware, IList<ISensor>> pair in sensors) {
+        x = leftBorder + 1;
+        g.DrawImage(HardwareTypeImage.Instance.GetImage(pair.Key.HardwareType),
+          new Rectangle(x, y + 2, iconSize, iconSize));
+        x += iconSize + 1;
+        g.DrawString(pair.Key.Name, largeFont, Brushes.White,
+          new Rectangle(x, y, w - rightBorder - x, 15));
+        y += hardwareLineHeight;
+
+        foreach (ISensor sensor in pair.Value) {
+
+          g.DrawString(sensor.Name + ":", smallFont, darkWhite,
+            new Rectangle(9, y, 64, 15));
+          
+          if (sensor.SensorType != SensorType.Load && 
+            sensor.SensorType != SensorType.Control) 
+          {
+            string format = "";
+            switch (sensor.SensorType) {
+              case SensorType.Voltage:
+                format = "{0:F2} V";
+                break;
+              case SensorType.Clock:
+                format = "{0:F0} MHz";
+                break;
+              case SensorType.Temperature:
+                format = "{0:F1} °C";
+                break;
+              case SensorType.Fan:
+                format = "{0:F0} RPM";
+                break;
+              case SensorType.Flow:
+                format = "{0:F0} L/h";
+                break;
+            }
+
+            string formattedValue;
+            if (sensor.SensorType == SensorType.Temperature &&
+              unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit) {
+              formattedValue = string.Format("{0:F1} °F",
+                sensor.Value * 1.8 + 32);
+            } else {
+              formattedValue = string.Format(format, sensor.Value);
+            }
+
+            x = 75;
+            g.DrawString(formattedValue, smallFont, darkWhite,
+              new RectangleF(x, y, w - x - 9, 15), stringFormat);            
+          } else {
+            x = 80;
+            DrawProgress(g, x, y + 4, w - x - 9, 6, 0.01f * sensor.Value.Value);
+          }
+
+          y += sensorLineHeight;
+        }
+      }
+    }
+
+    private class HardwareComparer : IComparer<IHardware> {
+      public int Compare(IHardware x, IHardware y) {
+        if (x == null && y == null)
+          return 0;
+        if (x == null)
+          return -1;
+        if (y == null)
+          return 1;
+
+        if (x.HardwareType != y.HardwareType)
+          return x.HardwareType.CompareTo(y.HardwareType);
+
+        return x.Name.CompareTo(y.Name);
+      }
+    }
+  }
+}
+
diff -r e4ee19d583bd -r c16fd81b520a GUI/SensorNotifyIcon.cs
--- a/GUI/SensorNotifyIcon.cs	Tue Aug 24 22:11:10 2010 +0000
+++ b/GUI/SensorNotifyIcon.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -36,14 +36,12 @@
 */
 
 using System;
-using System.Collections.Generic;
 using System.Drawing;
 using System.Drawing.Drawing2D;
 using System.Drawing.Imaging;
 using System.Drawing.Text;
-using System.Text;
+using System.Runtime.InteropServices;
 using System.Windows.Forms;
-using System.Runtime.InteropServices;
 using OpenHardwareMonitor.Hardware;
 using OpenHardwareMonitor.Utilities;
 
diff -r e4ee19d583bd -r c16fd81b520a GUI/ShowDesktop.cs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/GUI/ShowDesktop.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -0,0 +1,172 @@
+/*
+  
+  Version: MPL 1.1/GPL 2.0/LGPL 2.1
+
+  The contents of this file are subject to the Mozilla Public License Version
+  1.1 (the "License"); you may not use this file except in compliance with
+  the License. You may obtain a copy of the License at
+ 
+  http://www.mozilla.org/MPL/
+
+  Software distributed under the License is distributed on an "AS IS" basis,
+  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+  for the specific language governing rights and limitations under the License.
+
+  The Original Code is the Open Hardware Monitor code.
+
+  The Initial Developer of the Original Code is 
+  Michael Möller <m.moeller@gmx.ch>.
+  Portions created by the Initial Developer are Copyright (C) 2010
+  the Initial Developer. All Rights Reserved.
+
+  Contributor(s):
+
+  Alternatively, the contents of this file may be used under the terms of
+  either the GNU General Public License Version 2 or later (the "GPL"), or
+  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+  in which case the provisions of the GPL or the LGPL are applicable instead
+  of those above. If you wish to allow use of your version of this file only
+  under the terms of either the GPL or the LGPL, and not to allow others to
+  use your version of this file under the terms of the MPL, indicate your
+  decision by deleting the provisions above and replace them with the notice
+  and other provisions required by the GPL or the LGPL. If you do not delete
+  the provisions above, a recipient may use your version of this file under
+  the terms of any one of the MPL, the GPL or the LGPL.
+ 
+*/
+
+using System;
+using System.Runtime.InteropServices;
+using System.Windows.Forms;
+
+namespace OpenHardwareMonitor.GUI {
+  public class ShowDesktop {
+    private static ShowDesktop instance = new ShowDesktop();
+
+    public delegate void ShowDesktopChangedEventHandler(bool showDesktop);
+    
+    private event ShowDesktopChangedEventHandler ShowDesktopChangedEvent;
+
+    private System.Threading.Timer timer;
+    private bool showDesktop = false;   
+    private NativeWindow referenceWindow;
+    private string referenceWindowCaption =
+      "OpenHardwareMonitorShowDesktopReferenceWindow";
+
+    private ShowDesktop() {
+      // create a reference window to detect show desktop
+      referenceWindow = new NativeWindow();
+      CreateParams cp = new CreateParams();
+      cp.ExStyle = GadgetWindow.WS_EX_TOOLWINDOW;
+      cp.Caption = referenceWindowCaption;
+      referenceWindow.CreateHandle(cp);
+      NativeMethods.SetWindowPos(referenceWindow.Handle, 
+        GadgetWindow.HWND_BOTTOM, 0, 0, 0, 0, GadgetWindow.SWP_NOMOVE | 
+        GadgetWindow.SWP_NOSIZE | GadgetWindow.SWP_NOACTIVATE | 
+        GadgetWindow.SWP_NOSENDCHANGING);
+
+      // start a repeated timer to detect "Show Desktop" events 
+      timer = new System.Threading.Timer(OnTimer, null,
+        System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
+    }
+
+    private void StartTimer() {
+      timer.Change(0, 200);
+    }
+
+    private void StopTimer() {
+      timer.Change(System.Threading.Timeout.Infinite,
+        System.Threading.Timeout.Infinite);
+    }
+
+    // the desktop worker window (if available) can hide the reference window
+    private IntPtr GetDesktopWorkerWindow() {
+      IntPtr shellWindow = NativeMethods.GetShellWindow();
+      if (shellWindow == IntPtr.Zero)
+        return IntPtr.Zero;
+
+      int shellId;
+      NativeMethods.GetWindowThreadProcessId(shellWindow, out shellId);
+
+      IntPtr workerWindow = IntPtr.Zero;
+      while ((workerWindow = NativeMethods.FindWindowEx(
+          IntPtr.Zero, workerWindow, "WorkerW", null)) != IntPtr.Zero) {
+
+        int workerId;
+        NativeMethods.GetWindowThreadProcessId(workerWindow, out workerId);
+        if (workerId == shellId) {
+          IntPtr window = NativeMethods.FindWindowEx(
+            workerWindow, IntPtr.Zero, "SHELLDLL_DefView", null);
+          if (window != IntPtr.Zero) {
+            IntPtr desktopWindow = NativeMethods.FindWindowEx(
+              window, IntPtr.Zero, "SysListView32", null);
+            if (desktopWindow != IntPtr.Zero)
+              return workerWindow;
+          }
+        }
+      }
+      return IntPtr.Zero;
+    }
+
+    private void OnTimer(Object state) {
+      bool showDesktopDetected;
+
+      IntPtr workerWindow = GetDesktopWorkerWindow();
+      if (workerWindow != IntPtr.Zero) {
+        // search if the reference window is behind the worker window
+        IntPtr reference = NativeMethods.FindWindowEx(
+          IntPtr.Zero, workerWindow, null, referenceWindowCaption);
+        showDesktopDetected = reference == referenceWindow.Handle;
+      } else {
+        // if there is no worker window, then nothing can hide the reference
+        showDesktopDetected = false;
+      }
+
+      if (showDesktop != showDesktopDetected) {
+        showDesktop = showDesktopDetected;
+        if (ShowDesktopChangedEvent != null) {
+          ShowDesktopChangedEvent(showDesktop);
+        }
+      }
+    }
+
+    public static ShowDesktop Instance {
+      get { return instance; }
+    }
+
+    // notify when the "show desktop" mode is changed
+    public event ShowDesktopChangedEventHandler ShowDesktopChanged {
+      add {
+        // start the monitor timer when someone is listening
+        if (ShowDesktopChangedEvent == null)           
+          StartTimer();
+        ShowDesktopChangedEvent += value;
+      }
+      remove {
+        ShowDesktopChangedEvent -= value;
+        // stop the monitor timer if nobody is interested
+        if (ShowDesktopChangedEvent == null)
+          StopTimer();
+      }
+    }
+
+    private static class NativeMethods {
+      private const string USER = "user32.dll";
+
+      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
+      public static extern bool SetWindowPos(IntPtr hWnd,
+        IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
+
+      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
+      public static extern IntPtr FindWindowEx(IntPtr hwndParent,
+        IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
+
+      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
+      public static extern IntPtr GetShellWindow();
+
+      [DllImport(USER, CallingConvention = CallingConvention.Winapi)]
+      public static extern int GetWindowThreadProcessId(IntPtr hWnd,
+        out int processId);
+    }  
+  }
+}
diff -r e4ee19d583bd -r c16fd81b520a Hardware/ATI/ATIGPU.cs
--- a/Hardware/ATI/ATIGPU.cs	Tue Aug 24 22:11:10 2010 +0000
+++ b/Hardware/ATI/ATIGPU.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -88,7 +88,7 @@
     }
 
     public override HardwareType HardwareType {
-      get { return HardwareType.GPU; }
+      get { return HardwareType.GpuAti; }
     }
 
     public override void Update() {
diff -r e4ee19d583bd -r c16fd81b520a Hardware/HDD/HDD.cs
--- a/Hardware/HDD/HDD.cs	Tue Aug 24 22:11:10 2010 +0000
+++ b/Hardware/HDD/HDD.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -85,6 +85,10 @@
       get { return new IHardware[0]; }
     }
 
+    public virtual IHardware Parent {
+      get { return null; }
+    }
+
     public ISensor[] Sensors {
       get {
         return new ISensor[] { temperature };
diff -r e4ee19d583bd -r c16fd81b520a Hardware/Hardware.cs
--- a/Hardware/Hardware.cs	Tue Aug 24 22:11:10 2010 +0000
+++ b/Hardware/Hardware.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -48,6 +48,10 @@
       get { return new IHardware[0]; }
     }
 
+    public virtual IHardware Parent {
+      get { return null; }
+    }
+
     public ISensor[] Sensors {
       get { return active.ToArray(); }
     }
diff -r e4ee19d583bd -r c16fd81b520a Hardware/IHardware.cs
--- a/Hardware/IHardware.cs	Tue Aug 24 22:11:10 2010 +0000
+++ b/Hardware/IHardware.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -42,14 +42,15 @@
 
   public delegate void SensorEventHandler(ISensor sensor);
 
-  public enum HardwareType {    
-    CPU,
-    GPU,
-    HDD,
-    Heatmaster,
+  public enum HardwareType {
     Mainboard,
     SuperIO,
-    TBalancer
+    CPU,
+    GpuNvidia,
+    GpuAti,    
+    TBalancer,
+    Heatmaster,
+    HDD,     
   }
 
   public interface IHardware : IElement {
@@ -65,6 +66,8 @@
 
     IHardware[] SubHardware { get; }
 
+    IHardware Parent { get; }
+
     ISensor[] Sensors { get; }
 
     event SensorEventHandler SensorAdded;
diff -r e4ee19d583bd -r c16fd81b520a Hardware/Identifier.cs
--- a/Hardware/Identifier.cs	Tue Aug 24 22:11:10 2010 +0000
+++ b/Hardware/Identifier.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -40,7 +40,7 @@
 using System.Text;
 
 namespace OpenHardwareMonitor.Hardware {
-  public class Identifier {
+  public class Identifier : IComparable<Identifier> {
     private string identifier;
 
     private static char SEPARATOR = '/';
@@ -92,5 +92,12 @@
     public override int GetHashCode() {
       return identifier.GetHashCode();
     }
+
+    public int CompareTo(Identifier other) {
+      if (other == null)
+        return 1;
+      else 
+        return this.identifier.CompareTo(other.identifier);
+    }
   }
 }
diff -r e4ee19d583bd -r c16fd81b520a Hardware/Mainboard/Mainboard.cs
--- a/Hardware/Mainboard/Mainboard.cs	Tue Aug 24 22:11:10 2010 +0000
+++ b/Hardware/Mainboard/Mainboard.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -78,7 +78,7 @@
       
       superIOHardware = new IHardware[superIO.Length];
       for (int i = 0; i < superIO.Length; i++)
-        superIOHardware[i] = new SuperIOHardware(superIO[i], 
+        superIOHardware[i] = new SuperIOHardware(this, superIO[i], 
           smbios.Board != null ? smbios.Board.Manufacturer : 
           Manufacturer.Unknown, smbios.Board != null ? smbios.Board.Model :
           Model.Unknown, settings);
@@ -96,6 +96,10 @@
       get { return HardwareType.Mainboard; }
     }
 
+    public virtual IHardware Parent {
+      get { return null; }
+    }
+
     public string GetReport() {
       StringBuilder r = new StringBuilder(); 
 
diff -r e4ee19d583bd -r c16fd81b520a Hardware/Mainboard/SuperIOHardware.cs
--- a/Hardware/Mainboard/SuperIOHardware.cs	Tue Aug 24 22:11:10 2010 +0000
+++ b/Hardware/Mainboard/SuperIOHardware.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -43,6 +43,7 @@
 namespace OpenHardwareMonitor.Hardware.Mainboard {
   internal class SuperIOHardware : Hardware {
 
+    private Mainboard mainboard;
     private ISuperIO superIO;
     private string name;
 
@@ -51,9 +52,10 @@
     private List<Sensor> fans = new List<Sensor>();
 
 
-    public SuperIOHardware(ISuperIO superIO, Manufacturer manufacturer,
-      Model model, ISettings settings) 
+    public SuperIOHardware(Mainboard mainboard, ISuperIO superIO, 
+      Manufacturer manufacturer, Model model, ISettings settings) 
     {
+      this.mainboard = mainboard;
       this.superIO = superIO;
       this.name = ChipName.GetName(superIO.Chip);
 
@@ -614,6 +616,10 @@
       get { return HardwareType.SuperIO; }
     }
 
+    public override IHardware Parent {
+      get { return mainboard; }
+    }
+
     public override string Name {
       get { return name; }
     }
diff -r e4ee19d583bd -r c16fd81b520a Hardware/Nvidia/NvidiaGPU.cs
--- a/Hardware/Nvidia/NvidiaGPU.cs	Tue Aug 24 22:11:10 2010 +0000
+++ b/Hardware/Nvidia/NvidiaGPU.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -123,7 +123,7 @@
     }
 
     public override HardwareType HardwareType {
-      get { return HardwareType.GPU; }
+      get { return HardwareType.GpuNvidia; }
     }
 
     private NvGPUThermalSettings GetThermalSettings() {
diff -r e4ee19d583bd -r c16fd81b520a Hardware/TBalancer/TBalancer.cs
--- a/Hardware/TBalancer/TBalancer.cs	Tue Aug 24 22:11:10 2010 +0000
+++ b/Hardware/TBalancer/TBalancer.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -285,6 +285,10 @@
       get { return new IHardware[0]; }
     }
 
+    public virtual IHardware Parent {
+      get { return null; }
+    }
+
     public ISensor[] Sensors {
       get { return active.ToArray(); }
     }
diff -r e4ee19d583bd -r c16fd81b520a OpenHardwareMonitor.csproj
--- a/OpenHardwareMonitor.csproj	Tue Aug 24 22:11:10 2010 +0000
+++ b/OpenHardwareMonitor.csproj	Mon Sep 06 19:53:13 2010 +0000
@@ -70,6 +70,9 @@
     </Reference>
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="GUI\GadgetWindow.cs" />
+    <Compile Include="GUI\Gadget.cs" />
+    <Compile Include="GUI\HardwareTypeImage.cs" />
     <Compile Include="GUI\PlotPanel.cs">
       <SubType>UserControl</SubType>
     </Compile>
@@ -92,7 +95,9 @@
     <Compile Include="GUI\ParameterForm.Designer.cs">
       <DependentUpon>ParameterForm.cs</DependentUpon>
     </Compile>
+    <Compile Include="GUI\SensorGadget.cs" />
     <Compile Include="GUI\SensorNotifyIcon.cs" />
+    <Compile Include="GUI\ShowDesktop.cs" />
     <Compile Include="GUI\SplitContainerAdv.cs">
       <SubType>Component</SubType>
     </Compile>
@@ -197,6 +202,11 @@
       <Install>true</Install>
     </BootstrapperPackage>
   </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Resources\barback.png" />
+    <EmbeddedResource Include="Resources\barblue.png" />
+    <EmbeddedResource Include="Resources\gadget.png" />
+  </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <ProjectExtensions>
     <VisualStudio AllowExistingFolder="true" />
diff -r e4ee19d583bd -r c16fd81b520a Properties/AssemblyVersion.cs
--- a/Properties/AssemblyVersion.cs	Tue Aug 24 22:11:10 2010 +0000
+++ b/Properties/AssemblyVersion.cs	Mon Sep 06 19:53:13 2010 +0000
@@ -38,5 +38,5 @@
 using System;
 using System.Reflection;
 
-[assembly: AssemblyVersion("0.1.37.9")]
-[assembly: AssemblyFileVersion("0.1.37.9")]
+[assembly: AssemblyVersion("0.1.37.10")]
+[assembly: AssemblyFileVersion("0.1.37.10")]
diff -r e4ee19d583bd -r c16fd81b520a Resources/barback.png
Binary file Resources/barback.png has changed
diff -r e4ee19d583bd -r c16fd81b520a Resources/barblue.png
Binary file Resources/barblue.png has changed
diff -r e4ee19d583bd -r c16fd81b520a Resources/gadget.png
Binary file Resources/gadget.png has changed
diff -r e4ee19d583bd -r c16fd81b520a Resources/gadget.xcf
Binary file Resources/gadget.xcf has changed