GUI/SensorGadget.cs
author moel.mich
Tue, 16 Nov 2010 21:48:26 +0000
changeset 243 07a9329cd87c
parent 217 d93ddd6ca0af
child 244 99f16e21cdc8
permissions -rw-r--r--
Improved the moving and resizing of the gadget on multi-monitor systems. Fixed one problem where the underlying gadget window size would be reduced unrequested. Fine tuned the progress bar size in the gadget.
     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       try {
   309         return new Font(SystemFonts.MessageBoxFont.FontFamily, size, style);
   310       } catch (ArgumentException) {
   311         // if the style is not supported, fall back to the original one
   312         return new Font(SystemFonts.MessageBoxFont.FontFamily, size, 
   313           SystemFonts.MessageBoxFont.Style);
   314       }
   315     }
   316 
   317     private void SetFontSize(float size) {
   318       fontSize = size;
   319       largeFont = CreateFont(fontSize, FontStyle.Bold);
   320       smallFont = CreateFont(fontSize, FontStyle.Regular);
   321       iconSize = (int)Math.Round(1.5 * fontSize);
   322       hardwareLineHeight = (int)Math.Round(1.66 * fontSize);
   323       sensorLineHeight = (int)Math.Round(1.33 * fontSize);      
   324       leftMargin = leftBorder + (int)Math.Round(0.3 * fontSize);
   325       rightMargin = rightBorder + (int)Math.Round(0.3 * fontSize);
   326       topMargin = topBorder;
   327       bottomMargin = bottomBorder + (int)Math.Round(0.3 * fontSize);
   328       progressWidth = (int)Math.Round(5.3 * fontSize);
   329       Resize((int)Math.Round(17.3 * fontSize));
   330     }
   331 
   332     private void Resize() {
   333       Resize(this.Size.Width);
   334     }
   335 
   336     private void Resize(int width) {
   337       int y = topMargin;      
   338       foreach (KeyValuePair<IHardware, IList<ISensor>> pair in sensors) {
   339         if (hardwareNames.Value) {
   340           if (y > topMargin)
   341             y += hardwareLineHeight - sensorLineHeight;
   342           y += hardwareLineHeight;
   343         }
   344         y += pair.Value.Count * sensorLineHeight;
   345       }
   346       y += bottomMargin;
   347       y = Math.Max(y, topBorder + hardwareLineHeight + bottomBorder);
   348       this.Size = new Size(width, y);
   349     }
   350 
   351     private void DrawBackground(Graphics g) {
   352       int w = Size.Width;
   353       int h = Size.Height;
   354       int t = topBorder;
   355       int b = bottomBorder;
   356       int l = leftBorder;
   357       int r = rightBorder;
   358       GraphicsUnit u = GraphicsUnit.Pixel;
   359 
   360       g.DrawImage(back, new Rectangle(0, 0, l, t),
   361         new Rectangle(0, 0, l, t), u);
   362       g.DrawImage(back, new Rectangle(l, 0, w - l - r, t),
   363         new Rectangle(l, 0, back.Width - l - r, t), u);
   364       g.DrawImage(back, new Rectangle(w - r, 0, r, t),
   365         new Rectangle(back.Width - r, 0, r, t), u);
   366 
   367       g.DrawImage(back, new Rectangle(0, t, l, h - t - b),
   368         new Rectangle(0, t, l, back.Height - t - b), u);
   369       g.DrawImage(back, new Rectangle(l, t, w - l - r, h - t - b),
   370         new Rectangle(l, t, back.Width - l - r, back.Height - t - b), u);
   371       g.DrawImage(back, new Rectangle(w - r, t, r, h - t - b),
   372         new Rectangle(back.Width - r, t, r, back.Height - t - b), u);
   373 
   374       g.DrawImage(back, new Rectangle(0, h - b, l, b),
   375         new Rectangle(0, back.Height - b, l, b), u);
   376       g.DrawImage(back, new Rectangle(l, h - b, w - l - r, b),
   377         new Rectangle(l, back.Height - b, back.Width - l - r, b), u);
   378       g.DrawImage(back, new Rectangle(w - r, h - b, r, b),
   379         new Rectangle(back.Width - r, back.Height - b, r, b), u);
   380     }
   381 
   382     private void DrawProgress(Graphics g, float x, float y, 
   383       float width, float height, float progress) 
   384     {
   385       g.DrawImage(barBack, 
   386         new RectangleF(x + width * progress, y, width * (1 - progress), height), 
   387         new RectangleF(barBack.Width * progress, 0, 
   388           (1 - progress) * barBack.Width, barBack.Height), 
   389         GraphicsUnit.Pixel);
   390       g.DrawImage(barblue,
   391         new RectangleF(x, y, width * progress, height),
   392         new RectangleF(0, 0, progress * barblue.Width, barblue.Height),
   393         GraphicsUnit.Pixel);
   394     }
   395 
   396     protected override void OnPaint(PaintEventArgs e) {
   397       Graphics g = e.Graphics;
   398       int w = Size.Width;
   399 
   400       g.Clear(Color.Transparent);
   401       
   402       DrawBackground(g);
   403 
   404       int x;
   405       int y = topMargin;
   406 
   407       if (sensors.Count == 0) {
   408         x = leftBorder + 1;
   409         g.DrawString("Add a sensor ...", smallFont, Brushes.White,
   410           new Rectangle(x, y - 1, w - rightBorder - x, 0));
   411       }
   412 
   413       foreach (KeyValuePair<IHardware, IList<ISensor>> pair in sensors) {
   414         if (hardwareNames.Value) {
   415           if (y > topMargin)
   416             y += hardwareLineHeight - sensorLineHeight;
   417           x = leftBorder + 1;
   418           g.DrawImage(HardwareTypeImage.Instance.GetImage(pair.Key.HardwareType),
   419             new Rectangle(x, y + 1, iconSize, iconSize));
   420           x += iconSize + 1;
   421           g.DrawString(pair.Key.Name, largeFont, Brushes.White,
   422             new Rectangle(x, y - 1, w - rightBorder - x, 0), 
   423             stringFormat);
   424           y += hardwareLineHeight;
   425         }
   426 
   427         foreach (ISensor sensor in pair.Value) {
   428           int remainingWidth;
   429 
   430 
   431           if ((sensor.SensorType != SensorType.Load &&
   432                sensor.SensorType != SensorType.Control &&
   433                sensor.SensorType != SensorType.Level) || !sensor.Value.HasValue) 
   434           {
   435             string formatted;
   436 
   437             if (sensor.Value.HasValue) {
   438               string format = "";
   439               switch (sensor.SensorType) {
   440                 case SensorType.Voltage:
   441                   format = "{0:F2} V";
   442                   break;
   443                 case SensorType.Clock:
   444                   format = "{0:F0} MHz";
   445                   break;
   446                 case SensorType.Temperature:
   447                   format = "{0:F1} °C";
   448                   break;
   449                 case SensorType.Fan:
   450                   format = "{0:F0} RPM";
   451                   break;
   452                 case SensorType.Flow:
   453                   format = "{0:F0} L/h";
   454                   break;
   455               }
   456 
   457               if (sensor.SensorType == SensorType.Temperature &&
   458                 unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit) {
   459                 formatted = string.Format("{0:F1} °F",
   460                   sensor.Value * 1.8 + 32);
   461               } else {
   462                 formatted = string.Format(format, sensor.Value);
   463               }
   464             } else {
   465               formatted = "-";
   466             }
   467 
   468             g.DrawString(formatted, smallFont, darkWhite,
   469               new RectangleF(-1, y - 1, w - rightMargin + 3, 0),
   470               alignRightStringFormat);
   471 
   472             remainingWidth = w - (int)Math.Floor(g.MeasureString(formatted,
   473               smallFont, w, StringFormat.GenericTypographic).Width) -
   474               rightMargin;
   475           } else {
   476             DrawProgress(g, w - progressWidth - rightMargin,
   477               y + 0.35f * sensorLineHeight, progressWidth,
   478               0.6f * sensorLineHeight, 0.01f * sensor.Value.Value);
   479 
   480             remainingWidth = w - progressWidth - rightMargin;
   481           }
   482            
   483           remainingWidth -= leftMargin + 2;
   484           if (remainingWidth > 0) {
   485             g.DrawString(sensor.Name, smallFont, darkWhite,
   486               new RectangleF(leftMargin - 1, y - 1, remainingWidth, 0), 
   487               trimStringFormat);
   488           }
   489 
   490           y += sensorLineHeight;
   491         }
   492       }
   493     }
   494 
   495     private class HardwareComparer : IComparer<IHardware> {
   496       public int Compare(IHardware x, IHardware y) {
   497         if (x == null && y == null)
   498           return 0;
   499         if (x == null)
   500           return -1;
   501         if (y == null)
   502           return 1;
   503 
   504         if (x.HardwareType != y.HardwareType)
   505           return x.HardwareType.CompareTo(y.HardwareType);
   506 
   507         return x.Identifier.CompareTo(y.Identifier);
   508       }
   509     }
   510   }
   511 }
   512