Changed the system tray icon font scaling to adapt to the size of the tray icon in pixels, because on Windows 7 the tray icon scales with the system font size, while on Windows XP the tray icons are always 16 pixels large.
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 UnitManager unitManager;
26 private ISensor sensor;
27 private NotifyIconAdv notifyIcon;
28 private Bitmap bitmap;
29 private Graphics graphics;
31 private Color darkColor;
33 private Brush darkBrush;
36 private Font smallFont;
38 public SensorNotifyIcon(SystemTray sensorSystemTray, ISensor sensor,
39 bool balloonTip, PersistentSettings settings, UnitManager unitManager)
41 this.unitManager = unitManager;
43 this.notifyIcon = new NotifyIconAdv();
45 Color defaultColor = Color.Black;
46 if (sensor.SensorType == SensorType.Load ||
47 sensor.SensorType == SensorType.Control ||
48 sensor.SensorType == SensorType.Level)
50 defaultColor = Color.FromArgb(0xff, 0x70, 0x8c, 0xf1);
52 Color = settings.GetValue(new Identifier(sensor.Identifier,
53 "traycolor").ToString(), defaultColor);
55 this.pen = new Pen(Color.FromArgb(96, Color.Black));
57 ContextMenu contextMenu = new ContextMenu();
58 MenuItem hideShowItem = new MenuItem("Hide/Show");
59 hideShowItem.Click += delegate(object obj, EventArgs args) {
60 sensorSystemTray.SendHideShowCommand();
62 contextMenu.MenuItems.Add(hideShowItem);
63 contextMenu.MenuItems.Add(new MenuItem("-"));
64 MenuItem removeItem = new MenuItem("Remove Sensor");
65 removeItem.Click += delegate(object obj, EventArgs args) {
66 sensorSystemTray.Remove(this.sensor);
68 contextMenu.MenuItems.Add(removeItem);
69 MenuItem colorItem = new MenuItem("Change Color...");
70 colorItem.Click += delegate(object obj, EventArgs args) {
71 ColorDialog dialog = new ColorDialog();
73 if (dialog.ShowDialog() == DialogResult.OK) {
75 settings.SetValue(new Identifier(sensor.Identifier,
76 "traycolor").ToString(), Color);
79 contextMenu.MenuItems.Add(colorItem);
80 contextMenu.MenuItems.Add(new MenuItem("-"));
81 MenuItem exitItem = new MenuItem("Exit");
82 exitItem.Click += delegate(object obj, EventArgs args) {
83 sensorSystemTray.SendExitCommand();
85 contextMenu.MenuItems.Add(exitItem);
86 this.notifyIcon.ContextMenu = contextMenu;
87 this.notifyIcon.DoubleClick += delegate(object obj, EventArgs args) {
88 sensorSystemTray.SendHideShowCommand();
91 // get the default dpi to create an icon with the correct size
93 using (Bitmap b = new Bitmap(1, 1, PixelFormat.Format32bppArgb)) {
94 dpiX = b.HorizontalResolution;
95 dpiY = b.VerticalResolution;
98 // adjust the size of the icon to current dpi (default is 16x16 at 96 dpi)
99 int width = (int)Math.Round(16 * dpiX / 96);
100 int height = (int)Math.Round(16 * dpiY / 96);
102 // make sure it does never get smaller than 16x16
103 width = width < 16 ? 16 : width;
104 height = height < 16 ? 16 : height;
106 // adjust the font size to the icon size
107 FontFamily family = SystemFonts.MessageBoxFont.FontFamily;
109 switch (family.Name) {
110 case "Segoe UI": baseSize = 12; break;
111 case "Tahoma": baseSize = 11; break;
112 default: baseSize = 12; break;
115 this.font = new Font(family,
116 baseSize * width / 16.0f, GraphicsUnit.Pixel);
117 this.smallFont = new Font(family,
118 0.75f * baseSize * width / 16.0f, GraphicsUnit.Pixel);
120 this.bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
121 this.graphics = Graphics.FromImage(this.bitmap);
123 if (Environment.OSVersion.Version.Major > 5) {
124 this.graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
125 this.graphics.SmoothingMode = SmoothingMode.HighQuality;
129 public ISensor Sensor {
130 get { return sensor; }
134 get { return color; }
137 this.darkColor = Color.FromArgb(255,
141 Brush brush = this.brush;
142 this.brush = new SolidBrush(this.color);
145 Brush darkBrush = this.darkBrush;
146 this.darkBrush = new SolidBrush(this.darkColor);
147 if (darkBrush != null)
152 public void Dispose() {
153 Icon icon = notifyIcon.Icon;
154 notifyIcon.Icon = null;
157 notifyIcon.Dispose();
161 if (darkBrush != null)
170 private string GetString() {
171 if (!sensor.Value.HasValue)
174 switch (sensor.SensorType) {
175 case SensorType.Voltage:
176 return string.Format("{0:F1}", sensor.Value);
177 case SensorType.Clock:
178 return string.Format("{0:F1}", 1e-3f * sensor.Value);
179 case SensorType.Load:
180 return string.Format("{0:F0}", sensor.Value);
181 case SensorType.Temperature:
182 if (unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit)
183 return string.Format("{0:F0}",
184 UnitManager.CelsiusToFahrenheit(sensor.Value));
186 return string.Format("{0:F0}", sensor.Value);
188 return string.Format("{0:F1}", 1e-3f * sensor.Value);
189 case SensorType.Flow:
190 return string.Format("{0:F1}", 1e-3f * sensor.Value);
191 case SensorType.Control:
192 return string.Format("{0:F0}", sensor.Value);
193 case SensorType.Level:
194 return string.Format("{0:F0}", sensor.Value);
195 case SensorType.Power:
196 return string.Format("{0:F0}", sensor.Value);
197 case SensorType.Data:
198 return string.Format("{0:F0}", sensor.Value);
199 case SensorType.Factor:
200 return string.Format("{0:F1}", sensor.Value);
205 private Icon CreateTransparentIcon() {
206 string text = GetString();
208 for (int i = 0; i < text.Length; i++)
209 if ((text[i] >= '0' && text[i] <= '9') || text[i] == '-')
211 bool small = count > 2;
213 graphics.Clear(Color.Black);
214 TextRenderer.DrawText(graphics, text, small ? smallFont : font,
215 new Point(-2, small ? 1 : 0), Color.White, Color.Black);
217 BitmapData data = bitmap.LockBits(
218 new Rectangle(0, 0, bitmap.Width, bitmap.Height),
219 ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
221 IntPtr Scan0 = data.Scan0;
223 int numBytes = bitmap.Width * bitmap.Height * 4;
224 byte[] bytes = new byte[numBytes];
225 Marshal.Copy(Scan0, bytes, 0, numBytes);
226 bitmap.UnlockBits(data);
228 byte red, green, blue;
229 for (int i = 0; i < bytes.Length; i += 4) {
231 green = bytes[i + 1];
235 bytes[i + 1] = color.G;
236 bytes[i + 2] = color.R;
237 bytes[i + 3] = (byte)(0.3 * red + 0.59 * green + 0.11 * blue);
240 return IconFactory.Create(bytes, bitmap.Width, bitmap.Height,
241 PixelFormat.Format32bppArgb);
244 private Icon CreatePercentageIcon() {
246 graphics.Clear(Color.Transparent);
247 } catch (ArgumentException) {
248 graphics.Clear(Color.Black);
250 graphics.FillRectangle(darkBrush, 0.5f, -0.5f, bitmap.Width - 2, bitmap.Height);
251 float value = sensor.Value.GetValueOrDefault();
252 float y = 0.16f * (100 - value);
253 graphics.FillRectangle(brush, 0.5f, -0.5f + y, bitmap.Width - 2, bitmap.Height - y);
254 graphics.DrawRectangle(pen, 1, 0, bitmap.Width - 3, bitmap.Height - 1);
256 BitmapData data = bitmap.LockBits(
257 new Rectangle(0, 0, bitmap.Width, bitmap.Height),
258 ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
259 byte[] bytes = new byte[bitmap.Width * bitmap.Height * 4];
260 Marshal.Copy(data.Scan0, bytes, 0, bytes.Length);
261 bitmap.UnlockBits(data);
263 return IconFactory.Create(bytes, bitmap.Width, bitmap.Height,
264 PixelFormat.Format32bppArgb);
267 public void Update() {
268 Icon icon = notifyIcon.Icon;
270 switch (sensor.SensorType) {
271 case SensorType.Load:
272 case SensorType.Control:
273 case SensorType.Level:
274 notifyIcon.Icon = CreatePercentageIcon();
277 notifyIcon.Icon = CreateTransparentIcon();
285 switch (sensor.SensorType) {
286 case SensorType.Voltage: format = "\n{0}: {1:F2} V"; break;
287 case SensorType.Clock: format = "\n{0}: {1:F0} MHz"; break;
288 case SensorType.Load: format = "\n{0}: {1:F1} %"; break;
289 case SensorType.Temperature: format = "\n{0}: {1:F1} °C"; break;
290 case SensorType.Fan: format = "\n{0}: {1:F0} RPM"; break;
291 case SensorType.Flow: format = "\n{0}: {1:F0} L/h"; break;
292 case SensorType.Control: format = "\n{0}: {1:F1} %"; break;
293 case SensorType.Level: format = "\n{0}: {1:F1} %"; break;
294 case SensorType.Power: format = "\n{0}: {1:F0} W"; break;
295 case SensorType.Data: format = "\n{0}: {1:F0} GB"; break;
296 case SensorType.Factor: format = "\n{0}: {1:F3} GB"; break;
298 string formattedValue = string.Format(format, sensor.Name, sensor.Value);
300 if (sensor.SensorType == SensorType.Temperature &&
301 unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit)
303 format = "\n{0}: {1:F1} °F";
304 formattedValue = string.Format(format, sensor.Name,
305 UnitManager.CelsiusToFahrenheit(sensor.Value));
308 string hardwareName = sensor.Hardware.Name;
309 hardwareName = hardwareName.Substring(0,
310 Math.Min(63 - formattedValue.Length, hardwareName.Length));
311 string text = hardwareName + formattedValue;
312 if (text.Length > 63)
315 notifyIcon.Text = text;
316 notifyIcon.Visible = true;