moel@202: /* moel@176: moel@176: Version: MPL 1.1/GPL 2.0/LGPL 2.1 moel@176: moel@176: The contents of this file are subject to the Mozilla Public License Version moel@176: 1.1 (the "License"); you may not use this file except in compliance with moel@176: the License. You may obtain a copy of the License at moel@176: moel@176: http://www.mozilla.org/MPL/ moel@176: moel@176: Software distributed under the License is distributed on an "AS IS" basis, moel@176: WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License moel@176: for the specific language governing rights and limitations under the License. moel@176: moel@176: The Original Code is the Open Hardware Monitor code. moel@176: moel@176: The Initial Developer of the Original Code is moel@176: Michael Möller . moel@300: Portions created by the Initial Developer are Copyright (C) 2010-2011 moel@176: the Initial Developer. All Rights Reserved. moel@176: moel@176: Contributor(s): moel@176: moel@176: Alternatively, the contents of this file may be used under the terms of moel@176: either the GNU General Public License Version 2 or later (the "GPL"), or moel@176: the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), moel@176: in which case the provisions of the GPL or the LGPL are applicable instead moel@176: of those above. If you wish to allow use of your version of this file only moel@176: under the terms of either the GPL or the LGPL, and not to allow others to moel@176: use your version of this file under the terms of the MPL, indicate your moel@176: decision by deleting the provisions above and replace them with the notice moel@176: and other provisions required by the GPL or the LGPL. If you do not delete moel@176: the provisions above, a recipient may use your version of this file under moel@176: the terms of any one of the MPL, the GPL or the LGPL. moel@176: moel@176: */ moel@176: moel@176: using System; moel@176: using System.Collections.Generic; moel@176: using System.Drawing; moel@316: using System.Drawing.Imaging; moel@176: using System.Windows.Forms; moel@176: using OpenHardwareMonitor.Hardware; moel@176: moel@176: namespace OpenHardwareMonitor.GUI { moel@176: public class SensorGadget : Gadget { moel@176: moel@176: private UnitManager unitManager; moel@176: moel@176: private Image back = Utilities.EmbeddedResources.GetImage("gadget.png"); moel@176: private Image barBack = Utilities.EmbeddedResources.GetImage("barback.png"); moel@316: private Image barBlue = Utilities.EmbeddedResources.GetImage("barblue.png"); moel@183: private const int topBorder = 6; moel@183: private const int bottomBorder = 7; moel@176: private const int leftBorder = 6; moel@183: private const int rightBorder = 7; moel@316: private Image background = new Bitmap(1, 1); moel@183: moel@252: private readonly float scale; moel@183: private float fontSize; moel@183: private int iconSize; moel@183: private int hardwareLineHeight; moel@183: private int sensorLineHeight; moel@183: private int rightMargin; moel@183: private int leftMargin; moel@183: private int topMargin; moel@183: private int bottomMargin; moel@183: private int progressWidth; moel@176: moel@176: private IDictionary> sensors = moel@176: new SortedDictionary>(new HardwareComparer()); moel@176: moel@176: private PersistentSettings settings; moel@181: private UserOption hardwareNames; moel@176: private UserOption alwaysOnTop; moel@183: private UserOption lockPositionAndSize; moel@176: moel@176: private Font largeFont; moel@176: private Font smallFont; moel@181: private Brush darkWhite; moel@183: private StringFormat stringFormat; moel@181: private StringFormat trimStringFormat; moel@181: private StringFormat alignRightStringFormat; moel@176: moel@176: public SensorGadget(IComputer computer, PersistentSettings settings, moel@176: UnitManager unitManager) moel@176: { moel@176: this.unitManager = unitManager; moel@176: this.settings = settings; moel@176: computer.HardwareAdded += new HardwareEventHandler(HardwareAdded); moel@183: computer.HardwareRemoved += new HardwareEventHandler(HardwareRemoved); moel@176: moel@181: this.darkWhite = new SolidBrush(Color.FromArgb(0xF0, 0xF0, 0xF0)); moel@181: moel@183: this.stringFormat = new StringFormat(); moel@183: this.stringFormat.FormatFlags = StringFormatFlags.NoWrap; moel@183: moel@181: this.trimStringFormat = new StringFormat(); moel@181: this.trimStringFormat.Trimming = StringTrimming.EllipsisCharacter; moel@183: this.trimStringFormat.FormatFlags = StringFormatFlags.NoWrap; moel@181: moel@181: this.alignRightStringFormat = new StringFormat(); moel@181: this.alignRightStringFormat.Alignment = StringAlignment.Far; moel@183: this.alignRightStringFormat.FormatFlags = StringFormatFlags.NoWrap; moel@176: moel@176: this.Location = new Point( moel@176: settings.GetValue("sensorGadget.Location.X", 100), moel@176: settings.GetValue("sensorGadget.Location.Y", 100)); moel@176: LocationChanged += delegate(object sender, EventArgs e) { moel@176: settings.SetValue("sensorGadget.Location.X", Location.X); moel@176: settings.SetValue("sensorGadget.Location.Y", Location.Y); moel@176: }; moel@183: moel@252: // get the custom to default dpi ratio moel@252: using (Bitmap b = new Bitmap(1, 1)) { moel@252: scale = b.HorizontalResolution / 96.0f; moel@252: } moel@252: moel@183: SetFontSize(settings.GetValue("sensorGadget.FontSize", 7.5f)); moel@183: Resize(settings.GetValue("sensorGadget.Width", Size.Width)); moel@176: moel@176: ContextMenu contextMenu = new ContextMenu(); moel@181: MenuItem hardwareNamesItem = new MenuItem("Hardware Names"); moel@181: contextMenu.MenuItems.Add(hardwareNamesItem); moel@183: MenuItem fontSizeMenu = new MenuItem("Font Size"); moel@183: for (int i = 0; i < 4; i++) { moel@183: float size; moel@183: string name; moel@183: switch (i) { moel@183: case 0: size = 6.5f; name = "Small"; break; moel@183: case 1: size = 7.5f; name = "Medium"; break; moel@183: case 2: size = 9f; name = "Large"; break; moel@183: case 3: size = 11f; name = "Very Large"; break; moel@183: default: throw new NotImplementedException(); moel@183: } moel@183: MenuItem item = new MenuItem(name); moel@183: item.Checked = fontSize == size; moel@183: item.Click += delegate(object sender, EventArgs e) { moel@183: SetFontSize(size); moel@183: settings.SetValue("sensorGadget.FontSize", size); moel@183: foreach (MenuItem mi in fontSizeMenu.MenuItems) moel@183: mi.Checked = mi == item; moel@183: }; moel@183: fontSizeMenu.MenuItems.Add(item); moel@183: } moel@183: contextMenu.MenuItems.Add(fontSizeMenu); moel@181: contextMenu.MenuItems.Add(new MenuItem("-")); moel@183: MenuItem lockItem = new MenuItem("Lock Position and Size"); moel@176: contextMenu.MenuItems.Add(lockItem); moel@176: contextMenu.MenuItems.Add(new MenuItem("-")); moel@176: MenuItem alwaysOnTopItem = new MenuItem("Always on Top"); moel@176: contextMenu.MenuItems.Add(alwaysOnTopItem); moel@176: MenuItem opacityMenu = new MenuItem("Opacity"); moel@176: contextMenu.MenuItems.Add(opacityMenu); moel@176: Opacity = (byte)settings.GetValue("sensorGadget.Opacity", 255); moel@176: for (int i = 0; i < 5; i++) { moel@176: MenuItem item = new MenuItem((20 * (i + 1)).ToString() + " %"); moel@176: byte o = (byte)(51 * (i + 1)); moel@176: item.Checked = Opacity == o; moel@176: item.Click += delegate(object sender, EventArgs e) { moel@178: Opacity = o; moel@176: settings.SetValue("sensorGadget.Opacity", Opacity); moel@176: foreach (MenuItem mi in opacityMenu.MenuItems) moel@178: mi.Checked = mi == item; moel@176: }; moel@176: opacityMenu.MenuItems.Add(item); moel@176: } moel@176: this.ContextMenu = contextMenu; moel@176: moel@181: hardwareNames = new UserOption("sensorGadget.Hardwarenames", true, moel@181: hardwareNamesItem, settings); moel@181: hardwareNames.Changed += delegate(object sender, EventArgs e) { moel@181: Resize(); moel@181: }; moel@181: moel@176: alwaysOnTop = new UserOption("sensorGadget.AlwaysOnTop", false, moel@176: alwaysOnTopItem, settings); moel@176: alwaysOnTop.Changed += delegate(object sender, EventArgs e) { moel@176: this.AlwaysOnTop = alwaysOnTop.Value; moel@176: }; moel@183: lockPositionAndSize = new UserOption("sensorGadget.LockPositionAndSize", moel@183: false, lockItem, settings); moel@183: lockPositionAndSize.Changed += delegate(object sender, EventArgs e) { moel@183: this.LockPositionAndSize = lockPositionAndSize.Value; moel@176: }; moel@176: moel@183: HitTest += delegate(object sender, HitTestEventArgs e) { moel@183: if (lockPositionAndSize.Value) moel@183: return; moel@183: moel@183: if (e.Location.X < leftBorder) { moel@183: e.HitResult = HitResult.Left; moel@183: return; moel@183: } moel@183: if (e.Location.X > Size.Width - 1 - rightBorder) { moel@183: e.HitResult = HitResult.Right; moel@183: return; moel@183: } moel@183: }; moel@183: moel@183: SizeChanged += delegate(object sender, EventArgs e) { moel@183: settings.SetValue("sensorGadget.Width", Size.Width); moel@183: Redraw(); moel@183: }; moel@244: moel@289: VisibleChanged += delegate(object sender, EventArgs e) { moel@289: Rectangle bounds = new Rectangle(Location, Size); moel@289: Screen screen = Screen.FromRectangle(bounds); moel@289: Rectangle intersection = moel@289: Rectangle.Intersect(screen.WorkingArea, bounds); moel@289: if (intersection.Width < Math.Min(16, bounds.Width) || moel@289: intersection.Height < Math.Min(16, bounds.Height)) moel@289: { moel@289: Location = new Point( moel@289: screen.WorkingArea.Width / 2 - bounds.Width / 2, moel@289: screen.WorkingArea.Height / 2 - bounds.Height / 2); moel@289: } moel@289: }; moel@289: moel@244: MouseDoubleClick += delegate(object obj, MouseEventArgs args) { moel@244: SendHideShowCommand(); moel@244: }; moel@176: } moel@176: moel@181: public override void Dispose() { moel@181: moel@181: largeFont.Dispose(); moel@181: largeFont = null; moel@181: moel@181: smallFont.Dispose(); moel@181: smallFont = null; moel@181: moel@181: darkWhite.Dispose(); moel@181: darkWhite = null; moel@181: moel@183: stringFormat.Dispose(); moel@183: stringFormat = null; moel@183: moel@181: trimStringFormat.Dispose(); moel@181: trimStringFormat = null; moel@181: moel@181: alignRightStringFormat.Dispose(); moel@316: alignRightStringFormat = null; moel@316: moel@316: back.Dispose(); moel@316: back = null; moel@316: moel@316: barBlue.Dispose(); moel@316: barBlue = null; moel@316: moel@316: barBack.Dispose(); moel@316: barBack = null; moel@316: moel@316: background.Dispose(); moel@316: background = null; moel@181: moel@181: base.Dispose(); moel@181: } moel@181: moel@176: private void HardwareRemoved(IHardware hardware) { moel@176: hardware.SensorAdded -= new SensorEventHandler(SensorAdded); moel@176: hardware.SensorRemoved -= new SensorEventHandler(SensorRemoved); moel@176: foreach (ISensor sensor in hardware.Sensors) moel@176: SensorRemoved(sensor); moel@176: foreach (IHardware subHardware in hardware.SubHardware) moel@176: HardwareRemoved(subHardware); moel@176: } moel@176: moel@176: private void HardwareAdded(IHardware hardware) { moel@176: foreach (ISensor sensor in hardware.Sensors) moel@176: SensorAdded(sensor); moel@176: hardware.SensorAdded += new SensorEventHandler(SensorAdded); moel@176: hardware.SensorRemoved += new SensorEventHandler(SensorRemoved); moel@176: foreach (IHardware subHardware in hardware.SubHardware) moel@176: HardwareAdded(subHardware); moel@176: } moel@176: moel@176: private void SensorAdded(ISensor sensor) { moel@176: if (settings.GetValue(new Identifier(sensor.Identifier, moel@176: "gadget").ToString(), false)) moel@176: Add(sensor); moel@176: } moel@176: moel@176: private void SensorRemoved(ISensor sensor) { moel@176: if (Contains(sensor)) moel@176: Remove(sensor, false); moel@176: } moel@176: moel@176: public bool Contains(ISensor sensor) { moel@176: foreach (IList list in sensors.Values) moel@176: if (list.Contains(sensor)) moel@176: return true; moel@176: return false; moel@176: } moel@176: moel@176: public void Add(ISensor sensor) { moel@176: if (Contains(sensor)) { moel@176: return; moel@176: } else { moel@176: // get the right hardware moel@176: IHardware hardware = sensor.Hardware; moel@176: while (hardware.Parent != null) moel@176: hardware = hardware.Parent; moel@176: moel@176: // get the sensor list associated with the hardware moel@176: IList list; moel@176: if (!sensors.TryGetValue(hardware, out list)) { moel@176: list = new List(); moel@176: sensors.Add(hardware, list); moel@176: } moel@176: moel@176: // insert the sensor at the right position moel@176: int i = 0; moel@176: while (i < list.Count && (list[i].SensorType < sensor.SensorType || moel@176: (list[i].SensorType == sensor.SensorType && moel@176: list[i].Index < sensor.Index))) i++; moel@176: list.Insert(i, sensor); moel@176: moel@176: settings.SetValue( moel@176: new Identifier(sensor.Identifier, "gadget").ToString(), true); moel@176: moel@176: Resize(); moel@176: } moel@176: } moel@176: moel@176: public void Remove(ISensor sensor) { moel@176: Remove(sensor, true); moel@176: } moel@176: moel@176: private void Remove(ISensor sensor, bool deleteConfig) { moel@176: if (deleteConfig) moel@176: settings.Remove(new Identifier(sensor.Identifier, "gadget").ToString()); moel@176: moel@176: foreach (KeyValuePair> keyValue in sensors) moel@176: if (keyValue.Value.Contains(sensor)) { moel@176: keyValue.Value.Remove(sensor); moel@176: if (keyValue.Value.Count == 0) { moel@176: sensors.Remove(keyValue.Key); moel@176: break; moel@176: } moel@176: } moel@176: Resize(); moel@183: } moel@183: moel@244: public event EventHandler HideShowCommand; moel@244: moel@244: public void SendHideShowCommand() { moel@244: if (HideShowCommand != null) moel@244: HideShowCommand(this, null); moel@244: } moel@244: moel@183: private Font CreateFont(float size, FontStyle style) { moel@215: try { moel@215: return new Font(SystemFonts.MessageBoxFont.FontFamily, size, style); moel@215: } catch (ArgumentException) { moel@215: // if the style is not supported, fall back to the original one moel@215: return new Font(SystemFonts.MessageBoxFont.FontFamily, size, moel@215: SystemFonts.MessageBoxFont.Style); moel@215: } moel@183: } moel@183: moel@183: private void SetFontSize(float size) { moel@183: fontSize = size; moel@183: largeFont = CreateFont(fontSize, FontStyle.Bold); moel@183: smallFont = CreateFont(fontSize, FontStyle.Regular); moel@252: moel@252: double scaledFontSize = fontSize * scale; moel@252: iconSize = (int)Math.Round(1.5 * scaledFontSize); moel@252: hardwareLineHeight = (int)Math.Round(1.66 * scaledFontSize); moel@252: sensorLineHeight = (int)Math.Round(1.33 * scaledFontSize); moel@252: leftMargin = leftBorder + (int)Math.Round(0.3 * scaledFontSize); moel@252: rightMargin = rightBorder + (int)Math.Round(0.3 * scaledFontSize); moel@183: topMargin = topBorder; moel@252: bottomMargin = bottomBorder + (int)Math.Round(0.3 * scaledFontSize); moel@252: progressWidth = (int)Math.Round(5.3 * scaledFontSize); moel@252: moel@252: Resize((int)Math.Round(17.3 * scaledFontSize)); moel@176: } moel@176: moel@176: private void Resize() { moel@183: Resize(this.Size.Width); moel@183: } moel@183: moel@183: private void Resize(int width) { moel@183: int y = topMargin; moel@176: foreach (KeyValuePair> pair in sensors) { moel@181: if (hardwareNames.Value) { moel@183: if (y > topMargin) moel@183: y += hardwareLineHeight - sensorLineHeight; moel@181: y += hardwareLineHeight; moel@181: } moel@176: y += pair.Value.Count * sensorLineHeight; moel@263: } moel@263: if (sensors.Count == 0) moel@263: y += 4 * sensorLineHeight + hardwareLineHeight; moel@183: y += bottomMargin; moel@183: this.Size = new Size(width, y); moel@176: } moel@176: moel@176: private void DrawBackground(Graphics g) { moel@176: int w = Size.Width; moel@316: int h = Size.Height; moel@176: moel@316: if (w != background.Width || h != background.Height) { moel@176: moel@316: background.Dispose(); moel@316: background = new Bitmap(w, h, PixelFormat.Format32bppPArgb); moel@316: using (Graphics graphics = Graphics.FromImage(background)) { moel@176: moel@316: int t = topBorder; moel@316: int b = bottomBorder; moel@316: int l = leftBorder; moel@316: int r = rightBorder; moel@316: moel@316: GraphicsUnit u = GraphicsUnit.Pixel; moel@316: moel@316: graphics.DrawImage(back, new Rectangle(0, 0, l, t), moel@316: new Rectangle(0, 0, l, t), u); moel@316: graphics.DrawImage(back, new Rectangle(l, 0, w - l - r, t), moel@316: new Rectangle(l, 0, back.Width - l - r, t), u); moel@316: graphics.DrawImage(back, new Rectangle(w - r, 0, r, t), moel@316: new Rectangle(back.Width - r, 0, r, t), u); moel@316: moel@316: graphics.DrawImage(back, new Rectangle(0, t, l, h - t - b), moel@316: new Rectangle(0, t, l, back.Height - t - b), u); moel@316: graphics.DrawImage(back, new Rectangle(l, t, w - l - r, h - t - b), moel@316: new Rectangle(l, t, back.Width - l - r, back.Height - t - b), u); moel@316: graphics.DrawImage(back, new Rectangle(w - r, t, r, h - t - b), moel@316: new Rectangle(back.Width - r, t, r, back.Height - t - b), u); moel@316: moel@316: graphics.DrawImage(back, new Rectangle(0, h - b, l, b), moel@316: new Rectangle(0, back.Height - b, l, b), u); moel@316: graphics.DrawImage(back, new Rectangle(l, h - b, w - l - r, b), moel@316: new Rectangle(l, back.Height - b, back.Width - l - r, b), u); moel@316: graphics.DrawImage(back, new Rectangle(w - r, h - b, r, b), moel@316: new Rectangle(back.Width - r, back.Height - b, r, b), u); moel@316: } moel@316: } moel@316: moel@316: g.DrawImageUnscaled(background, 0, 0); moel@176: } moel@176: moel@243: private void DrawProgress(Graphics g, float x, float y, moel@243: float width, float height, float progress) moel@176: { moel@176: g.DrawImage(barBack, moel@176: new RectangleF(x + width * progress, y, width * (1 - progress), height), moel@176: new RectangleF(barBack.Width * progress, 0, moel@176: (1 - progress) * barBack.Width, barBack.Height), moel@176: GraphicsUnit.Pixel); moel@316: g.DrawImage(barBlue, moel@176: new RectangleF(x, y, width * progress, height), moel@316: new RectangleF(0, 0, progress * barBlue.Width, barBlue.Height), moel@176: GraphicsUnit.Pixel); moel@176: } moel@176: moel@176: protected override void OnPaint(PaintEventArgs e) { moel@176: Graphics g = e.Graphics; moel@176: int w = Size.Width; moel@176: moel@176: g.Clear(Color.Transparent); moel@183: moel@176: DrawBackground(g); moel@176: moel@176: int x; moel@183: int y = topMargin; moel@186: moel@186: if (sensors.Count == 0) { moel@186: x = leftBorder + 1; moel@263: g.DrawString("Right-click on a sensor in the main window and select " + moel@263: "\"Show in Gadget\" to show the sensor here.", moel@263: smallFont, Brushes.White, moel@186: new Rectangle(x, y - 1, w - rightBorder - x, 0)); moel@186: } moel@186: moel@176: foreach (KeyValuePair> pair in sensors) { moel@181: if (hardwareNames.Value) { moel@183: if (y > topMargin) moel@183: y += hardwareLineHeight - sensorLineHeight; moel@181: x = leftBorder + 1; moel@181: g.DrawImage(HardwareTypeImage.Instance.GetImage(pair.Key.HardwareType), moel@181: new Rectangle(x, y + 1, iconSize, iconSize)); moel@181: x += iconSize + 1; moel@181: g.DrawString(pair.Key.Name, largeFont, Brushes.White, moel@183: new Rectangle(x, y - 1, w - rightBorder - x, 0), moel@183: stringFormat); moel@181: y += hardwareLineHeight; moel@181: } moel@176: moel@176: foreach (ISensor sensor in pair.Value) { moel@183: int remainingWidth; moel@176: moel@187: moel@187: if ((sensor.SensorType != SensorType.Load && moel@217: sensor.SensorType != SensorType.Control && moel@217: sensor.SensorType != SensorType.Level) || !sensor.Value.HasValue) moel@176: { moel@187: string formatted; moel@187: moel@187: if (sensor.Value.HasValue) { moel@187: string format = ""; moel@187: switch (sensor.SensorType) { moel@187: case SensorType.Voltage: moel@300: format = "{0:F3} V"; moel@187: break; moel@187: case SensorType.Clock: moel@187: format = "{0:F0} MHz"; moel@187: break; moel@187: case SensorType.Temperature: moel@187: format = "{0:F1} °C"; moel@187: break; moel@187: case SensorType.Fan: moel@187: format = "{0:F0} RPM"; moel@187: break; moel@187: case SensorType.Flow: moel@187: format = "{0:F0} L/h"; moel@187: break; moel@318: case SensorType.Power: moel@318: format = "{0:F1} W"; moel@318: break; moel@324: case SensorType.Data: moel@324: format = "{0:F1} GB"; moel@324: break; moel@187: } moel@187: moel@187: if (sensor.SensorType == SensorType.Temperature && moel@187: unitManager.TemperatureUnit == TemperatureUnit.Fahrenheit) { moel@187: formatted = string.Format("{0:F1} °F", moel@187: sensor.Value * 1.8 + 32); moel@187: } else { moel@187: formatted = string.Format(format, sensor.Value); moel@187: } moel@187: } else { moel@187: formatted = "-"; moel@176: } moel@176: moel@187: g.DrawString(formatted, smallFont, darkWhite, moel@187: new RectangleF(-1, y - 1, w - rightMargin + 3, 0), moel@181: alignRightStringFormat); moel@181: moel@187: remainingWidth = w - (int)Math.Floor(g.MeasureString(formatted, moel@187: smallFont, w, StringFormat.GenericTypographic).Width) - moel@181: rightMargin; moel@187: } else { moel@187: DrawProgress(g, w - progressWidth - rightMargin, moel@243: y + 0.35f * sensorLineHeight, progressWidth, moel@243: 0.6f * sensorLineHeight, 0.01f * sensor.Value.Value); moel@183: moel@183: remainingWidth = w - progressWidth - rightMargin; moel@176: } moel@187: moel@183: remainingWidth -= leftMargin + 2; moel@183: if (remainingWidth > 0) { moel@183: g.DrawString(sensor.Name, smallFont, darkWhite, moel@183: new RectangleF(leftMargin - 1, y - 1, remainingWidth, 0), moel@183: trimStringFormat); moel@183: } moel@181: moel@176: y += sensorLineHeight; moel@176: } moel@176: } moel@176: } moel@176: moel@176: private class HardwareComparer : IComparer { moel@176: public int Compare(IHardware x, IHardware y) { moel@176: if (x == null && y == null) moel@176: return 0; moel@176: if (x == null) moel@176: return -1; moel@176: if (y == null) moel@176: return 1; moel@176: moel@176: if (x.HardwareType != y.HardwareType) moel@176: return x.HardwareType.CompareTo(y.HardwareType); moel@176: moel@184: return x.Identifier.CompareTo(y.Identifier); moel@176: } moel@176: } moel@176: } moel@176: } moel@176: