moel@202: /* moel@40: moel@40: Version: MPL 1.1/GPL 2.0/LGPL 2.1 moel@40: moel@40: The contents of this file are subject to the Mozilla Public License Version moel@40: 1.1 (the "License"); you may not use this file except in compliance with moel@40: the License. You may obtain a copy of the License at moel@40: moel@40: http://www.mozilla.org/MPL/ moel@40: moel@40: Software distributed under the License is distributed on an "AS IS" basis, moel@40: WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License moel@40: for the specific language governing rights and limitations under the License. moel@40: moel@40: The Original Code is the Open Hardware Monitor code. moel@40: moel@40: The Initial Developer of the Original Code is moel@40: Michael Möller . moel@40: Portions created by the Initial Developer are Copyright (C) 2009-2010 moel@40: the Initial Developer. All Rights Reserved. moel@40: moel@40: Contributor(s): moel@40: moel@40: Alternatively, the contents of this file may be used under the terms of moel@40: either the GNU General Public License Version 2 or later (the "GPL"), or moel@40: the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), moel@40: in which case the provisions of the GPL or the LGPL are applicable instead moel@40: of those above. If you wish to allow use of your version of this file only moel@40: under the terms of either the GPL or the LGPL, and not to allow others to moel@40: use your version of this file under the terms of the MPL, indicate your moel@40: decision by deleting the provisions above and replace them with the notice moel@40: and other provisions required by the GPL or the LGPL. If you do not delete moel@40: the provisions above, a recipient may use your version of this file under moel@40: the terms of any one of the MPL, the GPL or the LGPL. moel@40: moel@40: */ moel@40: moel@40: using System; moel@40: using System.Drawing; moel@40: using System.Drawing.Drawing2D; moel@40: using System.Drawing.Imaging; moel@40: using System.Drawing.Text; moel@176: using System.Runtime.InteropServices; moel@40: using System.Windows.Forms; moel@40: using OpenHardwareMonitor.Hardware; moel@40: using OpenHardwareMonitor.Utilities; moel@40: moel@40: namespace OpenHardwareMonitor.GUI { moel@40: public class SensorNotifyIcon : IDisposable { moel@40: moel@40: private ISensor sensor; moel@40: private NotifyIcon notifyIcon; moel@40: private Bitmap bitmap; moel@40: private Graphics graphics; moel@42: private Color color; moel@43: private Color darkColor; moel@43: private Brush brush; moel@43: private Brush darkBrush; moel@43: private Pen pen; moel@70: private Font font; moel@40: moel@133: public SensorNotifyIcon(SystemTray sensorSystemTray, ISensor sensor, moel@165: bool balloonTip, PersistentSettings settings) moel@42: { moel@40: this.sensor = sensor; moel@40: this.notifyIcon = new NotifyIcon(); moel@43: moel@43: Color defaultColor = Color.Black; moel@217: if (sensor.SensorType == SensorType.Load || moel@217: sensor.SensorType == SensorType.Control || moel@217: sensor.SensorType == SensorType.Level) moel@217: { moel@43: defaultColor = Color.FromArgb(0xff, 0x70, 0x8c, 0xf1); moel@43: } moel@166: Color = settings.GetValue(new Identifier(sensor.Identifier, moel@109: "traycolor").ToString(), defaultColor); moel@40: moel@43: this.pen = new Pen(Color.FromArgb(96, Color.Black)); moel@117: this.font = SystemFonts.MessageBoxFont; moel@43: moel@156: ContextMenu contextMenu = new ContextMenu(); moel@156: MenuItem hideShowItem = new MenuItem("Hide/Show"); moel@133: hideShowItem.Click += delegate(object obj, EventArgs args) { moel@133: sensorSystemTray.SendHideShowCommand(); moel@133: }; moel@156: contextMenu.MenuItems.Add(hideShowItem); moel@156: contextMenu.MenuItems.Add(new MenuItem("-")); moel@156: MenuItem removeItem = new MenuItem("Remove Sensor"); moel@42: removeItem.Click += delegate(object obj, EventArgs args) { moel@42: sensorSystemTray.Remove(this.sensor); moel@40: }; moel@156: contextMenu.MenuItems.Add(removeItem); moel@156: MenuItem colorItem = new MenuItem("Change Color..."); moel@42: colorItem.Click += delegate(object obj, EventArgs args) { moel@42: ColorDialog dialog = new ColorDialog(); moel@43: dialog.Color = Color; moel@42: if (dialog.ShowDialog() == DialogResult.OK) { moel@43: Color = dialog.Color; moel@166: settings.SetValue(new Identifier(sensor.Identifier, moel@109: "traycolor").ToString(), Color); moel@42: } moel@42: }; moel@156: contextMenu.MenuItems.Add(colorItem); moel@156: contextMenu.MenuItems.Add(new MenuItem("-")); moel@156: MenuItem exitItem = new MenuItem("Exit"); moel@133: exitItem.Click += delegate(object obj, EventArgs args) { moel@133: sensorSystemTray.SendExitCommand(); moel@133: }; moel@156: contextMenu.MenuItems.Add(exitItem); moel@156: this.notifyIcon.ContextMenu = contextMenu; moel@133: this.notifyIcon.DoubleClick += delegate(object obj, EventArgs args) { moel@133: sensorSystemTray.SendHideShowCommand(); moel@133: }; moel@40: moel@252: // get the default dpi to create an icon with the correct size moel@252: float dpiX, dpiY; moel@252: using (Bitmap b = new Bitmap(1, 1, PixelFormat.Format32bppArgb)) { moel@252: dpiX = b.HorizontalResolution; moel@252: dpiY = b.VerticalResolution; moel@252: } moel@252: moel@252: // adjust the size of the icon to current dpi (default is 16x16 at 96 dpi) moel@252: int width = (int)Math.Round(16 * dpiX / 96); moel@252: int height = (int)Math.Round(16 * dpiY / 96); moel@252: moel@252: // make sure it does never get smaller than 16x16 moel@252: width = width < 16 ? 16: width; moel@252: height = height < 16 ? 16: height; moel@252: moel@252: this.bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb); moel@40: this.graphics = Graphics.FromImage(this.bitmap); moel@117: moel@117: if (Environment.OSVersion.Version.Major > 5) { moel@117: this.graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; moel@117: this.graphics.SmoothingMode = SmoothingMode.HighQuality; moel@117: } moel@40: } moel@40: moel@40: public ISensor Sensor { moel@40: get { return sensor; } moel@40: } moel@40: moel@42: public Color Color { moel@42: get { return color; } moel@43: set { moel@43: this.color = value; moel@43: this.darkColor = Color.FromArgb(255, moel@44: this.color.R / 3, moel@44: this.color.G / 3, moel@44: this.color.B / 3); moel@43: Brush brush = this.brush; moel@43: this.brush = new SolidBrush(this.color); moel@43: if (brush != null) moel@43: brush.Dispose(); moel@43: Brush darkBrush = this.darkBrush; moel@43: this.darkBrush = new SolidBrush(this.darkColor); moel@43: if (darkBrush != null) moel@43: darkBrush.Dispose(); moel@43: } moel@42: } moel@42: moel@40: public void Dispose() { moel@40: Icon icon = notifyIcon.Icon; moel@40: notifyIcon.Icon = null; moel@40: if (icon != null) moel@40: icon.Dispose(); moel@40: notifyIcon.Dispose(); moel@40: moel@43: if (brush != null) moel@43: brush.Dispose(); moel@43: if (darkBrush != null) moel@43: darkBrush.Dispose(); moel@43: pen.Dispose(); moel@85: graphics.Dispose(); moel@85: bitmap.Dispose(); moel@40: } moel@40: moel@40: private string GetString() { moel@282: if (!sensor.Value.HasValue) moel@282: return "-"; moel@282: moel@40: switch (sensor.SensorType) { moel@40: case SensorType.Voltage: moel@40: return string.Format("{0:F11}", sensor.Value); moel@40: case SensorType.Clock: moel@40: return string.Format("{0:F11}", 1e-3f * sensor.Value); moel@40: case SensorType.Load: moel@118: return string.Format("{0:F0}", sensor.Value); moel@40: case SensorType.Temperature: moel@40: return string.Format("{0:F0}", sensor.Value); moel@40: case SensorType.Fan: moel@40: return string.Format("{0:F11}", 1e-3f * sensor.Value); moel@57: case SensorType.Flow: moel@57: return string.Format("{0:F11}", 1e-3f * sensor.Value); moel@118: case SensorType.Control: moel@118: return string.Format("{0:F0}", sensor.Value); moel@217: case SensorType.Level: moel@217: return string.Format("{0:F0}", sensor.Value); moel@40: } moel@40: return "-"; moel@40: } moel@40: moel@40: private Icon CreateTransparentIcon() { moel@40: moel@40: graphics.Clear(Color.Black); moel@70: TextRenderer.DrawText(graphics, GetString(), font, moel@117: new Point(-2, 0), Color.White, Color.Black); moel@40: moel@40: BitmapData data = bitmap.LockBits( moel@40: new Rectangle(0, 0, bitmap.Width, bitmap.Height), moel@40: ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); moel@40: moel@40: IntPtr Scan0 = data.Scan0; moel@40: moel@40: int numBytes = bitmap.Width * bitmap.Height * 4; moel@40: byte[] bytes = new byte[numBytes]; moel@40: Marshal.Copy(Scan0, bytes, 0, numBytes); moel@40: bitmap.UnlockBits(data); moel@40: moel@40: byte red, green, blue; moel@40: for (int i = 0; i < bytes.Length; i += 4) { moel@40: blue = bytes[i]; moel@40: green = bytes[i + 1]; moel@40: red = bytes[i + 2]; moel@40: moel@42: bytes[i] = color.B; moel@42: bytes[i + 1] = color.G; moel@42: bytes[i + 2] = color.R; moel@40: bytes[i + 3] = (byte)(0.3 * red + 0.59 * green + 0.11 * blue); moel@40: } moel@40: moel@252: return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, moel@252: PixelFormat.Format32bppArgb); moel@40: } moel@40: moel@217: private Icon CreatePercentageIcon() { moel@86: try { moel@86: graphics.Clear(Color.Transparent); moel@86: } catch (ArgumentException) { moel@86: graphics.Clear(Color.Black); moel@86: } moel@252: graphics.FillRectangle(darkBrush, 0.5f, -0.5f, bitmap.Width - 2, bitmap.Height); moel@282: float value = sensor.Value.GetValueOrDefault(); moel@282: float y = 0.16f * (100 - value); moel@252: graphics.FillRectangle(brush, 0.5f, -0.5f + y, bitmap.Width - 2, bitmap.Height - y); moel@252: graphics.DrawRectangle(pen, 1, 0, bitmap.Width - 3, bitmap.Height - 1); moel@43: moel@43: BitmapData data = bitmap.LockBits( moel@43: new Rectangle(0, 0, bitmap.Width, bitmap.Height), moel@43: ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); moel@43: byte[] bytes = new byte[bitmap.Width * bitmap.Height * 4]; moel@43: Marshal.Copy(data.Scan0, bytes, 0, bytes.Length); moel@43: bitmap.UnlockBits(data); moel@43: moel@252: return IconFactory.Create(bytes, bitmap.Width, bitmap.Height, moel@252: PixelFormat.Format32bppArgb); moel@43: } moel@43: moel@40: public void Update() { moel@40: Icon icon = notifyIcon.Icon; moel@40: moel@217: switch (sensor.SensorType) { moel@217: case SensorType.Load: moel@217: case SensorType.Control: moel@217: case SensorType.Level: moel@217: notifyIcon.Icon = CreatePercentageIcon(); moel@217: break; moel@217: default: moel@217: notifyIcon.Icon = CreateTransparentIcon(); moel@217: break; moel@40: } moel@217: moel@40: if (icon != null) moel@40: icon.Dispose(); moel@40: moel@40: string format = ""; moel@40: switch (sensor.SensorType) { moel@116: case SensorType.Voltage: format = "\n{0}: {1:F2} V"; break; moel@116: case SensorType.Clock: format = "\n{0}: {1:F0} MHz"; break; moel@116: case SensorType.Load: format = "\n{0}: {1:F1} %"; break; moel@116: case SensorType.Temperature: format = "\n{0}: {1:F1} °C"; break; moel@116: case SensorType.Fan: format = "\n{0}: {1:F0} RPM"; break; moel@116: case SensorType.Flow: format = "\n{0}: {1:F0} L/h"; break; moel@118: case SensorType.Control: format = "\n{0}: {1:F1} %"; break; moel@217: case SensorType.Level: format = "\n{0}: {1:F1} %"; break; moel@40: } moel@116: string formattedValue = string.Format(format, sensor.Name, sensor.Value); moel@116: string hardwareName = sensor.Hardware.Name; moel@116: hardwareName = hardwareName.Substring(0, moel@116: Math.Min(63 - formattedValue.Length, hardwareName.Length)); moel@116: string text = hardwareName + formattedValue; moel@116: if (text.Length > 63) moel@116: text = null; moel@40: moel@116: notifyIcon.Text = text; moel@42: notifyIcon.Visible = true; moel@40: } moel@40: } moel@40: }