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