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).
moel@202
     1
/*
moel@40
     2
  
moel@40
     3
  Version: MPL 1.1/GPL 2.0/LGPL 2.1
moel@40
     4
moel@40
     5
  The contents of this file are subject to the Mozilla Public License Version
moel@40
     6
  1.1 (the "License"); you may not use this file except in compliance with
moel@40
     7
  the License. You may obtain a copy of the License at
moel@40
     8
 
moel@40
     9
  http://www.mozilla.org/MPL/
moel@40
    10
moel@40
    11
  Software distributed under the License is distributed on an "AS IS" basis,
moel@40
    12
  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
moel@40
    13
  for the specific language governing rights and limitations under the License.
moel@40
    14
moel@40
    15
  The Original Code is the Open Hardware Monitor code.
moel@40
    16
moel@40
    17
  The Initial Developer of the Original Code is 
moel@40
    18
  Michael Möller <m.moeller@gmx.ch>.
moel@340
    19
  Portions created by the Initial Developer are Copyright (C) 2009-2012
moel@40
    20
  the Initial Developer. All Rights Reserved.
moel@40
    21
moel@40
    22
  Contributor(s):
moel@40
    23
moel@40
    24
  Alternatively, the contents of this file may be used under the terms of
moel@40
    25
  either the GNU General Public License Version 2 or later (the "GPL"), or
moel@40
    26
  the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
moel@40
    27
  in which case the provisions of the GPL or the LGPL are applicable instead
moel@40
    28
  of those above. If you wish to allow use of your version of this file only
moel@40
    29
  under the terms of either the GPL or the LGPL, and not to allow others to
moel@40
    30
  use your version of this file under the terms of the MPL, indicate your
moel@40
    31
  decision by deleting the provisions above and replace them with the notice
moel@40
    32
  and other provisions required by the GPL or the LGPL. If you do not delete
moel@40
    33
  the provisions above, a recipient may use your version of this file under
moel@40
    34
  the terms of any one of the MPL, the GPL or the LGPL.
moel@40
    35
 
moel@40
    36
*/
moel@40
    37
moel@40
    38
using System;
moel@40
    39
using System.Drawing;
moel@40
    40
using System.Drawing.Drawing2D;
moel@40
    41
using System.Drawing.Imaging;
moel@40
    42
using System.Drawing.Text;
moel@176
    43
using System.Runtime.InteropServices;
moel@40
    44
using System.Windows.Forms;
moel@40
    45
using OpenHardwareMonitor.Hardware;
moel@40
    46
using OpenHardwareMonitor.Utilities;
moel@40
    47
moel@40
    48
