GUI/SensorNotifyIcon.cs
author moel.mich
Sun, 21 Jul 2013 14:17:11 +0000
changeset 413 362c5e77197d
parent 363 daa9590e1bee
permissions -rw-r--r--
Added experimental support for the Nuvoton NCT6791D super I/O chip.
moel@202
     1
/*
moel@40
     2
 
moel@344
     3
  This Source Code Form is subject to the terms of the Mozilla Public
moel@344
     4
  License, v. 2.0. If a copy of the MPL was not distributed with this
moel@344
     5
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
moel@40
     6
 
moel@344
     7
  Copyright (C) 2009-2012 Michael Möller <mmoeller@openhardwaremonitor.org>
moel@344
     8
	
moel@40
     9
*/
moel@40
    10
moel@40
    11
using System;
moel@40
    12
using System.Drawing;
moel@40
    13
using System.Drawing.Drawing2D;
moel@40
    14
using System.Drawing.Imaging;
moel@40
    15
using System.Drawing.Text;
moel@176
    16
using System.Runtime.InteropServices;
moel@40
    17
using System.Windows.Forms;
moel@40
    18
using OpenHardwareMonitor.Hardware;
moel@40
    19
using OpenHardwareMonitor.Utilities;
moel@40
    20
moel@40
    21
