GUI/SensorGadget.cs
author moel.mich
Mon, 16 May 2011 22:04:04 +0000
changeset 289 d4798e7f4388
parent 263 575f8d4c378d
child 300 ee03b05bce9f
permissions -rw-r--r--
Fixed Issue 215.
     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       VisibleChanged += delegate(object sender, EventArgs e) {
   205         Rectangle bounds = new Rectangle(Location, Size);
   206         Screen screen = Screen.FromRectangle(bounds);
   207         Rectangle intersection = 
   208           Rectangle.Intersect(screen.WorkingArea, bounds);
   209         if (intersection.Width < Math.Min(16, bounds.Width) || 
   210             intersection.Height < Math.Min(16, bounds.Height)) 
   211         {
   212           Location = new Point(
   213             screen.WorkingArea.Width / 2 - bounds.Width / 2, 
   214             screen.WorkingArea.Height / 2 - bounds.Height / 2);
   215         }
   216       };
   217 
   218       MouseDoubleClick += delegate(object obj, MouseEventArgs args) {
   219         SendHideShowCommand();
   220       };
   221     }
   222 
   223     public override void Dispose() {
   224 
   225       largeFont.Dispose();
   226       largeFont = null;
   227 
   228       smallFont.Dispose();
   229       smallFont = null;
   230 
   231       darkWhite.Dispose();
   232       darkWhite = null;
   233 
   234       stringFormat.Dispose();
   235       stringFormat = null;
   236 
   237       trimStringFormat.Dispose();
   238       trimStringFormat = null;
   239 
   240       alignRightStringFormat.Dispose();
   241       alignRightStringFormat = null;      
   242 
   243       base.Dispose();
   244     }
   245 
   246     private void HardwareRemoved(IHardware hardware) {
   247       hardware.SensorAdded -= new SensorEventHandler(SensorAdded);
   248       hardware.SensorRemoved -= new SensorEventHandler(SensorRemoved);
   249       foreach (ISensor sensor in hardware.Sensors)
   250         SensorRemoved(sensor);
   251       foreach (IHardware subHardware in hardware.SubHardware)
   252         HardwareRemoved(subHardware);
   253     }
   254 
   255     private void HardwareAdded(IHardware hardware) {
   256       foreach (ISensor sensor in hardware.Sensors)
   257         SensorAdded(sensor);
   258       hardware.SensorAdded += new SensorEventHandler(SensorAdded);
   259       hardware.SensorRemoved += new SensorEventHandler(SensorRemoved);
   260       foreach (IHardware subHardware in hardware.SubHardware)
   261         HardwareAdded(subHardware);
   262     }
   263 
   264     private void SensorAdded(ISensor sensor) {
   265       if (settings.GetValue(new Identifier(sensor.Identifier,
   266         "gadget").ToString(), false)) 
   267         Add(sensor);
   268     }
   269 
   270     private void SensorRemoved(ISensor sensor) {
   271       if (Contains(sensor))
   272         Remove(sensor, false);
   273     }
   274 
   275     public bool Contains(ISensor sensor) {
   276       foreach (IList<ISensor> list in sensors.Values)
   277         if (list.Contains(sensor))
   278           return true;
   279       return false;
   280     }
   281 
   282     public void Add(ISensor sensor) {
   283       if (Contains(sensor)) {
   284         return;
   285       } else {
   286         // get the right hardware
   287         IHardware hardware = sensor.Hardware;
   288         while (hardware.Parent != null)
   289           hardware = hardware.Parent;
   290 
   291         // get the sensor list associated with the hardware
   292         IList<ISensor> list;
   293         if (!sensors.TryGetValue(hardware, out list)) {
   294           list = new List<ISensor>();
   295           sensors.Add(hardware, list);
   296         }
   297 
   298         // insert the sensor at the right position
   299         int i = 0;
   300         while (i < list.Count && (list[i].SensorType < sensor.SensorType || 
   301           (list[i].SensorType == sensor.SensorType && 
   302            list[i].Index < sensor.Index))) i++;
   303         list.Insert(i, sensor);
   304 
   305         settings.SetValue(
   306           new Identifier(sensor.Identifier, "gadget").ToString(), true);
   307         
   308         Resize();
   309       }
   310     }
   311 
   312     public void Remove(ISensor sensor) {
   313       Remove(sensor, true);
   314     }
   315 
   316     private void Remove(ISensor sensor, bool deleteConfig) {
   317       if (deleteConfig) 
   318         settings.Remove(new Identifier(sensor.Identifier, "gadget").ToString());
   319 
   320       foreach (KeyValuePair<IHardware, IList<ISensor>> keyValue in sensors)
   321         if (keyValue.Value.Contains(sensor)) {
   322           keyValue.Value.Remove(sensor);          
   323           if (keyValue.Value.Count == 0) {
   324             sensors.Remove(keyValue.Key);
   325             break;
   326           }
   327         }
   328       Resize();
   329     }
   330 
   331     public event EventHandler HideShowCommand;
   332 
   333     public void SendHideShowCommand() {
   334       if (HideShowCommand != null)
   335         HideShowCommand(this, null);
   336     }
   337 
   338     private Font CreateFont(float size, FontStyle style) {
   339       try {
   340         return new Font(SystemFonts.MessageBoxFont.FontFamily, size, style);
   341       } catch (ArgumentException) {
   342         // if the style is not supported, fall back to the original one
   343         return new Font(SystemFonts.MessageBoxFont.FontFamily, size, 
   344           SystemFonts.MessageBoxFont.Style);
   345       }
   346     }
   347 
   348     private void SetFontSize(float size) {
   349       fontSize = size;
   350       largeFont = CreateFont(fontSize, FontStyle.Bold);
   351       smallFont = CreateFont(fontSize, FontStyle.Regular);
   352       
   353       double scaledFontSize = fontSize * scale;
   354       iconSize = (int)Math.Round(1.5 * scaledFontSize);
   355       hardwareLineHeight = (int)Math.Round(1.66 * scaledFontSize);
   356       sensorLineHeight = (int)Math.Round(1.33 * scaledFontSize);
   357       leftMargin = leftBorder + (int)Math.Round(0.3 * scaledFontSize);
   358       rightMargin = rightBorder + (int)Math.Round(0.3 * scaledFontSize);
   359       topMargin = topBorder;
   360       bottomMargin = bottomBorder + (int)Math.Round(0.3 * scaledFontSize);
   361       progressWidth = (int)Math.Round(5.3 * scaledFontSize);
   362 
   363       Resize((int)Math.Round(17.3 * scaledFontSize));
   364     }
   365 
   366     private void Resize() {
   367       Resize(this.Size.Width);
   368     }
   369 
   370     private void Resize(int width) {
   371       int y = topMargin;      
   372       foreach (KeyValuePair<IHardware, IList<ISensor>> pair in sensors) {
   373         if (hardwareNames.Value) {
   374           if (y > topMargin)
   375             y += hardwareLineHeight - sensorLineHeight;
   376           y += hardwareLineHeight;
   377         }
   378         y += pair.Value.Count * sensorLineHeight;
   379       }      
   380       if (sensors.Count == 0)
   381         y += 4 * sensorLineHeight + hardwareLineHeight;
   382       y += bottomMargin;
   383       this.Size = new Size(width, y);
   384     }
   385 
   386     private void DrawBackground(Graphics g) {
   387       int w = Size.Width;
   388       int h = Size.Height;
   389       int t = topBorder;
   390       int b = bottomBorder;
   391       int l = leftBorder;
   392       int r = rightBorder;
   393       GraphicsUnit u = GraphicsUnit.Pixel;
   394 
   395       g.DrawImage(back, new Rectangle(0, 0, l, t),
   396         new Rectangle(0, 0, l, t), u);
   397       g.DrawImage(back, new Rectangle(l, 0, w - l - r, t),
   398         new Rectangle(l, 0, back.Width - l - r, t), u);
   399       g.DrawImage(back, new Rectangle(w - r, 0, r, t),
   400         new Rectangle(back.Width - r, 0, r, t), u);
   401 
   402       g.DrawImage(back, new Rectangle(0, t, l, h - t - b),
   403         new Rectangle(0, t, l, back.Height - t - b), u);
   404       g.DrawImage(back, new Rectangle(l, t, w - l - r, h - t - b),
   405         new Rectangle(l, t, back.Width - l - r, back.Height - t - b), u);
   406       g.DrawImage(back, new Rectangle(w - r, t, r, h - t - b),
   407         new Rectangle(back.Width - r, t, r, back.Height - t - b), u);
   408 
   409       g.DrawImage(back, new Rectangle(0, h - b, l, b),
   410         new Rectangle(0, back.Height - b, l, b), u);
   411       g.DrawImage(back, new Rectangle(l, h - b, w - l - r, b),
   412         new Rectangle(l, back.Height - b, back.Width - l - r, b), u);
   413       g.DrawImage(back, new Rectangle(w - r, h - b, r, b),
   414         new Rectangle(back.Width - r, back.Height - b, r, b), u);
   415     }
   416 
   417     private void DrawProgress(Graphics g, float x, float y, 
   418       float width, float height, float progress) 
   419     {
   420       g.DrawImage(barBack, 
   421         new RectangleF(x + width * progress, y, width * (1 - progress), height), 
   422         new RectangleF(barBack.Width * progress, 0, 
   423           (1 - progress) * barBack.Width, barBack.Height), 
   424         GraphicsUnit.Pixel);
   425       g.DrawImage(barblue,
   426         new RectangleF(x, y, width * progress, height),
   427         new RectangleF(0, 0, progress * barblue.Width, barblue.Height),
   428         GraphicsUnit.Pixel);
   429     }
   430 
   431     protected override void OnPaint(PaintEventArgs e) {
   432       Graphics g = e.Graphics;
   433       int w = Size.Width;
   434 
   435       g.Clear(Color.Transparent);
   436       
   437       DrawBackground(g);
   438 
   439       int x;
   440       int y = topMargin;
   441 
   442       if (sensors.Count == 0) {
   443         x = leftBorder + 1;
   444         g.DrawString("Right-click on a sensor in the main window and select " + 
   445           "\"Show in Gadget\" to show the sensor here.", 
   446           smallFont, Brushes.White,
   447           new Rectangle(x, y - 1, w - rightBorder - x, 0));
   448       }
   449 
   450       foreach (KeyValuePair<IHardware, IList<ISensor>> pair in sensors) {
   451         if (hardwareNames.Value) {
   452           if (y > topMargin)
   453             y += hardwareLineHeight - sensorLineHeight;
   454           x = leftBorder + 1;
   455           g.DrawImage(HardwareTypeImage.Instance.GetImage(pair.Key.HardwareType),
   456             new Rectangle(x, y + 1, iconSize, iconSize));
   457           x += iconSize + 1;
   458           g.DrawString(pair.Key.Name, largeFont, Brushes.White,
   459             new Rectangle(x, y - 1, w - rightBorder - x, 0), 
   460             stringFormat);
   461           y += hardwareLineHeight;
   462         }
   463 
   464         foreach (ISensor sensor in pair.Value) {
   465           int remainingWidth;
   466 
   467 
   468           if ((sensor.SensorType != SensorType.Load &&
   469                sensor.SensorType != SensorType.Control &&
   470                sensor.SensorType != SensorType.Level) || !sensor.Value.HasValue) 
   471           {
   472             string formatted;
   473 
   474             if (sensor.Value.HasValue) {
   475               string format = "";
   476               switch (sensor.SensorType) {
   477                 case SensorType.Voltage:
   478                   format = "{0:F2} V";
   479                   break;
   480                 case SensorType.Clock:
   481                   format = "{0:F0} MHz";
   482                   break;
   483                 case SensorType.Temperature:
   484                   format = "{0:F1} °C";
   485                   break;
   486                 case SensorType.Fan:
   487                   format = "{0:F0} RPM";
   488                   break;
   489                 case SensorType.Flow:
   490                   format = "{0:F0} L/h";
   491                   break;
   492               }
   493 
   494               if (sensor.SensorType == SensorType.Temperature &&
   495                 unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit) {
   496                 formatted = string.Format("{0:F1} °F",
   497                   sensor.Value * 1.8 + 32);
   498               } else {
   499                 formatted = string.Format(format, sensor.Value);
   500               }
   501             } else {
   502               formatted = "-";
   503             }
   504 
   505             g.DrawString(formatted, smallFont, darkWhite,
   506               new RectangleF(-1, y - 1, w - rightMargin + 3, 0),
   507               alignRightStringFormat);
   508 
   509             remainingWidth = w - (int)Math.Floor(g.MeasureString(formatted,
   510               smallFont, w, StringFormat.GenericTypographic).Width) -
   511               rightMargin;
   512           } else {
   513             DrawProgress(g, w - progressWidth - rightMargin,
   514               y + 0.35f * sensorLineHeight, progressWidth,
   515               0.6f * sensorLineHeight, 0.01f * sensor.Value.Value);
   516 
   517             remainingWidth = w - progressWidth - rightMargin;
   518           }
   519            
   520           remainingWidth -= leftMargin + 2;
   521           if (remainingWidth > 0) {
   522             g.DrawString(sensor.Name, smallFont, darkWhite,
   523               new RectangleF(leftMargin - 1, y - 1, remainingWidth, 0), 
   524               trimStringFormat);
   525           }
   526 
   527           y += sensorLineHeight;
   528         }
   529       }
   530     }
   531 
   532     private class HardwareComparer : IComparer<IHardware> {
   533       public int Compare(IHardware x, IHardware y) {
   534         if (x == null && y == null)
   535           return 0;
   536         if (x == null)
   537           return -1;
   538         if (y == null)
   539           return 1;
   540 
   541         if (x.HardwareType != y.HardwareType)
   542           return x.HardwareType.CompareTo(y.HardwareType);
   543 
   544         return x.Identifier.CompareTo(y.Identifier);
   545       }
   546     }
   547   }
   548 }
   549