GUI/SensorGadget.cs
author moel.mich
Fri, 17 Sep 2010 18:32:11 +0000
changeset 187 a7c93597137f
parent 186 010d719f9245
child 202 551243a66b32
permissions -rw-r--r--
Fixed an InvalidOperationException in OpenHardwareMonitor.GUI.SensorGadget.OnPaint caused by sensors with null as value.
     1 /*
     2   
     3   Version: MPL 1.1/GPL 2.0/LGPL 2.1
     4 
     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
     8  
     9   http://www.mozilla.org/MPL/
    10 
    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.
    14 
    15   The Original Code is the Open Hardware Monitor code.
    16 
    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
    20   the Initial Developer. All Rights Reserved.
    21 
    22   Contributor(s):
    23 
    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.
    35  
    36 */
    37 
    38 using System;
    39 using System.Collections.Generic;
    40 using System.Drawing;
    41 using System.Windows.Forms;
    42 using OpenHardwareMonitor.Hardware;
    43 
    44 namespace OpenHardwareMonitor.GUI {
    45   public class SensorGadget : Gadget {
    46 
    47     private UnitManager unitManager;
    48 
    49     private Image back = Utilities.EmbeddedResources.GetImage("gadget.png");
    50     private Image barBack = Utilities.EmbeddedResources.GetImage("barback.png");
    51     private Image barblue = Utilities.EmbeddedResources.GetImage("barblue.png");
    52     private const int topBorder = 6;
    53     private const int bottomBorder = 7;
    54     private const int leftBorder = 6;
    55     private const int rightBorder = 7;
    56 
    57     private float fontSize;
    58     private int iconSize;
    59     private int hardwareLineHeight;
    60     private int sensorLineHeight;
    61     private int rightMargin;
    62     private int leftMargin;
    63     private int topMargin;
    64     private int bottomMargin;
    65     private int progressWidth;
    66 
    67     private IDictionary<IHardware, IList<ISensor>> sensors =
    68       new SortedDictionary<IHardware, IList<ISensor>>(new HardwareComparer());
    69 
    70     private PersistentSettings settings;
    71     private UserOption hardwareNames;
    72     private UserOption alwaysOnTop;
    73     private UserOption lockPositionAndSize;
    74 
    75     private Font largeFont;
    76     private Font smallFont;
    77     private Brush darkWhite;
    78     private StringFormat stringFormat;
    79     private StringFormat trimStringFormat;
    80     private StringFormat alignRightStringFormat;
    81 
    82     public SensorGadget(IComputer computer, PersistentSettings settings, 
    83       UnitManager unitManager) 
    84     {
    85       this.unitManager = unitManager;
    86       this.settings = settings;
    87       computer.HardwareAdded += new HardwareEventHandler(HardwareAdded);
    88       computer.HardwareRemoved += new HardwareEventHandler(HardwareRemoved);      
    89 
    90       this.darkWhite = new SolidBrush(Color.FromArgb(0xF0, 0xF0, 0xF0));
    91 
    92       this.stringFormat = new StringFormat();
    93       this.stringFormat.FormatFlags = StringFormatFlags.NoWrap;
    94 
    95       this.trimStringFormat = new StringFormat();
    96       this.trimStringFormat.Trimming = StringTrimming.EllipsisCharacter;
    97       this.trimStringFormat.FormatFlags = StringFormatFlags.NoWrap;
    98 
    99       this.alignRightStringFormat = new StringFormat();
   100       this.alignRightStringFormat.Alignment = StringAlignment.Far;
   101       this.alignRightStringFormat.FormatFlags = StringFormatFlags.NoWrap;
   102 
   103       this.Location = new Point(
   104         settings.GetValue("sensorGadget.Location.X", 100),
   105         settings.GetValue("sensorGadget.Location.Y", 100)); 
   106       LocationChanged += delegate(object sender, EventArgs e) {
   107         settings.SetValue("sensorGadget.Location.X", Location.X);
   108         settings.SetValue("sensorGadget.Location.Y", Location.Y);
   109       };
   110 
   111       SetFontSize(settings.GetValue("sensorGadget.FontSize", 7.5f));
   112       Resize(settings.GetValue("sensorGadget.Width", Size.Width));
   113       
   114       ContextMenu contextMenu = new ContextMenu();
   115       MenuItem hardwareNamesItem = new MenuItem("Hardware Names");
   116       contextMenu.MenuItems.Add(hardwareNamesItem);
   117       MenuItem fontSizeMenu = new MenuItem("Font Size");
   118       for (int i = 0; i < 4; i++) {
   119         float size;
   120         string name;
   121         switch (i) {
   122           case 0: size = 6.5f; name = "Small"; break;
   123           case 1: size = 7.5f; name = "Medium"; break;
   124           case 2: size = 9f; name = "Large"; break;
   125           case 3: size = 11f; name = "Very Large"; break;
   126           default: throw new NotImplementedException();
   127         }
   128         MenuItem item = new MenuItem(name);
   129         item.Checked = fontSize == size;
   130         item.Click += delegate(object sender, EventArgs e) {
   131           SetFontSize(size);
   132           settings.SetValue("sensorGadget.FontSize", size);
   133           foreach (MenuItem mi in fontSizeMenu.MenuItems)
   134             mi.Checked = mi == item;
   135         };
   136         fontSizeMenu.MenuItems.Add(item);
   137       }
   138       contextMenu.MenuItems.Add(fontSizeMenu);
   139       contextMenu.MenuItems.Add(new MenuItem("-"));
   140       MenuItem lockItem = new MenuItem("Lock Position and Size");
   141       contextMenu.MenuItems.Add(lockItem);
   142       contextMenu.MenuItems.Add(new MenuItem("-"));
   143       MenuItem alwaysOnTopItem = new MenuItem("Always on Top");
   144       contextMenu.MenuItems.Add(alwaysOnTopItem);
   145       MenuItem opacityMenu = new MenuItem("Opacity");
   146       contextMenu.MenuItems.Add(opacityMenu);
   147       Opacity = (byte)settings.GetValue("sensorGadget.Opacity", 255);      
   148       for (int i = 0; i < 5; i++) {
   149         MenuItem item = new MenuItem((20 * (i + 1)).ToString() + " %");
   150         byte o = (byte)(51 * (i + 1));
   151         item.Checked = Opacity == o;
   152         item.Click += delegate(object sender, EventArgs e) {
   153           Opacity = o;
   154           settings.SetValue("sensorGadget.Opacity", Opacity);
   155           foreach (MenuItem mi in opacityMenu.MenuItems)
   156             mi.Checked = mi == item;          
   157         };
   158         opacityMenu.MenuItems.Add(item);
   159       }
   160       this.ContextMenu = contextMenu;
   161 
   162       hardwareNames = new UserOption("sensorGadget.Hardwarenames", true,
   163         hardwareNamesItem, settings);
   164       hardwareNames.Changed += delegate(object sender, EventArgs e) {
   165         Resize();
   166       };
   167 
   168       alwaysOnTop = new UserOption("sensorGadget.AlwaysOnTop", false, 
   169         alwaysOnTopItem, settings);
   170       alwaysOnTop.Changed += delegate(object sender, EventArgs e) {
   171         this.AlwaysOnTop = alwaysOnTop.Value;
   172       };
   173       lockPositionAndSize = new UserOption("sensorGadget.LockPositionAndSize", 
   174         false, lockItem, settings);
   175       lockPositionAndSize.Changed += delegate(object sender, EventArgs e) {
   176         this.LockPositionAndSize = lockPositionAndSize.Value;
   177       };
   178 
   179       HitTest += delegate(object sender, HitTestEventArgs e) {
   180         if (lockPositionAndSize.Value)
   181           return;
   182 
   183         if (e.Location.X < leftBorder) {
   184           e.HitResult = HitResult.Left;
   185           return;
   186         }
   187         if (e.Location.X > Size.Width - 1 - rightBorder) {
   188           e.HitResult = HitResult.Right;
   189           return;
   190         }
   191       };
   192 
   193       SizeChanged += delegate(object sender, EventArgs e) {
   194         settings.SetValue("sensorGadget.Width", Size.Width);
   195         Redraw();
   196       };
   197     }
   198 
   199     public override void Dispose() {
   200 
   201       largeFont.Dispose();
   202       largeFont = null;
   203 
   204       smallFont.Dispose();
   205       smallFont = null;
   206 
   207       darkWhite.Dispose();
   208       darkWhite = null;
   209 
   210       stringFormat.Dispose();
   211       stringFormat = null;
   212 
   213       trimStringFormat.Dispose();
   214       trimStringFormat = null;
   215 
   216       alignRightStringFormat.Dispose();
   217       alignRightStringFormat = null;      
   218 
   219       base.Dispose();
   220     }
   221 
   222     private void HardwareRemoved(IHardware hardware) {
   223       hardware.SensorAdded -= new SensorEventHandler(SensorAdded);
   224       hardware.SensorRemoved -= new SensorEventHandler(SensorRemoved);
   225       foreach (ISensor sensor in hardware.Sensors)
   226         SensorRemoved(sensor);
   227       foreach (IHardware subHardware in hardware.SubHardware)
   228         HardwareRemoved(subHardware);
   229     }
   230 
   231     private void HardwareAdded(IHardware hardware) {
   232       foreach (ISensor sensor in hardware.Sensors)
   233         SensorAdded(sensor);
   234       hardware.SensorAdded += new SensorEventHandler(SensorAdded);
   235       hardware.SensorRemoved += new SensorEventHandler(SensorRemoved);
   236       foreach (IHardware subHardware in hardware.SubHardware)
   237         HardwareAdded(subHardware);
   238     }
   239 
   240     private void SensorAdded(ISensor sensor) {
   241       if (settings.GetValue(new Identifier(sensor.Identifier,
   242         "gadget").ToString(), false)) 
   243         Add(sensor);
   244     }
   245 
   246     private void SensorRemoved(ISensor sensor) {
   247       if (Contains(sensor))
   248         Remove(sensor, false);
   249     }
   250 
   251     public bool Contains(ISensor sensor) {
   252       foreach (IList<ISensor> list in sensors.Values)
   253         if (list.Contains(sensor))
   254           return true;
   255       return false;
   256     }
   257 
   258     public void Add(ISensor sensor) {
   259       if (Contains(sensor)) {
   260         return;
   261       } else {
   262         // get the right hardware
   263         IHardware hardware = sensor.Hardware;
   264         while (hardware.Parent != null)
   265           hardware = hardware.Parent;
   266 
   267         // get the sensor list associated with the hardware
   268         IList<ISensor> list;
   269         if (!sensors.TryGetValue(hardware, out list)) {
   270           list = new List<ISensor>();
   271           sensors.Add(hardware, list);
   272         }
   273 
   274         // insert the sensor at the right position
   275         int i = 0;
   276         while (i < list.Count && (list[i].SensorType < sensor.SensorType || 
   277           (list[i].SensorType == sensor.SensorType && 
   278            list[i].Index < sensor.Index))) i++;
   279         list.Insert(i, sensor);
   280 
   281         settings.SetValue(
   282           new Identifier(sensor.Identifier, "gadget").ToString(), true);
   283         
   284         Resize();
   285       }
   286     }
   287 
   288     public void Remove(ISensor sensor) {
   289       Remove(sensor, true);
   290     }
   291 
   292     private void Remove(ISensor sensor, bool deleteConfig) {
   293       if (deleteConfig) 
   294         settings.Remove(new Identifier(sensor.Identifier, "gadget").ToString());
   295 
   296       foreach (KeyValuePair<IHardware, IList<ISensor>> keyValue in sensors)
   297         if (keyValue.Value.Contains(sensor)) {
   298           keyValue.Value.Remove(sensor);          
   299           if (keyValue.Value.Count == 0) {
   300             sensors.Remove(keyValue.Key);
   301             break;
   302           }
   303         }
   304       Resize();
   305     }
   306 
   307     private Font CreateFont(float size, FontStyle style) {
   308       return new Font(SystemFonts.MessageBoxFont.FontFamily, size,
   309         style);
   310     }
   311 
   312     private void SetFontSize(float size) {
   313       fontSize = size;
   314       largeFont = CreateFont(fontSize, FontStyle.Bold);
   315       smallFont = CreateFont(fontSize, FontStyle.Regular);
   316       iconSize = (int)Math.Round(1.5 * fontSize);
   317       hardwareLineHeight = (int)Math.Round(1.66 * fontSize);
   318       sensorLineHeight = (int)Math.Round(1.33 * fontSize);      
   319       leftMargin = leftBorder + (int)Math.Round(0.3 * fontSize);
   320       rightMargin = rightBorder + (int)Math.Round(0.3 * fontSize);
   321       topMargin = topBorder;
   322       bottomMargin = bottomBorder + (int)Math.Round(0.3 * fontSize);
   323       progressWidth = (int)Math.Round(5.3 * fontSize);
   324       Resize((int)Math.Round(17.3 * fontSize));
   325     }
   326 
   327     private void Resize() {
   328       Resize(this.Size.Width);
   329     }
   330 
   331     private void Resize(int width) {
   332       int y = topMargin;      
   333       foreach (KeyValuePair<IHardware, IList<ISensor>> pair in sensors) {
   334         if (hardwareNames.Value) {
   335           if (y > topMargin)
   336             y += hardwareLineHeight - sensorLineHeight;
   337           y += hardwareLineHeight;
   338         }
   339         y += pair.Value.Count * sensorLineHeight;
   340       }
   341       y += bottomMargin;
   342       y = Math.Max(y, topBorder + hardwareLineHeight + bottomBorder);
   343       this.Size = new Size(width, y);
   344     }
   345 
   346     private void DrawBackground(Graphics g) {
   347       int w = Size.Width;
   348       int h = Size.Height;
   349       int t = topBorder;
   350       int b = bottomBorder;
   351       int l = leftBorder;
   352       int r = rightBorder;
   353       GraphicsUnit u = GraphicsUnit.Pixel;
   354 
   355       g.DrawImage(back, new Rectangle(0, 0, l, t),
   356         new Rectangle(0, 0, l, t), u);
   357       g.DrawImage(back, new Rectangle(l, 0, w - l - r, t),
   358         new Rectangle(l, 0, back.Width - l - r, t), u);
   359       g.DrawImage(back, new Rectangle(w - r, 0, r, t),
   360         new Rectangle(back.Width - r, 0, r, t), u);
   361 
   362       g.DrawImage(back, new Rectangle(0, t, l, h - t - b),
   363         new Rectangle(0, t, l, back.Height - t - b), u);
   364       g.DrawImage(back, new Rectangle(l, t, w - l - r, h - t - b),
   365         new Rectangle(l, t, back.Width - l - r, back.Height - t - b), u);
   366       g.DrawImage(back, new Rectangle(w - r, t, r, h - t - b),
   367         new Rectangle(back.Width - r, t, r, back.Height - t - b), u);
   368 
   369       g.DrawImage(back, new Rectangle(0, h - b, l, b),
   370         new Rectangle(0, back.Height - b, l, b), u);
   371       g.DrawImage(back, new Rectangle(l, h - b, w - l - r, b),
   372         new Rectangle(l, back.Height - b, back.Width - l - r, b), u);
   373       g.DrawImage(back, new Rectangle(w - r, h - b, r, b),
   374         new Rectangle(back.Width - r, back.Height - b, r, b), u);
   375     }
   376 
   377     private void DrawProgress(Graphics g, int x, int y, int width, int height,
   378       float progress) 
   379     {
   380       g.DrawImage(barBack, 
   381         new RectangleF(x + width * progress, y, width * (1 - progress), height), 
   382         new RectangleF(barBack.Width * progress, 0, 
   383           (1 - progress) * barBack.Width, barBack.Height), 
   384         GraphicsUnit.Pixel);
   385       g.DrawImage(barblue,
   386         new RectangleF(x, y, width * progress, height),
   387         new RectangleF(0, 0, progress * barblue.Width, barblue.Height),
   388         GraphicsUnit.Pixel);
   389     }
   390 
   391     protected override void OnPaint(PaintEventArgs e) {
   392       Graphics g = e.Graphics;
   393       int w = Size.Width;
   394       int h = Size.Height;
   395 
   396       g.Clear(Color.Transparent);
   397       
   398       DrawBackground(g);
   399 
   400       int x;
   401       int y = topMargin;
   402 
   403       if (sensors.Count == 0) {
   404         x = leftBorder + 1;
   405         g.DrawString("Add a sensor ...", smallFont, Brushes.White,
   406           new Rectangle(x, y - 1, w - rightBorder - x, 0));
   407       }
   408 
   409       foreach (KeyValuePair<IHardware, IList<ISensor>> pair in sensors) {
   410         if (hardwareNames.Value) {
   411           if (y > topMargin)
   412             y += hardwareLineHeight - sensorLineHeight;
   413           x = leftBorder + 1;
   414           g.DrawImage(HardwareTypeImage.Instance.GetImage(pair.Key.HardwareType),
   415             new Rectangle(x, y + 1, iconSize, iconSize));
   416           x += iconSize + 1;
   417           g.DrawString(pair.Key.Name, largeFont, Brushes.White,
   418             new Rectangle(x, y - 1, w - rightBorder - x, 0), 
   419             stringFormat);
   420           y += hardwareLineHeight;
   421         }
   422 
   423         foreach (ISensor sensor in pair.Value) {
   424           int remainingWidth;
   425 
   426 
   427           if ((sensor.SensorType != SensorType.Load &&
   428             sensor.SensorType != SensorType.Control) || !sensor.Value.HasValue) 
   429           {
   430             string formatted;
   431 
   432             if (sensor.Value.HasValue) {
   433               string format = "";
   434               switch (sensor.SensorType) {
   435                 case SensorType.Voltage:
   436                   format = "{0:F2} V";
   437                   break;
   438                 case SensorType.Clock:
   439                   format = "{0:F0} MHz";
   440                   break;
   441                 case SensorType.Temperature:
   442                   format = "{0:F1} °C";
   443                   break;
   444                 case SensorType.Fan:
   445                   format = "{0:F0} RPM";
   446                   break;
   447                 case SensorType.Flow:
   448                   format = "{0:F0} L/h";
   449                   break;
   450               }
   451 
   452               if (sensor.SensorType == SensorType.Temperature &&
   453                 unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit) {
   454                 formatted = string.Format("{0:F1} °F",
   455                   sensor.Value * 1.8 + 32);
   456               } else {
   457                 formatted = string.Format(format, sensor.Value);
   458               }
   459             } else {
   460               formatted = "-";
   461             }
   462 
   463             g.DrawString(formatted, smallFont, darkWhite,
   464               new RectangleF(-1, y - 1, w - rightMargin + 3, 0),
   465               alignRightStringFormat);
   466 
   467             remainingWidth = w - (int)Math.Floor(g.MeasureString(formatted,
   468               smallFont, w, StringFormat.GenericTypographic).Width) -
   469               rightMargin;
   470           } else {
   471             DrawProgress(g, w - progressWidth - rightMargin,
   472               y + 4, progressWidth, 6, 0.01f * sensor.Value.Value);
   473 
   474             remainingWidth = w - progressWidth - rightMargin;
   475           }
   476            
   477           remainingWidth -= leftMargin + 2;
   478           if (remainingWidth > 0) {
   479             g.DrawString(sensor.Name, smallFont, darkWhite,
   480               new RectangleF(leftMargin - 1, y - 1, remainingWidth, 0), 
   481               trimStringFormat);
   482           }
   483 
   484           y += sensorLineHeight;
   485         }
   486       }
   487     }
   488 
   489     private class HardwareComparer : IComparer<IHardware> {
   490       public int Compare(IHardware x, IHardware y) {
   491         if (x == null && y == null)
   492           return 0;
   493         if (x == null)
   494           return -1;
   495         if (y == null)
   496           return 1;
   497 
   498         if (x.HardwareType != y.HardwareType)
   499           return x.HardwareType.CompareTo(y.HardwareType);
   500 
   501         return x.Identifier.CompareTo(y.Identifier);
   502       }
   503     }
   504   }
   505 }
   506