namespace OpenHardwareMonitor.GUI {
moel@40
    22
  public class SensorNotifyIcon : IDisposable {
moel@40
    23
moel@362
    24
    private UnitManager unitManager;
moel@362
    25
moel@40
    26
    private ISensor sensor;
moel@363
    27
    private NotifyIconAdv notifyIcon;
moel@40
    28
    private Bitmap bitmap;
moel@40
    29
    private Graphics graphics;
moel@42
    30
    private Color color;
moel@43
    31
    private Color darkColor;
moel@43
    32
    private Brush brush;
moel@43
    33
    private Brush darkBrush;
moel@43
    34
    private Pen pen;
moel@70
    35
    private Font font;
moel@362
    36
    private Font smallFont;
moel@40
    37
moel@133
    38
    public SensorNotifyIcon(SystemTray sensorSystemTray, ISensor sensor,
moel@362
    39
      bool balloonTip, PersistentSettings settings, UnitManager unitManager) 
moel@42
    40
    {
moel@362
    41
      this.unitManager = unitManager;
moel@40
    42
      this.sensor = sensor;
moel@363
    43
      this.notifyIcon = new NotifyIconAdv();
moel@43
    44
moel@43
    45
      Color defaultColor = Color.Black;
moel@217
    46
      if (sensor.SensorType == SensorType.Load ||
moel@217
    47
          sensor.SensorType == SensorType.Control ||
moel@217
    48
          sensor.SensorType == SensorType.Level) 
moel@217
    49
      {
moel@43
    50
        defaultColor = Color.FromArgb(0xff, 0x70, 0x8c, 0xf1);
moel@43
    51
      }
moel@166
    52
      Color = settings.GetValue(new Identifier(sensor.Identifier, 
moel@109
    53
        "traycolor").ToString(), defaultColor);      
moel@40
    54
      
moel@43
    55
      this.pen = new Pen(Color.FromArgb(96, Color.Black));
moel@43
    56
moel@156
    57
      ContextMenu contextMenu = new ContextMenu();
moel@156
    58
      MenuItem hideShowItem = new MenuItem("Hide/Show");
moel@133
    59
      hideShowItem.Click += delegate(object obj, EventArgs args) {
moel@133
    60
        sensorSystemTray.SendHideShowCommand();
moel@133
    61
      };
moel@156
    62
      contextMenu.MenuItems.Add(hideShowItem);
moel@156
    63
      contextMenu.MenuItems.Add(new MenuItem("-"));
moel@156
    64
      MenuItem removeItem = new MenuItem("Remove Sensor");
moel@42
    65
      removeItem.Click += delegate(object obj, EventArgs args) {
moel@42
    66
        sensorSystemTray.Remove(this.sensor);
moel@40
    67
      };
moel@156
    68
      contextMenu.MenuItems.Add(removeItem);
moel@156
    69
      MenuItem colorItem = new MenuItem("Change Color...");
moel@42
    70
      colorItem.Click += delegate(object obj, EventArgs args) {
moel@42
    71
        ColorDialog dialog = new ColorDialog();
moel@43
    72
        dialog.Color = Color;
moel@42
    73
        if (dialog.ShowDialog() == DialogResult.OK) {
moel@43
    74
          Color = dialog.Color;
moel@166
    75
          settings.SetValue(new Identifier(sensor.Identifier,
moel@109
    76
            "traycolor").ToString(), Color);
moel@42
    77
        }
moel@42
    78
      };
moel@156
    79
      contextMenu.MenuItems.Add(colorItem);
moel@156
    80
      contextMenu.MenuItems.Add(new MenuItem("-"));
moel@156
    81
      MenuItem exitItem = new MenuItem("Exit");
moel@133
    82
      exitItem.Click += delegate(object obj, EventArgs args) {
moel@133
    83
        sensorSystemTray.SendExitCommand();
moel@133
    84
      };
moel@156
    85
      contextMenu.MenuItems.Add(exitItem);
moel@156
    86
      this.notifyIcon.ContextMenu = contextMenu;
moel@133
    87
      this.notifyIcon.DoubleClick += delegate(object obj, EventArgs args) {
moel@133
    88
        sensorSystemTray.SendHideShowCommand();
moel@366
    89
      };      
moel@40
    90
moel@252
    91
      // get the default dpi to create an icon with the correct size
moel@252
    92
      float dpiX, dpiY;
moel@252
    93
      using (Bitmap b = new Bitmap(1, 1, PixelFormat.Format32bppArgb)) {
moel@252
    94
        dpiX = b.HorizontalResolution;
moel@252
    95
        dpiY = b.VerticalResolution;
moel@252
    96
      }
moel@252
    97
moel@252
    98
      // adjust the size of the icon to current dpi (default is 16x16 at 96 dpi) 
moel@252
    99
      int width = (int)Math.Round(16 * dpiX / 96);
moel@252
   100
      int height = (int)Math.Round(16 * dpiY / 96);
moel@252
   101
moel@252
   102
      // make sure it does never get smaller than 16x16
moel@366
   103
      width = width < 16 ? 16 : width;
moel@366
   104
      height = height < 16 ? 16 : height;
moel@366
   105
moel@366
   106
      // adjust the font size to the icon size
moel@366
   107
      FontFamily family = SystemFonts.MessageBoxFont.FontFamily;
moel@366
   108
      float baseSize;
moel@366
   109
      switch (family.Name) {
moel@366
   110
        case "Segoe UI": baseSize = 12; break;
moel@366
   111
        case "Tahoma": baseSize = 11; break;
moel@366
   112
        default: baseSize = 12; break;
moel@366
   113
      }
moel@366
   114
moel@366
   115
      this.font = new Font(family,
moel@366
   116
        baseSize * width / 16.0f, GraphicsUnit.Pixel);
moel@366
   117
      this.smallFont = new Font(family, 
moel@366
   118
        0.75f * baseSize * width / 16.0f, GraphicsUnit.Pixel);
moel@252
   119
moel@252
   120
      this.bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);      
moel@40
   121
      this.graphics = Graphics.FromImage(this.bitmap);
moel@117
   122
moel@117
   123
      if (Environment.OSVersion.Version.Major > 5) {
moel@117
   124
        this.graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
moel@117
   125
        this.graphics.SmoothingMode = SmoothingMode.HighQuality;
moel@117
   126
      }
moel@40
   127
    }
moel@40
   128
moel@40
   129
    public ISensor Sensor {
moel@40
   130
      get { return sensor; }
moel@40
   131
    }
moel@40
   132
moel@42
   133
    public Color Color {
moel@42
   134
      get { return color; }
moel@43
   135
      set { 
moel@43
   136
        this.color = value;
moel@43
   137
        this.darkColor = Color.FromArgb(255,
moel@44
   138
          this.color.R / 3,
moel@44
   139
          this.color.G / 3,
moel@44
   140
          this.color.B / 3);
moel@43
   141
        Brush brush = this.brush;
moel@43
   142
        this.brush = new SolidBrush(this.color);
moel@43
   143
        if (brush != null)
moel@43
   144
          brush.Dispose();
moel@43
   145
        Brush darkBrush = this.darkBrush;
moel@43
   146
        this.darkBrush = new SolidBrush(this.darkColor);
moel@43
   147
        if (darkBrush != null)
moel@43
   148
          darkBrush.Dispose();
moel@43
   149
      }
moel@42
   150
    }
moel@42
   151
moel@40
   152
    public void Dispose() {      
moel@40
   153
      Icon icon = notifyIcon.Icon;
moel@40
   154
      notifyIcon.Icon = null;
moel@40
   155
      if (icon != null)
moel@40
   156
        icon.Dispose();      
moel@40
   157
      notifyIcon.Dispose();
moel@40
   158
moel@43
   159
      if (brush != null)
moel@43
   160
        brush.Dispose();
moel@43
   161
      if (darkBrush != null)
moel@43
   162
        darkBrush.Dispose();
moel@43
   163
      pen.Dispose();
moel@85
   164
      graphics.Dispose();      
moel@362
   165
      bitmap.Dispose();
moel@366
   166
      font.Dispose();
moel@362
   167
      smallFont.Dispose();
moel@40
   168
    }
moel@40
   169
moel@40
   170
    private string GetString() {
moel@282
   171
      if (!sensor.Value.HasValue)
moel@282
   172
        return "-";
moel@282
   173
moel@40
   174
      switch (sensor.SensorType) {
moel@40
   175
        case SensorType.Voltage:
moel@340
   176
          return string.Format("{0:F1}", sensor.Value);
moel@40
   177
        case SensorType.Clock:
moel@340
   178
          return string.Format("{0:F1}", 1e-3f * sensor.Value);
moel@40
   179
        case SensorType.Load: 
moel@118
   180
          return string.Format("{0:F0}", sensor.Value);
moel@362
   181
        case SensorType.Temperature:
moel@362
   182
          if (unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit)
moel@362
   183
            return string.Format("{0:F0}", 
moel@362
   184
              UnitManager.CelsiusToFahrenheit(sensor.Value));
moel@366
   185
          else
moel@362
   186
            return string.Format("{0:F0}", sensor.Value);
moel@40
   187
        case SensorType.Fan: 
moel@340
   188
          return string.Format("{0:F1}", 1e-3f * sensor.Value);
moel@57
   189
        case SensorType.Flow:
moel@340
   190
          return string.Format("{0:F1}", 1e-3f * sensor.Value);
moel@118
   191
        case SensorType.Control:
moel@118
   192
          return string.Format("{0:F0}", sensor.Value);
moel@217
   193
        case SensorType.Level:
moel@217
   194
          return string.Format("{0:F0}", sensor.Value);
moel@317
   195
        case SensorType.Power:
moel@317
   196
          return string.Format("{0:F0}", sensor.Value);
moel@324
   197
        case SensorType.Data:
moel@324
   198
          return string.Format("{0:F0}", sensor.Value);
moel@340
   199
        case SensorType.Factor:
moel@340
   200
          return string.Format("{0:F1}", sensor.Value);
moel@40
   201
      }
moel@40
   202
      return "-";
moel@40
   203
    }
moel@40
   204
moel@40
   205
    private Icon CreateTransparentIcon() {
moel@362
   206
      string text = GetString();
moel@362
   207
      int count = 0;
moel@362
   208
      for (int i = 0; i < text.Length; i++)
moel@362
   209
        if ((text[i] >= '0' && text[i] <= '9') || text[i] == '-')
moel@362
   210
          count++;
moel@362
   211
      bool small = count > 2;
moel@40
   212
moel@40
   213
      graphics.Clear(Color.Black);
moel@362
   214
      TextRenderer.DrawText(graphics, text, small ? smallFont : font,
moel@362
   215
        new Point(-2, small ? 1 : 0), Color.White, Color.Black);        
moel@40
   216
moel@40
   217
      BitmapData data = bitmap.LockBits(
moel@40
   218
        new Rectangle(0, 0, bitmap.Width, bitmap.Height),
moel@40
   219
        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
moel@40
   220
moel@40
   221
      IntPtr Scan0 = data.Scan0;
moel@40
   222
moel@40
   223
      int numBytes = bitmap.Width * bitmap.Height * 4;
moel@40
   224
      byte[] bytes = new byte[numBytes];
moel@40
   225
      Marshal.Copy(Scan0, bytes, 0, numBytes);
moel@40
   226
      bitmap.UnlockBits(data);
moel@40
   227
moel@40
   228
      byte red, green, blue;
moel@40
   229
      for (int i = 0; i < bytes.Length; i += 4) {
moel@40
   230
        blue = bytes[i];
moel@40
   231
        green = bytes[i + 1];
moel@40
   232
        red = bytes[i + 2];
moel@40
   233
moel@42
   234
        bytes[i] = color.B;
moel@42
   235
        bytes[i + 1] = color.G;
moel@42
   236
        bytes[i + 2] = color.R;
moel@40
   237
        bytes[i + 3] = (byte)(0.3 * red + 0.59 * green + 0.11 * blue);
moel@40
   238
      }
moel@40
   239
moel@252
   240
      return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, 
moel@252
   241
        PixelFormat.Format32bppArgb);
moel@40
   242
    }
moel@40
   243
moel@217
   244
    private Icon CreatePercentageIcon() {      
moel@86
   245
      try {
moel@86
   246
        graphics.Clear(Color.Transparent);
moel@86
   247
      } catch (ArgumentException) {
moel@86
   248
        graphics.Clear(Color.Black);
moel@86
   249
      }
moel@252
   250
      graphics.FillRectangle(darkBrush, 0.5f, -0.5f, bitmap.Width - 2, bitmap.Height);
moel@282
   251
      float value = sensor.Value.GetValueOrDefault();
moel@282
   252
      float y = 0.16f * (100 - value);
moel@252
   253
      graphics.FillRectangle(brush, 0.5f, -0.5f + y, bitmap.Width - 2, bitmap.Height - y);
moel@252
   254
      graphics.DrawRectangle(pen, 1, 0, bitmap.Width - 3, bitmap.Height - 1);
moel@43
   255
moel@43
   256
      BitmapData data = bitmap.LockBits(
moel@43
   257
        new Rectangle(0, 0, bitmap.Width, bitmap.Height),
moel@43
   258
        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
moel@43
   259
      byte[] bytes = new byte[bitmap.Width * bitmap.Height * 4];
moel@43
   260
      Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
moel@43
   261
      bitmap.UnlockBits(data);
moel@43
   262
moel@252
   263
      return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, 
moel@252
   264
        PixelFormat.Format32bppArgb);
moel@43
   265
    }
moel@43
   266
moel@40
   267
    public void Update() {
moel@40
   268
      Icon icon = notifyIcon.Icon;
moel@40
   269
moel@217
   270
      switch (sensor.SensorType) {
moel@217
   271
        case SensorType.Load:
moel@217
   272
        case SensorType.Control:
moel@217
   273
        case SensorType.Level:
moel@217
   274
          notifyIcon.Icon = CreatePercentageIcon();
moel@217
   275
          break;
moel@217
   276
        default:
moel@217
   277
          notifyIcon.Icon = CreateTransparentIcon();
moel@217
   278
          break;
moel@40
   279
      }
moel@217
   280
moel@40
   281
      if (icon != null) 
moel@40
   282
        icon.Dispose();
moel@40
   283
moel@40
   284
      string format = "";
moel@40
   285
      switch (sensor.SensorType) {
moel@116
   286
        case SensorType.Voltage: format = "\n{0}: {1:F2} V"; break;
moel@116
   287
        case SensorType.Clock: format = "\n{0}: {1:F0} MHz"; break;
moel@116
   288
        case SensorType.Load: format = "\n{0}: {1:F1} %"; break;
moel@116
   289
        case SensorType.Temperature: format = "\n{0}: {1:F1} °C"; break;
moel@116
   290
        case SensorType.Fan: format = "\n{0}: {1:F0} RPM"; break;
moel@116
   291
        case SensorType.Flow: format = "\n{0}: {1:F0} L/h"; break;
moel@118
   292
        case SensorType.Control: format = "\n{0}: {1:F1} %"; break;
moel@217
   293
        case SensorType.Level: format = "\n{0}: {1:F1} %"; break;
moel@317
   294
        case SensorType.Power: format = "\n{0}: {1:F0} W"; break;
moel@324
   295
        case SensorType.Data: format = "\n{0}: {1:F0} GB"; break;
moel@340
   296
        case SensorType.Factor: format = "\n{0}: {1:F3} GB"; break;
moel@40
   297
      }
moel@116
   298
      string formattedValue = string.Format(format, sensor.Name, sensor.Value);
moel@362
   299
moel@362
   300
      if (sensor.SensorType == SensorType.Temperature &&
moel@362
   301
        unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit) 
moel@362
   302
      {
moel@362
   303
        format = "\n{0}: {1:F1} °F";
moel@362
   304
        formattedValue = string.Format(format, sensor.Name,
moel@362
   305
          UnitManager.CelsiusToFahrenheit(sensor.Value));
moel@362
   306
      }
moel@362
   307
moel@116
   308
      string hardwareName = sensor.Hardware.Name;
moel@116
   309
      hardwareName = hardwareName.Substring(0, 
moel@116
   310
        Math.Min(63 - formattedValue.Length, hardwareName.Length));
moel@116
   311
      string text = hardwareName + formattedValue;
moel@116
   312
      if (text.Length > 63)
moel@116
   313
        text = null;
moel@40
   314
moel@116
   315
      notifyIcon.Text = text;
moel@42
   316
      notifyIcon.Visible = true;         
moel@40
   317
    }
moel@40
   318
  }
moel@40
   319
}