GUI/MainForm.cs
author moel.mich
Sat, 31 Dec 2011 17:31:04 +0000
changeset 324 c6ee430d6995
parent 299 072989c22a87
child 326 a41745e3828d
permissions -rw-r--r--
Modified and extended version of the patch v4 by Roland Reinl (see Issue 256). Main differences to the original patch: DeviceIoControl refactorings removed, SmartAttribute is now descriptive only and does not hold any state, report is written as one 80 columns table, sensors are created only for meaningful values and without duplicates (remaining life, temperatures, host writes and reads). Also the current implementation should really preserve all the functionality of the old system. Additionally there is now a simple SMART devices emulation class (DebugSmart) that can be used in place of WindowsSmart for testing with reported data.
     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) 2009-2011
    20   the Initial Developer. All Rights Reserved.
    21 
    22   Contributor(s): Paul Werelds
    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.ComponentModel;
    41 using System.Drawing;
    42 using System.IO;
    43 using System.Reflection;
    44 using System.Windows.Forms;
    45 using Aga.Controls.Tree;
    46 using Aga.Controls.Tree.NodeControls;
    47 using OpenHardwareMonitor.Hardware;
    48 using OpenHardwareMonitor.WMI;
    49 
    50 namespace OpenHardwareMonitor.GUI {
    51   public partial class MainForm : Form {
    52 
    53     private PersistentSettings settings;
    54     private UnitManager unitManager;
    55     private Computer computer;
    56     private Node root;
    57     private TreeModel treeModel;
    58     private IDictionary<ISensor, Color> sensorPlotColors = 
    59       new Dictionary<ISensor, Color>();
    60     private Color[] plotColorPalette;
    61     private SystemTray systemTray;    
    62     private StartupManager startupManager = new StartupManager();
    63     private UpdateVisitor updateVisitor = new UpdateVisitor();
    64     private SensorGadget gadget;
    65     private Form plotForm;
    66 
    67     private UserOption showHiddenSensors;
    68     private UserOption showPlot;
    69     private UserOption showValue;
    70     private UserOption showMin;
    71     private UserOption showMax;
    72     private UserOption startMinimized;
    73     private UserOption minimizeToTray;
    74     private UserOption minimizeOnClose;
    75     private UserOption autoStart;
    76     private UserOption readHddSensors;
    77     private UserOption showGadget;
    78     private UserRadioGroup plotLocation;
    79 
    80     private WmiProvider wmiProvider;
    81 
    82     private bool selectionDragging = false;
    83 
    84     public MainForm() {      
    85       InitializeComponent();
    86 
    87       // check if the OpenHardwareMonitorLib assembly has the correct version
    88       if (Assembly.GetAssembly(typeof(Computer)).GetName().Version !=
    89         Assembly.GetExecutingAssembly().GetName().Version) {
    90         MessageBox.Show(
    91           "The version of the file OpenHardwareMonitorLib.dll is incompatible.",
    92           "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    93         Environment.Exit(0);
    94       }
    95 
    96       this.settings = new PersistentSettings();      
    97       this.settings.Load(Path.ChangeExtension(
    98         Application.ExecutablePath, ".config"));
    99 
   100       this.unitManager = new UnitManager(settings);
   101 
   102       // make sure the buffers used for double buffering are not disposed 
   103       // after each draw call
   104       BufferedGraphicsManager.Current.MaximumBuffer =
   105         Screen.PrimaryScreen.Bounds.Size;  
   106 
   107       // set the DockStyle here, to avoid conflicts with the MainMenu
   108       this.splitContainer.Dock = DockStyle.Fill;
   109             
   110       this.Font = SystemFonts.MessageBoxFont;
   111       treeView.Font = SystemFonts.MessageBoxFont;
   112       plotPanel.Font = SystemFonts.MessageBoxFont;           
   113       
   114       nodeCheckBox.IsVisibleValueNeeded += nodeCheckBox_IsVisibleValueNeeded;
   115       nodeCheckBox.CheckStateChanged += UpdatePlotSelection;
   116       nodeTextBoxText.DrawText += nodeTextBoxText_DrawText;
   117       nodeTextBoxValue.DrawText += nodeTextBoxText_DrawText;
   118       nodeTextBoxMin.DrawText += nodeTextBoxText_DrawText;
   119       nodeTextBoxMax.DrawText += nodeTextBoxText_DrawText;
   120       nodeTextBoxText.EditorShowing += nodeTextBoxText_EditorShowing;
   121 
   122       foreach (TreeColumn column in treeView.Columns) 
   123         column.Width = Math.Max(20, Math.Min(400,
   124           settings.GetValue("treeView.Columns." + column.Header + ".Width",
   125           column.Width)));
   126 
   127       treeModel = new TreeModel();
   128       root = new Node(System.Environment.MachineName);
   129       root.Image = Utilities.EmbeddedResources.GetImage("computer.png");
   130       
   131       treeModel.Nodes.Add(root);
   132       treeView.Model = treeModel;
   133 
   134       this.computer = new Computer(settings);
   135 
   136       systemTray = new SystemTray(computer, settings);
   137       systemTray.HideShowCommand += hideShowClick;
   138       systemTray.ExitCommand += exitClick;
   139 
   140       int p = (int)Environment.OSVersion.Platform;
   141       if ((p == 4) || (p == 128)) { // Unix
   142         treeView.RowHeight = Math.Max(treeView.RowHeight, 18); 
   143         splitContainer.BorderStyle = BorderStyle.None;
   144         splitContainer.Border3DStyle = Border3DStyle.Adjust;
   145         splitContainer.SplitterWidth = 4;
   146         treeView.BorderStyle = BorderStyle.Fixed3D;
   147         plotPanel.BorderStyle = BorderStyle.Fixed3D;
   148         gadgetMenuItem.Visible = false;
   149         minCloseMenuItem.Visible = false;
   150         minTrayMenuItem.Visible = false;
   151         startMinMenuItem.Visible = false;
   152       } else { // Windows
   153         treeView.RowHeight = Math.Max(treeView.Font.Height + 1, 18); 
   154 
   155         gadget = new SensorGadget(computer, settings, unitManager);
   156         gadget.HideShowCommand += hideShowClick;
   157 
   158         wmiProvider = new WmiProvider(computer);
   159       }    
   160       
   161       computer.HardwareAdded += new HardwareEventHandler(HardwareAdded);
   162       computer.HardwareRemoved += new HardwareEventHandler(HardwareRemoved);        
   163 
   164       computer.Open();
   165 
   166       timer.Enabled = true;
   167 
   168       plotColorPalette = new Color[13];
   169       plotColorPalette[0] = Color.Blue;
   170       plotColorPalette[1] = Color.OrangeRed;
   171       plotColorPalette[2] = Color.Green;
   172       plotColorPalette[3] = Color.LightSeaGreen;
   173       plotColorPalette[4] = Color.Goldenrod;
   174       plotColorPalette[5] = Color.DarkViolet;
   175       plotColorPalette[6] = Color.YellowGreen;
   176       plotColorPalette[7] = Color.SaddleBrown;
   177       plotColorPalette[8] = Color.RoyalBlue;
   178       plotColorPalette[9] = Color.DeepPink;
   179       plotColorPalette[10] = Color.MediumSeaGreen;
   180       plotColorPalette[11] = Color.Olive;
   181       plotColorPalette[12] = Color.Firebrick;
   182 
   183       showHiddenSensors = new UserOption("hiddenMenuItem", false,
   184         hiddenMenuItem, settings);
   185       showHiddenSensors.Changed += delegate(object sender, EventArgs e) {
   186         treeModel.ForceVisible = showHiddenSensors.Value;
   187       };
   188 
   189       showValue = new UserOption("valueMenuItem", true, valueMenuItem,
   190         settings);
   191       showValue.Changed += delegate(object sender, EventArgs e) {
   192         treeView.Columns[1].IsVisible = showValue.Value;
   193       };
   194 
   195       showMin = new UserOption("minMenuItem", false, minMenuItem, settings);
   196       showMin.Changed += delegate(object sender, EventArgs e) {
   197         treeView.Columns[2].IsVisible = showMin.Value;
   198       };
   199 
   200       showMax = new UserOption("maxMenuItem", true, maxMenuItem, settings);
   201       showMax.Changed += delegate(object sender, EventArgs e) {
   202         treeView.Columns[3].IsVisible = showMax.Value;
   203       };
   204 
   205       startMinimized = new UserOption("startMinMenuItem", false,
   206         startMinMenuItem, settings);
   207 
   208       minimizeToTray = new UserOption("minTrayMenuItem", true,
   209         minTrayMenuItem, settings);
   210       minimizeToTray.Changed += delegate(object sender, EventArgs e) {
   211         systemTray.IsMainIconEnabled = minimizeToTray.Value;
   212       };
   213 
   214       minimizeOnClose = new UserOption("minCloseMenuItem", false,
   215         minCloseMenuItem, settings);
   216 
   217       autoStart = new UserOption(null, startupManager.Startup,
   218         startupMenuItem, settings);
   219       autoStart.Changed += delegate(object sender, EventArgs e) {
   220         try {
   221           startupManager.Startup = autoStart.Value;
   222         } catch (InvalidOperationException) {
   223           MessageBox.Show("Updating the auto-startup option failed.", "Error", 
   224             MessageBoxButtons.OK, MessageBoxIcon.Error);
   225           autoStart.Value = startupManager.Startup;
   226         }
   227       };
   228 
   229       readHddSensors = new UserOption("hddMenuItem", true, hddMenuItem,
   230         settings);
   231       readHddSensors.Changed += delegate(object sender, EventArgs e) {
   232         computer.HDDEnabled = readHddSensors.Value;
   233         UpdatePlotSelection(null, null);
   234       };
   235 
   236       showGadget = new UserOption("gadgetMenuItem", false, gadgetMenuItem,
   237         settings);
   238       showGadget.Changed += delegate(object sender, EventArgs e) {
   239         if (gadget != null) 
   240           gadget.Visible = showGadget.Value;
   241       };
   242 
   243       celsiusMenuItem.Checked = 
   244         unitManager.TemperatureUnit == TemperatureUnit.Celsius;
   245       fahrenheitMenuItem.Checked = !celsiusMenuItem.Checked;
   246 
   247       InitializePlotForm();
   248 
   249       startupMenuItem.Visible = startupManager.IsAvailable;
   250       
   251       if (startMinMenuItem.Checked) {
   252         if (!minTrayMenuItem.Checked) {
   253           WindowState = FormWindowState.Minimized;
   254           Show();
   255         }
   256       } else {
   257         Show();
   258       }
   259 
   260       // Create a handle, otherwise calling Close() does not fire FormClosed     
   261       IntPtr handle = Handle;
   262 
   263       // Make sure the settings are saved when the user logs off
   264       Microsoft.Win32.SystemEvents.SessionEnded += delegate {
   265         SaveConfiguration();
   266       };
   267     }
   268 
   269     private void InitializePlotForm() {
   270       plotForm = new Form();
   271       plotForm.FormBorderStyle = FormBorderStyle.SizableToolWindow;
   272       plotForm.ShowInTaskbar = false;
   273       plotForm.StartPosition = FormStartPosition.Manual;
   274       this.AddOwnedForm(plotForm);
   275       plotForm.Bounds = new Rectangle {
   276         X = settings.GetValue("plotForm.Location.X", -100000),
   277         Y = settings.GetValue("plotForm.Location.Y", 100),
   278         Width = settings.GetValue("plotForm.Width", 600),
   279         Height = settings.GetValue("plotForm.Height", 400)
   280       };
   281 
   282       showPlot = new UserOption("plotMenuItem", false, plotMenuItem, settings);
   283       plotLocation = new UserRadioGroup("plotLocation", 0,
   284         new[] { plotWindowMenuItem, plotBottomMenuItem, plotRightMenuItem },
   285         settings);
   286 
   287       showPlot.Changed += delegate(object sender, EventArgs e) {
   288         if (plotLocation.Value == 0) {
   289           if (showPlot.Value && this.Visible)
   290             plotForm.Show();
   291           else
   292             plotForm.Hide();
   293         } else {
   294           splitContainer.Panel2Collapsed = !showPlot.Value;
   295         }
   296         treeView.Invalidate();
   297       };
   298       plotLocation.Changed += delegate(object sender, EventArgs e) {
   299         switch (plotLocation.Value) {
   300           case 0:
   301             splitContainer.Panel2.Controls.Clear();
   302             splitContainer.Panel2Collapsed = true;
   303             plotForm.Controls.Add(plotPanel);
   304             if (showPlot.Value && this.Visible)
   305               plotForm.Show();
   306             break;
   307           case 1:
   308             plotForm.Controls.Clear();
   309             plotForm.Hide();
   310             splitContainer.Orientation = Orientation.Horizontal;
   311             splitContainer.Panel2.Controls.Add(plotPanel);
   312             splitContainer.Panel2Collapsed = !showPlot.Value;
   313             break;
   314           case 2:
   315             plotForm.Controls.Clear();
   316             plotForm.Hide();
   317             splitContainer.Orientation = Orientation.Vertical;
   318             splitContainer.Panel2.Controls.Add(plotPanel);
   319             splitContainer.Panel2Collapsed = !showPlot.Value;
   320             break;
   321         }
   322       };
   323 
   324       plotForm.FormClosing += delegate(object sender, FormClosingEventArgs e) {
   325         if (e.CloseReason == CloseReason.UserClosing) {
   326           // just switch off the plotting when the user closes the form
   327           if (plotLocation.Value == 0) {
   328             showPlot.Value = false;
   329           }
   330           e.Cancel = true;
   331         }
   332       };
   333 
   334       EventHandler moveOrResizePlotForm = delegate(object sender, EventArgs e) {
   335         if (plotForm.WindowState != FormWindowState.Minimized) {
   336           settings.SetValue("plotForm.Location.X", plotForm.Bounds.X);
   337           settings.SetValue("plotForm.Location.Y", plotForm.Bounds.Y);
   338           settings.SetValue("plotForm.Width", plotForm.Bounds.Width);
   339           settings.SetValue("plotForm.Height", plotForm.Bounds.Height);
   340         }
   341       };
   342       plotForm.Move += moveOrResizePlotForm;
   343       plotForm.Resize += moveOrResizePlotForm;
   344 
   345       plotForm.VisibleChanged += delegate(object sender, EventArgs e) {
   346         Rectangle bounds = new Rectangle(plotForm.Location, plotForm.Size);
   347         Screen screen = Screen.FromRectangle(bounds);
   348         Rectangle intersection =
   349           Rectangle.Intersect(screen.WorkingArea, bounds);
   350         if (intersection.Width < Math.Min(16, bounds.Width) ||
   351             intersection.Height < Math.Min(16, bounds.Height)) {
   352           plotForm.Location = new Point(
   353             screen.WorkingArea.Width / 2 - bounds.Width / 2,
   354             screen.WorkingArea.Height / 2 - bounds.Height / 2);
   355         }
   356       };
   357 
   358       this.VisibleChanged += delegate(object sender, EventArgs e) {
   359         if (this.Visible && showPlot.Value && plotLocation.Value == 0)
   360           plotForm.Show();
   361         else
   362           plotForm.Hide();
   363       };
   364     }
   365     
   366     private void SubHardwareAdded(IHardware hardware, Node node) {
   367       Node hardwareNode = new HardwareNode(hardware, settings, unitManager);
   368       node.Nodes.Add(hardwareNode);
   369       foreach (IHardware subHardware in hardware.SubHardware)
   370         SubHardwareAdded(subHardware, hardwareNode);  
   371     }
   372 
   373     private void HardwareAdded(IHardware hardware) {
   374       Node hardwareNode = new HardwareNode(hardware, settings, unitManager);
   375       root.Nodes.Add(hardwareNode);
   376       foreach (IHardware subHardware in hardware.SubHardware)
   377         SubHardwareAdded(subHardware, hardwareNode);     
   378     }
   379 
   380     private void HardwareRemoved(IHardware hardware) {      
   381       List<Node> nodesToRemove = new List<Node>();
   382       foreach (Node node in root.Nodes) {
   383         HardwareNode hardwareNode = node as HardwareNode;
   384         if (hardwareNode != null && hardwareNode.Hardware == hardware)
   385           nodesToRemove.Add(node);
   386       }
   387       foreach (Node node in nodesToRemove)
   388         root.Nodes.Remove(node);
   389     }
   390 
   391     private void nodeTextBoxText_DrawText(object sender, DrawEventArgs e) {       
   392       Node node = e.Node.Tag as Node;
   393       if (node != null) {
   394         Color color;
   395         if (node.IsVisible) {
   396           SensorNode sensorNode = node as SensorNode;
   397           if (plotMenuItem.Checked && sensorNode != null &&
   398             sensorPlotColors.TryGetValue(sensorNode.Sensor, out color))
   399             e.TextColor = color;
   400         } else {
   401           e.TextColor = Color.DarkGray;
   402         }
   403       }
   404     }
   405 
   406     private void UpdatePlotSelection(object sender, 
   407       TreePathEventArgs e) 
   408     {
   409       List<ISensor> selected = new List<ISensor>();
   410       IDictionary<ISensor, Color> colors = new Dictionary<ISensor, Color>();
   411       int colorIndex = 0;
   412       foreach (TreeNodeAdv node in treeView.AllNodes) {
   413         SensorNode sensorNode = node.Tag as SensorNode;
   414         if (sensorNode != null && 
   415           sensorNode.Sensor.SensorType == SensorType.Temperature) {
   416           if (sensorNode.Plot) {
   417             colors.Add(sensorNode.Sensor,
   418               plotColorPalette[colorIndex % plotColorPalette.Length]);
   419             selected.Add(sensorNode.Sensor);
   420           }
   421           colorIndex++;
   422         }
   423       }
   424       sensorPlotColors = colors;
   425       plotPanel.SetSensors(selected, colors);
   426     }
   427 
   428     private void nodeTextBoxText_EditorShowing(object sender,
   429       CancelEventArgs e) 
   430     {
   431       e.Cancel = !(treeView.CurrentNode != null &&
   432         (treeView.CurrentNode.Tag is SensorNode || 
   433          treeView.CurrentNode.Tag is HardwareNode));
   434     }
   435 
   436     private void nodeCheckBox_IsVisibleValueNeeded(object sender, 
   437       NodeControlValueEventArgs e) {
   438       SensorNode node = e.Node.Tag as SensorNode;
   439       e.Value = (node != null) && 
   440         (node.Sensor.SensorType == SensorType.Temperature) && 
   441         plotMenuItem.Checked;
   442     }
   443 
   444     private void exitClick(object sender, EventArgs e) {
   445       Close();
   446     }
   447 
   448     private void timer_Tick(object sender, EventArgs e) {
   449       computer.Accept(updateVisitor);
   450       treeView.Invalidate();
   451       plotPanel.Invalidate();
   452       systemTray.Redraw();
   453       if (gadget != null)
   454         gadget.Redraw();
   455 
   456       if (wmiProvider != null)
   457         wmiProvider.Update();
   458     }
   459 
   460     private void SaveConfiguration() {
   461       foreach (TreeColumn column in treeView.Columns)
   462         settings.SetValue("treeView.Columns." + column.Header + ".Width",
   463           column.Width);
   464 
   465       string fileName = Path.ChangeExtension(
   466           System.Windows.Forms.Application.ExecutablePath, ".config");
   467       try {
   468         settings.Save(fileName);
   469       } catch (UnauthorizedAccessException) {
   470         MessageBox.Show("Access to the path '" + fileName + "' is denied. " +
   471           "The current settings could not be saved.",
   472           "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
   473       } catch (IOException) {
   474         MessageBox.Show("The path '" + fileName + "' is not writeable. " +
   475           "The current settings could not be saved.",
   476           "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
   477       }
   478     }
   479 
   480     private void MainForm_Load(object sender, EventArgs e) {
   481       Rectangle newBounds = new Rectangle {
   482         X = settings.GetValue("mainForm.Location.X", Location.X),
   483         Y = settings.GetValue("mainForm.Location.Y", Location.Y),
   484         Width = settings.GetValue("mainForm.Width", 470),
   485         Height = settings.GetValue("mainForm.Height", 640)
   486       };
   487 
   488       Rectangle fullWorkingArea = new Rectangle(int.MaxValue, int.MaxValue,
   489         int.MinValue, int.MinValue);
   490 
   491       foreach (Screen screen in Screen.AllScreens)
   492         fullWorkingArea = Rectangle.Union(fullWorkingArea, screen.Bounds);
   493 
   494       Rectangle intersection = Rectangle.Intersect(fullWorkingArea, newBounds);
   495       if (intersection.Width < 20 || intersection.Height < 20 ||
   496         !settings.Contains("mainForm.Location.X")
   497       ) {
   498         newBounds.X = (Screen.PrimaryScreen.WorkingArea.Width / 2) -
   499                       (newBounds.Width/2);
   500 
   501         newBounds.Y = (Screen.PrimaryScreen.WorkingArea.Height / 2) -
   502                       (newBounds.Height / 2);
   503       }
   504 
   505       this.Bounds = newBounds;
   506     }
   507     
   508     private void MainForm_FormClosed(object sender, FormClosedEventArgs e) {
   509       Visible = false;      
   510       systemTray.IsMainIconEnabled = false;
   511       timer.Enabled = false;            
   512       computer.Close();
   513       SaveConfiguration();
   514       systemTray.Dispose();
   515     }
   516 
   517     private void aboutMenuItem_Click(object sender, EventArgs e) {
   518       new AboutBox().ShowDialog();
   519     }
   520 
   521     private void treeView_Click(object sender, EventArgs e) {
   522 
   523       MouseEventArgs m = e as MouseEventArgs;
   524       if (m == null || m.Button != MouseButtons.Right)
   525         return;
   526 
   527       NodeControlInfo info = treeView.GetNodeControlInfoAt(
   528         new Point(m.X, m.Y)
   529       );
   530       treeView.SelectedNode = info.Node;
   531       if (info.Node != null) {
   532         SensorNode node = info.Node.Tag as SensorNode;
   533         if (node != null && node.Sensor != null) {
   534           treeContextMenu.MenuItems.Clear();
   535           if (node.Sensor.Parameters.Length > 0) {
   536             MenuItem item = new MenuItem("Parameters...");
   537             item.Click += delegate(object obj, EventArgs args) {
   538               ShowParameterForm(node.Sensor);
   539             };
   540             treeContextMenu.MenuItems.Add(item);
   541           }
   542           if (nodeTextBoxText.EditEnabled) {
   543             MenuItem item = new MenuItem("Rename");
   544             item.Click += delegate(object obj, EventArgs args) {
   545               nodeTextBoxText.BeginEdit();
   546             };
   547             treeContextMenu.MenuItems.Add(item);
   548           }
   549           if (node.IsVisible) {
   550             MenuItem item = new MenuItem("Hide");
   551             item.Click += delegate(object obj, EventArgs args) {
   552               node.IsVisible = false;
   553             };
   554             treeContextMenu.MenuItems.Add(item);
   555           } else {
   556             MenuItem item = new MenuItem("Unhide");
   557             item.Click += delegate(object obj, EventArgs args) {
   558               node.IsVisible = true;
   559             };
   560             treeContextMenu.MenuItems.Add(item);
   561           }
   562           treeContextMenu.MenuItems.Add(new MenuItem("-"));
   563           {
   564             MenuItem item = new MenuItem("Show in Tray");
   565             item.Checked = systemTray.Contains(node.Sensor);
   566             item.Click += delegate(object obj, EventArgs args) {
   567               if (item.Checked)
   568                 systemTray.Remove(node.Sensor);
   569               else
   570                 systemTray.Add(node.Sensor, true);
   571             };
   572             treeContextMenu.MenuItems.Add(item);
   573           }
   574           if (gadget != null) {
   575             MenuItem item = new MenuItem("Show in Gadget");
   576             item.Checked = gadget.Contains(node.Sensor);
   577             item.Click += delegate(object obj, EventArgs args) {
   578               if (item.Checked) {
   579                 gadget.Remove(node.Sensor);
   580               } else {
   581                 gadget.Add(node.Sensor);
   582               }
   583             };
   584             treeContextMenu.MenuItems.Add(item);
   585           }
   586           if (node.Sensor.Control != null) {
   587             treeContextMenu.MenuItems.Add(new MenuItem("-"));
   588             IControl control = node.Sensor.Control;
   589             MenuItem controlItem = new MenuItem("Control");
   590             MenuItem defaultItem = new MenuItem("Default");
   591             defaultItem.Checked = control.ControlMode == ControlMode.Default;
   592             controlItem.MenuItems.Add(defaultItem);
   593             defaultItem.Click += delegate(object obj, EventArgs args) {
   594               control.SetDefault();
   595             };
   596             MenuItem manualItem = new MenuItem("Manual");
   597             controlItem.MenuItems.Add(manualItem);
   598             manualItem.Checked = control.ControlMode == ControlMode.Software;
   599             for (int i = 0; i <= 100; i += 5) {
   600               if (i <= control.MaxSoftwareValue &&
   601                   i >= control.MinSoftwareValue) {
   602                 MenuItem item = new MenuItem(i + " %");
   603                 manualItem.MenuItems.Add(item);
   604                 item.Checked = control.ControlMode == ControlMode.Software &&
   605                   Math.Round(control.SoftwareValue) == i;
   606                 int softwareValue = i;
   607                 item.Click += delegate(object obj, EventArgs args) {
   608                   control.SetSoftware(softwareValue);
   609                 };
   610               }
   611             }
   612             treeContextMenu.MenuItems.Add(controlItem);
   613           }
   614 
   615           treeContextMenu.Show(treeView, new Point(m.X, m.Y));
   616         }
   617 
   618         HardwareNode hardwareNode = info.Node.Tag as HardwareNode;
   619         if (hardwareNode != null && hardwareNode.Hardware != null) {
   620           treeContextMenu.MenuItems.Clear();
   621 
   622           if (nodeTextBoxText.EditEnabled) {
   623             MenuItem item = new MenuItem("Rename");
   624             item.Click += delegate(object obj, EventArgs args) {
   625               nodeTextBoxText.BeginEdit();
   626             };
   627             treeContextMenu.MenuItems.Add(item);
   628           }
   629 
   630           treeContextMenu.Show(treeView, new Point(m.X, m.Y));
   631         }
   632       }
   633     }
   634 
   635     private void saveReportMenuItem_Click(object sender, EventArgs e) {
   636       string report = computer.GetReport();
   637       if (saveFileDialog.ShowDialog() == DialogResult.OK) {
   638         using (TextWriter w = new StreamWriter(saveFileDialog.FileName)) {
   639           w.Write(report);
   640         }
   641       }
   642     }
   643 
   644     private void SysTrayHideShow() {
   645       Visible = !Visible;
   646       if (Visible)
   647         Activate();    
   648     }
   649 
   650     protected override void WndProc(ref Message m) {
   651       const int WM_SYSCOMMAND = 0x112;
   652       const int SC_MINIMIZE = 0xF020;
   653       const int SC_CLOSE = 0xF060;
   654 
   655       if (minimizeToTray.Value && 
   656         m.Msg == WM_SYSCOMMAND && m.WParam.ToInt32() == SC_MINIMIZE) {
   657         SysTrayHideShow();
   658       } else if(minimizeOnClose.Value && 
   659         m.Msg == WM_SYSCOMMAND && m.WParam.ToInt32() == SC_CLOSE) {
   660         /*
   661          * Apparently the user wants to minimize rather than close
   662          * Now we still need to check if we're going to the tray or not
   663          * 
   664          * Note: the correct way to do this would be to send out SC_MINIMIZE,
   665          * but since the code here is so simple,
   666          * that would just be a waste of time.
   667          */
   668         if (minimizeToTray.Value)
   669           SysTrayHideShow();
   670         else
   671           WindowState = FormWindowState.Minimized;
   672       } else {      
   673         base.WndProc(ref m);
   674       }
   675     }
   676 
   677     private void hideShowClick(object sender, EventArgs e) {
   678       SysTrayHideShow();
   679     }
   680 
   681     private void ShowParameterForm(ISensor sensor) {
   682       ParameterForm form = new ParameterForm();
   683       form.Parameters = sensor.Parameters;
   684       form.captionLabel.Text = sensor.Name;
   685       form.ShowDialog();
   686     }
   687 
   688     private void treeView_NodeMouseDoubleClick(object sender, 
   689       TreeNodeAdvMouseEventArgs e) {
   690       SensorNode node = e.Node.Tag as SensorNode;
   691       if (node != null && node.Sensor != null && 
   692         node.Sensor.Parameters.Length > 0) {
   693         ShowParameterForm(node.Sensor);
   694       }
   695     }
   696 
   697     private void celsiusMenuItem_Click(object sender, EventArgs e) {
   698       celsiusMenuItem.Checked = true;
   699       fahrenheitMenuItem.Checked = false;
   700       unitManager.TemperatureUnit = TemperatureUnit.Celsius;
   701     }
   702 
   703     private void fahrenheitMenuItem_Click(object sender, EventArgs e) {
   704       celsiusMenuItem.Checked = false;
   705       fahrenheitMenuItem.Checked = true;
   706       unitManager.TemperatureUnit = TemperatureUnit.Fahrenheit;
   707     }
   708 
   709     private void sumbitReportMenuItem_Click(object sender, EventArgs e) 
   710     {
   711       ReportForm form = new ReportForm();
   712       form.Report = computer.GetReport();
   713       form.ShowDialog();      
   714     }
   715 
   716     private void resetMinMaxMenuItem_Click(object sender, EventArgs e) {
   717       computer.Accept(new SensorVisitor(delegate(ISensor sensor) {
   718         sensor.ResetMin();
   719         sensor.ResetMax();
   720       }));
   721     }
   722 
   723     private void MainForm_MoveOrResize(object sender, EventArgs e) {
   724       if (WindowState != FormWindowState.Minimized) {
   725         settings.SetValue("mainForm.Location.X", Bounds.X);
   726         settings.SetValue("mainForm.Location.Y", Bounds.Y);
   727         settings.SetValue("mainForm.Width", Bounds.Width);
   728         settings.SetValue("mainForm.Height", Bounds.Height);
   729       }
   730     }
   731 
   732     private void resetClick(object sender, EventArgs e) {
   733       // disable the fallback MainIcon during reset, otherwise icon visibility
   734       // might be lost 
   735       systemTray.IsMainIconEnabled = false;
   736       computer.Close();
   737       computer.Open();
   738       // restore the MainIcon setting
   739       systemTray.IsMainIconEnabled = minimizeToTray.Value;
   740     }
   741 
   742     private void treeView_MouseMove(object sender, MouseEventArgs e) {
   743       selectionDragging = selectionDragging &
   744         (e.Button & (MouseButtons.Left | MouseButtons.Right)) > 0; 
   745 
   746       if (selectionDragging)
   747         treeView.SelectedNode = treeView.GetNodeAt(e.Location);     
   748     }
   749 
   750     private void treeView_MouseDown(object sender, MouseEventArgs e) {
   751       selectionDragging = true;
   752     }
   753 
   754     private void treeView_MouseUp(object sender, MouseEventArgs e) {
   755       selectionDragging = false;
   756     }
   757   }
   758 }