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