source: branches/eraser6/Eraser/Program.cs @ 741

Revision 741, 23.7 KB checked in by lowjoel, 5 years ago (diff)

Created interfaces such that event handlers can be associated with tasks being added or removed; this allows for other remote clients to add tasks to the executor instance and the owning program to be aware of such changes (and update UI accordingly.)

  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.Windows.Forms;
25
26using Eraser.Manager;
27using Eraser.Util;
28using Microsoft.Win32;
29using System.IO;
30using System.Runtime.Serialization.Formatters.Binary;
31using System.Globalization;
32using System.Reflection;
33using System.Diagnostics;
34
35namespace Eraser
36{
37    static class Program
38    {
39        /// <summary>
40        /// The main entry point for the application.
41        /// </summary>
42        [STAThread]
43        static void Main(string[] commandLine)
44        {
45            //Trivial case: no command parameters
46            if (commandLine.Length == 0)
47                GUIMain(false);
48
49            //Determine if the sole parameter is --restart; if it is, start the GUI
50            //passing isRestart as true. Otherwise, we're a console application.
51            else if (commandLine.Length == 1)
52            {
53                if (commandLine[0] == "/restart" || commandLine[0] == "--restart")
54                {
55                    GUIMain(true);
56                }
57                else
58                {
59                    CommandMain(commandLine);
60                }
61            }
62
63            //The other trivial case: definitely a console application.
64            else
65                CommandMain(commandLine);
66        }
67
68        /// <summary>
69        /// Runs Eraser as a command-line application.
70        /// </summary>
71        /// <param name="commandLine">The command line parameters passed to Eraser.</param>
72        private static void CommandMain(string[] commandLine)
73        {
74            //True if the user specified a quiet command.
75            bool isQuiet = false;
76
77            try
78            {
79                CommandLineProgram program = new CommandLineProgram(commandLine);
80                isQuiet = program.Arguments.Quiet;
81
82                using (ManagerLibrary library = new ManagerLibrary(new Settings()))
83                using (Program.eraserClient = new RemoteExecutorClient())
84                {
85                    if (!((RemoteExecutorClient)Program.eraserClient).Connect())
86                    {
87                        //The client cannot connect to the server. This probably means
88                        //that the server process isn't running. Start an instance.
89                        Process eraserInstance = Process.Start(
90                            Assembly.GetExecutingAssembly().Location);
91                        eraserInstance.WaitForInputIdle();
92
93                        if (!((RemoteExecutorClient)Program.eraserClient).Connect())
94                            throw new Exception("Eraser cannot connect to the running " +
95                                "instance for erasures.");
96                    }
97
98                    eraserClient.Run();
99                    program.Run();
100                }
101            }
102            catch (Exception e)
103            {
104                Console.WriteLine(e.Message);
105            }
106            finally
107            {
108                //Flush the buffered output to the console
109                Console.Out.Flush();
110
111                //Don't ask for a key to press if the user specified Quiet
112                if (!isQuiet)
113                {
114                    Console.Write("\nPress any key to continue . . . ");
115                    Console.Out.Flush();
116                    Console.ReadLine();
117                }
118
119                KernelAPI.FreeConsole();
120            }
121        }
122
123        /// <summary>
124        /// Runs Eraser as a GUI application.
125        /// </summary>
126        /// <param name="isRestart">True if the program was passed the --restart
127        /// switch.</param>
128        private static void GUIMain(bool isRestart)
129        {
130            Application.EnableVisualStyles();
131            Application.SetCompatibleTextRenderingDefault(false);
132            Application.SafeTopLevelCaptionFormat = S._("Eraser");
133
134            using (ManagerLibrary library = new ManagerLibrary(new Settings()))
135            using (eraserClient = new RemoteExecutorServer())
136            {
137                //Set our UI language
138                EraserSettings settings = new EraserSettings();
139                System.Threading.Thread.CurrentThread.CurrentUICulture =
140                    new CultureInfo(settings.Language);
141
142                //Load the task list
143                if (settings.TaskList != null)
144                    using (MemoryStream stream = new MemoryStream(settings.TaskList))
145                        try
146                        {
147                            eraserClient.LoadTaskList(stream);
148                        }
149                        catch (Exception)
150                        {
151                            settings.TaskList = null;
152                            MessageBox.Show(S._("Could not load task list. All task entries have " +
153                                "been lost."), S._("Eraser"), MessageBoxButtons.OK,
154                                MessageBoxIcon.Error);
155                        }
156
157                //Create the main form
158                MainForm form = new MainForm();
159
160                //Run tasks which are meant to be run on restart
161                if (isRestart)
162                {
163                    eraserClient.QueueRestartTasks();
164                }
165
166                //Run the program
167                eraserClient.Run();
168                Application.Run(form);
169
170                //Save the task list
171                using (MemoryStream stream = new MemoryStream())
172                {
173                    eraserClient.SaveTaskList(stream);
174                    settings.TaskList = stream.ToArray();
175                }
176            }
177        }
178
179        /// <summary>
180        /// The global Executor instance.
181        /// </summary>
182        public static Executor eraserClient;
183
184        /// <summary>
185        /// Handles commands passed to the program
186        /// </summary>
187        /// <param name="arguments">The arguments to the command</param>
188        private delegate void CommandHandler(Dictionary<string, string> arguments);
189    }
190
191    class CommandLineProgram
192    {
193        #region Command Line parsing classes
194        /// <summary>
195        /// Manages a command line.
196        /// </summary>
197        public class CommandLine
198        {
199            /// <summary>
200            /// Constructor.
201            /// </summary>
202            /// <param name="cmdParams">The raw arguments passed to the program.</param>
203            public CommandLine(string[] cmdParams)
204            {
205                //Get the action.
206                if (cmdParams.Length < 1)
207                    throw new ArgumentException("An action must be specified.");
208                Action = cmdParams[0];
209
210                //Iterate over each argument, resolving them ourselves and letting
211                //subclasses resolve them if we don't know how to.
212                for (int i = 1; i != cmdParams.Length; ++i)
213                {
214                    if (IsParam(cmdParams[i], "quiet", "q"))
215                        Quiet = true;
216                    else if (!ResolveParameter(cmdParams[i]))
217                        throw new ArgumentException("Unknown argument: " + cmdParams[i]);
218                }
219            }
220
221            /// <summary>
222            /// Called when a parameter is not used by the current CommandLine object
223            /// for subclasses to handle their parameters.
224            /// </summary>
225            /// <param name="param">The parameter to resolve.</param>
226            /// <returns>Return true if the parameter was resolved and accepted.</returns>
227            virtual protected bool ResolveParameter(string param)
228            {
229                return false;
230            }
231
232            /// <summary>
233            /// Checks if the given <paramref name="parameter"/> refers to the
234            /// <paramref name="expectedParameter"/>, regardless of whether it is specified
235            /// with -, --, or /
236            /// </summary>
237            /// <param name="parameter">The parameter on the command line.</param>
238            /// <param name="expectedParameter">The parameter the program is looking for, without
239            /// the - or / prefix.</param>
240            /// <param name="shortParameter">The short parameter when used with a single hyphen,
241            /// without the - or / prefix.</param>
242            /// <returns>True if the parameter references the given expected parameter.</returns>
243            protected static bool IsParam(string parameter, string expectedParameter,
244                string shortParameter)
245            {
246                //Trivial case
247                if (parameter.Length < 1)
248                    return false;
249
250                //Extract the bits before the equal sign.
251                {
252                    int equalPos = parameter.IndexOf('=');
253                    if (equalPos != -1)
254                        parameter = parameter.Substring(0, equalPos);
255                }
256
257                //Get the first letter.
258                switch (parameter[0])
259                {
260                    case '-':
261                        //Can be a - or a --. Check for the second parameter
262                        if (parameter.Length < 2)
263                            //Nothing specified at the end... it's invalid.
264                            return false;
265
266                        if (parameter[1] == '-')
267                            return parameter.Substring(2) == expectedParameter;
268                        else if (shortParameter == null || shortParameter == string.Empty)
269                            return parameter.Substring(1) == expectedParameter;
270                        else
271                            return parameter.Substring(1) == shortParameter;
272                       
273                    case '/':
274                        //The / can be used with both long and short parameters.
275                        parameter = parameter.Substring(1);
276                        return parameter == expectedParameter || (
277                            shortParameter != null && shortParameter != string.Empty &&
278                            parameter == shortParameter
279                        );
280
281                    default:
282                        return false;
283                }
284            }
285
286            /// <summary>
287            /// Gets the list of subparameters of the parameter. Subparameters are text
288            /// after the first =, separated by commas.
289            /// </summary>
290            /// <param name="param">The subparameter text to parse.</param>
291            /// <returns>The list of subparameters in the parameter.</returns>
292            protected static List<KeyValuePair<string, string>> GetSubParameters(string param)
293            {
294                List<KeyValuePair<string, string>> result =
295                    new List<KeyValuePair<string, string>>();
296                int lastPos = 0;
297                int commaPos = (param += ',').IndexOf(',');
298
299                while (commaPos != -1)
300                {
301                    //Extract the current subparameter, and dissect the subparameter at
302                    //the first =.
303                    string subParam = param.Substring(lastPos, commaPos - lastPos);
304                    int equalPos = subParam.IndexOf('=');
305                    if (equalPos == -1)
306                        result.Add(new KeyValuePair<string, string>(subParam, null));
307                    else
308                        result.Add(new KeyValuePair<string, string>(subParam.Substring(0, equalPos),
309                            subParam.Substring(equalPos + 1)));
310
311                    //Find the next ,
312                    lastPos = ++commaPos;
313                    commaPos = param.IndexOf(',', commaPos);
314                }
315
316                return result;
317            }
318
319            /// <summary>
320            /// The action that the command line specifies.
321            /// </summary>
322            public string Action
323            {
324                get
325                {
326                    return action;
327                }
328                private set
329                {
330                    action = value;
331                }
332            }
333
334            /// <summary>
335            /// True if no console window should be created.
336            /// </summary>
337            public bool Quiet
338            {
339                get
340                {
341                    return quiet;
342                }
343                private set
344                {
345                    quiet = value;
346                }
347            }
348
349            private string action;
350            private bool quiet;
351        }
352
353        /// <summary>
354        /// Manages a command line for adding tasks to the global DirectExecutor
355        /// </summary>
356        class AddTaskCommandLine : CommandLine
357        {
358            /// <summary>
359            /// Constructor.
360            /// </summary>
361            /// <param name="cmdParams">The raw command line arguments passed to the program.</param>
362            public AddTaskCommandLine(string[] cmdParams)
363                : base(cmdParams)
364            {
365            }
366
367            protected override bool ResolveParameter(string param)
368            {
369                int equalPos = param.IndexOf('=');
370                if (IsParam(param, "method", "m"))
371                {
372                    if (equalPos == -1)
373                        throw new ArgumentException("--method must be specified with an Erasure " +
374                            "method GUID.");
375                    ErasureMethod = new Guid(param.Substring(equalPos + 1));
376                }
377                else if (IsParam(param, "recycled", "r"))
378                {
379                    targets.Add(new Task.RecycleBin());
380                }
381                else if (IsParam(param, "unused", "u"))
382                {
383                    if (equalPos == -1)
384                        throw new ArgumentException("--unused must be specified with the Volume " +
385                            "to erase.");
386
387                    //Create the UnusedSpace target for inclusion into the task.
388                    Task.UnusedSpace target = new Task.UnusedSpace();
389
390                    //Determine if cluster tips should be erased.
391                    target.EraseClusterTips = false;
392                    List<KeyValuePair<string, string>> subParams =
393                        GetSubParameters(param.Substring(equalPos + 1));
394                    foreach (KeyValuePair<string, string> kvp in subParams)
395                        if (kvp.Value == null && target.Drive == null)
396                            target.Drive = kvp.Key;
397                        else if (kvp.Key == "clusterTips")
398                            target.EraseClusterTips = true;
399                        else
400                            throw new ArgumentException("Unknown subparameter: " + kvp.Key);
401                    targets.Add(target);
402                }
403                else if (IsParam(param, "dir", "d") || IsParam(param, "directory", null))
404                {
405                    if (equalPos == -1)
406                        throw new ArgumentException("--directory must be specified with the " +
407                            "directory to erase.");
408
409                    //Create the base target
410                    Task.Folder target = new Task.Folder();
411
412                    //Parse the subparameters.
413                    List<KeyValuePair<string, string>> subParams =
414                        GetSubParameters(param.Substring(equalPos + 1));
415                    foreach (KeyValuePair<string, string> kvp in subParams)
416                        if (kvp.Value == null && target.Path == null)
417                            target.Path = kvp.Key;
418                        else if (kvp.Key == "excludeMask")
419                        {
420                            if (kvp.Value == null)
421                                throw new ArgumentException("The exclude mask must be specified " +
422                                    "if the excludeMask subparameter is specified");
423                            target.ExcludeMask = kvp.Value;
424                        }
425                        else if (kvp.Key == "includeMask")
426                        {
427                            if (kvp.Value == null)
428                                throw new ArgumentException("The include mask must be specified " +
429                                    "if the includeMask subparameter is specified");
430                            target.IncludeMask = kvp.Value;
431                        }
432                        else if (kvp.Key == "delete")
433                            target.DeleteIfEmpty = true;
434                        else
435                            throw new ArgumentException("Unknown subparameter: " + kvp.Key);
436
437                    //Add the target to the list of targets
438                    targets.Add(target);
439                }
440                else
441                {
442                    //It's just a file!
443                    Task.File target = new Task.File();
444                    target.Path = param;
445                    targets.Add(target);
446                }
447
448                return true;
449            }
450
451            /// <summary>
452            /// The erasure method which the user specified on the command line.
453            /// </summary>
454            public Guid ErasureMethod
455            {
456                get
457                {
458                    return erasureMethod;
459                }
460                private set
461                {
462                    erasureMethod = value;
463                }
464            }
465
466            /// <summary>
467            /// The list of targets which was specified on the command line.
468            /// </summary>
469            public List<Task.ErasureTarget> Targets
470            {
471                get
472                {
473                    return new List<Task.ErasureTarget>(targets.ToArray());
474                }
475                set
476                {
477                    targets = value;
478                }
479            }
480
481            private Guid erasureMethod;
482            private List<Task.ErasureTarget> targets = new List<Task.ErasureTarget>();
483        }
484        #endregion
485
486        /// <summary>
487        /// Constructor.
488        /// </summary>
489        /// <param name="cmdParams">The raw command line arguments passed to the program.</param>
490        public CommandLineProgram(string[] cmdParams)
491        {
492            try
493            {
494                //Parse the command line arguments.
495                if (cmdParams.Length < 1)
496                    throw new ArgumentException("An action must be specified.");
497
498                switch (cmdParams[0])
499                {
500                    case "addtask":
501                        Arguments = new AddTaskCommandLine(cmdParams);
502                        break;
503                    case "querymethods":
504                    case "help":
505                    default:
506                        Arguments = new CommandLine(cmdParams);
507                        break;
508                }
509
510                //If the user did not specify the quiet command line, then create the console.
511                if (!Arguments.Quiet)
512                    CreateConsole();
513
514                //Map actions to their handlers
515                actionHandlers.Add("addtask", AddTask);
516                actionHandlers.Add("querymethods", QueryMethods);
517                actionHandlers.Add("help", Help);
518            }
519            finally
520            {
521                if (Arguments == null || !Arguments.Quiet)
522                    CreateConsole();
523            }
524        }
525
526        /// <summary>
527        /// Runs the program, analogous to System.Windows.Forms.Application.Run.
528        /// </summary>
529        public void Run()
530        {
531            //Call the function handling the current command line.
532            actionHandlers[Arguments.Action]();
533        }
534
535        /// <summary>
536        /// Creates a console for our application, setting the input/output streams to the
537        /// defaults.
538        /// </summary>
539        private void CreateConsole()
540        {
541            if (KernelAPI.AllocConsole())
542            {
543                Console.SetOut(new StreamWriter(Console.OpenStandardOutput()));
544                Console.SetIn(new StreamReader(Console.OpenStandardInput()));
545            }
546        }
547
548        /// <summary>
549        /// Prints the command line help for Eraser.
550        /// </summary>
551        private static void CommandUsage()
552        {
553            Console.WriteLine(@"usage: Eraser <action> <arguments>
554where action is
555    addtask                 Adds tasks to the current task list.
556    querymethods            Lists all registered Erasure methods.
557
558global parameters:
559    --quiet, -q             Do not create a Console window to display progress.
560
561parameters for addtask:
562    eraser addtask --method=<methodGUID> (--recycled | --unused=<volume> | " +
563@"--dir=<directory> | [file1 [file2 [...]]])
564
565    --method, -m            The Erasure method to use.
566    --recycled, -r          Erases files and folders in the recycle bin
567    --unused, -u            Erases unused space in the volume.
568        optional arguments: --unused=<drive>[,clusterTips]
569            clusterTips     If specified, the drive's files will have their cluster tips
570                            erased.
571    --dir, --directory, -d  Erases files and folders in the directory
572        optional arguments: --dir=<directory>[,e=excludeMask][,i=includeMask][,delete]
573            excludeMask     A wildcard expression for files and folders to exclude.
574            includeMask     A wildcard expression for files and folders to include.
575                            The include mask is applied before the exclude mask.
576            delete          Deletes the folder at the end of the erasure if specified.
577    file1 ... fileN         The list of files to erase.
578
579parameters for querymethods:
580    eraser querymethods
581
582    no parameters to set.
583
584All arguments are case sensitive.");
585            Console.Out.Flush();
586        }
587
588        #region Action Handlers
589        /// <summary>
590        /// The command line arguments passed to the program.
591        /// </summary>
592        public CommandLine Arguments
593        {
594            get
595            {
596                return arguments;
597            }
598            private set
599            {
600                arguments = value;
601            }
602        }
603
604        /// <summary>
605        /// Prints the help text for Eraser (with copyright)
606        /// </summary>
607        private void Help()
608        {
609            Console.WriteLine(@"Eraser {0}
610(c) 2008 The Eraser Project
611Eraser is Open-Source Software: see http://eraser.heidi.ie/ for details.
612", Assembly.GetExecutingAssembly().GetName().Version);
613
614            Console.Out.Flush();
615            CommandUsage();
616        }
617
618        /// <summary>
619        /// Lists all registered erasure methods.
620        /// </summary>
621        /// <param name="commandLine">The command line parameters passed to the program.</param>
622        private void QueryMethods()
623        {
624            //Output the header
625            const string methodFormat = "{0,-2} {1,-39} {2}";
626            Console.WriteLine(methodFormat, "", "Method", "GUID");
627            Console.WriteLine(new string('-', 79));
628
629            //Refresh the list of erasure methods
630            Dictionary<Guid, ErasureMethod> methods = ErasureMethodManager.GetAll();
631            foreach (ErasureMethod method in methods.Values)
632            {
633                Console.WriteLine(methodFormat, (method is UnusedSpaceErasureMethod) ?
634                    "U" : "", method.Name, method.GUID.ToString());
635            }
636        }
637
638        /// <summary>
639        /// Parses the command line for tasks and adds them using the
640        /// <see cref="RemoteExecutor"/> class.
641        /// </summary>
642        /// <param name="commandLine">The command line parameters passed to the program.</param>
643        private void AddTask()
644        {
645            AddTaskCommandLine arguments = (AddTaskCommandLine)Arguments;
646           
647            //Create the task, and set the method to use.
648            Task task = new Task();
649            ErasureMethod method = arguments.ErasureMethod == Guid.Empty ? 
650                ErasureMethodManager.Default :
651                ErasureMethodManager.GetInstance(arguments.ErasureMethod);
652            foreach (Task.ErasureTarget target in arguments.Targets)
653            {
654                target.Method = method;
655                task.Targets.Add(target);
656            }
657
658            //Send the task out.
659            Program.eraserClient.AddTask(ref task);
660        }
661        #endregion
662
663        /// <see cref="Arguments"/>
664        private CommandLine arguments;
665
666        /// <summary>
667        /// The prototype of an action handler in the class which executes an
668        /// action as specified in the command line.
669        /// </summary>
670        private delegate void ActionHandler();
671
672        /// <summary>
673        /// Matches an action handler to a function in the class.
674        /// </summary>
675        private Dictionary<string, ActionHandler> actionHandlers =
676            new Dictionary<string, ActionHandler>();
677    }
678
679    internal class Settings : Manager.SettingsManager
680    {
681        /// <summary>
682        /// Registry-based storage backing for the Settings class.
683        /// </summary>
684        private class RegistrySettings : Manager.Settings
685        {
686            /// <summary>
687            /// Constructor.
688            /// </summary>
689            /// <param name="key">The registry key to look for the settings in.</param>
690            public RegistrySettings(Guid pluginID, RegistryKey key)
691            {
692                this.key = key;
693            }
694
695            public override object this[string setting]
696            {
697                get
698                {
699                    byte[] currentSetting = (byte[])key.GetValue(setting, null);
700                    if (currentSetting != null && currentSetting.Length != 0)
701                        using (MemoryStream stream = new MemoryStream(currentSetting))
702                            try
703                            {
704                                return new BinaryFormatter().Deserialize(stream);
705                            }
706                            catch (Exception)
707                            {
708                                key.DeleteValue(setting);
709                                MessageBox.Show(S._("Could not load the setting {0} for plugin {1}. " +
710                                    "The setting has been lost.", key, pluginID.ToString()),
711                                    S._("Eraser"), MessageBoxButtons.OK, MessageBoxIcon.Error);
712                            }
713
714                    return null;
715                }
716                set
717                {
718                    if (value == null)
719                    {
720                        key.DeleteValue(setting);
721                    }
722                    else
723                    {
724                        using (MemoryStream stream = new MemoryStream())
725                        {
726                            new BinaryFormatter().Serialize(stream, value);
727                            key.SetValue(setting, stream.ToArray(), RegistryValueKind.Binary);
728                        }
729                    }
730                }
731            }
732
733            /// <summary>
734            /// The GUID of the plugin whose settings this object is storing.
735            /// </summary>
736            private Guid pluginID;
737
738            /// <summary>
739            /// The registry key where the data is stored.
740            /// </summary>
741            private RegistryKey key;
742        }
743
744        public override void Save()
745        {
746        }
747
748        protected override Manager.Settings GetSettings(Guid guid)
749        {
750            //Open the registry key containing the settings
751            const string eraserKeyPath = @"SOFTWARE\Eraser\Eraser 6";
752            RegistryKey eraserKey = Registry.CurrentUser.OpenSubKey(eraserKeyPath, true);
753            if (eraserKey == null)
754                eraserKey = Registry.CurrentUser.CreateSubKey(eraserKeyPath);
755
756            RegistryKey pluginsKey = eraserKey.OpenSubKey(guid.ToString(), true);
757            if (pluginsKey == null)
758                pluginsKey = eraserKey.CreateSubKey(guid.ToString());
759
760            //Return the Settings object.
761            return new RegistrySettings(guid, pluginsKey);
762        }
763    }
764
765    internal class EraserSettings
766    {
767        public EraserSettings()
768        {
769            settings = Manager.ManagerLibrary.Instance.SettingsManager.ModuleSettings;
770        }
771
772        /// <summary>
773        /// Gets or sets the task list, serialised in binary form by the Manager assembly.
774        /// </summary>
775        public byte[] TaskList
776        {
777            get
778            {
779                return (byte[])settings["TaskList"];
780            }
781            set
782            {
783                settings["TaskList"] = value;
784            }
785        }
786
787        /// <summary>
788        /// Gets or sets the LCID of the language which the UI should be displayed in.
789        /// </summary>
790        public string Language
791        {
792            get
793            {
794                return settings["Language"] == null ? 
795                    GetCurrentCulture().Name :
796                    (string)settings["Language"];
797            }
798            set
799            {
800                settings["Language"] = value;
801            }
802        }
803
804        /// <summary>
805        /// Gets or sets a value on whether the main frame should be minimised to the
806        /// system notification area.
807        /// </summary>
808        public bool HideWhenMinimised
809        {
810            get
811            {
812                return settings["HideWhenMinimised"] == null ?
813                    true : (bool)settings["HideWhenMinimised"];
814            }
815            set
816            {
817                settings["HideWhenMinimised"] = value;
818            }
819        }
820
821        /// <summary>
822        /// Gets the current UI culture, correct to the top-level culture (i.e., English
823        /// instead of English (United Kingdom))
824        /// </summary>
825        /// <returns>The CultureInfo of the current UI culture, correct to the top level.</returns>
826        private static CultureInfo GetCurrentCulture()
827        {
828            CultureInfo culture = CultureInfo.CurrentUICulture;
829            while (culture.Parent != CultureInfo.InvariantCulture)
830                culture = culture.Parent;
831
832            return culture;
833        }
834
835        private Manager.Settings settings;
836    }
837}
Note: See TracBrowser for help on using the repository browser.