GUI/SensorGadget.cs
author moel.mich
Thu, 17 Mar 2011 20:16:01 +0000
changeset 263 575f8d4c378d
parent 252 e62afa69214f
child 289 d4798e7f4388
permissions -rw-r--r--
Fixed Issue 178.
     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       if (sensors.Count == 0)
   367         y += 4 * sensorLineHeight + hardwareLineHeight;
   368       y += bottomMargin;
   369       this.Size = new Size(width, y);
   370     }
   371 
   372     private void DrawBackground(Graphics g) {
   373       int w = Size.Width;
   374       int h = Size.Height;
   375       int t = topBorder;
   376       int b = bottomBorder;
   377       int l = leftBorder;
   378       int r = rightBorder;
   379       GraphicsUnit u = GraphicsUnit.Pixel;
   380 
   381       g.DrawImage(back, new Rectangle(0, 0, l, t),
   382         new Rectangle(0, 0, l, t), u);
   383       g.DrawImage(back, new Rectangle(l, 0, w - l - r, t),
   384         new Rectangle(l, 0, back.Width - l - r, t), u);
   385       g.DrawImage(back, new Rectangle(w - r, 0, r, t),
   386         new Rectangle(back.Width - r, 0, r, t), u);
   387 
   388       g.DrawImage(back, new Rectangle(0, t, l, h - t - b),
   389         new Rectangle(0, t, l, back.Height - t - b), u);
   390       g.DrawImage(back, new Rectangle(l, t, w - l - r, h - t - b),
   391         new Rectangle(l, t, back.Width - l - r, back.Height - t - b), u);
   392       g.DrawImage(back, new Rectangle(w - r, t, r, h - t - b),
   393         new Rectangle(back.Width - r, t, r, back.Height - t - b), u);
   394 
   395       g.DrawImage(back, new Rectangle(0, h - b, l, b),
   396         new Rectangle(0, back.Height - b, l, b), u);
   397       g.DrawImage(back, new Rectangle(l, h - b, w - l - r, b),
   398         new Rectangle(l, back.Height - b, back.Width - l - r, b), u);
   399       g.DrawImage(back, new Rectangle(w - r, h - b, r, b),
   400         new Rectangle(back.Width - r, back.Height - b, r, b), u);
   401     }
   402 
   403     private void DrawProgress(Graphics g, float x, float y, 
   404       float width, float height, float progress) 
   405     {
   406       g.DrawImage(barBack, 
   407         new RectangleF(x + width * progress, y, width * (1 - progress), height), 
   408         new RectangleF(barBack.Width * progress, 0, 
   409           (1 - progress) * barBack.Width, barBack.Height), 
   410         GraphicsUnit.Pixel);
   411       g.DrawImage(barblue,
   412         new RectangleF(x, y, width * progress, height),
   413         new RectangleF(0, 0, progress * barblue.Width, barblue.Height),
   414         GraphicsUnit.Pixel);
   415     }
   416 
   417     protected override void OnPaint(PaintEventArgs e) {
   418       Graphics g = e.Graphics;
   419       int w = Size.Width;
   420 
   421       g.Clear(Color.Transparent);
   422       
   423       DrawBackground(g);
   424 
   425       int x;
   426       int y = topMargin;
   427 
   428       if (sensors.Count == 0) {
   429         x = leftBorder + 1;
   430         g.DrawString("Right-click on a sensor in the main window and select " + 
   431           "\"Show in Gadget\" to show the sensor here.", 
   432           smallFont, Brushes.White,
   433           new Rectangle(x, y - 1, w - rightBorder - x, 0));
   434       }
   435 
   436       foreach (KeyValuePair<IHardware, IList<ISensor>> pair in sensors) {
   437         if (hardwareNames.Value) {
   438           if (y > topMargin)
   439             y += hardwareLineHeight - sensorLineHeight;
   440           x = leftBorder + 1;
   441           g.DrawImage(HardwareTypeImage.Instance.GetImage(pair.Key.HardwareType),
   442             new Rectangle(x, y + 1, iconSize, iconSize));
   443           x += iconSize + 1;
   444           g.DrawString(pair.Key.Name, largeFont, Brushes.White,
   445             new Rectangle(x, y - 1, w - rightBorder - x, 0), 
   446             stringFormat);
   447           y += hardwareLineHeight;
   448         }
   449 
   450         foreach (ISensor sensor in pair.Value) {
   451           int remainingWidth;
   452 
   453 
   454           if ((sensor.SensorType != SensorType.Load &&
   455                sensor.SensorType != SensorType.Control &&
   456                sensor.SensorType != SensorType.Level) || !sensor.Value.HasValue) 
   457           {
   458             string formatted;
   459 
   460             if (sensor.Value.HasValue) {
   461               string format = "";
   462               switch (sensor.SensorType) {
   463                 case SensorType.Voltage:
   464                   format = "{0:F2} V";
   465                   break;
   466                 case SensorType.Clock:
   467                   format = "{0:F0} MHz";
   468                   break;
   469                 case SensorType.Temperature:
   470                   format = "{0:F1} °C";
   471                   break;
   472                 case SensorType.Fan:
   473                   format = "{0:F0} RPM";
   474                   break;
   475                 case SensorType.Flow:
   476                   format = "{0:F0} L/h";
   477                   break;
   478               }
   479 
   480               if (sensor.SensorType == SensorType.Temperature &&
   481                 unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit) {
   482                 formatted = string.Format("{0:F1} °F",
   483                   sensor.Value * 1.8 + 32);
   484               } else {
   485                 formatted = string.Format(format, sensor.Value);
   486               }
   487             } else {
   488               formatted = "-";
   489             }
   490 
   491             g.DrawString(formatted, smallFont, darkWhite,
   492               new RectangleF(-1, y - 1, w - rightMargin + 3, 0),
   493               alignRightStringFormat);
   494 
   495             remainingWidth = w - (int)Math.Floor(g.MeasureString(formatted,
   496               smallFont, w, StringFormat.GenericTypographic).Width) -
   497               rightMargin;
   498           } else {
   499             DrawProgress(g, w - progressWidth - rightMargin,
   500               y + 0.35f * sensorLineHeight, progressWidth,
   501               0.6f * sensorLineHeight, 0.01f * sensor.Value.Value);
   502 
   503             remainingWidth = w - progressWidth - rightMargin;
   504           }
   505            
   506           remainingWidth -= leftMargin + 2;
   507           if (remainingWidth > 0) {
   508             g.DrawString(sensor.Name, smallFont, darkWhite,
   509               new RectangleF(leftMargin - 1, y - 1, remainingWidth, 0), 
   510               trimStringFormat);
   511           }
   512 
   513           y += sensorLineHeight;
   514         }
   515       }
   516     }
   517 
   518     private class HardwareComparer : IComparer<IHardware> {
   519       public int Compare(IHardware x, IHardware y) {
   520         if (x == null && y == null)
   521           return 0;
   522         if (x == null)
   523           return -1;
   524         if (y == null)
   525           return 1;
   526 
   527         if (x.HardwareType != y.HardwareType)
   528           return x.HardwareType.CompareTo(y.HardwareType);
   529 
   530         return x.Identifier.CompareTo(y.Identifier);
   531       }
   532     }
   533   }
   534 }
   535