Changed the license to the Mozilla Public License 2.0 and update the licensing information.
3 This Source Code Form is subject to the terms of the Mozilla Public
4 License, v. 2.0. If a copy of the MPL was not distributed with this
5 file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 Copyright (C) 2009-2012 Michael Möller <mmoeller@openhardwaremonitor.org>
13 using System.Drawing.Drawing2D;
14 using System.Drawing.Imaging;
15 using System.Drawing.Text;
16 using System.Runtime.InteropServices;
17 using System.Windows.Forms;
18 using OpenHardwareMonitor.Hardware;
19 using OpenHardwareMonitor.Utilities;
21 namespace OpenHardwareMonitor.GUI {
22 public class SensorNotifyIcon : IDisposable {
24 private ISensor sensor;
25 private NotifyIcon notifyIcon;
26 private Bitmap bitmap;
27 private Graphics graphics;
29 private Color darkColor;
31 private Brush darkBrush;
35 public SensorNotifyIcon(SystemTray sensorSystemTray, ISensor sensor,
36 bool balloonTip, PersistentSettings settings)
39 this.notifyIcon = new NotifyIcon();
41 Color defaultColor = Color.Black;
42 if (sensor.SensorType == SensorType.Load ||
43 sensor.SensorType == SensorType.Control ||
44 sensor.SensorType == SensorType.Level)
46 defaultColor = Color.FromArgb(0xff, 0x70, 0x8c, 0xf1);
48 Color = settings.GetValue(new Identifier(sensor.Identifier,
49 "traycolor").ToString(), defaultColor);
51 this.pen = new Pen(Color.FromArgb(96, Color.Black));
52 this.font = SystemFonts.MessageBoxFont;
54 ContextMenu contextMenu = new ContextMenu();
55 MenuItem hideShowItem = new MenuItem("Hide/Show");
56 hideShowItem.Click += delegate(object obj, EventArgs args) {
57 sensorSystemTray.SendHideShowCommand();
59 contextMenu.MenuItems.Add(hideShowItem);
60 contextMenu.MenuItems.Add(new MenuItem("-"));
61 MenuItem removeItem = new MenuItem("Remove Sensor");
62 removeItem.Click += delegate(object obj, EventArgs args) {
63 sensorSystemTray.Remove(this.sensor);
65 contextMenu.MenuItems.Add(removeItem);
66 MenuItem colorItem = new MenuItem("Change Color...");
67 colorItem.Click += delegate(object obj, EventArgs args) {
68 ColorDialog dialog = new ColorDialog();
70 if (dialog.ShowDialog() == DialogResult.OK) {
72 settings.SetValue(new Identifier(sensor.Identifier,
73 "traycolor").ToString(), Color);
76 contextMenu.MenuItems.Add(colorItem);
77 contextMenu.MenuItems.Add(new MenuItem("-"));
78 MenuItem exitItem = new MenuItem("Exit");
79 exitItem.Click += delegate(object obj, EventArgs args) {
80 sensorSystemTray.SendExitCommand();
82 contextMenu.MenuItems.Add(exitItem);
83 this.notifyIcon.ContextMenu = contextMenu;
84 this.notifyIcon.DoubleClick += delegate(object obj, EventArgs args) {
85 sensorSystemTray.SendHideShowCommand();
88 // get the default dpi to create an icon with the correct size
90 using (Bitmap b = new Bitmap(1, 1, PixelFormat.Format32bppArgb)) {
91 dpiX = b.HorizontalResolution;
92 dpiY = b.VerticalResolution;
95 // adjust the size of the icon to current dpi (default is 16x16 at 96 dpi)
96 int width = (int)Math.Round(16 * dpiX / 96);
97 int height = (int)Math.Round(16 * dpiY / 96);
99 // make sure it does never get smaller than 16x16
100 width = width < 16 ? 16: width;
101 height = height < 16 ? 16: height;
103 this.bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
104 this.graphics = Graphics.FromImage(this.bitmap);
106 if (Environment.OSVersion.Version.Major > 5) {
107 this.graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
108 this.graphics.SmoothingMode = SmoothingMode.HighQuality;
112 public ISensor Sensor {
113 get { return sensor; }
117 get { return color; }
120 this.darkColor = Color.FromArgb(255,
124 Brush brush = this.brush;
125 this.brush = new SolidBrush(this.color);
128 Brush darkBrush = this.darkBrush;
129 this.darkBrush = new SolidBrush(this.darkColor);
130 if (darkBrush != null)
135 public void Dispose() {
136 Icon icon = notifyIcon.Icon;
137 notifyIcon.Icon = null;
140 notifyIcon.Dispose();
144 if (darkBrush != null)
151 private string GetString() {
152 if (!sensor.Value.HasValue)
155 switch (sensor.SensorType) {
156 case SensorType.Voltage:
157 return string.Format("{0:F1}", sensor.Value);
158 case SensorType.Clock:
159 return string.Format("{0:F1}", 1e-3f * sensor.Value);
160 case SensorType.Load:
161 return string.Format("{0:F0}", sensor.Value);
162 case SensorType.Temperature:
163 return string.Format("{0:F0}", sensor.Value);
165 return string.Format("{0:F1}", 1e-3f * sensor.Value);
166 case SensorType.Flow:
167 return string.Format("{0:F1}", 1e-3f * sensor.Value);
168 case SensorType.Control:
169 return string.Format("{0:F0}", sensor.Value);
170 case SensorType.Level:
171 return string.Format("{0:F0}", sensor.Value);
172 case SensorType.Power:
173 return string.Format("{0:F0}", sensor.Value);
174 case SensorType.Data:
175 return string.Format("{0:F0}", sensor.Value);
176 case SensorType.Factor:
177 return string.Format("{0:F1}", sensor.Value);
182 private Icon CreateTransparentIcon() {
184 graphics.Clear(Color.Black);
185 TextRenderer.DrawText(graphics, GetString(), font,
186 new Point(-2, 0), Color.White, Color.Black);
188 BitmapData data = bitmap.LockBits(
189 new Rectangle(0, 0, bitmap.Width, bitmap.Height),
190 ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
192 IntPtr Scan0 = data.Scan0;
194 int numBytes = bitmap.Width * bitmap.Height * 4;
195 byte[] bytes = new byte[numBytes];
196 Marshal.Copy(Scan0, bytes, 0, numBytes);
197 bitmap.UnlockBits(data);
199 byte red, green, blue;
200 for (int i = 0; i < bytes.Length; i += 4) {
202 green = bytes[i + 1];
206 bytes[i + 1] = color.G;
207 bytes[i + 2] = color.R;
208 bytes[i + 3] = (byte)(0.3 * red + 0.59 * green + 0.11 * blue);
211 return IconFactory.Create(bytes, bitmap.Width, bitmap.Height,
212 PixelFormat.Format32bppArgb);
215 private Icon CreatePercentageIcon() {
217 graphics.Clear(Color.Transparent);
218 } catch (ArgumentException) {
219 graphics.Clear(Color.Black);
221 graphics.FillRectangle(darkBrush, 0.5f, -0.5f, bitmap.Width - 2, bitmap.Height);
222 float value = sensor.Value.GetValueOrDefault();
223 float y = 0.16f * (100 - value);
224 graphics.FillRectangle(brush, 0.5f, -0.5f + y, bitmap.Width - 2, bitmap.Height - y);
225 graphics.DrawRectangle(pen, 1, 0, bitmap.Width - 3, bitmap.Height - 1);
227 BitmapData data = bitmap.LockBits(
228 new Rectangle(0, 0, bitmap.Width, bitmap.Height),
229 ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
230 byte[] bytes = new byte[bitmap.Width * bitmap.Height * 4];
231 Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
232 bitmap.UnlockBits(data);
234 return IconFactory.Create(bytes, bitmap.Width, bitmap.Height,
235 PixelFormat.Format32bppArgb);
238 public void Update() {
239 Icon icon = notifyIcon.Icon;
241 switch (sensor.SensorType) {
242 case SensorType.Load:
243 case SensorType.Control:
244 case SensorType.Level:
245 notifyIcon.Icon = CreatePercentageIcon();
248 notifyIcon.Icon = CreateTransparentIcon();
256 switch (sensor.SensorType) {
257 case SensorType.Voltage: format = "\n{0}: {1:F2} V"; break;
258 case SensorType.Clock: format = "\n{0}: {1:F0} MHz"; break;
259 case SensorType.Load: format = "\n{0}: {1:F1} %"; break;
260 case SensorType.Temperature: format = "\n{0}: {1:F1} °C"; break;
261 case SensorType.Fan: format = "\n{0}: {1:F0} RPM"; break;
262 case SensorType.Flow: format = "\n{0}: {1:F0} L/h"; break;
263 case SensorType.Control: format = "\n{0}: {1:F1} %"; break;
264 case SensorType.Level: format = "\n{0}: {1:F1} %"; break;
265 case SensorType.Power: format = "\n{0}: {1:F0} W"; break;
266 case SensorType.Data: format = "\n{0}: {1:F0} GB"; break;
267 case SensorType.Factor: format = "\n{0}: {1:F3} GB"; break;
269 string formattedValue = string.Format(format, sensor.Name, sensor.Value);
270 string hardwareName = sensor.Hardware.Name;
271 hardwareName = hardwareName.Substring(0,
272 Math.Min(63 - formattedValue.Length, hardwareName.Length));
273 string text = hardwareName + formattedValue;
274 if (text.Length > 63)
277 notifyIcon.Text = text;
278 notifyIcon.Visible = true;