GUI/SensorNotifyIcon.cs
author moel.mich
Tue, 14 Feb 2012 23:07:55 +0000
changeset 340 600962f8a298
parent 324 c6ee430d6995
child 344 3145aadca3d2
permissions -rw-r--r--
Added a new sensor type "Factor" for dimensionless values (and similar) that are not to be shown as percent ("Level" type). Changed the write amplification sensor to use the new "Factor" sensor type. Added the temperature SMART attribute for Sandforce SSDs as hidden sensor (as it may show fake results on some hardware).
     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-2012
    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:F1}", sensor.Value);
   185         case SensorType.Clock:
   186           return string.Format("{0:F1}", 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:F1}", 1e-3f * sensor.Value);
   193         case SensorType.Flow:
   194           return string.Format("{0:F1}", 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         case SensorType.Factor:
   204           return string.Format("{0:F1}", sensor.Value);
   205       }
   206       return "-";
   207     }
   208 
   209     private Icon CreateTransparentIcon() {
   210 
   211       graphics.Clear(Color.Black);
   212       TextRenderer.DrawText(graphics, GetString(), font,
   213         new Point(-2, 0), Color.White, Color.Black);        
   214 
   215       BitmapData data = bitmap.LockBits(
   216         new Rectangle(0, 0, bitmap.Width, bitmap.Height),
   217         ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
   218 
   219       IntPtr Scan0 = data.Scan0;
   220 
   221       int numBytes = bitmap.Width * bitmap.Height * 4;
   222       byte[] bytes = new byte[numBytes];
   223       Marshal.Copy(Scan0, bytes, 0, numBytes);
   224       bitmap.UnlockBits(data);
   225 
   226       byte red, green, blue;
   227       for (int i = 0; i < bytes.Length; i += 4) {
   228         blue = bytes[i];
   229         green = bytes[i + 1];
   230         red = bytes[i + 2];
   231 
   232         bytes[i] = color.B;
   233         bytes[i + 1] = color.G;
   234         bytes[i + 2] = color.R;
   235         bytes[i + 3] = (byte)(0.3 * red + 0.59 * green + 0.11 * blue);
   236       }
   237 
   238       return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, 
   239         PixelFormat.Format32bppArgb);
   240     }
   241 
   242     private Icon CreatePercentageIcon() {      
   243       try {
   244         graphics.Clear(Color.Transparent);
   245       } catch (ArgumentException) {
   246         graphics.Clear(Color.Black);
   247       }
   248       graphics.FillRectangle(darkBrush, 0.5f, -0.5f, bitmap.Width - 2, bitmap.Height);
   249       float value = sensor.Value.GetValueOrDefault();
   250       float y = 0.16f * (100 - value);
   251       graphics.FillRectangle(brush, 0.5f, -0.5f + y, bitmap.Width - 2, bitmap.Height - y);
   252       graphics.DrawRectangle(pen, 1, 0, bitmap.Width - 3, bitmap.Height - 1);
   253 
   254       BitmapData data = bitmap.LockBits(
   255         new Rectangle(0, 0, bitmap.Width, bitmap.Height),
   256         ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
   257       byte[] bytes = new byte[bitmap.Width * bitmap.Height * 4];
   258       Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
   259       bitmap.UnlockBits(data);
   260 
   261       return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, 
   262         PixelFormat.Format32bppArgb);
   263     }
   264 
   265     public void Update() {
   266       Icon icon = notifyIcon.Icon;
   267 
   268       switch (sensor.SensorType) {
   269         case SensorType.Load:
   270         case SensorType.Control:
   271         case SensorType.Level:
   272           notifyIcon.Icon = CreatePercentageIcon();
   273           break;
   274         default:
   275           notifyIcon.Icon = CreateTransparentIcon();
   276           break;
   277       }
   278 
   279       if (icon != null) 
   280         icon.Dispose();
   281 
   282       string format = "";
   283       switch (sensor.SensorType) {
   284         case SensorType.Voltage: format = "\n{0}: {1:F2} V"; break;
   285         case SensorType.Clock: format = "\n{0}: {1:F0} MHz"; break;
   286         case SensorType.Load: format = "\n{0}: {1:F1} %"; break;
   287         case SensorType.Temperature: format = "\n{0}: {1:F1} °C"; break;
   288         case SensorType.Fan: format = "\n{0}: {1:F0} RPM"; break;
   289         case SensorType.Flow: format = "\n{0}: {1:F0} L/h"; break;
   290         case SensorType.Control: format = "\n{0}: {1:F1} %"; break;
   291         case SensorType.Level: format = "\n{0}: {1:F1} %"; break;
   292         case SensorType.Power: format = "\n{0}: {1:F0} W"; break;
   293         case SensorType.Data: format = "\n{0}: {1:F0} GB"; break;
   294         case SensorType.Factor: format = "\n{0}: {1:F3} GB"; break;
   295       }
   296       string formattedValue = string.Format(format, sensor.Name, sensor.Value);
   297       string hardwareName = sensor.Hardware.Name;
   298       hardwareName = hardwareName.Substring(0, 
   299         Math.Min(63 - formattedValue.Length, hardwareName.Length));
   300       string text = hardwareName + formattedValue;
   301       if (text.Length > 63)
   302         text = null;
   303 
   304       notifyIcon.Text = text;
   305       notifyIcon.Visible = true;         
   306     }
   307   }
   308 }