GUI/SensorNotifyIcon.cs
author moel.mich
Sat, 31 Dec 2011 17:31:04 +0000
changeset 324 c6ee430d6995
parent 317 1ccf99e620a9
child 340 600962f8a298
permissions -rw-r--r--
Modified and extended version of the patch v4 by Roland Reinl (see Issue 256). Main differences to the original patch: DeviceIoControl refactorings removed, SmartAttribute is now descriptive only and does not hold any state, report is written as one 80 columns table, sensors are created only for meaningful values and without duplicates (remaining life, temperatures, host writes and reads). Also the current implementation should really preserve all the functionality of the old system. Additionally there is now a simple SMART devices emulation class (DebugSmart) that can be used in place of WindowsSmart for testing with reported data.
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@317
    19
  Portions created by the Initial Developer are Copyright (C) 2009-2011
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@40
   184
          return string.Format("{0:F11}", sensor.Value);
moel@40
   185
        case SensorType.Clock:
moel@40
   186
          return string.Format("{0:F11}", 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@40
   192
          return string.Format("{0:F11}", 1e-3f * sensor.Value);
moel@57
   193
        case SensorType.Flow:
moel@57
   194
          return string.Format("{0:F11}", 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@40
   203
      }
moel@40
   204
      return "-";
moel@40
   205
    }
moel@40
   206
moel@40
   207
    private Icon CreateTransparentIcon() {
moel@40
   208
moel@40
   209
      graphics.Clear(Color.Black);
moel@70
   210
      TextRenderer.DrawText(graphics, GetString(), font,
moel@117
   211
        new Point(-2, 0), Color.White, Color.Black);        
moel@40
   212
moel@40
   213
      BitmapData data = bitmap.LockBits(
moel@40
   214
        new Rectangle(0, 0, bitmap.Width, bitmap.Height),
moel@40
   215
        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
moel@40
   216
moel@40
   217
      IntPtr Scan0 = data.Scan0;
moel@40
   218
moel@40
   219
      int numBytes = bitmap.Width * bitmap.Height * 4;
moel@40
   220
      byte[] bytes = new byte[numBytes];
moel@40
   221
      Marshal.Copy(Scan0, bytes, 0, numBytes);
moel@40
   222
      bitmap.UnlockBits(data);
moel@40
   223
moel@40
   224
      byte red, green, blue;
moel@40
   225
      for (int i = 0; i < bytes.Length; i += 4) {
moel@40
   226
        blue = bytes[i];
moel@40
   227
        green = bytes[i + 1];
moel@40
   228
        red = bytes[i + 2];
moel@40
   229
moel@42
   230
        bytes[i] = color.B;
moel@42
   231
        bytes[i + 1] = color.G;
moel@42
   232
        bytes[i + 2] = color.R;
moel@40
   233
        bytes[i + 3] = (byte)(0.3 * red + 0.59 * green + 0.11 * blue);
moel@40
   234
      }
moel@40
   235
moel@252
   236
      return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, 
moel@252
   237
        PixelFormat.Format32bppArgb);
moel@40
   238
    }
moel@40
   239
moel@217
   240
    private Icon CreatePercentageIcon() {      
moel@86
   241
      try {
moel@86
   242
        graphics.Clear(Color.Transparent);
moel@86
   243
      } catch (ArgumentException) {
moel@86
   244
        graphics.Clear(Color.Black);
moel@86
   245
      }
moel@252
   246
      graphics.FillRectangle(darkBrush, 0.5f, -0.5f, bitmap.Width - 2, bitmap.Height);
moel@282
   247
      float value = sensor.Value.GetValueOrDefault();
moel@282
   248
      float y = 0.16f * (100 - value);
moel@252
   249
      graphics.FillRectangle(brush, 0.5f, -0.5f + y, bitmap.Width - 2, bitmap.Height - y);
moel@252
   250
      graphics.DrawRectangle(pen, 1, 0, bitmap.Width - 3, bitmap.Height - 1);
moel@43
   251
moel@43
   252
      BitmapData data = bitmap.LockBits(
moel@43
   253
        new Rectangle(0, 0, bitmap.Width, bitmap.Height),
moel@43
   254
        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
moel@43
   255
      byte[] bytes = new byte[bitmap.Width * bitmap.Height * 4];
moel@43
   256
      Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
moel@43
   257
      bitmap.UnlockBits(data);
moel@43
   258
moel@252
   259
      return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, 
moel@252
   260
        PixelFormat.Format32bppArgb);
moel@43
   261
    }
moel@43
   262
moel@40
   263
    public void Update() {
moel@40
   264
      Icon icon = notifyIcon.Icon;
moel@40
   265
moel@217
   266
      switch (sensor.SensorType) {
moel@217
   267
        case SensorType.Load:
moel@217
   268
        case SensorType.Control:
moel@217
   269
        case SensorType.Level:
moel@217
   270
          notifyIcon.Icon = CreatePercentageIcon();
moel@217
   271
          break;
moel@217
   272
        default:
moel@217
   273
          notifyIcon.Icon = CreateTransparentIcon();
moel@217
   274
          break;
moel@40
   275
      }
moel@217
   276
moel@40
   277
      if (icon != null) 
moel@40
   278
        icon.Dispose();
moel@40
   279
moel@40
   280
      string format = "";
moel@40
   281
      switch (sensor.SensorType) {
moel@116
   282
        case SensorType.Voltage: format = "\n{0}: {1:F2} V"; break;
moel@116
   283
        case SensorType.Clock: format = "\n{0}: {1:F0} MHz"; break;
moel@116
   284
        case SensorType.Load: format = "\n{0}: {1:F1} %"; break;
moel@116
   285
        case SensorType.Temperature: format = "\n{0}: {1:F1} °C"; break;
moel@116
   286
        case SensorType.Fan: format = "\n{0}: {1:F0} RPM"; break;
moel@116
   287
        case SensorType.Flow: format = "\n{0}: {1:F0} L/h"; break;
moel@118
   288
        case SensorType.Control: format = "\n{0}: {1:F1} %"; break;
moel@217
   289
        case SensorType.Level: format = "\n{0}: {1:F1} %"; break;
moel@317
   290
        case SensorType.Power: format = "\n{0}: {1:F0} W"; break;
moel@324
   291
        case SensorType.Data: format = "\n{0}: {1:F0} GB"; break;
moel@40
   292
      }
moel@116
   293
      string formattedValue = string.Format(format, sensor.Name, sensor.Value);
moel@116
   294
      string hardwareName = sensor.Hardware.Name;
moel@116
   295
      hardwareName = hardwareName.Substring(0, 
moel@116
   296
        Math.Min(63 - formattedValue.Length, hardwareName.Length));
moel@116
   297
      string text = hardwareName + formattedValue;
moel@116
   298
      if (text.Length > 63)
moel@116
   299
        text = null;
moel@40
   300
moel@116
   301
      notifyIcon.Text = text;
moel@42
   302
      notifyIcon.Visible = true;         
moel@40
   303
    }
moel@40
   304
  }
moel@40
   305
}