GUI/SensorNotifyIcon.cs
author moel.mich
Wed, 27 Jul 2011 18:27:16 +0000
changeset 317 1ccf99e620a9
parent 282 a2793a38aac0
child 324 c6ee430d6995
permissions -rw-r--r--
Added support for Intel CPU power sensors (package and cores).
     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       }
   202       return "-";
   203     }
   204 
   205     private Icon CreateTransparentIcon() {
   206 
   207       graphics.Clear(Color.Black);
   208       TextRenderer.DrawText(graphics, GetString(), font,
   209         new Point(-2, 0), Color.White, Color.Black);        
   210 
   211       BitmapData data = bitmap.LockBits(
   212         new Rectangle(0, 0, bitmap.Width, bitmap.Height),
   213         ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
   214 
   215       IntPtr Scan0 = data.Scan0;
   216 
   217       int numBytes = bitmap.Width * bitmap.Height * 4;
   218       byte[] bytes = new byte[numBytes];
   219       Marshal.Copy(Scan0, bytes, 0, numBytes);
   220       bitmap.UnlockBits(data);
   221 
   222       byte red, green, blue;
   223       for (int i = 0; i < bytes.Length; i += 4) {
   224         blue = bytes[i];
   225         green = bytes[i + 1];
   226         red = bytes[i + 2];
   227 
   228         bytes[i] = color.B;
   229         bytes[i + 1] = color.G;
   230         bytes[i + 2] = color.R;
   231         bytes[i + 3] = (byte)(0.3 * red + 0.59 * green + 0.11 * blue);
   232       }
   233 
   234       return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, 
   235         PixelFormat.Format32bppArgb);
   236     }
   237 
   238     private Icon CreatePercentageIcon() {      
   239       try {
   240         graphics.Clear(Color.Transparent);
   241       } catch (ArgumentException) {
   242         graphics.Clear(Color.Black);
   243       }
   244       graphics.FillRectangle(darkBrush, 0.5f, -0.5f, bitmap.Width - 2, bitmap.Height);
   245       float value = sensor.Value.GetValueOrDefault();
   246       float y = 0.16f * (100 - value);
   247       graphics.FillRectangle(brush, 0.5f, -0.5f + y, bitmap.Width - 2, bitmap.Height - y);
   248       graphics.DrawRectangle(pen, 1, 0, bitmap.Width - 3, bitmap.Height - 1);
   249 
   250       BitmapData data = bitmap.LockBits(
   251         new Rectangle(0, 0, bitmap.Width, bitmap.Height),
   252         ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
   253       byte[] bytes = new byte[bitmap.Width * bitmap.Height * 4];
   254       Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
   255       bitmap.UnlockBits(data);
   256 
   257       return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, 
   258         PixelFormat.Format32bppArgb);
   259     }
   260 
   261     public void Update() {
   262       Icon icon = notifyIcon.Icon;
   263 
   264       switch (sensor.SensorType) {
   265         case SensorType.Load:
   266         case SensorType.Control:
   267         case SensorType.Level:
   268           notifyIcon.Icon = CreatePercentageIcon();
   269           break;
   270         default:
   271           notifyIcon.Icon = CreateTransparentIcon();
   272           break;
   273       }
   274 
   275       if (icon != null) 
   276         icon.Dispose();
   277 
   278       string format = "";
   279       switch (sensor.SensorType) {
   280         case SensorType.Voltage: format = "\n{0}: {1:F2} V"; break;
   281         case SensorType.Clock: format = "\n{0}: {1:F0} MHz"; break;
   282         case SensorType.Load: format = "\n{0}: {1:F1} %"; break;
   283         case SensorType.Temperature: format = "\n{0}: {1:F1} °C"; break;
   284         case SensorType.Fan: format = "\n{0}: {1:F0} RPM"; break;
   285         case SensorType.Flow: format = "\n{0}: {1:F0} L/h"; break;
   286         case SensorType.Control: format = "\n{0}: {1:F1} %"; break;
   287         case SensorType.Level: format = "\n{0}: {1:F1} %"; break;
   288         case SensorType.Power: format = "\n{0}: {1:F0} W"; break;
   289       }
   290       string formattedValue = string.Format(format, sensor.Name, sensor.Value);
   291       string hardwareName = sensor.Hardware.Name;
   292       hardwareName = hardwareName.Substring(0, 
   293         Math.Min(63 - formattedValue.Length, hardwareName.Length));
   294       string text = hardwareName + formattedValue;
   295       if (text.Length > 63)
   296         text = null;
   297 
   298       notifyIcon.Text = text;
   299       notifyIcon.Visible = true;         
   300     }
   301   }
   302 }