namespace OpenHardwareMonitor.GUI {
moel@40
    49
  public class SensorNotifyIcon : IDisposable {
moel@40
    50
moel@40
    51
    private ISensor sensor;
moel@40
    52
    private NotifyIcon notifyIcon;
moel@40
    53
    private Bitmap bitmap;
moel@40
    54
    private Graphics graphics;
moel@42
    55
    private Color color;
moel@43
    56
    private Color darkColor;
moel@43
    57
    private Brush brush;
moel@43
    58
    private Brush darkBrush;
moel@43
    59
    private Pen pen;
moel@70
    60
    private Font font;
moel@40
    61
moel@133
    62
    public SensorNotifyIcon(SystemTray sensorSystemTray, ISensor sensor,
moel@165
    63
      bool balloonTip, PersistentSettings settings) 
moel@42
    64
    {
moel@40
    65
      this.sensor = sensor;
moel@40
    66
      this.notifyIcon = new NotifyIcon();
moel@43
    67
moel@43
    68
      Color defaultColor = Color.Black;
moel@217
    69
      if (sensor.SensorType == SensorType.Load ||
moel@217
    70
          sensor.SensorType == SensorType.Control ||
moel@217
    71
          sensor.SensorType == SensorType.Level) 
moel@217
    72
      {
moel@43
    73
        defaultColor = Color.FromArgb(0xff, 0x70, 0x8c, 0xf1);
moel@43
    74
      }
moel@166
    75
      Color = settings.GetValue(new Identifier(sensor.Identifier, 
moel@109
    76
        "traycolor").ToString(), defaultColor);      
moel@40
    77
      
moel@43
    78
      this.pen = new Pen(Color.FromArgb(96, Color.Black));
moel@117
    79
      this.font = SystemFonts.MessageBoxFont;
moel@43
    80
moel@156
    81
      ContextMenu contextMenu = new ContextMenu();
moel@156
    82
      MenuItem hideShowItem = new MenuItem("Hide/Show");
moel@133
    83
      hideShowItem.Click += delegate(object obj, EventArgs args) {
moel@133
    84
        sensorSystemTray.SendHideShowCommand();
moel@133
    85
      };
moel@156
    86
      contextMenu.MenuItems.Add(hideShowItem);
moel@156
    87
      contextMenu.MenuItems.Add(new MenuItem("-"));
moel@156
    88
      MenuItem removeItem = new MenuItem("Remove Sensor");
moel@42
    89
      removeItem.Click += delegate(object obj, EventArgs args) {
moel@42
    90
        sensorSystemTray.Remove(this.sensor);
moel@40
    91
      };
moel@156
    92
      contextMenu.MenuItems.Add(removeItem);
moel@156
    93
      MenuItem colorItem = new MenuItem("Change Color...");
moel@42
    94
      colorItem.Click += delegate(object obj, EventArgs args) {
moel@42
    95
        ColorDialog dialog = new ColorDialog();
moel@43
    96
        dialog.Color = Color;
moel@42
    97
        if (dialog.ShowDialog() == DialogResult.OK) {
moel@43
    98
          Color = dialog.Color;
moel@166
    99
          settings.SetValue(new Identifier(sensor.Identifier,
moel@109
   100
            "traycolor").ToString(), Color);
moel@42
   101
        }
moel@42
   102
      };
moel@156
   103
      contextMenu.MenuItems.Add(colorItem);
moel@156
   104
      contextMenu.MenuItems.Add(new MenuItem("-"));
moel@156
   105
      MenuItem exitItem = new MenuItem("Exit");
moel@133
   106
      exitItem.Click += delegate(object obj, EventArgs args) {
moel@133
   107
        sensorSystemTray.SendExitCommand();
moel@133
   108
      };
moel@156
   109
      contextMenu.MenuItems.Add(exitItem);
moel@156
   110
      this.notifyIcon.ContextMenu = contextMenu;
moel@133
   111
      this.notifyIcon.DoubleClick += delegate(object obj, EventArgs args) {
moel@133
   112
        sensorSystemTray.SendHideShowCommand();
moel@133
   113
      };
moel@40
   114
moel@252
   115
      // get the default dpi to create an icon with the correct size
moel@252
   116
      float dpiX, dpiY;
moel@252
   117
      using (Bitmap b = new Bitmap(1, 1, PixelFormat.Format32bppArgb)) {
moel@252
   118
        dpiX = b.HorizontalResolution;
moel@252
   119
        dpiY = b.VerticalResolution;
moel@252
   120
      }
moel@252
   121
moel@252
   122
      // adjust the size of the icon to current dpi (default is 16x16 at 96 dpi) 
moel@252
   123
      int width = (int)Math.Round(16 * dpiX / 96);
moel@252
   124
      int height = (int)Math.Round(16 * dpiY / 96);
moel@252
   125
moel@252
   126
      // make sure it does never get smaller than 16x16
moel@252
   127
      width = width < 16 ? 16: width;
moel@252
   128
      height = height < 16 ? 16: height;
moel@252
   129
moel@252
   130
      this.bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);      
moel@40
   131
      this.graphics = Graphics.FromImage(this.bitmap);
moel@117
   132
moel@117
   133
      if (Environment.OSVersion.Version.Major > 5) {
moel@117
   134
        this.graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
moel@117
   135
        this.graphics.SmoothingMode = SmoothingMode.HighQuality;
moel@117
   136
      }
moel@40
   137
    }
moel@40
   138
moel@40
   139
    public ISensor Sensor {
moel@40
   140
      get { return sensor; }
moel@40
   141
    }
moel@40
   142
moel@42
   143
    public Color Color {
moel@42
   144
      get { return color; }
moel@43
   145
      set { 
moel@43
   146
        this.color = value;
moel@43
   147
        this.darkColor = Color.FromArgb(255,
moel@44
   148
          this.color.R / 3,
moel@44
   149
          this.color.G / 3,
moel@44
   150
          this.color.B / 3);
moel@43
   151
        Brush brush = this.brush;
moel@43
   152
        this.brush = new SolidBrush(this.color);
moel@43
   153
        if (brush != null)
moel@43
   154
          brush.Dispose();
moel@43
   155
        Brush darkBrush = this.darkBrush;
moel@43
   156
        this.darkBrush = new SolidBrush(this.darkColor);
moel@43
   157
        if (darkBrush != null)
moel@43
   158
          darkBrush.Dispose();
moel@43
   159
      }
moel@42
   160
    }
moel@42
   161
moel@40
   162
    public void Dispose() {      
moel@40
   163
      Icon icon = notifyIcon.Icon;
moel@40
   164
      notifyIcon.Icon = null;
moel@40
   165
      if (icon != null)
moel@40
   166
        icon.Dispose();      
moel@40
   167
      notifyIcon.Dispose();
moel@40
   168
moel@43
   169
      if (brush != null)
moel@43
   170
        brush.Dispose();
moel@43
   171
      if (darkBrush != null)
moel@43
   172
        darkBrush.Dispose();
moel@43
   173
      pen.Dispose();
moel@85
   174
      graphics.Dispose();      
moel@85
   175
      bitmap.Dispose();      
moel@40
   176
    }
moel@40
   177
moel@40
   178
    private string GetString() {
moel@282
   179
      if (!sensor.Value.HasValue)
moel@282
   180
        return "-";
moel@282
   181
moel@40
   182
      switch (sensor.SensorType) {
moel@40
   183
        case SensorType.Voltage:
moel@340
   184
          return string.Format("{0:F1}", sensor.Value);
moel@40
   185
        case SensorType.Clock:
moel@340
   186
          return string.Format("{0:F1}", 1e-3f * sensor.Value);
moel@40
   187
        case SensorType.Load: 
moel@118
   188
          return string.Format("{0:F0}", sensor.Value);
moel@40
   189
        case SensorType.Temperature: 
moel@40
   190
          return string.Format("{0:F0}", sensor.Value);
moel@40
   191
        case SensorType.Fan: 
moel@340
   192
          return string.Format("{0:F1}", 1e-3f * sensor.Value);
moel@57
   193
        case SensorType.Flow:
moel@340
   194
          return string.Format("{0:F1}", 1e-3f * sensor.Value);
moel@118
   195
        case SensorType.Control:
moel@118
   196
          return string.Format("{0:F0}", sensor.Value);
moel@217
   197
        case SensorType.Level:
moel@217
   198
          return string.Format("{0:F0}", sensor.Value);
moel@317
   199
        case SensorType.Power:
moel@317
   200
          return string.Format("{0:F0}", sensor.Value);
moel@324
   201
        case SensorType.Data:
moel@324
   202
          return string.Format("{0:F0}", sensor.Value);
moel@340
   203
        case SensorType.Factor:
moel@340
   204
          return string.Format("{0:F1}", sensor.Value);
moel@40
   205
      }
moel@40
   206
      return "-";
moel@40
   207
    }
moel@40
   208
moel@40
   209
    private Icon CreateTransparentIcon() {
moel@40
   210
moel@40
   211
      graphics.Clear(Color.Black);
moel@70
   212
      TextRenderer.DrawText(graphics, GetString(), font,
moel@117
   213
        new Point(-2, 0), Color.White, Color.Black);        
moel@40
   214
moel@40
   215
      BitmapData data = bitmap.LockBits(
moel@40
   216
        new Rectangle(0, 0, bitmap.Width, bitmap.Height),
moel@40
   217
        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
moel@40
   218
moel@40
   219
      IntPtr Scan0 = data.Scan0;
moel@40
   220
moel@40
   221
      int numBytes = bitmap.Width * bitmap.Height * 4;
moel@40
   222
      byte[] bytes = new byte[numBytes];
moel@40
   223
      Marshal.Copy(Scan0, bytes, 0, numBytes);
moel@40
   224
      bitmap.UnlockBits(data);
moel@40
   225
moel@40
   226
      byte red, green, blue;
moel@40
   227
      for (int i = 0; i < bytes.Length; i += 4) {
moel@40
   228
        blue = bytes[i];
moel@40
   229
        green = bytes[i + 1];
moel@40
   230
        red = bytes[i + 2];
moel@40
   231
moel@42
   232
        bytes[i] = color.B;
moel@42
   233
        bytes[i + 1] = color.G;
moel@42
   234
        bytes[i + 2] = color.R;
moel@40
   235
        bytes[i + 3] = (byte)(0.3 * red + 0.59 * green + 0.11 * blue);
moel@40
   236
      }
moel@40
   237
moel@252
   238
      return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, 
moel@252
   239
        PixelFormat.Format32bppArgb);
moel@40
   240
    }
moel@40
   241
moel@217
   242
    private Icon CreatePercentageIcon() {      
moel@86
   243
      try {
moel@86
   244
        graphics.Clear(Color.Transparent);
moel@86
   245
      } catch (ArgumentException) {
moel@86
   246
        graphics.Clear(Color.Black);
moel@86
   247
      }
moel@252
   248
      graphics.FillRectangle(darkBrush, 0.5f, -0.5f, bitmap.Width - 2, bitmap.Height);
moel@282
   249
      float value = sensor.Value.GetValueOrDefault();
moel@282
   250
      float y = 0.16f * (100 - value);
moel@252
   251
      graphics.FillRectangle(brush, 0.5f, -0.5f + y, bitmap.Width - 2, bitmap.Height - y);
moel@252
   252
      graphics.DrawRectangle(pen, 1, 0, bitmap.Width - 3, bitmap.Height - 1);
moel@43
   253
moel@43
   254
      BitmapData data = bitmap.LockBits(
moel@43
   255
        new Rectangle(0, 0, bitmap.Width, bitmap.Height),
moel@43
   256
        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
moel@43
   257
      byte[] bytes = new byte[bitmap.Width * bitmap.Height * 4];
moel@43
   258
      Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
moel@43
   259
      bitmap.UnlockBits(data);
moel@43
   260
moel@252
   261
      return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, 
moel@252
   262
        PixelFormat.Format32bppArgb);
moel@43
   263
    }
moel@43
   264
moel@40
   265
    public void Update() {
moel@40
   266
      Icon icon = notifyIcon.Icon;
moel@40
   267
moel@217
   268
      switch (sensor.SensorType) {
moel@217
   269
        case SensorType.Load:
moel@217
   270
        case SensorType.Control:
moel@217
   271
        case SensorType.Level:
moel@217
   272
          notifyIcon.Icon = CreatePercentageIcon();
moel@217
   273
          break;
moel@217
   274
        default:
moel@217
   275
          notifyIcon.Icon = CreateTransparentIcon();
moel@217
   276
          break;
moel@40
   277
      }
moel@217
   278
moel@40
   279
      if (icon != null) 
moel@40
   280
        icon.Dispose();
moel@40
   281
moel@40
   282
      string format = "";
moel@40
   283
      switch (sensor.SensorType) {
moel@116
   284
        case SensorType.Voltage: format = "\n{0}: {1:F2} V"; break;
moel@116
   285
        case SensorType.Clock: format = "\n{0}: {1:F0} MHz"; break;
moel@116
   286
        case SensorType.Load: format = "\n{0}: {1:F1} %"; break;
moel@116
   287
        case SensorType.Temperature: format = "\n{0}: {1:F1} °C"; break;
moel@116
   288
        case SensorType.Fan: format = "\n{0}: {1:F0} RPM"; break;
moel@116
   289
        case SensorType.Flow: format = "\n{0}: {1:F0} L/h"; break;
moel@118
   290
        case SensorType.Control: format = "\n{0}: {1:F1} %"; break;
moel@217
   291
        case SensorType.Level: format = "\n{0}: {1:F1} %"; break;
moel@317
   292
        case SensorType.Power: format = "\n{0}: {1:F0} W"; break;
moel@324
   293
        case SensorType.Data: format = "\n{0}: {1:F0} GB"; break;
moel@340
   294
        case SensorType.Factor: format = "\n{0}: {1:F3} GB"; break;
moel@40
   295
      }
moel@116
   296
      string formattedValue = string.Format(format, sensor.Name, sensor.Value);
moel@116
   297
      string hardwareName = sensor.Hardware.Name;
moel@116
   298
      hardwareName = hardwareName.Substring(0, 
moel@116
   299
        Math.Min(63 - formattedValue.Length, hardwareName.Length));
moel@116
   300
      string text = hardwareName + formattedValue;
moel@116
   301
      if (text.Length > 63)
moel@116
   302
        text = null;
moel@40
   303
moel@116
   304
      notifyIcon.Text = text;
moel@42
   305
      notifyIcon.Visible = true;         
moel@40
   306
    }
moel@40
   307
  }
moel@40
   308
}