GUI/SensorNotifyIcon.cs
author moel.mich
Tue, 25 May 2010 18:57:28 +0000
changeset 127 76aaf45a01c7
parent 117 14329d236f05
child 133 9ad699538c89
permissions -rw-r--r--
Added a workaround for the "You must keep the stream open for the lifetime of the Image." problem of the Image.FromStream method. This also reduced the overall memory usage (private working set).
moel@40
     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@40
    19
  Portions created by the Initial Developer are Copyright (C) 2009-2010
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.Collections.Generic;
moel@40
    40
using System.Drawing;
moel@40
    41
using System.Drawing.Drawing2D;
moel@40
    42
using System.Drawing.Imaging;
moel@40
    43
using System.Drawing.Text;
moel@40
    44
using System.Text;
moel@40
    45
using System.Windows.Forms;
moel@40
    46
using System.Runtime.InteropServices;
moel@40
    47
using OpenHardwareMonitor.Hardware;
moel@40
    48
using OpenHardwareMonitor.Utilities;
moel@40
    49
moel@40
    50
namespace OpenHardwareMonitor.GUI {
moel@40
    51
  public class SensorNotifyIcon : IDisposable {
moel@40
    52
moel@40
    53
    private ISensor sensor;
moel@40
    54
    private NotifyIcon notifyIcon;
moel@40
    55
    private Bitmap bitmap;
moel@40
    56
    private Graphics graphics;
moel@42
    57
    private Color color;
moel@43
    58
    private Color darkColor;
moel@43
    59
    private Brush brush;
moel@43
    60
    private Brush darkBrush;
moel@43
    61
    private Pen pen;
moel@70
    62
    private Font font;
moel@40
    63
moel@42
    64
    public SensorNotifyIcon(SensorSystemTray sensorSystemTray, ISensor sensor,
moel@42
    65
      bool balloonTip) 
moel@42
    66
    {
moel@40
    67
      this.sensor = sensor;
moel@40
    68
      this.notifyIcon = new NotifyIcon();
moel@43
    69
moel@43
    70
      Color defaultColor = Color.Black;
moel@43
    71
      if (sensor.SensorType == SensorType.Load) {
moel@43
    72
        defaultColor = Color.FromArgb(0xff, 0x70, 0x8c, 0xf1);
moel@43
    73
      }
moel@109
    74
      Color = Config.Get(new Identifier(sensor.Identifier, 
moel@109
    75
        "traycolor").ToString(), defaultColor);      
moel@40
    76
      
moel@43
    77
      this.pen = new Pen(Color.FromArgb(96, Color.Black));
moel@117
    78
      this.font = SystemFonts.MessageBoxFont;
moel@43
    79
moel@40
    80
      ContextMenuStrip contextMenuStrip = new ContextMenuStrip();
moel@42
    81
      ToolStripMenuItem removeItem = new ToolStripMenuItem("Remove");
moel@42
    82
      removeItem.Click += delegate(object obj, EventArgs args) {
moel@42
    83
        sensorSystemTray.Remove(this.sensor);
moel@40
    84
      };
moel@42
    85
      contextMenuStrip.Items.Add(removeItem);
moel@42
    86
      ToolStripMenuItem colorItem = new ToolStripMenuItem("Change Color...");
moel@42
    87
      colorItem.Click += delegate(object obj, EventArgs args) {
moel@42
    88
        ColorDialog dialog = new ColorDialog();
moel@43
    89
        dialog.Color = Color;
moel@42
    90
        if (dialog.ShowDialog() == DialogResult.OK) {
moel@43
    91
          Color = dialog.Color;
moel@109
    92
          Config.Set(new Identifier(sensor.Identifier,
moel@109
    93
            "traycolor").ToString(), Color);
moel@42
    94
        }
moel@42
    95
      };
moel@42
    96
      contextMenuStrip.Items.Add(colorItem);
moel@40
    97
      this.notifyIcon.ContextMenuStrip = contextMenuStrip;
moel@40
    98
moel@40
    99
      this.bitmap = new Bitmap(16, 16, PixelFormat.Format32bppArgb);
moel@40
   100
      this.graphics = Graphics.FromImage(this.bitmap);
moel@117
   101
moel@117
   102
      if (Environment.OSVersion.Version.Major > 5) {
moel@117
   103
        this.graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
moel@117
   104
        this.graphics.SmoothingMode = SmoothingMode.HighQuality;
moel@117
   105
      }
moel@40
   106
    }
moel@40
   107
moel@40
   108
    public ISensor Sensor {
moel@40
   109
      get { return sensor; }
moel@40
   110
    }
moel@40
   111
moel@42
   112
    public Color Color {
moel@42
   113
      get { return color; }
moel@43
   114
      set { 
moel@43
   115
        this.color = value;
moel@43
   116
        this.darkColor = Color.FromArgb(255,
moel@44
   117
          this.color.R / 3,
moel@44
   118
          this.color.G / 3,
moel@44
   119
          this.color.B / 3);
moel@43
   120
        Brush brush = this.brush;
moel@43
   121
        this.brush = new SolidBrush(this.color);
moel@43
   122
        if (brush != null)
moel@43
   123
          brush.Dispose();
moel@43
   124
        Brush darkBrush = this.darkBrush;
moel@43
   125
        this.darkBrush = new SolidBrush(this.darkColor);
moel@43
   126
        if (darkBrush != null)
moel@43
   127
          darkBrush.Dispose();
moel@43
   128
      }
moel@42
   129
    }
moel@42
   130
moel@40
   131
    public void Dispose() {      
moel@40
   132
      Icon icon = notifyIcon.Icon;
moel@40
   133
      notifyIcon.Icon = null;
moel@40
   134
      if (icon != null)
moel@40
   135
        icon.Dispose();      
moel@40
   136
      notifyIcon.Dispose();
moel@40
   137
moel@43
   138
      if (brush != null)
moel@43
   139
        brush.Dispose();
moel@43
   140
      if (darkBrush != null)
moel@43
   141
        darkBrush.Dispose();
moel@43
   142
      pen.Dispose();
moel@85
   143
      graphics.Dispose();      
moel@85
   144
      bitmap.Dispose();      
moel@40
   145
    }
moel@40
   146
moel@40
   147
    private string GetString() {
moel@40
   148
      switch (sensor.SensorType) {
moel@40
   149
        case SensorType.Voltage:
moel@40
   150
          return string.Format("{0:F11}", sensor.Value);
moel@40
   151
        case SensorType.Clock:
moel@40
   152
          return string.Format("{0:F11}", 1e-3f * sensor.Value);
moel@40
   153
        case SensorType.Load: 
moel@118
   154
          return string.Format("{0:F0}", sensor.Value);
moel@40
   155
        case SensorType.Temperature: 
moel@40
   156
          return string.Format("{0:F0}", sensor.Value);
moel@40
   157
        case SensorType.Fan: 
moel@40
   158
          return string.Format("{0:F11}", 1e-3f * sensor.Value);
moel@57
   159
        case SensorType.Flow:
moel@57
   160
          return string.Format("{0:F11}", 1e-3f * sensor.Value);
moel@118
   161
        case SensorType.Control:
moel@118
   162
          return string.Format("{0:F0}", sensor.Value);
moel@40
   163
      }
moel@40
   164
      return "-";
moel@40
   165
    }
moel@40
   166
moel@40
   167
    private Icon CreateTransparentIcon() {
moel@40
   168
moel@40
   169
      graphics.Clear(Color.Black);
moel@70
   170
      TextRenderer.DrawText(graphics, GetString(), font,
moel@117
   171
        new Point(-2, 0), Color.White, Color.Black);        
moel@40
   172
moel@40
   173
      BitmapData data = bitmap.LockBits(
moel@40
   174
        new Rectangle(0, 0, bitmap.Width, bitmap.Height),
moel@40
   175
        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
moel@40
   176
moel@40
   177
      int stride = data.Stride;
moel@40
   178
      IntPtr Scan0 = data.Scan0;
moel@40
   179
moel@40
   180
      int numBytes = bitmap.Width * bitmap.Height * 4;
moel@40
   181
      byte[] bytes = new byte[numBytes];
moel@40
   182
      Marshal.Copy(Scan0, bytes, 0, numBytes);
moel@40
   183
      bitmap.UnlockBits(data);
moel@40
   184
moel@40
   185
      byte red, green, blue;
moel@40
   186
      for (int i = 0; i < bytes.Length; i += 4) {
moel@40
   187
        blue = bytes[i];
moel@40
   188
        green = bytes[i + 1];
moel@40
   189
        red = bytes[i + 2];
moel@40
   190
moel@42
   191
        bytes[i] = color.B;
moel@42
   192
        bytes[i + 1] = color.G;
moel@42
   193
        bytes[i + 2] = color.R;
moel@40
   194
        bytes[i + 3] = (byte)(0.3 * red + 0.59 * green + 0.11 * blue);
moel@40
   195
      }
moel@40
   196
moel@40
   197
      return IconFactory.Create(bytes, 16, 16, PixelFormat.Format32bppArgb);
moel@40
   198
    }
moel@40
   199
moel@86
   200
    private Icon CreateLoadIcon() {      
moel@86
   201
      try {
moel@86
   202
        graphics.Clear(Color.Transparent);
moel@86
   203
      } catch (ArgumentException) {
moel@86
   204
        graphics.Clear(Color.Black);
moel@86
   205
      }
moel@43
   206
      graphics.FillRectangle(darkBrush, 0.5f, -0.5f, 14, 16);
moel@43
   207
      float y = 0.16f * (100 - sensor.Value.Value);
moel@43
   208
      graphics.FillRectangle(brush, 0.5f, -0.5f + y, 14, 16 - y);
moel@43
   209
      graphics.DrawRectangle(pen, 1, 0, 13, 15);
moel@43
   210
moel@43
   211
      BitmapData data = bitmap.LockBits(
moel@43
   212
        new Rectangle(0, 0, bitmap.Width, bitmap.Height),
moel@43
   213
        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
moel@43
   214
      byte[] bytes = new byte[bitmap.Width * bitmap.Height * 4];
moel@43
   215
      Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
moel@43
   216
      bitmap.UnlockBits(data);
moel@43
   217
moel@43
   218
      return IconFactory.Create(bytes, 16, 16, PixelFormat.Format32bppArgb);
moel@43
   219
    }
moel@43
   220
moel@40
   221
    public void Update() {
moel@40
   222
      Icon icon = notifyIcon.Icon;
moel@40
   223
moel@43
   224
      if (sensor.SensorType == SensorType.Load) {
moel@43
   225
        notifyIcon.Icon = CreateLoadIcon();
moel@40
   226
      } else {
moel@40
   227
        notifyIcon.Icon = CreateTransparentIcon();
moel@40
   228
      }
moel@40
   229
      if (icon != null) 
moel@40
   230
        icon.Dispose();
moel@40
   231
moel@40
   232
      string format = "";
moel@40
   233
      switch (sensor.SensorType) {
moel@116
   234
        case SensorType.Voltage: format = "\n{0}: {1:F2} V"; break;
moel@116
   235
        case SensorType.Clock: format = "\n{0}: {1:F0} MHz"; break;
moel@116
   236
        case SensorType.Load: format = "\n{0}: {1:F1} %"; break;
moel@116
   237
        case SensorType.Temperature: format = "\n{0}: {1:F1} °C"; break;
moel@116
   238
        case SensorType.Fan: format = "\n{0}: {1:F0} RPM"; break;
moel@116
   239
        case SensorType.Flow: format = "\n{0}: {1:F0} L/h"; break;
moel@118
   240
        case SensorType.Control: format = "\n{0}: {1:F1} %"; break;
moel@40
   241
      }
moel@116
   242
      string formattedValue = string.Format(format, sensor.Name, sensor.Value);
moel@116
   243
      string hardwareName = sensor.Hardware.Name;
moel@116
   244
      hardwareName = hardwareName.Substring(0, 
moel@116
   245
        Math.Min(63 - formattedValue.Length, hardwareName.Length));
moel@116
   246
      string text = hardwareName + formattedValue;
moel@116
   247
      if (text.Length > 63)
moel@116
   248
        text = null;
moel@40
   249
moel@116
   250
      notifyIcon.Text = text;
moel@42
   251
      notifyIcon.Visible = true;         
moel@40
   252
    }
moel@40
   253
  }
moel@40
   254
}