GUI/SensorNotifyIcon.cs
author moel.mich
Sat, 31 Dec 2011 17:31:04 +0000
changeset 324 c6ee430d6995
parent 317 1ccf99e620a9
child 340 600962f8a298
permissions -rw-r--r--
Modified and extended version of the patch v4 by Roland Reinl (see Issue 256). Main differences to the original patch: DeviceIoControl refactorings removed, SmartAttribute is now descriptive only and does not hold any state, report is written as one 80 columns table, sensors are created only for meaningful values and without duplicates (remaining life, temperatures, host writes and reads). Also the current implementation should really preserve all the functionality of the old system. Additionally there is now a simple SMART devices emulation class (DebugSmart) that can be used in place of WindowsSmart for testing with reported data.
     1 /*
     2   
     3   Version: MPL 1.1/GPL 2.0/LGPL 2.1
     4 
     5   The contents of this file are subject to the Mozilla Public License Version
     6   1.1 (the "License"); you may not use this file except in compliance with
     7   the License. You may obtain a copy of the License at
     8  
     9   http://www.mozilla.org/MPL/
    10 
    11   Software distributed under the License is distributed on an "AS IS" basis,
    12   WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    13   for the specific language governing rights and limitations under the License.
    14 
    15   The Original Code is the Open Hardware Monitor code.
    16 
    17   The Initial Developer of the Original Code is 
    18   Michael Möller <m.moeller@gmx.ch>.
    19   Portions created by the Initial Developer are Copyright (C) 2009-2011
    20   the Initial Developer. All Rights Reserved.
    21 
    22   Contributor(s):
    23 
    24   Alternatively, the contents of this file may be used under the terms of
    25   either the GNU General Public License Version 2 or later (the "GPL"), or
    26   the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
    27   in which case the provisions of the GPL or the LGPL are applicable instead
    28   of those above. If you wish to allow use of your version of this file only
    29   under the terms of either the GPL or the LGPL, and not to allow others to
    30   use your version of this file under the terms of the MPL, indicate your
    31   decision by deleting the provisions above and replace them with the notice
    32   and other provisions required by the GPL or the LGPL. If you do not delete
    33   the provisions above, a recipient may use your version of this file under
    34   the terms of any one of the MPL, the GPL or the LGPL.
    35  
    36 */
    37 
    38 using System;
    39 using System.Drawing;
    40 using System.Drawing.Drawing2D;
    41 using System.Drawing.Imaging;
    42 using System.Drawing.Text;
    43 using System.Runtime.InteropServices;
    44 using System.Windows.Forms;
    45 using OpenHardwareMonitor.Hardware;
    46 using OpenHardwareMonitor.Utilities;
    47 
    48 namespace OpenHardwareMonitor.GUI {
    49   public class SensorNotifyIcon : IDisposable {
    50 
    51     private ISensor sensor;
    52     private NotifyIcon notifyIcon;
    53     private Bitmap bitmap;
    54     private Graphics graphics;
    55     private Color color;
    56     private Color darkColor;
    57     private Brush brush;
    58     private Brush darkBrush;
    59     private Pen pen;
    60     private Font font;
    61 
    62     public SensorNotifyIcon(SystemTray sensorSystemTray, ISensor sensor,
    63       bool balloonTip, PersistentSettings settings) 
    64     {
    65       this.sensor = sensor;
    66       this.notifyIcon = new NotifyIcon();
    67 
    68       Color defaultColor = Color.Black;
    69       if (sensor.SensorType == SensorType.Load ||
    70           sensor.SensorType == SensorType.Control ||
    71           sensor.SensorType == SensorType.Level) 
    72       {
    73         defaultColor = Color.FromArgb(0xff, 0x70, 0x8c, 0xf1);
    74       }
    75       Color = settings.GetValue(new Identifier(sensor.Identifier, 
    76         "traycolor").ToString(), defaultColor);      
    77       
    78       this.pen = new Pen(Color.FromArgb(96, Color.Black));
    79       this.font = SystemFonts.MessageBoxFont;
    80 
    81       ContextMenu contextMenu = new ContextMenu();
    82       MenuItem hideShowItem = new MenuItem("Hide/Show");
    83       hideShowItem.Click += delegate(object obj, EventArgs args) {
    84         sensorSystemTray.SendHideShowCommand();
    85       };
    86       contextMenu.MenuItems.Add(hideShowItem);
    87       contextMenu.MenuItems.Add(new MenuItem("-"));
    88       MenuItem removeItem = new MenuItem("Remove Sensor");
    89       removeItem.Click += delegate(object obj, EventArgs args) {
    90         sensorSystemTray.Remove(this.sensor);
    91       };
    92       contextMenu.MenuItems.Add(removeItem);
    93       MenuItem colorItem = new MenuItem("Change Color...");
    94       colorItem.Click += delegate(object obj, EventArgs args) {
    95         ColorDialog dialog = new ColorDialog();
    96         dialog.Color = Color;
    97         if (dialog.ShowDialog() == DialogResult.OK) {
    98           Color = dialog.Color;
    99           settings.SetValue(new Identifier(sensor.Identifier,
   100             "traycolor").ToString(), Color);
   101         }
   102       };
   103       contextMenu.MenuItems.Add(colorItem);
   104       contextMenu.MenuItems.Add(new MenuItem("-"));
   105       MenuItem exitItem = new MenuItem("Exit");
   106       exitItem.Click += delegate(object obj, EventArgs args) {
   107         sensorSystemTray.SendExitCommand();
   108       };
   109       contextMenu.MenuItems.Add(exitItem);
   110       this.notifyIcon.ContextMenu = contextMenu;
   111       this.notifyIcon.DoubleClick += delegate(object obj, EventArgs args) {
   112         sensorSystemTray.SendHideShowCommand();
   113       };
   114 
   115       // get the default dpi to create an icon with the correct size
   116       float dpiX, dpiY;
   117       using (Bitmap b = new Bitmap(1, 1, PixelFormat.Format32bppArgb)) {
   118         dpiX = b.HorizontalResolution;
   119         dpiY = b.VerticalResolution;
   120       }
   121 
   122       // adjust the size of the icon to current dpi (default is 16x16 at 96 dpi) 
   123       int width = (int)Math.Round(16 * dpiX / 96);
   124       int height = (int)Math.Round(16 * dpiY / 96);
   125 
   126       // make sure it does never get smaller than 16x16
   127       width = width < 16 ? 16: width;
   128       height = height < 16 ? 16: height;
   129 
   130       this.bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);      
   131       this.graphics = Graphics.FromImage(this.bitmap);
   132 
   133       if (Environment.OSVersion.Version.Major > 5) {
   134         this.graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
   135         this.graphics.SmoothingMode = SmoothingMode.HighQuality;
   136       }
   137     }
   138 
   139     public ISensor Sensor {
   140       get { return sensor; }
   141     }
   142 
   143     public Color Color {
   144       get { return color; }
   145       set { 
   146         this.color = value;
   147         this.darkColor = Color.FromArgb(255,
   148           this.color.R / 3,
   149           this.color.G / 3,
   150           this.color.B / 3);
   151         Brush brush = this.brush;
   152         this.brush = new SolidBrush(this.color);
   153         if (brush != null)
   154           brush.Dispose();
   155         Brush darkBrush = this.darkBrush;
   156         this.darkBrush = new SolidBrush(this.darkColor);
   157         if (darkBrush != null)
   158           darkBrush.Dispose();
   159       }
   160     }
   161 
   162     public void Dispose() {      
   163       Icon icon = notifyIcon.Icon;
   164       notifyIcon.Icon = null;
   165       if (icon != null)
   166         icon.Dispose();      
   167       notifyIcon.Dispose();
   168 
   169       if (brush != null)
   170         brush.Dispose();
   171       if (darkBrush != null)
   172         darkBrush.Dispose();
   173       pen.Dispose();
   174       graphics.Dispose();      
   175       bitmap.Dispose();      
   176     }
   177 
   178     private string GetString() {
   179       if (!sensor.Value.HasValue)
   180         return "-";
   181 
   182       switch (sensor.SensorType) {
   183         case SensorType.Voltage:
   184           return string.Format("{0:F11}", sensor.Value);
   185         case SensorType.Clock:
   186           return string.Format("{0:F11}", 1e-3f * sensor.Value);
   187         case SensorType.Load: 
   188           return string.Format("{0:F0}", sensor.Value);
   189         case SensorType.Temperature: 
   190           return string.Format("{0:F0}", sensor.Value);
   191         case SensorType.Fan: 
   192           return string.Format("{0:F11}", 1e-3f * sensor.Value);
   193         case SensorType.Flow:
   194           return string.Format("{0:F11}", 1e-3f * sensor.Value);
   195         case SensorType.Control:
   196           return string.Format("{0:F0}", sensor.Value);
   197         case SensorType.Level:
   198           return string.Format("{0:F0}", sensor.Value);
   199         case SensorType.Power:
   200           return string.Format("{0:F0}", sensor.Value);
   201         case SensorType.Data:
   202           return string.Format("{0:F0}", sensor.Value);
   203       }
   204       return "-";
   205     }
   206 
   207     private Icon CreateTransparentIcon() {
   208 
   209       graphics.Clear(Color.Black);
   210       TextRenderer.DrawText(graphics, GetString(), font,
   211         new Point(-2, 0), Color.White, Color.Black);        
   212 
   213       BitmapData data = bitmap.LockBits(
   214         new Rectangle(0, 0, bitmap.Width, bitmap.Height),
   215         ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
   216 
   217       IntPtr Scan0 = data.Scan0;
   218 
   219       int numBytes = bitmap.Width * bitmap.Height * 4;
   220       byte[] bytes = new byte[numBytes];
   221       Marshal.Copy(Scan0, bytes, 0, numBytes);
   222       bitmap.UnlockBits(data);
   223 
   224       byte red, green, blue;
   225       for (int i = 0; i < bytes.Length; i += 4) {
   226         blue = bytes[i];
   227         green = bytes[i + 1];
   228         red = bytes[i + 2];
   229 
   230         bytes[i] = color.B;
   231         bytes[i + 1] = color.G;
   232         bytes[i + 2] = color.R;
   233         bytes[i + 3] = (byte)(0.3 * red + 0.59 * green + 0.11 * blue);
   234       }
   235 
   236       return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, 
   237         PixelFormat.Format32bppArgb);
   238     }
   239 
   240     private Icon CreatePercentageIcon() {      
   241       try {
   242         graphics.Clear(Color.Transparent);
   243       } catch (ArgumentException) {
   244         graphics.Clear(Color.Black);
   245       }
   246       graphics.FillRectangle(darkBrush, 0.5f, -0.5f, bitmap.Width - 2, bitmap.Height);
   247       float value = sensor.Value.GetValueOrDefault();
   248       float y = 0.16f * (100 - value);
   249       graphics.FillRectangle(brush, 0.5f, -0.5f + y, bitmap.Width - 2, bitmap.Height - y);
   250       graphics.DrawRectangle(pen, 1, 0, bitmap.Width - 3, bitmap.Height - 1);
   251 
   252       BitmapData data = bitmap.LockBits(
   253         new Rectangle(0, 0, bitmap.Width, bitmap.Height),
   254         ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
   255       byte[] bytes = new byte[bitmap.Width * bitmap.Height * 4];
   256       Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
   257       bitmap.UnlockBits(data);
   258 
   259       return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, 
   260         PixelFormat.Format32bppArgb);
   261     }
   262 
   263     public void Update() {
   264       Icon icon = notifyIcon.Icon;
   265 
   266       switch (sensor.SensorType) {
   267         case SensorType.Load:
   268         case SensorType.Control:
   269         case SensorType.Level:
   270           notifyIcon.Icon = CreatePercentageIcon();
   271           break;
   272         default:
   273           notifyIcon.Icon = CreateTransparentIcon();
   274           break;
   275       }
   276 
   277       if (icon != null) 
   278         icon.Dispose();
   279 
   280       string format = "";
   281       switch (sensor.SensorType) {
   282         case SensorType.Voltage: format = "\n{0}: {1:F2} V"; break;
   283         case SensorType.Clock: format = "\n{0}: {1:F0} MHz"; break;
   284         case SensorType.Load: format = "\n{0}: {1:F1} %"; break;
   285         case SensorType.Temperature: format = "\n{0}: {1:F1} °C"; break;
   286         case SensorType.Fan: format = "\n{0}: {1:F0} RPM"; break;
   287         case SensorType.Flow: format = "\n{0}: {1:F0} L/h"; break;
   288         case SensorType.Control: format = "\n{0}: {1:F1} %"; break;
   289         case SensorType.Level: format = "\n{0}: {1:F1} %"; break;
   290         case SensorType.Power: format = "\n{0}: {1:F0} W"; break;
   291         case SensorType.Data: format = "\n{0}: {1:F0} GB"; break;
   292       }
   293       string formattedValue = string.Format(format, sensor.Name, sensor.Value);
   294       string hardwareName = sensor.Hardware.Name;
   295       hardwareName = hardwareName.Substring(0, 
   296         Math.Min(63 - formattedValue.Length, hardwareName.Length));
   297       string text = hardwareName + formattedValue;
   298       if (text.Length > 63)
   299         text = null;
   300 
   301       notifyIcon.Text = text;
   302       notifyIcon.Visible = true;         
   303     }
   304   }
   305 }