GUI/SensorNotifyIcon.cs
author moel.mich
Sun, 04 Aug 2013 06:54:46 +0000
changeset 419 07d44b20bbd4
parent 363 daa9590e1bee
permissions -rw-r--r--
Fixed an issue in the NCT677X class causing a crash when calling Update.
     1 /*
     2  
     3   This Source Code Form is subject to the terms of the Mozilla Public
     4   License, v. 2.0. If a copy of the MPL was not distributed with this
     5   file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  
     7   Copyright (C) 2009-2012 Michael Möller <mmoeller@openhardwaremonitor.org>
     8 	
     9 */
    10 
    11 using System;
    12 using System.Drawing;
    13 using System.Drawing.Drawing2D;
    14 using System.Drawing.Imaging;
    15 using System.Drawing.Text;
    16 using System.Runtime.InteropServices;
    17 using System.Windows.Forms;
    18 using OpenHardwareMonitor.Hardware;
    19 using OpenHardwareMonitor.Utilities;
    20 
    21 namespace OpenHardwareMonitor.GUI {
    22   public class SensorNotifyIcon : IDisposable {
    23 
    24     private UnitManager unitManager;
    25 
    26     private ISensor sensor;
    27     private NotifyIconAdv notifyIcon;
    28     private Bitmap bitmap;
    29     private Graphics graphics;
    30     private Color color;
    31     private Color darkColor;
    32     private Brush brush;
    33     private Brush darkBrush;
    34     private Pen pen;
    35     private Font font;
    36     private Font smallFont;
    37 
    38     public SensorNotifyIcon(SystemTray sensorSystemTray, ISensor sensor,
    39       bool balloonTip, PersistentSettings settings, UnitManager unitManager) 
    40     {
    41       this.unitManager = unitManager;
    42       this.sensor = sensor;
    43       this.notifyIcon = new NotifyIconAdv();
    44 
    45       Color defaultColor = Color.Black;
    46       if (sensor.SensorType == SensorType.Load ||
    47           sensor.SensorType == SensorType.Control ||
    48           sensor.SensorType == SensorType.Level) 
    49       {
    50         defaultColor = Color.FromArgb(0xff, 0x70, 0x8c, 0xf1);
    51       }
    52       Color = settings.GetValue(new Identifier(sensor.Identifier, 
    53         "traycolor").ToString(), defaultColor);      
    54       
    55       this.pen = new Pen(Color.FromArgb(96, Color.Black));
    56 
    57       ContextMenu contextMenu = new ContextMenu();
    58       MenuItem hideShowItem = new MenuItem("Hide/Show");
    59       hideShowItem.Click += delegate(object obj, EventArgs args) {
    60         sensorSystemTray.SendHideShowCommand();
    61       };
    62       contextMenu.MenuItems.Add(hideShowItem);
    63       contextMenu.MenuItems.Add(new MenuItem("-"));
    64       MenuItem removeItem = new MenuItem("Remove Sensor");
    65       removeItem.Click += delegate(object obj, EventArgs args) {
    66         sensorSystemTray.Remove(this.sensor);
    67       };
    68       contextMenu.MenuItems.Add(removeItem);
    69       MenuItem colorItem = new MenuItem("Change Color...");
    70       colorItem.Click += delegate(object obj, EventArgs args) {
    71         ColorDialog dialog = new ColorDialog();
    72         dialog.Color = Color;
    73         if (dialog.ShowDialog() == DialogResult.OK) {
    74           Color = dialog.Color;
    75           settings.SetValue(new Identifier(sensor.Identifier,
    76             "traycolor").ToString(), Color);
    77         }
    78       };
    79       contextMenu.MenuItems.Add(colorItem);
    80       contextMenu.MenuItems.Add(new MenuItem("-"));
    81       MenuItem exitItem = new MenuItem("Exit");
    82       exitItem.Click += delegate(object obj, EventArgs args) {
    83         sensorSystemTray.SendExitCommand();
    84       };
    85       contextMenu.MenuItems.Add(exitItem);
    86       this.notifyIcon.ContextMenu = contextMenu;
    87       this.notifyIcon.DoubleClick += delegate(object obj, EventArgs args) {
    88         sensorSystemTray.SendHideShowCommand();
    89       };      
    90 
    91       // get the default dpi to create an icon with the correct size
    92       float dpiX, dpiY;
    93       using (Bitmap b = new Bitmap(1, 1, PixelFormat.Format32bppArgb)) {
    94         dpiX = b.HorizontalResolution;
    95         dpiY = b.VerticalResolution;
    96       }
    97 
    98       // adjust the size of the icon to current dpi (default is 16x16 at 96 dpi) 
    99       int width = (int)Math.Round(16 * dpiX / 96);
   100       int height = (int)Math.Round(16 * dpiY / 96);
   101 
   102       // make sure it does never get smaller than 16x16
   103       width = width < 16 ? 16 : width;
   104       height = height < 16 ? 16 : height;
   105 
   106       // adjust the font size to the icon size
   107       FontFamily family = SystemFonts.MessageBoxFont.FontFamily;
   108       float baseSize;
   109       switch (family.Name) {
   110         case "Segoe UI": baseSize = 12; break;
   111         case "Tahoma": baseSize = 11; break;
   112         default: baseSize = 12; break;
   113       }
   114 
   115       this.font = new Font(family,
   116         baseSize * width / 16.0f, GraphicsUnit.Pixel);
   117       this.smallFont = new Font(family, 
   118         0.75f * baseSize * width / 16.0f, GraphicsUnit.Pixel);
   119 
   120       this.bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);      
   121       this.graphics = Graphics.FromImage(this.bitmap);
   122 
   123       if (Environment.OSVersion.Version.Major > 5) {
   124         this.graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
   125         this.graphics.SmoothingMode = SmoothingMode.HighQuality;
   126       }
   127     }
   128 
   129     public ISensor Sensor {
   130       get { return sensor; }
   131     }
   132 
   133     public Color Color {
   134       get { return color; }
   135       set { 
   136         this.color = value;
   137         this.darkColor = Color.FromArgb(255,
   138           this.color.R / 3,
   139           this.color.G / 3,
   140           this.color.B / 3);
   141         Brush brush = this.brush;
   142         this.brush = new SolidBrush(this.color);
   143         if (brush != null)
   144           brush.Dispose();
   145         Brush darkBrush = this.darkBrush;
   146         this.darkBrush = new SolidBrush(this.darkColor);
   147         if (darkBrush != null)
   148           darkBrush.Dispose();
   149       }
   150     }
   151 
   152     public void Dispose() {      
   153       Icon icon = notifyIcon.Icon;
   154       notifyIcon.Icon = null;
   155       if (icon != null)
   156         icon.Dispose();      
   157       notifyIcon.Dispose();
   158 
   159       if (brush != null)
   160         brush.Dispose();
   161       if (darkBrush != null)
   162         darkBrush.Dispose();
   163       pen.Dispose();
   164       graphics.Dispose();      
   165       bitmap.Dispose();
   166       font.Dispose();
   167       smallFont.Dispose();
   168     }
   169 
   170     private string GetString() {
   171       if (!sensor.Value.HasValue)
   172         return "-";
   173 
   174       switch (sensor.SensorType) {
   175         case SensorType.Voltage:
   176           return string.Format("{0:F1}", sensor.Value);
   177         case SensorType.Clock:
   178           return string.Format("{0:F1}", 1e-3f * sensor.Value);
   179         case SensorType.Load: 
   180           return string.Format("{0:F0}", sensor.Value);
   181         case SensorType.Temperature:
   182           if (unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit)
   183             return string.Format("{0:F0}", 
   184               UnitManager.CelsiusToFahrenheit(sensor.Value));
   185           else
   186             return string.Format("{0:F0}", sensor.Value);
   187         case SensorType.Fan: 
   188           return string.Format("{0:F1}", 1e-3f * sensor.Value);
   189         case SensorType.Flow:
   190           return string.Format("{0:F1}", 1e-3f * sensor.Value);
   191         case SensorType.Control:
   192           return string.Format("{0:F0}", sensor.Value);
   193         case SensorType.Level:
   194           return string.Format("{0:F0}", sensor.Value);
   195         case SensorType.Power:
   196           return string.Format("{0:F0}", sensor.Value);
   197         case SensorType.Data:
   198           return string.Format("{0:F0}", sensor.Value);
   199         case SensorType.Factor:
   200           return string.Format("{0:F1}", sensor.Value);
   201       }
   202       return "-";
   203     }
   204 
   205     private Icon CreateTransparentIcon() {
   206       string text = GetString();
   207       int count = 0;
   208       for (int i = 0; i < text.Length; i++)
   209         if ((text[i] >= '0' && text[i] <= '9') || text[i] == '-')
   210           count++;
   211       bool small = count > 2;
   212 
   213       graphics.Clear(Color.Black);
   214       TextRenderer.DrawText(graphics, text, small ? smallFont : font,
   215         new Point(-2, small ? 1 : 0), Color.White, Color.Black);        
   216 
   217       BitmapData data = bitmap.LockBits(
   218         new Rectangle(0, 0, bitmap.Width, bitmap.Height),
   219         ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
   220 
   221       IntPtr Scan0 = data.Scan0;
   222 
   223       int numBytes = bitmap.Width * bitmap.Height * 4;
   224       byte[] bytes = new byte[numBytes];
   225       Marshal.Copy(Scan0, bytes, 0, numBytes);
   226       bitmap.UnlockBits(data);
   227 
   228       byte red, green, blue;
   229       for (int i = 0; i < bytes.Length; i += 4) {
   230         blue = bytes[i];
   231         green = bytes[i + 1];
   232         red = bytes[i + 2];
   233 
   234         bytes[i] = color.B;
   235         bytes[i + 1] = color.G;
   236         bytes[i + 2] = color.R;
   237         bytes[i + 3] = (byte)(0.3 * red + 0.59 * green + 0.11 * blue);
   238       }
   239 
   240       return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, 
   241         PixelFormat.Format32bppArgb);
   242     }
   243 
   244     private Icon CreatePercentageIcon() {      
   245       try {
   246         graphics.Clear(Color.Transparent);
   247       } catch (ArgumentException) {
   248         graphics.Clear(Color.Black);
   249       }
   250       graphics.FillRectangle(darkBrush, 0.5f, -0.5f, bitmap.Width - 2, bitmap.Height);
   251       float value = sensor.Value.GetValueOrDefault();
   252       float y = 0.16f * (100 - value);
   253       graphics.FillRectangle(brush, 0.5f, -0.5f + y, bitmap.Width - 2, bitmap.Height - y);
   254       graphics.DrawRectangle(pen, 1, 0, bitmap.Width - 3, bitmap.Height - 1);
   255 
   256       BitmapData data = bitmap.LockBits(
   257         new Rectangle(0, 0, bitmap.Width, bitmap.Height),
   258         ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
   259       byte[] bytes = new byte[bitmap.Width * bitmap.Height * 4];
   260       Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
   261       bitmap.UnlockBits(data);
   262 
   263       return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, 
   264         PixelFormat.Format32bppArgb);
   265     }
   266 
   267     public void Update() {
   268       Icon icon = notifyIcon.Icon;
   269 
   270       switch (sensor.SensorType) {
   271         case SensorType.Load:
   272         case SensorType.Control:
   273         case SensorType.Level:
   274           notifyIcon.Icon = CreatePercentageIcon();
   275           break;
   276         default:
   277           notifyIcon.Icon = CreateTransparentIcon();
   278           break;
   279       }
   280 
   281       if (icon != null) 
   282         icon.Dispose();
   283 
   284       string format = "";
   285       switch (sensor.SensorType) {
   286         case SensorType.Voltage: format = "\n{0}: {1:F2} V"; break;
   287         case SensorType.Clock: format = "\n{0}: {1:F0} MHz"; break;
   288         case SensorType.Load: format = "\n{0}: {1:F1} %"; break;
   289         case SensorType.Temperature: format = "\n{0}: {1:F1} °C"; break;
   290         case SensorType.Fan: format = "\n{0}: {1:F0} RPM"; break;
   291         case SensorType.Flow: format = "\n{0}: {1:F0} L/h"; break;
   292         case SensorType.Control: format = "\n{0}: {1:F1} %"; break;
   293         case SensorType.Level: format = "\n{0}: {1:F1} %"; break;
   294         case SensorType.Power: format = "\n{0}: {1:F0} W"; break;
   295         case SensorType.Data: format = "\n{0}: {1:F0} GB"; break;
   296         case SensorType.Factor: format = "\n{0}: {1:F3} GB"; break;
   297       }
   298       string formattedValue = string.Format(format, sensor.Name, sensor.Value);
   299 
   300       if (sensor.SensorType == SensorType.Temperature &&
   301         unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit) 
   302       {
   303         format = "\n{0}: {1:F1} °F";
   304         formattedValue = string.Format(format, sensor.Name,
   305           UnitManager.CelsiusToFahrenheit(sensor.Value));
   306       }
   307 
   308       string hardwareName = sensor.Hardware.Name;
   309       hardwareName = hardwareName.Substring(0, 
   310         Math.Min(63 - formattedValue.Length, hardwareName.Length));
   311       string text = hardwareName + formattedValue;
   312       if (text.Length > 63)
   313         text = null;
   314 
   315       notifyIcon.Text = text;
   316       notifyIcon.Visible = true;         
   317     }
   318   }
   319 }