GUI/SensorNotifyIcon.cs
author StephaneLenclud
Sun, 03 Feb 2013 18:01:50 +0100
branchMiniDisplay
changeset 433 090259cfd699
parent 363 daa9590e1bee
permissions -rw-r--r--
Adding SoundGraphDisplay and SensorFrontView classes.
They were respectively based on SystemTray and SensorNotifyIcon.
SoundGraphDisplay is now able to load iMONDisplay.dll providing it lives on your PATH.
Adding option to sensor context menu for adding it into FrontView.
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
}