Fixed Issue 209.
3 Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 The contents of this file are subject to the Mozilla Public License Version
6 1.1 (the "License"); you may not use this file except in compliance with
7 the License. You may obtain a copy of the License at
9 http://www.mozilla.org/MPL/
11 Software distributed under the License is distributed on an "AS IS" basis,
12 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 for the specific language governing rights and limitations under the License.
15 The Original Code is the Open Hardware Monitor code.
17 The Initial Developer of the Original Code is
18 Michael Möller <m.moeller@gmx.ch>.
19 Portions created by the Initial Developer are Copyright (C) 2010-2011
20 the Initial Developer. All Rights Reserved.
24 Alternatively, the contents of this file may be used under the terms of
25 either the GNU General Public License Version 2 or later (the "GPL"), or
26 the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 in which case the provisions of the GPL or the LGPL are applicable instead
28 of those above. If you wish to allow use of your version of this file only
29 under the terms of either the GPL or the LGPL, and not to allow others to
30 use your version of this file under the terms of the MPL, indicate your
31 decision by deleting the provisions above and replace them with the notice
32 and other provisions required by the GPL or the LGPL. If you do not delete
33 the provisions above, a recipient may use your version of this file under
34 the terms of any one of the MPL, the GPL or the LGPL.
39 using System.Collections.Generic;
41 using System.Drawing.Imaging;
42 using System.Windows.Forms;
43 using OpenHardwareMonitor.Hardware;
45 namespace OpenHardwareMonitor.GUI {
46 public class SensorGadget : Gadget {
48 private UnitManager unitManager;
50 private Image back = Utilities.EmbeddedResources.GetImage("gadget.png");
51 private Image barBack = Utilities.EmbeddedResources.GetImage("barback.png");
52 private Image barBlue = Utilities.EmbeddedResources.GetImage("barblue.png");
53 private const int topBorder = 6;
54 private const int bottomBorder = 7;
55 private const int leftBorder = 6;
56 private const int rightBorder = 7;
57 private Image background = new Bitmap(1, 1);
59 private readonly float scale;
60 private float fontSize;
62 private int hardwareLineHeight;
63 private int sensorLineHeight;
64 private int rightMargin;
65 private int leftMargin;
66 private int topMargin;
67 private int bottomMargin;
68 private int progressWidth;
70 private IDictionary<IHardware, IList<ISensor>> sensors =
71 new SortedDictionary<IHardware, IList<ISensor>>(new HardwareComparer());
73 private PersistentSettings settings;
74 private UserOption hardwareNames;
75 private UserOption alwaysOnTop;
76 private UserOption lockPositionAndSize;
78 private Font largeFont;
79 private Font smallFont;
80 private Brush darkWhite;
81 private StringFormat stringFormat;
82 private StringFormat trimStringFormat;
83 private StringFormat alignRightStringFormat;
85 public SensorGadget(IComputer computer, PersistentSettings settings,
86 UnitManager unitManager)
88 this.unitManager = unitManager;
89 this.settings = settings;
90 computer.HardwareAdded += new HardwareEventHandler(HardwareAdded);
91 computer.HardwareRemoved += new HardwareEventHandler(HardwareRemoved);
93 this.darkWhite = new SolidBrush(Color.FromArgb(0xF0, 0xF0, 0xF0));
95 this.stringFormat = new StringFormat();
96 this.stringFormat.FormatFlags = StringFormatFlags.NoWrap;
98 this.trimStringFormat = new StringFormat();
99 this.trimStringFormat.Trimming = StringTrimming.EllipsisCharacter;
100 this.trimStringFormat.FormatFlags = StringFormatFlags.NoWrap;
102 this.alignRightStringFormat = new StringFormat();
103 this.alignRightStringFormat.Alignment = StringAlignment.Far;
104 this.alignRightStringFormat.FormatFlags = StringFormatFlags.NoWrap;
106 this.Location = new Point(
107 settings.GetValue("sensorGadget.Location.X", 100),
108 settings.GetValue("sensorGadget.Location.Y", 100));
109 LocationChanged += delegate(object sender, EventArgs e) {
110 settings.SetValue("sensorGadget.Location.X", Location.X);
111 settings.SetValue("sensorGadget.Location.Y", Location.Y);
114 // get the custom to default dpi ratio
115 using (Bitmap b = new Bitmap(1, 1)) {
116 scale = b.HorizontalResolution / 96.0f;
119 SetFontSize(settings.GetValue("sensorGadget.FontSize", 7.5f));
120 Resize(settings.GetValue("sensorGadget.Width", Size.Width));
122 ContextMenu contextMenu = new ContextMenu();
123 MenuItem hardwareNamesItem = new MenuItem("Hardware Names");
124 contextMenu.MenuItems.Add(hardwareNamesItem);
125 MenuItem fontSizeMenu = new MenuItem("Font Size");
126 for (int i = 0; i < 4; i++) {
130 case 0: size = 6.5f; name = "Small"; break;
131 case 1: size = 7.5f; name = "Medium"; break;
132 case 2: size = 9f; name = "Large"; break;
133 case 3: size = 11f; name = "Very Large"; break;
134 default: throw new NotImplementedException();
136 MenuItem item = new MenuItem(name);
137 item.Checked = fontSize == size;
138 item.Click += delegate(object sender, EventArgs e) {
140 settings.SetValue("sensorGadget.FontSize", size);
141 foreach (MenuItem mi in fontSizeMenu.MenuItems)
142 mi.Checked = mi == item;
144 fontSizeMenu.MenuItems.Add(item);
146 contextMenu.MenuItems.Add(fontSizeMenu);
147 contextMenu.MenuItems.Add(new MenuItem("-"));
148 MenuItem lockItem = new MenuItem("Lock Position and Size");
149 contextMenu.MenuItems.Add(lockItem);
150 contextMenu.MenuItems.Add(new MenuItem("-"));
151 MenuItem alwaysOnTopItem = new MenuItem("Always on Top");
152 contextMenu.MenuItems.Add(alwaysOnTopItem);
153 MenuItem opacityMenu = new MenuItem("Opacity");
154 contextMenu.MenuItems.Add(opacityMenu);
155 Opacity = (byte)settings.GetValue("sensorGadget.Opacity", 255);
156 for (int i = 0; i < 5; i++) {
157 MenuItem item = new MenuItem((20 * (i + 1)).ToString() + " %");
158 byte o = (byte)(51 * (i + 1));
159 item.Checked = Opacity == o;
160 item.Click += delegate(object sender, EventArgs e) {
162 settings.SetValue("sensorGadget.Opacity", Opacity);
163 foreach (MenuItem mi in opacityMenu.MenuItems)
164 mi.Checked = mi == item;
166 opacityMenu.MenuItems.Add(item);
168 this.ContextMenu = contextMenu;
170 hardwareNames = new UserOption("sensorGadget.Hardwarenames", true,
171 hardwareNamesItem, settings);
172 hardwareNames.Changed += delegate(object sender, EventArgs e) {
176 alwaysOnTop = new UserOption("sensorGadget.AlwaysOnTop", false,
177 alwaysOnTopItem, settings);
178 alwaysOnTop.Changed += delegate(object sender, EventArgs e) {
179 this.AlwaysOnTop = alwaysOnTop.Value;
181 lockPositionAndSize = new UserOption("sensorGadget.LockPositionAndSize",
182 false, lockItem, settings);
183 lockPositionAndSize.Changed += delegate(object sender, EventArgs e) {
184 this.LockPositionAndSize = lockPositionAndSize.Value;
187 HitTest += delegate(object sender, HitTestEventArgs e) {
188 if (lockPositionAndSize.Value)
191 if (e.Location.X < leftBorder) {
192 e.HitResult = HitResult.Left;
195 if (e.Location.X > Size.Width - 1 - rightBorder) {
196 e.HitResult = HitResult.Right;
201 SizeChanged += delegate(object sender, EventArgs e) {
202 settings.SetValue("sensorGadget.Width", Size.Width);
206 VisibleChanged += delegate(object sender, EventArgs e) {
207 Rectangle bounds = new Rectangle(Location, Size);
208 Screen screen = Screen.FromRectangle(bounds);
209 Rectangle intersection =
210 Rectangle.Intersect(screen.WorkingArea, bounds);
211 if (intersection.Width < Math.Min(16, bounds.Width) ||
212 intersection.Height < Math.Min(16, bounds.Height))
214 Location = new Point(
215 screen.WorkingArea.Width / 2 - bounds.Width / 2,
216 screen.WorkingArea.Height / 2 - bounds.Height / 2);
220 MouseDoubleClick += delegate(object obj, MouseEventArgs args) {
221 SendHideShowCommand();
225 public override void Dispose() {
236 stringFormat.Dispose();
239 trimStringFormat.Dispose();
240 trimStringFormat = null;
242 alignRightStringFormat.Dispose();
243 alignRightStringFormat = null;
254 background.Dispose();
260 private void HardwareRemoved(IHardware hardware) {
261 hardware.SensorAdded -= new SensorEventHandler(SensorAdded);
262 hardware.SensorRemoved -= new SensorEventHandler(SensorRemoved);
263 foreach (ISensor sensor in hardware.Sensors)
264 SensorRemoved(sensor);
265 foreach (IHardware subHardware in hardware.SubHardware)
266 HardwareRemoved(subHardware);
269 private void HardwareAdded(IHardware hardware) {
270 foreach (ISensor sensor in hardware.Sensors)
272 hardware.SensorAdded += new SensorEventHandler(SensorAdded);
273 hardware.SensorRemoved += new SensorEventHandler(SensorRemoved);
274 foreach (IHardware subHardware in hardware.SubHardware)
275 HardwareAdded(subHardware);
278 private void SensorAdded(ISensor sensor) {
279 if (settings.GetValue(new Identifier(sensor.Identifier,
280 "gadget").ToString(), false))
284 private void SensorRemoved(ISensor sensor) {
285 if (Contains(sensor))
286 Remove(sensor, false);
289 public bool Contains(ISensor sensor) {
290 foreach (IList<ISensor> list in sensors.Values)
291 if (list.Contains(sensor))
296 public void Add(ISensor sensor) {
297 if (Contains(sensor)) {
300 // get the right hardware
301 IHardware hardware = sensor.Hardware;
302 while (hardware.Parent != null)
303 hardware = hardware.Parent;
305 // get the sensor list associated with the hardware
307 if (!sensors.TryGetValue(hardware, out list)) {
308 list = new List<ISensor>();
309 sensors.Add(hardware, list);
312 // insert the sensor at the right position
314 while (i < list.Count && (list[i].SensorType < sensor.SensorType ||
315 (list[i].SensorType == sensor.SensorType &&
316 list[i].Index < sensor.Index))) i++;
317 list.Insert(i, sensor);
320 new Identifier(sensor.Identifier, "gadget").ToString(), true);
326 public void Remove(ISensor sensor) {
327 Remove(sensor, true);
330 private void Remove(ISensor sensor, bool deleteConfig) {
332 settings.Remove(new Identifier(sensor.Identifier, "gadget").ToString());
334 foreach (KeyValuePair<IHardware, IList<ISensor>> keyValue in sensors)
335 if (keyValue.Value.Contains(sensor)) {
336 keyValue.Value.Remove(sensor);
337 if (keyValue.Value.Count == 0) {
338 sensors.Remove(keyValue.Key);
345 public event EventHandler HideShowCommand;
347 public void SendHideShowCommand() {
348 if (HideShowCommand != null)
349 HideShowCommand(this, null);
352 private Font CreateFont(float size, FontStyle style) {
354 return new Font(SystemFonts.MessageBoxFont.FontFamily, size, style);
355 } catch (ArgumentException) {
356 // if the style is not supported, fall back to the original one
357 return new Font(SystemFonts.MessageBoxFont.FontFamily, size,
358 SystemFonts.MessageBoxFont.Style);
362 private void SetFontSize(float size) {
364 largeFont = CreateFont(fontSize, FontStyle.Bold);
365 smallFont = CreateFont(fontSize, FontStyle.Regular);
367 double scaledFontSize = fontSize * scale;
368 iconSize = (int)Math.Round(1.5 * scaledFontSize);
369 hardwareLineHeight = (int)Math.Round(1.66 * scaledFontSize);
370 sensorLineHeight = (int)Math.Round(1.33 * scaledFontSize);
371 leftMargin = leftBorder + (int)Math.Round(0.3 * scaledFontSize);
372 rightMargin = rightBorder + (int)Math.Round(0.3 * scaledFontSize);
373 topMargin = topBorder;
374 bottomMargin = bottomBorder + (int)Math.Round(0.3 * scaledFontSize);
375 progressWidth = (int)Math.Round(5.3 * scaledFontSize);
377 Resize((int)Math.Round(17.3 * scaledFontSize));
380 private void Resize() {
381 Resize(this.Size.Width);
384 private void Resize(int width) {
386 foreach (KeyValuePair<IHardware, IList<ISensor>> pair in sensors) {
387 if (hardwareNames.Value) {
389 y += hardwareLineHeight - sensorLineHeight;
390 y += hardwareLineHeight;
392 y += pair.Value.Count * sensorLineHeight;
394 if (sensors.Count == 0)
395 y += 4 * sensorLineHeight + hardwareLineHeight;
397 this.Size = new Size(width, y);
400 private void DrawBackground(Graphics g) {
404 if (w != background.Width || h != background.Height) {
406 background.Dispose();
407 background = new Bitmap(w, h, PixelFormat.Format32bppPArgb);
408 using (Graphics graphics = Graphics.FromImage(background)) {
411 int b = bottomBorder;
415 GraphicsUnit u = GraphicsUnit.Pixel;
417 graphics.DrawImage(back, new Rectangle(0, 0, l, t),
418 new Rectangle(0, 0, l, t), u);
419 graphics.DrawImage(back, new Rectangle(l, 0, w - l - r, t),
420 new Rectangle(l, 0, back.Width - l - r, t), u);
421 graphics.DrawImage(back, new Rectangle(w - r, 0, r, t),
422 new Rectangle(back.Width - r, 0, r, t), u);
424 graphics.DrawImage(back, new Rectangle(0, t, l, h - t - b),
425 new Rectangle(0, t, l, back.Height - t - b), u);
426 graphics.DrawImage(back, new Rectangle(l, t, w - l - r, h - t - b),
427 new Rectangle(l, t, back.Width - l - r, back.Height - t - b), u);
428 graphics.DrawImage(back, new Rectangle(w - r, t, r, h - t - b),
429 new Rectangle(back.Width - r, t, r, back.Height - t - b), u);
431 graphics.DrawImage(back, new Rectangle(0, h - b, l, b),
432 new Rectangle(0, back.Height - b, l, b), u);
433 graphics.DrawImage(back, new Rectangle(l, h - b, w - l - r, b),
434 new Rectangle(l, back.Height - b, back.Width - l - r, b), u);
435 graphics.DrawImage(back, new Rectangle(w - r, h - b, r, b),
436 new Rectangle(back.Width - r, back.Height - b, r, b), u);
440 g.DrawImageUnscaled(background, 0, 0);
443 private void DrawProgress(Graphics g, float x, float y,
444 float width, float height, float progress)
447 new RectangleF(x + width * progress, y, width * (1 - progress), height),
448 new RectangleF(barBack.Width * progress, 0,
449 (1 - progress) * barBack.Width, barBack.Height),
452 new RectangleF(x, y, width * progress, height),
453 new RectangleF(0, 0, progress * barBlue.Width, barBlue.Height),
457 protected override void OnPaint(PaintEventArgs e) {
458 Graphics g = e.Graphics;
461 g.Clear(Color.Transparent);
468 if (sensors.Count == 0) {
470 g.DrawString("Right-click on a sensor in the main window and select " +
471 "\"Show in Gadget\" to show the sensor here.",
472 smallFont, Brushes.White,
473 new Rectangle(x, y - 1, w - rightBorder - x, 0));
476 foreach (KeyValuePair<IHardware, IList<ISensor>> pair in sensors) {
477 if (hardwareNames.Value) {
479 y += hardwareLineHeight - sensorLineHeight;
481 g.DrawImage(HardwareTypeImage.Instance.GetImage(pair.Key.HardwareType),
482 new Rectangle(x, y + 1, iconSize, iconSize));
484 g.DrawString(pair.Key.Name, largeFont, Brushes.White,
485 new Rectangle(x, y - 1, w - rightBorder - x, 0),
487 y += hardwareLineHeight;
490 foreach (ISensor sensor in pair.Value) {
494 if ((sensor.SensorType != SensorType.Load &&
495 sensor.SensorType != SensorType.Control &&
496 sensor.SensorType != SensorType.Level) || !sensor.Value.HasValue)
500 if (sensor.Value.HasValue) {
502 switch (sensor.SensorType) {
503 case SensorType.Voltage:
506 case SensorType.Clock:
507 format = "{0:F0} MHz";
509 case SensorType.Temperature:
510 format = "{0:F1} °C";
513 format = "{0:F0} RPM";
515 case SensorType.Flow:
516 format = "{0:F0} L/h";
518 case SensorType.Power:
521 case SensorType.Data:
522 format = "{0:F1} GB";
526 if (sensor.SensorType == SensorType.Temperature &&
527 unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit) {
528 formatted = string.Format("{0:F1} °F",
529 sensor.Value * 1.8 + 32);
531 formatted = string.Format(format, sensor.Value);
537 g.DrawString(formatted, smallFont, darkWhite,
538 new RectangleF(-1, y - 1, w - rightMargin + 3, 0),
539 alignRightStringFormat);
541 remainingWidth = w - (int)Math.Floor(g.MeasureString(formatted,
542 smallFont, w, StringFormat.GenericTypographic).Width) -
545 DrawProgress(g, w - progressWidth - rightMargin,
546 y + 0.35f * sensorLineHeight, progressWidth,
547 0.6f * sensorLineHeight, 0.01f * sensor.Value.Value);
549 remainingWidth = w - progressWidth - rightMargin;
552 remainingWidth -= leftMargin + 2;
553 if (remainingWidth > 0) {
554 g.DrawString(sensor.Name, smallFont, darkWhite,
555 new RectangleF(leftMargin - 1, y - 1, remainingWidth, 0),
559 y += sensorLineHeight;
564 private class HardwareComparer : IComparer<IHardware> {
565 public int Compare(IHardware x, IHardware y) {
566 if (x == null && y == null)
573 if (x.HardwareType != y.HardwareType)
574 return x.HardwareType.CompareTo(y.HardwareType);
576 return x.Identifier.CompareTo(y.Identifier);