GUI/SensorNotifyIcon.cs
author sl
Sun, 03 Feb 2013 18:01:50 +0100
changeset 391 ca4c0e7ae75d
parent 363 daa9590e1bee
permissions -rw-r--r--
Converted project to VisualStudio 2012.
Adding SoundGraphDisplay and SensorFrontView classes.
They were respectively based on SystemTray and SensorNotifyIcon.
SoundGraphDisplay is now able to load iMONDisplay.dll providing it lives on your PATH.
Adding option to sensor context menu for adding it into FrontView.
     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 }