GUI/SensorGadget.cs
author moel.mich
Wed, 22 Feb 2012 23:32:06 +0000
changeset 342 2486df89b9e5
parent 340 600962f8a298
child 344 3145aadca3d2
permissions -rw-r--r--
Added a simple way to customize the sensor gadget background. If any of the files gadget_background.png, gadget_image.png, gadget_foreground.png, gadget_bar_background.png or gadget_bar_foreground.png is present in the working directory then these files are used instead of the built in files.
     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-2012
    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.Drawing.Imaging;
    42 using System.Windows.Forms;
    43 using System.IO;
    44 using OpenHardwareMonitor.Hardware;
    45 
    46 namespace OpenHardwareMonitor.GUI {
    47   public class SensorGadget : Gadget {
    48 
    49     private UnitManager unitManager;
    50 
    51     private Image back = Utilities.EmbeddedResources.GetImage("gadget.png");
    52     private Image image = null;
    53     private Image fore = null;
    54     private Image barBack = Utilities.EmbeddedResources.GetImage("barback.png");
    55     private Image barFore = Utilities.EmbeddedResources.GetImage("barblue.png");
    56     private const int topBorder = 6;
    57     private const int bottomBorder = 7;
    58     private const int leftBorder = 6;
    59     private const int rightBorder = 7;
    60     private Image background = new Bitmap(1, 1);
    61 
    62     private readonly float scale;
    63     private float fontSize;
    64     private int iconSize;
    65     private int hardwareLineHeight;
    66     private int sensorLineHeight;
    67     private int rightMargin;
    68     private int leftMargin;
    69     private int topMargin;
    70     private int bottomMargin;
    71     private int progressWidth;
    72 
    73     private IDictionary<IHardware, IList<ISensor>> sensors =
    74       new SortedDictionary<IHardware, IList<ISensor>>(new HardwareComparer());
    75 
    76     private PersistentSettings settings;
    77     private UserOption hardwareNames;
    78     private UserOption alwaysOnTop;
    79     private UserOption lockPositionAndSize;
    80 
    81     private Font largeFont;
    82     private Font smallFont;
    83     private Brush darkWhite;
    84     private StringFormat stringFormat;
    85     private StringFormat trimStringFormat;
    86     private StringFormat alignRightStringFormat;
    87 
    88     public SensorGadget(IComputer computer, PersistentSettings settings, 
    89       UnitManager unitManager) 
    90     {
    91       this.unitManager = unitManager;
    92       this.settings = settings;
    93       computer.HardwareAdded += new HardwareEventHandler(HardwareAdded);
    94       computer.HardwareRemoved += new HardwareEventHandler(HardwareRemoved);      
    95 
    96       this.darkWhite = new SolidBrush(Color.FromArgb(0xF0, 0xF0, 0xF0));
    97 
    98       this.stringFormat = new StringFormat();
    99       this.stringFormat.FormatFlags = StringFormatFlags.NoWrap;
   100 
   101       this.trimStringFormat = new StringFormat();
   102       this.trimStringFormat.Trimming = StringTrimming.EllipsisCharacter;
   103       this.trimStringFormat.FormatFlags = StringFormatFlags.NoWrap;
   104 
   105       this.alignRightStringFormat = new StringFormat();
   106       this.alignRightStringFormat.Alignment = StringAlignment.Far;
   107       this.alignRightStringFormat.FormatFlags = StringFormatFlags.NoWrap;
   108 
   109       if (File.Exists("gadget_background.png")) {
   110         try { 
   111           Image newBack = new Bitmap("gadget_background.png");
   112           back.Dispose();
   113           back = newBack;
   114         } catch { }
   115       }
   116 
   117       if (File.Exists("gadget_image.png")) {
   118         try {
   119           image = new Bitmap("gadget_image.png"); 
   120         } catch {}
   121       }
   122 
   123       if (File.Exists("gadget_foreground.png")) {
   124         try {
   125           fore = new Bitmap("gadget_foreground.png"); 
   126         } catch { }
   127       }
   128 
   129       if (File.Exists("gadget_bar_background.png")) {
   130         try {
   131           Image newBarBack = new Bitmap("gadget_bar_background.png");
   132           barBack.Dispose();
   133           barBack = newBarBack;
   134         } catch { }
   135       }
   136 
   137       if (File.Exists("gadget_bar_foreground.png")) {
   138         try {
   139           Image newBarColor = new Bitmap("gadget_bar_foreground.png");
   140           barFore.Dispose();
   141           barFore = newBarColor;
   142         } catch { }
   143       }
   144 
   145       this.Location = new Point(
   146         settings.GetValue("sensorGadget.Location.X", 100),
   147         settings.GetValue("sensorGadget.Location.Y", 100)); 
   148       LocationChanged += delegate(object sender, EventArgs e) {
   149         settings.SetValue("sensorGadget.Location.X", Location.X);
   150         settings.SetValue("sensorGadget.Location.Y", Location.Y);
   151       };
   152 
   153       // get the custom to default dpi ratio
   154       using (Bitmap b = new Bitmap(1, 1)) {
   155         scale = b.HorizontalResolution / 96.0f;
   156       }
   157 
   158       SetFontSize(settings.GetValue("sensorGadget.FontSize", 7.5f));
   159       Resize(settings.GetValue("sensorGadget.Width", Size.Width));
   160       
   161       ContextMenu contextMenu = new ContextMenu();
   162       MenuItem hardwareNamesItem = new MenuItem("Hardware Names");
   163       contextMenu.MenuItems.Add(hardwareNamesItem);
   164       MenuItem fontSizeMenu = new MenuItem("Font Size");
   165       for (int i = 0; i < 4; i++) {
   166         float size;
   167         string name;
   168         switch (i) {
   169           case 0: size = 6.5f; name = "Small"; break;
   170           case 1: size = 7.5f; name = "Medium"; break;
   171           case 2: size = 9f; name = "Large"; break;
   172           case 3: size = 11f; name = "Very Large"; break;
   173           default: throw new NotImplementedException();
   174         }
   175         MenuItem item = new MenuItem(name);
   176         item.Checked = fontSize == size;
   177         item.Click += delegate(object sender, EventArgs e) {
   178           SetFontSize(size);
   179           settings.SetValue("sensorGadget.FontSize", size);
   180           foreach (MenuItem mi in fontSizeMenu.MenuItems)
   181             mi.Checked = mi == item;
   182         };
   183         fontSizeMenu.MenuItems.Add(item);
   184       }
   185       contextMenu.MenuItems.Add(fontSizeMenu);
   186       contextMenu.MenuItems.Add(new MenuItem("-"));
   187       MenuItem lockItem = new MenuItem("Lock Position and Size");
   188       contextMenu.MenuItems.Add(lockItem);
   189       contextMenu.MenuItems.Add(new MenuItem("-"));
   190       MenuItem alwaysOnTopItem = new MenuItem("Always on Top");
   191       contextMenu.MenuItems.Add(alwaysOnTopItem);
   192       MenuItem opacityMenu = new MenuItem("Opacity");
   193       contextMenu.MenuItems.Add(opacityMenu);
   194       Opacity = (byte)settings.GetValue("sensorGadget.Opacity", 255);      
   195       for (int i = 0; i < 5; i++) {
   196         MenuItem item = new MenuItem((20 * (i + 1)).ToString() + " %");
   197         byte o = (byte)(51 * (i + 1));
   198         item.Checked = Opacity == o;
   199         item.Click += delegate(object sender, EventArgs e) {
   200           Opacity = o;
   201           settings.SetValue("sensorGadget.Opacity", Opacity);
   202           foreach (MenuItem mi in opacityMenu.MenuItems)
   203             mi.Checked = mi == item;          
   204         };
   205         opacityMenu.MenuItems.Add(item);
   206       }
   207       this.ContextMenu = contextMenu;
   208 
   209       hardwareNames = new UserOption("sensorGadget.Hardwarenames", true,
   210         hardwareNamesItem, settings);
   211       hardwareNames.Changed += delegate(object sender, EventArgs e) {
   212         Resize();
   213       };
   214 
   215       alwaysOnTop = new UserOption("sensorGadget.AlwaysOnTop", false, 
   216         alwaysOnTopItem, settings);
   217       alwaysOnTop.Changed += delegate(object sender, EventArgs e) {
   218         this.AlwaysOnTop = alwaysOnTop.Value;
   219       };
   220       lockPositionAndSize = new UserOption("sensorGadget.LockPositionAndSize", 
   221         false, lockItem, settings);
   222       lockPositionAndSize.Changed += delegate(object sender, EventArgs e) {
   223         this.LockPositionAndSize = lockPositionAndSize.Value;
   224       };
   225 
   226       HitTest += delegate(object sender, HitTestEventArgs e) {
   227         if (lockPositionAndSize.Value)
   228           return;
   229 
   230         if (e.Location.X < leftBorder) {
   231           e.HitResult = HitResult.Left;
   232           return;
   233         }
   234         if (e.Location.X > Size.Width - 1 - rightBorder) {
   235           e.HitResult = HitResult.Right;
   236           return;
   237         }
   238       };
   239 
   240       SizeChanged += delegate(object sender, EventArgs e) {
   241         settings.SetValue("sensorGadget.Width", Size.Width);
   242         Redraw();
   243       };
   244 
   245       VisibleChanged += delegate(object sender, EventArgs e) {
   246         Rectangle bounds = new Rectangle(Location, Size);
   247         Screen screen = Screen.FromRectangle(bounds);
   248         Rectangle intersection = 
   249           Rectangle.Intersect(screen.WorkingArea, bounds);
   250         if (intersection.Width < Math.Min(16, bounds.Width) || 
   251             intersection.Height < Math.Min(16, bounds.Height)) 
   252         {
   253           Location = new Point(
   254             screen.WorkingArea.Width / 2 - bounds.Width / 2, 
   255             screen.WorkingArea.Height / 2 - bounds.Height / 2);
   256         }
   257       };
   258 
   259       MouseDoubleClick += delegate(object obj, MouseEventArgs args) {
   260         SendHideShowCommand();
   261       };
   262     }
   263 
   264     public override void Dispose() {
   265 
   266       largeFont.Dispose();
   267       largeFont = null;
   268 
   269       smallFont.Dispose();
   270       smallFont = null;
   271 
   272       darkWhite.Dispose();
   273       darkWhite = null;
   274 
   275       stringFormat.Dispose();
   276       stringFormat = null;
   277 
   278       trimStringFormat.Dispose();
   279       trimStringFormat = null;
   280 
   281       alignRightStringFormat.Dispose();
   282       alignRightStringFormat = null;     
   283  
   284       back.Dispose();
   285       back = null;
   286 
   287       barFore.Dispose();
   288       barFore = null;
   289 
   290       barBack.Dispose();
   291       barBack = null;
   292 
   293       background.Dispose();
   294       background = null;
   295 
   296       if (image != null) {
   297         image.Dispose();
   298         image = null;
   299       }
   300 
   301       if (fore != null) {
   302         fore.Dispose();
   303         fore = null;
   304       }
   305 
   306       base.Dispose();
   307     }
   308 
   309     private void HardwareRemoved(IHardware hardware) {
   310       hardware.SensorAdded -= new SensorEventHandler(SensorAdded);
   311       hardware.SensorRemoved -= new SensorEventHandler(SensorRemoved);
   312       foreach (ISensor sensor in hardware.Sensors)
   313         SensorRemoved(sensor);
   314       foreach (IHardware subHardware in hardware.SubHardware)
   315         HardwareRemoved(subHardware);
   316     }
   317 
   318     private void HardwareAdded(IHardware hardware) {
   319       foreach (ISensor sensor in hardware.Sensors)
   320         SensorAdded(sensor);
   321       hardware.SensorAdded += new SensorEventHandler(SensorAdded);
   322       hardware.SensorRemoved += new SensorEventHandler(SensorRemoved);
   323       foreach (IHardware subHardware in hardware.SubHardware)
   324         HardwareAdded(subHardware);
   325     }
   326 
   327     private void SensorAdded(ISensor sensor) {
   328       if (settings.GetValue(new Identifier(sensor.Identifier,
   329         "gadget").ToString(), false)) 
   330         Add(sensor);
   331     }
   332 
   333     private void SensorRemoved(ISensor sensor) {
   334       if (Contains(sensor))
   335         Remove(sensor, false);
   336     }
   337 
   338     public bool Contains(ISensor sensor) {
   339       foreach (IList<ISensor> list in sensors.Values)
   340         if (list.Contains(sensor))
   341           return true;
   342       return false;
   343     }
   344 
   345     public void Add(ISensor sensor) {
   346       if (Contains(sensor)) {
   347         return;
   348       } else {
   349         // get the right hardware
   350         IHardware hardware = sensor.Hardware;
   351         while (hardware.Parent != null)
   352           hardware = hardware.Parent;
   353 
   354         // get the sensor list associated with the hardware
   355         IList<ISensor> list;
   356         if (!sensors.TryGetValue(hardware, out list)) {
   357           list = new List<ISensor>();
   358           sensors.Add(hardware, list);
   359         }
   360 
   361         // insert the sensor at the right position
   362         int i = 0;
   363         while (i < list.Count && (list[i].SensorType < sensor.SensorType || 
   364           (list[i].SensorType == sensor.SensorType && 
   365            list[i].Index < sensor.Index))) i++;
   366         list.Insert(i, sensor);
   367 
   368         settings.SetValue(
   369           new Identifier(sensor.Identifier, "gadget").ToString(), true);
   370         
   371         Resize();
   372       }
   373     }
   374 
   375     public void Remove(ISensor sensor) {
   376       Remove(sensor, true);
   377     }
   378 
   379     private void Remove(ISensor sensor, bool deleteConfig) {
   380       if (deleteConfig) 
   381         settings.Remove(new Identifier(sensor.Identifier, "gadget").ToString());
   382 
   383       foreach (KeyValuePair<IHardware, IList<ISensor>> keyValue in sensors)
   384         if (keyValue.Value.Contains(sensor)) {
   385           keyValue.Value.Remove(sensor);          
   386           if (keyValue.Value.Count == 0) {
   387             sensors.Remove(keyValue.Key);
   388             break;
   389           }
   390         }
   391       Resize();
   392     }
   393 
   394     public event EventHandler HideShowCommand;
   395 
   396     public void SendHideShowCommand() {
   397       if (HideShowCommand != null)
   398         HideShowCommand(this, null);
   399     }
   400 
   401     private Font CreateFont(float size, FontStyle style) {
   402       try {
   403         return new Font(SystemFonts.MessageBoxFont.FontFamily, size, style);
   404       } catch (ArgumentException) {
   405         // if the style is not supported, fall back to the original one
   406         return new Font(SystemFonts.MessageBoxFont.FontFamily, size, 
   407           SystemFonts.MessageBoxFont.Style);
   408       }
   409     }
   410 
   411     private void SetFontSize(float size) {
   412       fontSize = size;
   413       largeFont = CreateFont(fontSize, FontStyle.Bold);
   414       smallFont = CreateFont(fontSize, FontStyle.Regular);
   415       
   416       double scaledFontSize = fontSize * scale;
   417       iconSize = (int)Math.Round(1.5 * scaledFontSize);
   418       hardwareLineHeight = (int)Math.Round(1.66 * scaledFontSize);
   419       sensorLineHeight = (int)Math.Round(1.33 * scaledFontSize);
   420       leftMargin = leftBorder + (int)Math.Round(0.3 * scaledFontSize);
   421       rightMargin = rightBorder + (int)Math.Round(0.3 * scaledFontSize);
   422       topMargin = topBorder;
   423       bottomMargin = bottomBorder + (int)Math.Round(0.3 * scaledFontSize);
   424       progressWidth = (int)Math.Round(5.3 * scaledFontSize);
   425 
   426       Resize((int)Math.Round(17.3 * scaledFontSize));
   427     }
   428 
   429     private void Resize() {
   430       Resize(this.Size.Width);
   431     }
   432 
   433     private void Resize(int width) {
   434       int y = topMargin;      
   435       foreach (KeyValuePair<IHardware, IList<ISensor>> pair in sensors) {
   436         if (hardwareNames.Value) {
   437           if (y > topMargin)
   438             y += hardwareLineHeight - sensorLineHeight;
   439           y += hardwareLineHeight;
   440         }
   441         y += pair.Value.Count * sensorLineHeight;
   442       }      
   443       if (sensors.Count == 0)
   444         y += 4 * sensorLineHeight + hardwareLineHeight;
   445       y += bottomMargin;
   446       this.Size = new Size(width, y);
   447     }
   448 
   449     private void DrawImageWidthBorder(Graphics g, int width, int height, 
   450       Image back, int t, int b, int l, int r) 
   451     {
   452       GraphicsUnit u = GraphicsUnit.Pixel;
   453 
   454       g.DrawImage(back, new Rectangle(0, 0, l, t),
   455             new Rectangle(0, 0, l, t), u);
   456       g.DrawImage(back, new Rectangle(l, 0, width - l - r, t),
   457         new Rectangle(l, 0, back.Width - l - r, t), u);
   458       g.DrawImage(back, new Rectangle(width - r, 0, r, t),
   459         new Rectangle(back.Width - r, 0, r, t), u);
   460 
   461       g.DrawImage(back, new Rectangle(0, t, l, height - t - b),
   462         new Rectangle(0, t, l, back.Height - t - b), u);
   463       g.DrawImage(back, new Rectangle(l, t, width - l - r, height - t - b),
   464         new Rectangle(l, t, back.Width - l - r, back.Height - t - b), u);
   465       g.DrawImage(back, new Rectangle(width - r, t, r, height - t - b),
   466         new Rectangle(back.Width - r, t, r, back.Height - t - b), u);
   467 
   468       g.DrawImage(back, new Rectangle(0, height - b, l, b),
   469         new Rectangle(0, back.Height - b, l, b), u);
   470       g.DrawImage(back, new Rectangle(l, height - b, width - l - r, b),
   471         new Rectangle(l, back.Height - b, back.Width - l - r, b), u);
   472       g.DrawImage(back, new Rectangle(width - r, height - b, r, b),
   473         new Rectangle(back.Width - r, back.Height - b, r, b), u);
   474     }
   475 
   476     private void DrawBackground(Graphics g) {
   477       int w = Size.Width;
   478       int h = Size.Height;      
   479 
   480       if (w != background.Width || h != background.Height) {
   481 
   482         background.Dispose();
   483         background = new Bitmap(w, h, PixelFormat.Format32bppPArgb);
   484         using (Graphics graphics = Graphics.FromImage(background)) {
   485 
   486           DrawImageWidthBorder(graphics, w, h, back, topBorder, bottomBorder, 
   487             leftBorder, rightBorder);    
   488       
   489           if (fore != null)
   490             DrawImageWidthBorder(graphics, w, h, fore, topBorder, bottomBorder,
   491             leftBorder, rightBorder);
   492 
   493           if (image != null) {
   494             int width = w - leftBorder - rightBorder;
   495             int height = h - topBorder - bottomBorder;
   496             float xRatio = width / (float)image.Width;
   497             float yRatio = height / (float)image.Height;
   498             float destWidth, destHeight;
   499             float xOffset, yOffset;
   500             if (xRatio < yRatio) {
   501               destWidth = width;
   502               destHeight = image.Height * xRatio;
   503               xOffset = 0;
   504               yOffset = 0.5f * (height - destHeight);
   505             } else {
   506               destWidth = image.Width * yRatio;
   507               destHeight = height;
   508               xOffset = 0.5f * (width - destWidth);
   509               yOffset = 0;
   510             }
   511 
   512             graphics.DrawImage(image,
   513               new RectangleF(leftBorder + xOffset, topBorder + yOffset, 
   514                 destWidth, destHeight));
   515           }
   516         }
   517       }
   518 
   519       g.DrawImageUnscaled(background, 0, 0);
   520     }
   521 
   522     private void DrawProgress(Graphics g, float x, float y, 
   523       float width, float height, float progress) 
   524     {
   525       g.DrawImage(barBack, 
   526         new RectangleF(x + width * progress, y, width * (1 - progress), height), 
   527         new RectangleF(barBack.Width * progress, 0, 
   528           (1 - progress) * barBack.Width, barBack.Height), 
   529         GraphicsUnit.Pixel);
   530       g.DrawImage(barFore,
   531         new RectangleF(x, y, width * progress, height),
   532         new RectangleF(0, 0, progress * barFore.Width, barFore.Height),
   533         GraphicsUnit.Pixel);
   534     }
   535 
   536     protected override void OnPaint(PaintEventArgs e) {
   537       Graphics g = e.Graphics;
   538       int w = Size.Width;
   539 
   540       g.Clear(Color.Transparent);
   541       
   542       DrawBackground(g);
   543 
   544       int x;
   545       int y = topMargin;
   546 
   547       if (sensors.Count == 0) {
   548         x = leftBorder + 1;
   549         g.DrawString("Right-click on a sensor in the main window and select " + 
   550           "\"Show in Gadget\" to show the sensor here.", 
   551           smallFont, Brushes.White,
   552           new Rectangle(x, y - 1, w - rightBorder - x, 0));
   553       }
   554 
   555       foreach (KeyValuePair<IHardware, IList<ISensor>> pair in sensors) {
   556         if (hardwareNames.Value) {
   557           if (y > topMargin)
   558             y += hardwareLineHeight - sensorLineHeight;
   559           x = leftBorder + 1;
   560           g.DrawImage(HardwareTypeImage.Instance.GetImage(pair.Key.HardwareType),
   561             new Rectangle(x, y + 1, iconSize, iconSize));
   562           x += iconSize + 1;
   563           g.DrawString(pair.Key.Name, largeFont, Brushes.White,
   564             new Rectangle(x, y - 1, w - rightBorder - x, 0), 
   565             stringFormat);
   566           y += hardwareLineHeight;
   567         }
   568 
   569         foreach (ISensor sensor in pair.Value) {
   570           int remainingWidth;
   571 
   572 
   573           if ((sensor.SensorType != SensorType.Load &&
   574                sensor.SensorType != SensorType.Control &&
   575                sensor.SensorType != SensorType.Level) || !sensor.Value.HasValue) 
   576           {
   577             string formatted;
   578 
   579             if (sensor.Value.HasValue) {
   580               string format = "";
   581               switch (sensor.SensorType) {
   582                 case SensorType.Voltage:
   583                   format = "{0:F3} V";
   584                   break;
   585                 case SensorType.Clock:
   586                   format = "{0:F0} MHz";
   587                   break;
   588                 case SensorType.Temperature:
   589                   format = "{0:F1} °C";
   590                   break;
   591                 case SensorType.Fan:
   592                   format = "{0:F0} RPM";
   593                   break;
   594                 case SensorType.Flow:
   595                   format = "{0:F0} L/h";
   596                   break;
   597                 case SensorType.Power:
   598                   format = "{0:F1} W";
   599                   break;
   600                 case SensorType.Data:
   601                   format = "{0:F1} GB";
   602                   break;
   603                 case SensorType.Factor:
   604                   format = "{0:F3}";
   605                   break;
   606               }
   607 
   608               if (sensor.SensorType == SensorType.Temperature &&
   609                 unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit) {
   610                 formatted = string.Format("{0:F1} °F",
   611                   sensor.Value * 1.8 + 32);
   612               } else {
   613                 formatted = string.Format(format, sensor.Value);
   614               }
   615             } else {
   616               formatted = "-";
   617             }
   618 
   619             g.DrawString(formatted, smallFont, darkWhite,
   620               new RectangleF(-1, y - 1, w - rightMargin + 3, 0),
   621               alignRightStringFormat);
   622 
   623             remainingWidth = w - (int)Math.Floor(g.MeasureString(formatted,
   624               smallFont, w, StringFormat.GenericTypographic).Width) -
   625               rightMargin;
   626           } else {
   627             DrawProgress(g, w - progressWidth - rightMargin,
   628               y + 0.35f * sensorLineHeight, progressWidth,
   629               0.6f * sensorLineHeight, 0.01f * sensor.Value.Value);
   630 
   631             remainingWidth = w - progressWidth - rightMargin;
   632           }
   633            
   634           remainingWidth -= leftMargin + 2;
   635           if (remainingWidth > 0) {
   636             g.DrawString(sensor.Name, smallFont, darkWhite,
   637               new RectangleF(leftMargin - 1, y - 1, remainingWidth, 0), 
   638               trimStringFormat);
   639           }
   640 
   641           y += sensorLineHeight;
   642         }
   643       }
   644     }
   645 
   646     private class HardwareComparer : IComparer<IHardware> {
   647       public int Compare(IHardware x, IHardware y) {
   648         if (x == null && y == null)
   649           return 0;
   650         if (x == null)
   651           return -1;
   652         if (y == null)
   653           return 1;
   654 
   655         if (x.HardwareType != y.HardwareType)
   656           return x.HardwareType.CompareTo(y.HardwareType);
   657 
   658         return x.Identifier.CompareTo(y.Identifier);
   659       }
   660     }
   661   }
   662 }
   663