source: trunk/CrypPlugins/WorkspaceManager/WorkspaceManager.cs @ 2241

Last change on this file since 2241 was 2241, checked in by Matthäus Wander, 11 years ago

Removed further occurences of DisplayLevel and marked remainders in IPlugin interface as obsolete. This closes #122.

File size: 26.9 KB
Line 
1/*                             
2   Copyright 2010 Nils Kopal, Viktor M.
3
4   Licensed under the Apache License, Version 2.0 (the "License");
5   you may not use this file except in compliance with the License.
6   You may obtain a copy of the License at
7
8       http://www.apache.org/licenses/LICENSE-2.0
9
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15*/
16
17
18using System;
19using System.Collections.Generic;
20using System.Linq;
21using System.Text;
22using Cryptool.Core;
23using Cryptool.PluginBase.Editor;
24using Cryptool.UiPluginBase;
25using Cryptool.PluginBase.IO;
26using Cryptool.PluginBase;
27
28using WorkspaceManager.Model;
29using WorkspaceManager.View;
30using WorkspaceManager.Execution;
31using WorkspaceManager.View.Container;
32using WorkspaceManager.View.Converter;
33using System.Windows;
34using System.Windows.Threading;
35using System.Threading;
36using System.Windows.Controls;
37using System.Windows.Media;
38using Cryptool.PluginBase.Miscellaneous;
39using WorkspaceManager.View.VisualComponents;
40using System.Windows.Media.Imaging;
41using System.Printing;
42using System.Windows.Documents;
43using System.Windows.Markup;
44
45//Disable warnings for unused or unassigned fields and events:
46#pragma warning disable 0169, 0414, 0067
47
48namespace WorkspaceManager
49{
50    /// <summary>
51    /// Workspace Manager - PluginEditor based on MVC Pattern
52    /// </summary>
53    [EditorInfo("cwm")]
54    [Author("Viktor Matkovic,Nils Kopal", "nils.kopal@cryptool.org", "Universität Duisburg-Essen", "http://www.uni-due.de")]
55    [PluginInfo("WorkspaceManager.Resources.Attributes", false, "Workspace Manager", "Graphical plugin editor for the CrypTool workspace", null,
56      "AnotherEditor/icon.png",
57      "AnotherEditor/Images/addWorkspace.png",
58      "AnotherEditor/Images/deleteWorkspace.png",
59      "AnotherEditor/Images/importSubWorkspace.png")]
60    public class WorkspaceManager : IEditor
61    {
62
63        /// <summary>
64        /// Create a new Instance of the Editor
65        /// </summary>
66        public WorkspaceManager()
67        {
68            Settings = new WorkspaceManagerSettings(this);
69            WorkspaceModel = new WorkspaceModel();
70            WorkspaceModel.WorkspaceManagerEditor = this;
71            WorkspaceSpaceEditorView = new WorkSpaceEditorView(WorkspaceModel);
72            HasChanges = false;                               
73        }
74
75        #region private Members
76
77        private WorkspaceModel WorkspaceModel = null;
78        private WorkSpaceEditorView WorkspaceSpaceEditorView = null;
79        private ExecutionEngine ExecutionEngine = null;
80        private volatile bool executing = false;
81        private volatile bool stopping = false;
82
83        #endregion
84
85        /// <summary>
86        /// Is this Editor executing?
87        /// </summary>
88        public bool isExecuting(){
89            return executing;
90        }
91
92        #region IEditor Members
93
94        /// <summary>
95        ///
96        /// </summary>
97        public event SelectedPluginChangedHandler OnSelectedPluginChanged;
98
99        /// <summary>
100        ///
101        /// </summary>
102        public event ProjectTitleChangedHandler OnProjectTitleChanged;
103
104        /// <summary>
105        ///
106        /// </summary>
107        public event OpenProjectFileHandler OnOpenProjectFile;
108
109        /// <summary>
110        ///
111        /// </summary>
112        public event EditorSpecificPluginsChanged OnEditorSpecificPluginsChanged;
113
114        /// <summary>
115        /// Current filename
116        /// </summary>
117        public string CurrentFilename { private set; get; }
118
119        /// <summary>
120        /// Called by clicking on the new button of CrypTool
121        /// Creates a new Model
122        /// </summary>
123        public void New()
124        {
125            foreach (PluginModel pluginModel in new List<PluginModel>(WorkspaceModel.AllPluginModels))
126            {
127                WorkspaceModel.deletePluginModel(pluginModel);
128            }
129            this.HasChanges = false;
130            CurrentFilename = "unnamed project";
131            this.OnProjectTitleChanged.BeginInvoke(this, "unnamed project", null, null);
132        }
133
134        /// <summary>
135        /// Called by clicking on the open button of CrypTool
136        /// loads a serialized model
137        /// </summary>
138        /// <param name="fileName"></param>
139        public void Open(string fileName)
140        {
141            try
142            {
143                New();
144                GuiLogMessage("Loading Model: " + fileName, NotificationLevel.Info);               
145                WorkspaceModel = ModelPersistance.loadModel(fileName,this);               
146                WorkspaceSpaceEditorView.Load(WorkspaceModel);
147                HasChanges = false;
148                this.OnProjectTitleChanged.BeginInvoke(this, System.IO.Path.GetFileName(fileName), null, null);
149                CurrentFilename = fileName;
150            }
151            catch (Exception ex)
152            {
153                GuiLogMessage("Could not load Model:" + ex.ToString(), NotificationLevel.Error);
154            }
155        }
156
157        /// <summary>
158        /// Called by clicking on the save button of CrypTool
159        /// serializes the current model
160        /// </summary>
161        /// <param name="fileName"></param>
162        public void Save(string fileName)
163        {
164            try
165            {
166                GuiLogMessage("Saving Model: " + fileName, NotificationLevel.Info);
167                ModelPersistance.saveModel(this.WorkspaceModel, fileName);
168                HasChanges = false;
169                this.OnProjectTitleChanged.BeginInvoke(this, System.IO.Path.GetFileName(fileName), null, null);
170                CurrentFilename = fileName;
171            }
172            catch (Exception ex)
173            {
174                GuiLogMessage("Could not save Model:" + ex.ToString(), NotificationLevel.Error);               
175            }
176           
177        }
178
179        /// <summary>
180        /// Called by double clicking on a plugin symbol of CrypTool
181        /// Adds a new PluginModel wrapping an instance of the selected plugin
182        /// </summary>
183        /// <param name="type"></param>
184        public void Add(Type type)
185        {
186            /*if (!executing)
187            {
188                PluginModel newPluginModel = WorkspaceModel.newPluginModel(new Point(10, 10), 100, 100, type);
189                GuiLogMessage("Added by double click: " + newPluginModel.Name, NotificationLevel.Info);
190                HasChanges = true;
191            }*/
192        }
193
194        /// <summary>
195        ///
196        /// </summary>
197        /// <param name="espi"></param>
198        public void AddEditorSpecific(EditorSpecificPluginInfo espi)
199        {
200            //to be implemented
201        }
202
203        /// <summary>
204        ///
205        /// </summary>
206        /// <param name="espi"></param>
207        public void DeleteEditorSpecific(EditorSpecificPluginInfo espi)
208        {
209            //to be implemented   
210        }
211
212        /// <summary>
213        /// Undo changes
214        /// </summary>
215        public void Undo()
216        {
217            //to be implemented
218        }
219
220        /// <summary>
221        /// Redo changes
222        /// </summary>
223        public void Redo()
224        {
225            //to be implemented
226        }
227
228        public void Cut()
229        {
230            throw new NotImplementedException();
231        }
232
233        public void Copy()
234        {
235            throw new NotImplementedException();
236        }
237
238        public void Paste()
239        {
240            throw new NotImplementedException();
241        }
242
243        public void Remove()
244        {
245            throw new NotImplementedException();
246        }
247
248        public void Print()
249        {
250            try
251            {
252                Matrix m = PresentationSource.FromVisual(Application.Current.MainWindow).CompositionTarget.TransformToDevice;
253                double dx = m.M11 * 96;
254                double dy = m.M22 * 96;
255                this.GuiLogMessage("dx=" + dx + " dy=" + dy, NotificationLevel.Debug);
256                const int factor = 4;
257                ModifiedCanvas control = (ModifiedCanvas)((WorkSpaceEditorView)this.Presentation).ViewBox.Content;
258                PrintDialog dialog = new PrintDialog();
259                dialog.PageRangeSelection = PageRangeSelection.AllPages;
260                dialog.UserPageRangeEnabled = true;
261
262                Nullable<Boolean> print = dialog.ShowDialog();
263                if (print == true)
264                {
265                    this.GuiLogMessage("Printing document \"" + this.CurrentFilename + "\" now", NotificationLevel.Info);
266                   
267                    PrintCapabilities capabilities = dialog.PrintQueue.GetPrintCapabilities(dialog.PrintTicket);
268                    System.Windows.Size pageSize = new System.Windows.Size(dialog.PrintableAreaWidth, dialog.PrintableAreaHeight);
269                    System.Windows.Size visibleSize = new System.Windows.Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight);
270
271                    FixedDocument fixedDoc = new FixedDocument();
272                    control.Measure(new System.Windows.Size(double.PositiveInfinity, double.PositiveInfinity));
273                    control.Arrange(new Rect(new System.Windows.Point(0, 0), control.DesiredSize));
274                    System.Windows.Size size = control.DesiredSize;
275
276                    RenderTargetBitmap bmp = new RenderTargetBitmap((int)size.Width * factor, (int)size.Height * factor, dx * factor, dy * factor, PixelFormats.Pbgra32);
277                    bmp.Render(control);
278
279
280                    double xOffset = 0;
281                    double yOffset = 0;
282                    while (xOffset < size.Width)
283                    {
284                        yOffset = 0;
285                        while (yOffset < size.Height)
286                        {
287                            PageContent pageContent = new PageContent();
288                            FixedPage page = new FixedPage();
289                            ((IAddChild)pageContent).AddChild(page);
290                            fixedDoc.Pages.Add(pageContent);
291                            page.Width = pageSize.Width;
292                            page.Height = pageSize.Height;
293                            int width = (xOffset + visibleSize.Width) > size.Width ? (int)(size.Width - xOffset) : (int)visibleSize.Width;
294                            int height = (yOffset + visibleSize.Height) > size.Height ? (int)(size.Height - yOffset) : (int)visibleSize.Height;
295                            System.Windows.Controls.Image croppedImage = new System.Windows.Controls.Image();                           
296                            CroppedBitmap cb = new CroppedBitmap(bmp, new Int32Rect((int)xOffset * factor, (int)yOffset *factor, width * factor, height * factor));
297                            croppedImage.Source = cb;
298                            croppedImage.Width = width;
299                            croppedImage.Height = height;
300                            page.Children.Add(croppedImage);
301                            yOffset += visibleSize.Height;
302                        }
303                        xOffset += visibleSize.Width;
304                    }
305                    dialog.PrintDocument(fixedDoc.DocumentPaginator, "WorkspaceManager_" + this.CurrentFilename);
306                    this.GuiLogMessage("Printed \"" + fixedDoc.DocumentPaginator.PageCount + "\" pages of document \"" + this.CurrentFilename + "\"", NotificationLevel.Info);
307                }
308            }
309            catch (Exception ex)
310            {
311                this.GuiLogMessage("Exception while printing: " + ex.Message, NotificationLevel.Error);
312            }
313        }
314
315        /// <summary>
316        /// Show the Help site
317        /// </summary>
318        public void ShowHelp()
319        {
320            //to be implemented
321        }
322
323        /// <summary>
324        /// Show the Description of the selected plugin
325        /// </summary>
326        public void ShowSelectedPluginDescription()
327        {
328            //to be implemented
329        }
330
331        /// <summary>
332        /// Is Undo possible
333        /// </summary>
334        public bool CanUndo
335        {
336            get;
337            set;
338        }
339
340        /// <summary>
341        /// Is Redo possible?
342        /// </summary>
343        public bool CanRedo
344        {
345            get;
346            set;
347        }
348
349        public bool CanCut
350        {
351            get { return false; }
352        }
353
354        public bool CanCopy
355        {
356            get { return false; }
357        }
358
359        public bool CanPaste
360        {
361            get { return false; }
362        }
363
364        public bool CanRemove
365        {
366            get { return false; }
367        }
368
369        /// <summary>
370        /// Can the ExecutionEngine be started?
371        /// </summary>
372        public bool CanExecute
373        {
374            get{return !executing;}
375        }
376
377        /// <summary>
378        /// Can the ExecutionEngine be stopped?
379        /// </summary>
380        public bool CanStop
381        {
382            get { return executing; }
383        }
384
385        /// <summary>
386        /// Does this Editor has changes?
387        /// </summary>
388        public bool HasChanges
389        {
390            get;
391            set;
392        }
393
394        public bool CanPrint
395        {
396            get { return true; }
397        }
398
399        /// <summary>
400        ///
401        /// </summary>
402        public List<EditorSpecificPluginInfo> EditorSpecificPlugins
403        {
404            get;
405            set;
406        }
407
408        public bool ReadOnly { get; set; }
409
410        #endregion
411
412        #region IPlugin Members
413
414        /// <summary>
415        ///
416        /// </summary>
417        public event StatusChangedEventHandler OnPluginStatusChanged;
418
419        /// <summary>
420        ///
421        /// </summary>
422        public event GuiLogNotificationEventHandler OnGuiLogNotificationOccured;
423
424        /// <summary>
425        ///
426        /// </summary>
427        public event PluginProgressChangedEventHandler OnPluginProgressChanged;
428
429        /// <summary>
430        /// Settings of this editor
431        /// </summary>
432        public ISettings Settings
433        {
434            get;
435            set;
436        }
437
438        /// <summary>
439        /// The Presentation of this editor
440        /// </summary>
441        public System.Windows.Controls.UserControl Presentation
442        {
443            get {return WorkspaceSpaceEditorView;}
444            set { WorkspaceSpaceEditorView = (WorkSpaceEditorView)value; }
445        }
446
447        /// <summary>
448        /// The QuickWatchPresentation of this editor
449        /// </summary>
450        public System.Windows.Controls.UserControl QuickWatchPresentation
451        {
452            get;
453            set;
454        }
455
456        /// <summary>
457        /// Called before execution
458        /// </summary>
459        public void PreExecution()
460        {
461            //to be implemented
462        }
463
464        /// <summary>
465        /// Starts the ExecutionEngine to execute the model
466        /// </summary>
467        public void Execute()
468        {
469            if (executing)
470            {
471                return;
472            }
473            EventsHelper.AsynchronousPropertyChanged = false;
474            try
475            {
476                GuiLogMessage("Execute Model now!", NotificationLevel.Info);
477                executing = true;
478
479                if (((WorkspaceManagerSettings)this.Settings).SynchronousEvents)
480                {
481                    EventsHelper.AsynchronousProgressChanged = false;
482                    EventsHelper.AsynchronousGuiLogMessage = false;
483                    EventsHelper.AsynchronousStatusChanged = false;
484                }
485
486                //Get the gui Thread
487                this.WorkspaceSpaceEditorView.Dispatcher.Invoke(DispatcherPriority.Normal, (SendOrPostCallback)delegate
488                {
489                    this.WorkspaceSpaceEditorView.ResetConnections();
490                    this.WorkspaceSpaceEditorView.State = EditorState.BUSY;                   
491                }
492                , null);
493
494                this.ExecutionEngine = new ExecutionEngine(this);
495
496                try
497                {
498                    ExecutionEngine.GuiUpdateInterval = int.Parse(((WorkspaceManagerSettings)this.Settings).GuiUpdateInterval);
499                    if (ExecutionEngine.GuiUpdateInterval <= 0)
500                    {
501                        GuiLogMessage("GuiUpdateInterval can not be <=0; Use GuiUpdateInterval = 1", NotificationLevel.Warning);
502                        ExecutionEngine.GuiUpdateInterval = 1;
503                    }
504                }
505                catch (Exception ex)
506                {
507                    GuiLogMessage("Could not set GuiUpdateInterval: " + ex.Message, NotificationLevel.Warning);
508                    ExecutionEngine.GuiUpdateInterval = 100;
509                }
510
511                try
512                {
513                    ExecutionEngine.SleepTime = int.Parse(((WorkspaceManagerSettings)this.Settings).SleepTime);
514                    if (ExecutionEngine.SleepTime < 0)
515                    {
516                        GuiLogMessage("SleepTime can not be <=0; Use GuiUpdateInterval = 0", NotificationLevel.Warning);
517                        ExecutionEngine.SleepTime = 0;
518                    }
519                }
520                catch (Exception ex)
521                {
522                    GuiLogMessage("Could not set SleepTime: " + ex.Message, NotificationLevel.Warning);
523                    ExecutionEngine.GuiUpdateInterval = 0;
524                }
525
526                int schedulers=0;
527                try
528                {
529                   schedulers = int.Parse(((WorkspaceManagerSettings)this.Settings).Threads);
530                    if (ExecutionEngine.SleepTime < 0)
531                    {
532                        GuiLogMessage("Schedulers can not be <=0; Use Schedulers = 1", NotificationLevel.Warning);
533                        schedulers = 1;
534                    }
535                }
536                catch (Exception ex)
537                {
538                    GuiLogMessage("Could not set Schedulers: " + ex.Message, NotificationLevel.Warning);
539                    schedulers = 1;
540                }
541
542                ExecutionEngine.BenchmarkPlugins = ((WorkspaceManagerSettings)this.Settings).BenchmarkPlugins;
543                ExecutionEngine.ThreadPriority = ((WorkspaceManagerSettings)this.Settings).ThreadPriority;
544
545                ExecutionEngine.Execute(WorkspaceModel, schedulers);               
546            }
547            catch (Exception ex)
548            {
549                GuiLogMessage("Exception during the execution: " + ex.Message, NotificationLevel.Error);
550                executing = false;
551                if (((WorkspaceManagerSettings)this.Settings).SynchronousEvents)
552                {
553                    EventsHelper.AsynchronousProgressChanged = true;
554                    EventsHelper.AsynchronousGuiLogMessage = true;
555                    EventsHelper.AsynchronousStatusChanged = true;
556                }
557            }
558        }
559
560        /// <summary>
561        /// Called after the execution
562        /// </summary>
563        public void PostExecution()
564        {
565            //to be implemented
566        }
567
568        /// <summary>
569        /// Pause the execution
570        /// </summary>
571        public void Pause()
572        {   
573            //to be implemented
574        }
575
576        /// <summary>
577        /// Stop the ExecutionEngine
578        /// </summary>
579        public void Stop()
580        {
581            if (!executing || stopping)
582            {
583                return;
584            }
585
586            stopping = true;
587
588            Thread stopThread = new Thread(new ThreadStart(waitingStop));
589            stopThread.Start(); 
590
591            EventsHelper.AsynchronousPropertyChanged = true;
592
593            if (((WorkspaceManagerSettings)this.Settings).SynchronousEvents)
594            {
595                EventsHelper.AsynchronousProgressChanged = true;
596                EventsHelper.AsynchronousGuiLogMessage = true;
597                EventsHelper.AsynchronousStatusChanged = true;
598            }
599
600                       
601        }
602
603        /// <summary>
604        /// Stops the execution engine and blocks until this work is done
605        /// </summary>
606        private void waitingStop()
607        {
608            try
609            {
610                GuiLogMessage("Executing stopped by User!", NotificationLevel.Info);
611                ExecutionEngine.Stop();
612                //Get the gui Thread
613                this.WorkspaceSpaceEditorView.Dispatcher.Invoke(DispatcherPriority.Normal, (SendOrPostCallback)delegate
614                {
615                    this.WorkspaceSpaceEditorView.ResetConnections();
616                    this.WorkspaceSpaceEditorView.State = EditorState.READY;
617                }
618                , null);
619            }
620            catch (Exception ex)
621            {
622                GuiLogMessage("Exception during the stopping of the execution: " + ex.Message, NotificationLevel.Error);
623            }
624            executing = false;
625            this.ExecutionEngine = null;
626            GC.Collect();
627            stopping = false;
628        }
629
630        /// <summary>
631        /// Called to initialize the editor
632        /// </summary>
633        public void Initialize()
634        {
635            //to be implemented
636        }
637
638        /// <summary>
639        /// Called when the editor is disposed
640        /// </summary>
641        public void Dispose()
642        {
643            if (ExecutionEngine != null && ExecutionEngine.IsRunning)
644            {
645                ExecutionEngine.Stop();
646            }
647            EventsHelper.AsynchronousPropertyChanged = true;
648        }
649
650        #endregion
651
652        #region INotifyPropertyChanged Members
653
654        /// <summary>
655        ///
656        /// </summary>
657        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
658
659        #endregion
660
661        #region IApplication Members
662
663        /// <summary>
664        ///
665        /// </summary>
666        private PluginManager pluginManager;
667        public PluginManager PluginManager
668        {
669            get { return pluginManager; }
670            set
671            {
672                pluginManager = value;
673                DragDropDataObjectToPluginConverter.PluginManager = value;
674            }         
675        }
676
677        #endregion
678
679        #region GuiLogMessage, Progress
680
681        /// <summary>
682        /// Loggs a message to the logging mechanism of CrypTool
683        /// </summary>
684        /// <param name="Message"></param>
685        /// <param name="notificationLevel"></param>
686        public void GuiLogMessage(string Message, NotificationLevel notificationLevel)
687        {
688            if (OnGuiLogNotificationOccured != null)
689            {
690                GuiLogEventArgs args = new GuiLogEventArgs(Message, this, notificationLevel);
691                args.Title = "-";
692                OnGuiLogNotificationOccured(this, args);
693            }
694        }
695
696        /// <summary>
697        /// GuiLogNotificationOccured
698        /// </summary>
699        /// <param name="sender"></param>
700        /// <param name="args"></param>
701        public void GuiLogNotificationOccured(IPlugin sender, GuiLogEventArgs args)
702        {
703            //Check if the logging event is Warning or Error and set the State of the PluginModel to
704            //the corresponding PluginModelState
705            if (args.NotificationLevel == NotificationLevel.Warning)
706            {               
707                foreach (PluginModel pluginModel in this.WorkspaceModel.AllPluginModels)
708                {
709                    if (pluginModel.Plugin == sender)
710                    {
711                        pluginModel.State = PluginModelState.Warning;
712                        pluginModel.GuiNeedsUpdate = true;
713                    }
714                }
715            }
716
717            if (args.NotificationLevel == NotificationLevel.Error)
718            {               
719                foreach (PluginModel pluginModel in this.WorkspaceModel.AllPluginModels)
720                {
721                    if (pluginModel.Plugin == sender)
722                    {
723                        pluginModel.State = PluginModelState.Error;
724                        pluginModel.GuiNeedsUpdate = true;
725                    }
726                }
727            }
728
729            if (OnGuiLogNotificationOccured != null)
730            {
731                switch (((WorkspaceManagerSettings)this.Settings).LogLevel)
732                {
733                    case 3://Error
734                        if (args.NotificationLevel == NotificationLevel.Debug ||
735                            args.NotificationLevel == NotificationLevel.Info ||
736                            args.NotificationLevel == NotificationLevel.Warning)
737                        {
738                            return;
739                        }
740                        break;
741
742                    case 2://Warning
743                        if (args.NotificationLevel == NotificationLevel.Debug ||
744                            args.NotificationLevel == NotificationLevel.Info)
745                        {
746                            return;
747                        }
748                        break;
749
750                    case 1://Info
751                        if (args.NotificationLevel == NotificationLevel.Debug)
752                        {
753                            return;
754                        }
755                        break;
756                }
757                OnGuiLogNotificationOccured(sender, args);
758            }
759               
760        }
761
762        /// <summary>
763        /// Progress of this editor
764        /// </summary>
765        /// <param name="Value"></param>
766        /// <param name="MaxValue"></param>
767        private void Progress(int Value, int MaxValue)
768        {
769            if (OnPluginProgressChanged != null)
770            {
771                OnPluginProgressChanged(this, new PluginProgressEventArgs(Value, MaxValue));
772            }
773        }
774        #endregion GuiLogMessage, Progress
775
776        /// <summary>
777        /// Selected Plugin changed by View
778        /// </summary>
779        /// <param name="args"></param>
780        public void onSelectedPluginChanged(PluginChangedEventArgs args)
781        {
782            this.OnSelectedPluginChanged(this, args);
783        }
784
785        #region IEditor Members
786
787
788        public void Active()
789        {           
790        }
791
792        #endregion
793
794        #region IEditor Members
795
796
797        public event OpenTabHandler OnOpenTab;
798
799        #endregion
800    }
801}
802
803//Restore warnings for unused or unassigned fields and events:
804#pragma warning restore 0169, 0414, 0067
Note: See TracBrowser for help on using the repository browser.