GUI/MainForm.cs
author moel.mich
Mon, 07 Feb 2011 22:06:58 +0000
changeset 253 0044b05a3094
parent 252 e62afa69214f
child 262 8731a1b81301
permissions -rw-r--r--
Fixed Issue 162.
     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-2010
    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.Windows.Forms;
    44 using Aga.Controls.Tree;
    45 using Aga.Controls.Tree.NodeControls;
    46 using OpenHardwareMonitor.Hardware;
    47 using OpenHardwareMonitor.WMI;
    48 
    49 namespace OpenHardwareMonitor.GUI {
    50   public partial class MainForm : Form {
    51 
    52     private PersistentSettings settings;
    53     private UnitManager unitManager;
    54     private Computer computer;
    55     private Node root;
    56     private TreeModel treeModel;
    57     private IDictionary<ISensor, Color> sensorPlotColors = 
    58       new Dictionary<ISensor, Color>();
    59     private Color[] plotColorPalette;
    60     private SystemTray systemTray;    
    61     private StartupManager startupManager = new StartupManager();
    62     private UpdateVisitor updateVisitor = new UpdateVisitor();
    63     private SensorGadget gadget;
    64 
    65     private UserOption showHiddenSensors;
    66     private UserOption showPlot;
    67     private UserOption showValue;
    68     private UserOption showMin;
    69     private UserOption showMax;
    70     private UserOption startMinimized;
    71     private UserOption minimizeToTray;
    72     private UserOption minimizeOnClose;
    73     private UserOption autoStart;
    74     private UserOption readHddSensors;
    75     private UserOption showGadget;
    76 
    77     private WmiProvider wmiProvider;
    78 
    79     public MainForm() {      
    80       InitializeComponent();
    81 
    82       this.settings = new PersistentSettings();      
    83       this.settings.Load(Path.ChangeExtension(
    84         Application.ExecutablePath, ".config"));
    85 
    86       this.unitManager = new UnitManager(settings);
    87 
    88       // set the DockStyle here, to avoid conflicts with the MainMenu
    89       this.splitContainer.Dock = DockStyle.Fill;
    90             
    91       this.Font = SystemFonts.MessageBoxFont;
    92       treeView.Font = SystemFonts.MessageBoxFont;
    93       plotPanel.Font = SystemFonts.MessageBoxFont;
    94       treeView.RowHeight = treeView.Font.Height + 1;      
    95       
    96       nodeCheckBox.IsVisibleValueNeeded += nodeCheckBox_IsVisibleValueNeeded;
    97       nodeCheckBox.CheckStateChanged += UpdatePlotSelection;
    98       nodeTextBoxText.DrawText += nodeTextBoxText_DrawText;
    99       nodeTextBoxValue.DrawText += nodeTextBoxText_DrawText;
   100       nodeTextBoxMin.DrawText += nodeTextBoxText_DrawText;
   101       nodeTextBoxMax.DrawText += nodeTextBoxText_DrawText;
   102       nodeTextBoxText.EditorShowing += nodeTextBoxText_EditorShowing;
   103 
   104       foreach (TreeColumn column in treeView.Columns) 
   105         column.Width = Math.Max(20, Math.Min(400,
   106           settings.GetValue("treeView.Columns." + column.Header + ".Width",
   107           column.Width)));
   108 
   109       treeModel = new TreeModel();
   110       root = new Node(System.Environment.MachineName);
   111       root.Image = Utilities.EmbeddedResources.GetImage("computer.png");
   112       
   113       treeModel.Nodes.Add(root);
   114       treeView.Model = treeModel;
   115 
   116       this.computer = new Computer(settings);
   117 
   118       systemTray = new SystemTray(computer, settings);
   119       systemTray.HideShowCommand += hideShowClick;
   120       systemTray.ExitCommand += exitClick;
   121 
   122       int p = (int)Environment.OSVersion.Platform;
   123       if ((p == 4) || (p == 128)) { // Unix
   124         splitContainer.BorderStyle = BorderStyle.None;
   125         splitContainer.Border3DStyle = Border3DStyle.Adjust;
   126         splitContainer.SplitterWidth = 4;
   127         treeView.BorderStyle = BorderStyle.Fixed3D;
   128         plotPanel.BorderStyle = BorderStyle.Fixed3D;
   129         gadgetMenuItem.Visible = false;
   130         minCloseMenuItem.Visible = false;
   131       } else { // Windows
   132         gadget = new SensorGadget(computer, settings, unitManager);
   133         gadget.HideShowCommand += hideShowClick;
   134 
   135         wmiProvider = new WmiProvider(computer);
   136       }          
   137 
   138       computer.HardwareAdded += new HardwareEventHandler(HardwareAdded);
   139       computer.HardwareRemoved += new HardwareEventHandler(HardwareRemoved);        
   140 
   141       computer.Open();
   142 
   143       timer.Enabled = true;
   144 
   145       plotColorPalette = new Color[13];
   146       plotColorPalette[0] = Color.Blue;
   147       plotColorPalette[1] = Color.OrangeRed;
   148       plotColorPalette[2] = Color.Green;
   149       plotColorPalette[3] = Color.LightSeaGreen;
   150       plotColorPalette[4] = Color.Goldenrod;
   151       plotColorPalette[5] = Color.DarkViolet;
   152       plotColorPalette[6] = Color.YellowGreen;
   153       plotColorPalette[7] = Color.SaddleBrown;
   154       plotColorPalette[8] = Color.RoyalBlue;
   155       plotColorPalette[9] = Color.DeepPink;
   156       plotColorPalette[10] = Color.MediumSeaGreen;
   157       plotColorPalette[11] = Color.Olive;
   158       plotColorPalette[12] = Color.Firebrick;
   159 
   160       showHiddenSensors = new UserOption("hiddenMenuItem", false,
   161         hiddenMenuItem, settings);
   162       showHiddenSensors.Changed += delegate(object sender, EventArgs e) {
   163         treeModel.ForceVisible = showHiddenSensors.Value;
   164       };
   165 
   166       showPlot = new UserOption("plotMenuItem", false, plotMenuItem, settings);
   167       showPlot.Changed += delegate(object sender, EventArgs e) {
   168         splitContainer.Panel2Collapsed = !showPlot.Value;
   169         treeView.Invalidate();
   170       };
   171 
   172       showValue = new UserOption("valueMenuItem", true, valueMenuItem,
   173         settings);
   174       showValue.Changed += delegate(object sender, EventArgs e) {
   175         treeView.Columns[1].IsVisible = showValue.Value;
   176       };
   177 
   178       showMin = new UserOption("minMenuItem", false, minMenuItem, settings);
   179       showMin.Changed += delegate(object sender, EventArgs e) {
   180         treeView.Columns[2].IsVisible = showMin.Value;
   181       };
   182 
   183       showMax = new UserOption("maxMenuItem", true, maxMenuItem, settings);
   184       showMax.Changed += delegate(object sender, EventArgs e) {
   185         treeView.Columns[3].IsVisible = showMax.Value;
   186       };
   187 
   188       startMinimized = new UserOption("startMinMenuItem", false,
   189         startMinMenuItem, settings);
   190 
   191       minimizeToTray = new UserOption("minTrayMenuItem", true,
   192         minTrayMenuItem, settings);
   193       minimizeToTray.Changed += delegate(object sender, EventArgs e) {
   194         systemTray.IsMainIconEnabled = minimizeToTray.Value;
   195       };
   196 
   197       minimizeOnClose = new UserOption("minCloseMenuItem", false,
   198         minCloseMenuItem, settings);
   199 
   200       autoStart = new UserOption(null, startupManager.Startup,
   201         startupMenuItem, settings);
   202       autoStart.Changed += delegate(object sender, EventArgs e) {
   203         try {
   204           startupManager.Startup = autoStart.Value;
   205         } catch (InvalidOperationException) {
   206           MessageBox.Show("Updating the auto-startup option failed.", "Error", 
   207             MessageBoxButtons.OK, MessageBoxIcon.Error);
   208           autoStart.Value = startupManager.Startup;
   209         }
   210       };
   211 
   212       readHddSensors = new UserOption("hddMenuItem", true, hddMenuItem,
   213         settings);
   214       readHddSensors.Changed += delegate(object sender, EventArgs e) {
   215         computer.HDDEnabled = readHddSensors.Value;
   216         UpdatePlotSelection(null, null);
   217       };
   218 
   219       showGadget = new UserOption("gadgetMenuItem", false, gadgetMenuItem,
   220         settings);
   221       showGadget.Changed += delegate(object sender, EventArgs e) {
   222         if (gadget != null) 
   223           gadget.Visible = showGadget.Value;
   224       };
   225 
   226       celciusMenuItem.Checked = 
   227         unitManager.TemperatureUnit == TemperatureUnit.Celcius;
   228       fahrenheitMenuItem.Checked = !celciusMenuItem.Checked;
   229 
   230       startupMenuItem.Visible = startupManager.IsAvailable;
   231       
   232       if (startMinMenuItem.Checked) {
   233         if (!minTrayMenuItem.Checked) {
   234           WindowState = FormWindowState.Minimized;
   235           Show();
   236         }
   237       } else {
   238         Show();
   239       }
   240 
   241       // Create a handle, otherwise calling Close() does not fire FormClosed     
   242       IntPtr handle = Handle;
   243 
   244       // Make sure the settings are saved when the user logs off
   245       Microsoft.Win32.SystemEvents.SessionEnded += delegate {
   246         SaveConfiguration();
   247       };  
   248     }
   249     
   250     private void SubHardwareAdded(IHardware hardware, Node node) {
   251       Node hardwareNode = new HardwareNode(hardware, settings, unitManager);
   252       node.Nodes.Add(hardwareNode);
   253       foreach (IHardware subHardware in hardware.SubHardware)
   254         SubHardwareAdded(subHardware, hardwareNode);  
   255     }
   256 
   257     private void HardwareAdded(IHardware hardware) {
   258       Node hardwareNode = new HardwareNode(hardware, settings, unitManager);
   259       root.Nodes.Add(hardwareNode);
   260       foreach (IHardware subHardware in hardware.SubHardware)
   261         SubHardwareAdded(subHardware, hardwareNode);     
   262     }
   263 
   264     private void HardwareRemoved(IHardware hardware) {      
   265       List<Node> nodesToRemove = new List<Node>();
   266       foreach (Node node in root.Nodes) {
   267         HardwareNode hardwareNode = node as HardwareNode;
   268         if (hardwareNode != null && hardwareNode.Hardware == hardware)
   269           nodesToRemove.Add(node);
   270       }
   271       foreach (Node node in nodesToRemove)
   272         root.Nodes.Remove(node);
   273     }
   274 
   275     private void nodeTextBoxText_DrawText(object sender, DrawEventArgs e) {       
   276       Node node = e.Node.Tag as Node;
   277       if (node != null) {
   278         Color color;
   279         if (node.IsVisible) {
   280           SensorNode sensorNode = node as SensorNode;
   281           if (plotMenuItem.Checked && sensorNode != null &&
   282             sensorPlotColors.TryGetValue(sensorNode.Sensor, out color))
   283             e.TextColor = color;
   284         } else {
   285           e.TextColor = Color.DarkGray;
   286         }
   287       }
   288     }
   289 
   290     private void UpdatePlotSelection(object sender, 
   291       TreePathEventArgs e) 
   292     {
   293       List<ISensor> selected = new List<ISensor>();
   294       IDictionary<ISensor, Color> colors = new Dictionary<ISensor, Color>();
   295       int colorIndex = 0;
   296       foreach (TreeNodeAdv node in treeView.AllNodes) {
   297         SensorNode sensorNode = node.Tag as SensorNode;
   298         if (sensorNode != null && 
   299           sensorNode.Sensor.SensorType == SensorType.Temperature) {
   300           if (sensorNode.Plot) {
   301             colors.Add(sensorNode.Sensor,
   302               plotColorPalette[colorIndex % plotColorPalette.Length]);
   303             selected.Add(sensorNode.Sensor);
   304           }
   305           colorIndex++;
   306         }
   307       }
   308       sensorPlotColors = colors;
   309       plotPanel.SetSensors(selected, colors);
   310     }
   311 
   312     private void nodeTextBoxText_EditorShowing(object sender,
   313       CancelEventArgs e) 
   314     {
   315       e.Cancel = !(treeView.CurrentNode != null &&
   316         treeView.CurrentNode.Tag is SensorNode);
   317     }
   318 
   319     private void nodeCheckBox_IsVisibleValueNeeded(object sender, 
   320       NodeControlValueEventArgs e) {
   321       SensorNode node = e.Node.Tag as SensorNode;
   322       e.Value = (node != null) && 
   323         (node.Sensor.SensorType == SensorType.Temperature) && 
   324         plotMenuItem.Checked;
   325     }
   326 
   327     private void exitClick(object sender, EventArgs e) {
   328       Close();
   329     }
   330 
   331     private void timer_Tick(object sender, EventArgs e) {
   332       computer.Accept(updateVisitor);
   333       treeView.Invalidate();
   334       plotPanel.Invalidate();
   335       systemTray.Redraw();
   336       if (gadget != null)
   337         gadget.Redraw();
   338 
   339       if (wmiProvider != null)
   340         wmiProvider.Update();
   341     }
   342 
   343     private void SaveConfiguration() {
   344       foreach (TreeColumn column in treeView.Columns)
   345         settings.SetValue("treeView.Columns." + column.Header + ".Width",
   346           column.Width);
   347 
   348       string fileName = Path.ChangeExtension(
   349           System.Windows.Forms.Application.ExecutablePath, ".config");
   350       try {
   351         settings.Save(fileName);
   352       } catch (UnauthorizedAccessException) {
   353         MessageBox.Show("Access to the path '" + fileName + "' is denied. " +
   354           "The current seetings could not be saved.", 
   355           "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
   356       }
   357     }
   358 
   359     private void MainForm_Load(object sender, EventArgs e) {
   360       Rectangle newBounds = new Rectangle {
   361         X = settings.GetValue("mainForm.Location.X", Location.X),
   362         Y = settings.GetValue("mainForm.Location.Y", Location.Y),
   363         Width = settings.GetValue("mainForm.Width", 470),
   364         Height = settings.GetValue("mainForm.Height", 640)
   365       };
   366 
   367       Rectangle fullWorkingArea = new Rectangle(int.MaxValue, int.MaxValue,
   368         int.MinValue, int.MinValue);
   369 
   370       foreach (Screen screen in Screen.AllScreens)
   371         fullWorkingArea = Rectangle.Union(fullWorkingArea, screen.Bounds);
   372 
   373       Rectangle intersection = Rectangle.Intersect(fullWorkingArea, newBounds);
   374       if (intersection.Width < 20 || intersection.Height < 20 ||
   375         !settings.Contains("mainForm.Location.X")
   376       ) {
   377         newBounds.X = (Screen.PrimaryScreen.WorkingArea.Width / 2) -
   378                       (newBounds.Width/2);
   379 
   380         newBounds.Y = (Screen.PrimaryScreen.WorkingArea.Height / 2) -
   381                       (newBounds.Height / 2);
   382       }
   383 
   384       this.Bounds = newBounds;
   385     }
   386     
   387     private void MainForm_FormClosed(object sender, FormClosedEventArgs e) {
   388       Visible = false;
   389       SaveConfiguration();
   390 
   391       timer.Enabled = false;
   392       systemTray.Dispose();      
   393       computer.Close();
   394     }
   395 
   396     private void aboutMenuItem_Click(object sender, EventArgs e) {
   397       new AboutBox().ShowDialog();
   398     }
   399 
   400     private void treeView_Click(object sender, EventArgs e) {
   401       
   402       MouseEventArgs m = e as MouseEventArgs;
   403       if (m == null || m.Button != MouseButtons.Right)
   404         return;
   405 
   406       NodeControlInfo info = treeView.GetNodeControlInfoAt(
   407         new Point(m.X, m.Y)
   408       );
   409       treeView.SelectedNode = info.Node;
   410       if (info.Node != null) {
   411         SensorNode node = info.Node.Tag as SensorNode;
   412         if (node != null && node.Sensor != null) {
   413           sensorContextMenu.MenuItems.Clear();
   414           if (node.Sensor.Parameters.Length > 0) {
   415             MenuItem item = new MenuItem("Parameters...");
   416             item.Click += delegate(object obj, EventArgs args) {
   417               ShowParameterForm(node.Sensor);
   418             };
   419             sensorContextMenu.MenuItems.Add(item);
   420           }
   421           if (nodeTextBoxText.EditEnabled) {
   422             MenuItem item = new MenuItem("Rename");
   423             item.Click += delegate(object obj, EventArgs args) {
   424               nodeTextBoxText.BeginEdit();
   425             };
   426             sensorContextMenu.MenuItems.Add(item);
   427           }
   428           if (node.IsVisible) {
   429             MenuItem item = new MenuItem("Hide");
   430             item.Click += delegate(object obj, EventArgs args) {
   431               node.IsVisible = false;
   432             };
   433             sensorContextMenu.MenuItems.Add(item);
   434           } else {
   435             MenuItem item = new MenuItem("Unhide");
   436             item.Click += delegate(object obj, EventArgs args) {
   437               node.IsVisible = true;
   438             };
   439             sensorContextMenu.MenuItems.Add(item);
   440           }
   441           sensorContextMenu.MenuItems.Add(new MenuItem("-"));
   442           {
   443             MenuItem item = new MenuItem("Show in Tray");
   444             item.Checked = systemTray.Contains(node.Sensor);
   445             item.Click += delegate(object obj, EventArgs args) {
   446               if (item.Checked)
   447                 systemTray.Remove(node.Sensor);
   448               else
   449                 systemTray.Add(node.Sensor, true);
   450             };
   451             sensorContextMenu.MenuItems.Add(item);
   452           }
   453           if (gadget != null) {
   454             MenuItem item = new MenuItem("Show in Gadget");
   455             item.Checked = gadget.Contains(node.Sensor);
   456             item.Click += delegate(object obj, EventArgs args) {
   457               if (item.Checked) {
   458                 gadget.Remove(node.Sensor);
   459               } else {
   460                 gadget.Add(node.Sensor);
   461               }
   462             };
   463             sensorContextMenu.MenuItems.Add(item);
   464           }
   465           if (node.Sensor.Control != null) {
   466             sensorContextMenu.MenuItems.Add(new MenuItem("-"));
   467             IControl control = node.Sensor.Control;
   468             MenuItem controlItem = new MenuItem("Control");
   469             MenuItem defaultItem = new MenuItem("Default");
   470             defaultItem.Checked = control.ControlMode == ControlMode.Default;
   471             controlItem.MenuItems.Add(defaultItem);
   472             defaultItem.Click += delegate(object obj, EventArgs args) {
   473               control.SetDefault();
   474             };
   475             MenuItem manualItem = new MenuItem("Manual");            
   476             controlItem.MenuItems.Add(manualItem);
   477             manualItem.Checked = control.ControlMode == ControlMode.Software;
   478             for (int i = 0; i <= 100; i += 5) {
   479               if (i <= control.MaxSoftwareValue &&
   480                   i >= control.MinSoftwareValue) 
   481               {
   482                 MenuItem item = new MenuItem(i + " %");
   483                 manualItem.MenuItems.Add(item);
   484                 item.Checked = control.ControlMode == ControlMode.Software &&
   485                   Math.Round(control.SoftwareValue) == i;
   486                 int softwareValue = i;
   487                 item.Click += delegate(object obj, EventArgs args) {
   488                   control.SetSoftware(softwareValue);
   489                 };
   490               }
   491             }
   492             sensorContextMenu.MenuItems.Add(controlItem);
   493           }
   494 
   495           sensorContextMenu.Show(treeView, new Point(m.X, m.Y));
   496         }
   497       }
   498     }
   499 
   500     private void saveReportMenuItem_Click(object sender, EventArgs e) {
   501       string report = computer.GetReport();
   502       if (saveFileDialog.ShowDialog() == DialogResult.OK) {
   503         using (TextWriter w = new StreamWriter(saveFileDialog.FileName)) {
   504           w.Write(report);
   505         }
   506       }
   507     }
   508 
   509     private void SysTrayHideShow() {
   510       Visible = !Visible;
   511       if (Visible)
   512         Activate();    
   513     }
   514 
   515     protected override void WndProc(ref Message m) {
   516       const int WM_SYSCOMMAND = 0x112;
   517       const int SC_MINIMIZE = 0xF020;
   518       const int SC_CLOSE = 0xF060;
   519 
   520       if (minimizeToTray.Value && 
   521         m.Msg == WM_SYSCOMMAND && m.WParam.ToInt32() == SC_MINIMIZE) {
   522         SysTrayHideShow();
   523       } else if(minimizeOnClose.Value && 
   524         m.Msg == WM_SYSCOMMAND && m.WParam.ToInt32() == SC_CLOSE) {
   525         /*
   526          * Apparently the user wants to minimize rather than close
   527          * Now we still need to check if we're going to the tray or not
   528          * 
   529          * Note: the correct way to do this would be to send out SC_MINIMIZE,
   530          * but since the code here is so simple,
   531          * that would just be a waste of time.
   532          */
   533         if (minimizeToTray.Value)
   534           SysTrayHideShow();
   535         else
   536           WindowState = FormWindowState.Minimized;
   537       } else {      
   538         base.WndProc(ref m);
   539       }
   540     }
   541 
   542     private void hideShowClick(object sender, EventArgs e) {
   543       SysTrayHideShow();
   544     }
   545 
   546     private void ShowParameterForm(ISensor sensor) {
   547       ParameterForm form = new ParameterForm();
   548       form.Parameters = sensor.Parameters;
   549       form.captionLabel.Text = sensor.Name;
   550       form.ShowDialog();
   551     }
   552 
   553     private void treeView_NodeMouseDoubleClick(object sender, 
   554       TreeNodeAdvMouseEventArgs e) {
   555       SensorNode node = e.Node.Tag as SensorNode;
   556       if (node != null && node.Sensor != null && 
   557         node.Sensor.Parameters.Length > 0) {
   558         ShowParameterForm(node.Sensor);
   559       }
   560     }
   561 
   562     private void celciusMenuItem_Click(object sender, EventArgs e) {
   563       celciusMenuItem.Checked = true;
   564       fahrenheitMenuItem.Checked = false;
   565       unitManager.TemperatureUnit = TemperatureUnit.Celcius;
   566     }
   567 
   568     private void fahrenheitMenuItem_Click(object sender, EventArgs e) {
   569       celciusMenuItem.Checked = false;
   570       fahrenheitMenuItem.Checked = true;
   571       unitManager.TemperatureUnit = TemperatureUnit.Fahrenheit;
   572     }
   573 
   574     private void sumbitReportMenuItem_Click(object sender, EventArgs e) 
   575     {
   576       ReportForm form = new ReportForm();
   577       form.Report = computer.GetReport();
   578       form.ShowDialog();      
   579     }
   580 
   581     private void resetMinMaxMenuItem_Click(object sender, EventArgs e) {
   582       computer.Accept(new SensorVisitor(delegate(ISensor sensor) {
   583         sensor.ResetMin();
   584         sensor.ResetMax();
   585       }));
   586     }
   587 
   588     private void MainForm_MoveOrResize(object sender, EventArgs e) {
   589       if (WindowState != FormWindowState.Minimized) {
   590         settings.SetValue("mainForm.Location.X", Bounds.X);
   591         settings.SetValue("mainForm.Location.Y", Bounds.Y);
   592         settings.SetValue("mainForm.Width", Bounds.Width);
   593         settings.SetValue("mainForm.Height", Bounds.Height);
   594       }
   595     }
   596   }
   597 }