moel@40: /*
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 <m.moeller@gmx.ch>.
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.Collections.Generic;
moel@40: using System.Drawing;
moel@40: using System.Drawing.Drawing2D;
moel@40: using System.Drawing.Imaging;
moel@40: using System.Drawing.Text;
moel@40: using System.Text;
moel@40: using System.Windows.Forms;
moel@40: using System.Runtime.InteropServices;
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@42:     public SensorNotifyIcon(SensorSystemTray sensorSystemTray, ISensor sensor,
moel@42:       bool balloonTip) 
moel@42:     {
moel@40:       this.sensor = sensor;
moel@40:       this.notifyIcon = new NotifyIcon();
moel@43: 
moel@43:       Color defaultColor = Color.Black;
moel@43:       if (sensor.SensorType == SensorType.Load) {
moel@43:         defaultColor = Color.FromArgb(0xff, 0x70, 0x8c, 0xf1);
moel@43:       }
moel@43:       Color = Config.Get(sensor.Identifier + "/traycolor", defaultColor);      
moel@40:       
moel@43:       this.pen = new Pen(Color.FromArgb(96, Color.Black));
moel@72:       this.font = new Font(SystemFonts.MessageBoxFont.FontFamily, 9);
moel@43: 
moel@40:       ContextMenuStrip contextMenuStrip = new ContextMenuStrip();
moel@42:       ToolStripMenuItem removeItem = new ToolStripMenuItem("Remove");
moel@42:       removeItem.Click += delegate(object obj, EventArgs args) {
moel@42:         sensorSystemTray.Remove(this.sensor);
moel@40:       };
moel@42:       contextMenuStrip.Items.Add(removeItem);
moel@42:       ToolStripMenuItem colorItem = new ToolStripMenuItem("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@43:           Config.Set(sensor.Identifier + "/traycolor", Color);
moel@42:         }
moel@42:       };
moel@42:       contextMenuStrip.Items.Add(colorItem);
moel@40:       this.notifyIcon.ContextMenuStrip = contextMenuStrip;
moel@40: 
moel@40:       this.bitmap = new Bitmap(16, 16, PixelFormat.Format32bppArgb);
moel@40:       this.graphics = Graphics.FromImage(this.bitmap);
moel@40:       this.graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
moel@40:       this.graphics.SmoothingMode = SmoothingMode.HighQuality;
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@70:       font.Dispose();
moel@85:       graphics.Dispose();      
moel@85:       bitmap.Dispose();      
moel@40:     }
moel@40: 
moel@40:     private string GetString() {
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@40:           return string.Format("{0:F0}", sensor.Value < 99 ? sensor.Value : 99);
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@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@40:         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:       int stride = data.Stride;
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@40:       return IconFactory.Create(bytes, 16, 16, PixelFormat.Format32bppArgb);
moel@40:     }
moel@40: 
moel@86:     private Icon CreateLoadIcon() {      
moel@86:       try {
moel@86:         graphics.Clear(Color.Transparent);
moel@86:       } catch (ArgumentException) {
moel@86:         graphics.Clear(Color.Black);
moel@86:       }
moel@43:       graphics.FillRectangle(darkBrush, 0.5f, -0.5f, 14, 16);
moel@43:       float y = 0.16f * (100 - sensor.Value.Value);
moel@43:       graphics.FillRectangle(brush, 0.5f, -0.5f + y, 14, 16 - y);
moel@43:       graphics.DrawRectangle(pen, 1, 0, 13, 15);
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@43:       return IconFactory.Create(bytes, 16, 16, PixelFormat.Format32bppArgb);
moel@43:     }
moel@43: 
moel@40:     public void Update() {
moel@40:       Icon icon = notifyIcon.Icon;
moel@40: 
moel@43:       if (sensor.SensorType == SensorType.Load) {
moel@43:         notifyIcon.Icon = CreateLoadIcon();
moel@40:       } else {
moel@40:         notifyIcon.Icon = CreateTransparentIcon();
moel@40:       }
moel@40:       if (icon != null) 
moel@40:         icon.Dispose();
moel@40: 
moel@40:       string format = "";
moel@40:       switch (sensor.SensorType) {
moel@40:         case SensorType.Voltage: format = "{0}\n{1}: {2:F2} V"; break;
moel@40:         case SensorType.Clock: format = "{0}\n{1}: {2:F0} MHz"; break;
moel@40:         case SensorType.Load: format = "{0}\n{1}: {2:F1} %"; break;
moel@40:         case SensorType.Temperature: format = "{0}\n{1}: {2:F1} °C"; break;
moel@40:         case SensorType.Fan: format = "{0}\n{1}: {2:F0} RPM"; break;
moel@57:         case SensorType.Flow: format = "{0}\n{1}: {2:F0} L/h"; break;
moel@40:       }
moel@40: 
moel@42:       notifyIcon.Text = string.Format(format, sensor.Hardware.Name, sensor.Name,
moel@42:         sensor.Value);    
moel@42:       notifyIcon.Visible = true;         
moel@40:     }
moel@40:   }
moel@40: }