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