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