Statistics
| Branch: | Revision:

root / trunk / Pithos.ShellExtensions / Menus / FileContextMenu.cs @ 7e26c075

History | View | Annotate | Download (24.1 kB)

1
using System;
2
using System.Collections.Generic;
3
using System.ComponentModel.Composition;
4
using System.Diagnostics;
5
using System.Diagnostics.Contracts;
6
using System.Drawing;
7
using System.Linq;
8
using System.Runtime.InteropServices;
9
using System.Runtime.InteropServices.ComTypes;
10
using System.Text;
11
using Pithos.ShellExtensions.Properties;
12

    
13
namespace Pithos.ShellExtensions.Menus
14
{
15
    [ClassInterface(ClassInterfaceType.None)]
16
    [Guid("B1F1405D-94A1-4692-B72F-FC8CAF8B8700"), ComVisible(true)]
17
    public class FileContextMenu : IShellExtInit, IContextMenu
18
    {
19
        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger("Pithos.FileContextMenu");
20

    
21
        private const string MenuHandlername = "Pithos.FileContextMenu";
22

    
23

    
24
        private readonly Dictionary<string, MenuItem> _items;
25

    
26

    
27
        [Import]
28
        public FileContext Context { get; set; }
29

    
30
        private IntPtr _gotoBitmap=IntPtr.Zero;
31
        private IntPtr _versionBitmap = IntPtr.Zero;
32
        private IntPtr _propertiesBitmap = IntPtr.Zero;
33

    
34
        public FileContextMenu()
35
        {                        
36
            _gotoBitmap = GetBitmapPtr(Resources.MenuGoToPithos);
37
            _versionBitmap = GetBitmapPtr(Resources.MenuHistory);
38
            _propertiesBitmap = GetBitmapPtr(Resources.MenuProperties);
39

    
40

    
41
            
42

    
43
            _items = new Dictionary<string, MenuItem>{
44
                {"gotoPithos",new MenuItem{
45
                                           MenuText = "&Go to Pithos",
46
                                            Verb = "gotoPithos",
47
                                             VerbCanonicalName = "PITHOSGoTo",
48
                                              VerbHelpText = "Go to Pithos",
49
                                               MenuDisplayId = 1,
50
                                               MenuCommand=OnGotoPithos,
51
                                               DisplayFlags=DisplayFlags.All,
52
                                               MenuBitmap = _gotoBitmap
53
                                           }},
54
                /*{"showProperties",new MenuItem{
55
                                           MenuText = "&Pithos File Properties",
56
                                            Verb = "showProperties",
57
                                             VerbCanonicalName = "PITHOSProperties",
58
                                              VerbHelpText = "Pithos File Properties",
59
                                               MenuDisplayId = 2,
60
                                               MenuCommand=OnShowProperties,
61
                                               DisplayFlags=DisplayFlags.File,
62
                                               MenuBitmap = _propertiesBitmap
63
                                           }},*/
64
                {"prevVersions",new MenuItem{
65
                                           MenuText = "&Show Previous Versions",
66
                                            Verb = "prevVersions",
67
                                             VerbCanonicalName = "PITHOSPrevVersions",
68
                                              VerbHelpText = "Go to Pithos and display previous versions",
69
                                               MenuDisplayId = 3,
70
                                               MenuCommand=OnVerbDisplayFileName,
71
                                               DisplayFlags=DisplayFlags.File,
72
                                               MenuBitmap=_versionBitmap
73
                                           }}
74
            };
75

    
76
            IoC.Current.Compose(this);
77

    
78

    
79
        }
80

    
81

    
82
        ~FileContextMenu()
83
        {
84
            if (_gotoBitmap != IntPtr.Zero)
85
            {
86
                NativeMethods.DeleteObject(_gotoBitmap);
87
                _gotoBitmap= IntPtr.Zero;
88
            }
89
            if (_versionBitmap != IntPtr.Zero)
90
            {
91
                NativeMethods.DeleteObject(_versionBitmap);
92
                _versionBitmap = IntPtr.Zero;
93
            }
94
            if (_propertiesBitmap != IntPtr.Zero)
95
            {
96
                NativeMethods.DeleteObject(_propertiesBitmap);
97
                _propertiesBitmap = IntPtr.Zero;
98
            }
99

    
100
        }
101
        private static IntPtr GetBitmapPtr(Bitmap gotoBitmap)
102
        {
103
            gotoBitmap.MakeTransparent(gotoBitmap.GetPixel(0, 0));
104
            var hbitmap = gotoBitmap.GetHbitmap();
105
            return hbitmap;
106
        }
107

    
108
        void OnShowProperties(IntPtr hWnd)
109
        {
110
            
111
        }
112

    
113
        void OnVerbDisplayFileName(IntPtr hWnd)
114
        {
115
            string message = String.Format("The selected file is {0}\r\n\r\nThe selected Path is {1}",
116
                                           Context.CurrentFile,
117
                                           Context.CurrentFolder);
118

    
119
            System.Windows.Forms.MessageBox.Show(
120
                message,
121
                "Pithos Shell Extensions");
122
            NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_ASSOCCHANGED, HChangeNotifyFlags.SHCNF_IDLIST,
123
                                             IntPtr.Zero, IntPtr.Zero);
124
        }
125

    
126
        void OnGotoPithos(IntPtr hWnd)
127
        {
128
            var settings = Context.Settings;
129
            var activeAccount = settings.Accounts.FirstOrDefault(acc =>  Context.CurrentFile.StartsWith(acc.RootPath,StringComparison.InvariantCultureIgnoreCase));
130
            var address = String.Format("{0}/ui/?token={1}&user={2}",
131
                                        settings.PithosSite,
132
                                        activeAccount.ApiKey,
133
                                        Uri.EscapeUriString(activeAccount.AccountName));
134

    
135
            settings.Reload();
136
            Process.Start(address);
137
        }
138
        
139

    
140
        #region Shell Extension Registration
141

    
142
        [ComRegisterFunction]
143
        public static void Register(Type t)
144
        {
145
            try
146
            {
147
                ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, ".cs",
148
                    MenuHandlername);
149
                ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, "Directory",
150
                    MenuHandlername);
151
                ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, @"Directory\Background",
152
                    MenuHandlername);
153
                ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, "*",
154
                    MenuHandlername);
155

    
156
                //ShellExtReg.MarkApproved(t.GUID, MenuHandlername);
157
            }
158
            catch (Exception ex)
159
            {
160
                Console.WriteLine(ex.Message); // Log the error
161
                throw;  // Re-throw the exception
162
            }
163
        }
164

    
165
        [ComUnregisterFunction]
166
        public static void Unregister(Type t)
167
        {
168
            try
169
            {
170
                ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, ".cs", MenuHandlername);
171
                ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, "Directory", MenuHandlername);
172
                ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, @"Directory\Background", MenuHandlername);
173
                ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, "*", MenuHandlername);
174

    
175
                //ShellExtReg.RemoveApproved(t.GUID, MenuHandlername);
176
            }
177
            catch (Exception ex)
178
            {
179
                Console.WriteLine(ex.Message); // Log the error
180
                throw;  // Re-throw the exception
181
            }
182
        }
183

    
184
        #endregion
185

    
186

    
187
        #region IShellExtInit Members
188

    
189
        /// <summary>
190
        /// Initialize the context menu handler.
191
        /// </summary>
192
        /// <param name="pidlFolder">
193
        /// A pointer to an ITEMIDLIST structure that uniquely identifies a folder.
194
        /// </param>
195
        /// <param name="pDataObj">
196
        /// A pointer to an IDataObject interface object that can be used to retrieve 
197
        /// the objects being acted upon.
198
        /// </param>
199
        /// <param name="hKeyProgID">
200
        /// The registry key for the file object or folder type.
201
        /// </param>
202
        public void Initialize(IntPtr pidlFolder, IntPtr pDataObj, IntPtr hKeyProgID)
203
        {
204
            
205
            if(pDataObj == IntPtr.Zero && pidlFolder == IntPtr.Zero)
206
            {
207
                throw new ArgumentException("pidlFolder and pDataObj shouldn't be null at the same time");
208
            }
209

    
210

    
211
            Debug.WriteLine("Initializing", LogCategories.ShellMenu);
212

    
213
            if (pDataObj != IntPtr.Zero)
214
            {
215
                Debug.WriteLine("Got a data object", LogCategories.ShellMenu);
216

    
217
                FORMATETC fe = new FORMATETC();
218
                fe.cfFormat = (short)CLIPFORMAT.CF_HDROP;
219
                fe.ptd = IntPtr.Zero;
220
                fe.dwAspect = DVASPECT.DVASPECT_CONTENT;
221
                fe.lindex = -1;
222
                fe.tymed = TYMED.TYMED_HGLOBAL;
223
                STGMEDIUM stm = new STGMEDIUM();
224

    
225
                // The pDataObj pointer contains the objects being acted upon. In this 
226
                // example, we get an HDROP handle for enumerating the selected files 
227
                // and folders.
228
                IDataObject dataObject = (IDataObject)Marshal.GetObjectForIUnknown(pDataObj);
229
                dataObject.GetData(ref fe, out stm);
230

    
231
                try
232
                {
233
                    // Get an HDROP handle.
234
                    IntPtr hDrop = stm.unionmember;
235
                    if (hDrop == IntPtr.Zero)
236
                    {
237
                        throw new ArgumentException();
238
                    }
239

    
240
                    // Determine how many files are involved in this operation.
241
                    uint nFiles = NativeMethods.DragQueryFile(hDrop, UInt32.MaxValue, null, 0);
242

    
243
                    Debug.WriteLine(String.Format("Got {0} files", nFiles), LogCategories.ShellMenu);
244
                    // This code sample displays the custom context menu item when only 
245
                    // one file is selected. 
246
                    if (nFiles == 1)
247
                    {
248
                        // Get the path of the file.
249
                        var fileName = new StringBuilder(260);
250
                        if (0 == NativeMethods.DragQueryFile(hDrop, 0, fileName,
251
                                                             fileName.Capacity))
252
                        {
253
                            Marshal.ThrowExceptionForHR(WinError.E_FAIL);
254
                        }
255
                        Context.CurrentFile = fileName.ToString();
256
                    }
257
                    /* else
258
                     {
259
                         Marshal.ThrowExceptionForHR(WinError.E_FAIL);
260
                     }*/
261

    
262
                    // [-or-]
263

    
264
                    // Enumerate the selected files and folders.
265
                    //if (nFiles > 0)
266
                    //{
267
                    //    StringCollection selectedFiles = new StringCollection();
268
                    //    StringBuilder fileName = new StringBuilder(260);
269
                    //    for (uint i = 0; i < nFiles; i++)
270
                    //    {
271
                    //        // Get the next file name.
272
                    //        if (0 != NativeMethods.DragQueryFile(hDrop, i, fileName,
273
                    //            fileName.Capacity))
274
                    //        {
275
                    //            // Add the file name to the list.
276
                    //            selectedFiles.Add(fileName.ToString());
277
                    //        }
278
                    //    }
279
                    //
280
                    //    // If we did not find any files we can work with, throw 
281
                    //    // exception.
282
                    //    if (selectedFiles.Count == 0)
283
                    //    {
284
                    //        Marshal.ThrowExceptionForHR(WinError.E_FAIL);
285
                    //    }
286
                    //}
287
                    //else
288
                    //{
289
                    //    Marshal.ThrowExceptionForHR(WinError.E_FAIL);
290
                    //}
291
                }
292
                finally
293
                {
294
                    NativeMethods.ReleaseStgMedium(ref stm);
295
                }
296
            }
297

    
298
            if (pidlFolder != IntPtr.Zero)
299
            {
300
                Debug.WriteLine("Got a folder", LogCategories.ShellMenu);
301
                StringBuilder path = new StringBuilder();
302
                if (!NativeMethods.SHGetPathFromIDList(pidlFolder, path))
303
                {
304
                    int error = Marshal.GetHRForLastWin32Error();
305
                    Marshal.ThrowExceptionForHR(error);
306
                }
307
                Context.CurrentFolder = path.ToString();
308
                Debug.WriteLine(String.Format("Folder is {0}", Context.CurrentFolder), LogCategories.ShellMenu);
309
            }
310
        }
311

    
312
        #endregion
313

    
314

    
315
        #region IContextMenu Members
316

    
317
        /// <summary>
318
        /// Add commands to a shortcut menu.
319
        /// </summary>
320
        /// <param name="hMenu">A handle to the shortcut menu.</param>
321
        /// <param name="iMenu">
322
        /// The zero-based position at which to insert the first new menu item.
323
        /// </param>
324
        /// <param name="idCmdFirst">
325
        /// The minimum value that the handler can specify for a menu item ID.
326
        /// </param>
327
        /// <param name="idCmdLast">
328
        /// The maximum value that the handler can specify for a menu item ID.
329
        /// </param>
330
        /// <param name="uFlags">
331
        /// Optional flags that specify how the shortcut menu can be changed.
332
        /// </param>
333
        /// <returns>
334
        /// If successful, returns an HRESULT value that has its severity value set 
335
        /// to SEVERITY_SUCCESS and its code value set to the offset of the largest 
336
        /// command identifier that was assigned, plus one.
337
        /// </returns>
338
        public int QueryContextMenu(
339
            IntPtr hMenu,
340
            uint iMenu,
341
            uint idCmdFirst,
342
            uint idCmdLast,
343
            uint uFlags)
344
        {
345
            Debug.WriteLine("Start qcm", LogCategories.ShellMenu);
346
            // If uFlags include CMF_DEFAULTONLY then we should not do anything.
347
            Debug.WriteLine(String.Format("Flags {0}", uFlags), LogCategories.ShellMenu);
348

    
349
            if (((uint)CMF.CMF_DEFAULTONLY & uFlags) != 0)
350
            {
351
                Debug.WriteLine("Default only flag, returning", LogCategories.ShellMenu);
352
                return WinError.MAKE_HRESULT(WinError.SEVERITY_SUCCESS, 0, 0);
353
            }
354

    
355
            if (!Context.IsManaged)
356
            {
357
                Debug.WriteLine("Not a PITHOS folder",LogCategories.ShellMenu);
358
                return WinError.MAKE_HRESULT(WinError.SEVERITY_SUCCESS, 0, 0);
359
            }
360

    
361
            /*
362
                        if (!selectedFolder.ToLower().Contains("pithos"))
363
                            return WinError.MAKE_HRESULT(WinError.SEVERITY_SUCCESS, 0, 0);
364
            */
365

    
366
            // Use either InsertMenu or InsertMenuItem to add menu items.
367

    
368
            uint largestID = 0;
369

    
370
            DisplayFlags itemType = (Context.IsFolder) ? DisplayFlags.Folder : DisplayFlags.File;
371

    
372
            Debug.WriteLine(String.Format("Item Flags {0}", itemType), LogCategories.ShellMenu);
373

    
374
            if (!NativeMethods.InsertMenu(hMenu, idCmdFirst, MF.MF_SEPARATOR | MF.MF_BYPOSITION, 0, String.Empty))
375
            {
376
                Log.ErrorFormat("Error adding separator 1\r\n{0}", Marshal.GetLastWin32Error());
377
                return Marshal.GetHRForLastWin32Error();
378
            }
379

    
380
            foreach (var menuItem in _items.Values)
381
            {
382
                Debug.WriteLine(String.Format("Menu Flags {0}", menuItem.DisplayFlags), LogCategories.ShellMenu);
383
                if ((itemType & menuItem.DisplayFlags) != DisplayFlags.None)
384
                {
385
                    Debug.WriteLine("Adding Menu", LogCategories.ShellMenu);
386

    
387
                    MENUITEMINFO mii = menuItem.CreateInfo(idCmdFirst);
388
                    if (!NativeMethods.InsertMenuItem(hMenu, iMenu, true, ref mii))
389
                    {
390
                        var lastError = Marshal.GetLastWin32Error();
391
                        var lastErrorHR = Marshal.GetHRForLastWin32Error();
392
                        return lastErrorHR;
393
                    }
394
                    if (largestID < menuItem.MenuDisplayId)
395
                        largestID = menuItem.MenuDisplayId;
396
                }
397
            }
398

    
399
            Debug.Write("Adding Separator 1", LogCategories.ShellMenu);
400
            // Add a separator.
401
           /* MENUITEMINFO sep = new MENUITEMINFO();
402
            sep.cbSize = (uint)Marshal.SizeOf(sep);
403
            sep.fMask = MIIM.MIIM_TYPE;
404
            sep.fType = MFT.MFT_SEPARATOR;*/
405
            if (!NativeMethods.InsertMenu(hMenu, (uint)_items.Values.Count + idCmdFirst+1,MF.MF_SEPARATOR|MF.MF_BYPOSITION, 0, String.Empty))
406
            {
407
                Log.ErrorFormat("Error adding separator 1\r\n{0}", Marshal.GetLastWin32Error());
408
                return Marshal.GetHRForLastWin32Error();
409
            }
410

    
411

    
412

    
413

    
414
            Debug.WriteLine("Menus added", LogCategories.ShellOverlays);
415
            // Return an HRESULT value with the severity set to SEVERITY_SUCCESS. 
416
            // Set the code value to the offset of the largest command identifier 
417
            // that was assigned, plus one (1).
418
            return WinError.MAKE_HRESULT(WinError.SEVERITY_SUCCESS, 0,
419
                largestID + 1);
420
        }
421

    
422
        /// <summary>
423
        /// Carry out the command associated with a shortcut menu item.
424
        /// </summary>
425
        /// <param name="pici">
426
        /// A pointer to a CMINVOKECOMMANDINFO or CMINVOKECOMMANDINFOEX structure 
427
        /// containing information about the command. 
428
        /// </param>
429
        public void InvokeCommand(IntPtr pici)
430
        {
431
            bool isUnicode = false;
432

    
433
            // Determine which structure is being passed in, CMINVOKECOMMANDINFO or 
434
            // CMINVOKECOMMANDINFOEX based on the cbSize member of lpcmi. Although 
435
            // the lpcmi parameter is declared in Shlobj.h as a CMINVOKECOMMANDINFO 
436
            // structure, in practice it often points to a CMINVOKECOMMANDINFOEX 
437
            // structure. This struct is an extended version of CMINVOKECOMMANDINFO 
438
            // and has additional members that allow Unicode strings to be passed.
439
            CMINVOKECOMMANDINFO ici = (CMINVOKECOMMANDINFO)Marshal.PtrToStructure(
440
                pici, typeof(CMINVOKECOMMANDINFO));
441
            CMINVOKECOMMANDINFOEX iciex = new CMINVOKECOMMANDINFOEX();
442
            if (ici.cbSize == Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX)))
443
            {
444
                if ((ici.fMask & CMIC.CMIC_MASK_UNICODE) != 0)
445
                {
446
                    isUnicode = true;
447
                    iciex = (CMINVOKECOMMANDINFOEX)Marshal.PtrToStructure(pici,
448
                        typeof(CMINVOKECOMMANDINFOEX));
449
                }
450
            }
451

    
452
            // Determines whether the command is identified by its offset or verb.
453
            // There are two ways to identify commands:
454
            // 
455
            //   1) The command's verb string 
456
            //   2) The command's identifier offset
457
            // 
458
            // If the high-order word of lpcmi->lpVerb (for the ANSI case) or 
459
            // lpcmi->lpVerbW (for the Unicode case) is nonzero, lpVerb or lpVerbW 
460
            // holds a verb string. If the high-order word is zero, the command 
461
            // offset is in the low-order word of lpcmi->lpVerb.
462

    
463
            // For the ANSI case, if the high-order word is not zero, the command's 
464
            // verb string is in lpcmi->lpVerb. 
465
            if (!isUnicode && NativeMethods.HighWord(ici.verb.ToInt32()) != 0)
466
            {
467
                // Is the verb supported by this context menu extension?
468
                string verbAnsi = Marshal.PtrToStringAnsi(ici.verb);
469
                if (_items.ContainsKey(verbAnsi))
470
                {
471
                    _items[verbAnsi].MenuCommand(ici.hwnd);
472
                }
473
                else
474
                {
475
                    // If the verb is not recognized by the context menu handler, it 
476
                    // must return E_FAIL to allow it to be passed on to the other 
477
                    // context menu handlers that might implement that verb.
478
                    Marshal.ThrowExceptionForHR(WinError.E_FAIL);
479
                }
480
            }
481

    
482
                // For the Unicode case, if the high-order word is not zero, the 
483
            // command's verb string is in lpcmi->lpVerbW. 
484
            else if (isUnicode && NativeMethods.HighWord(iciex.verbW.ToInt32()) != 0)
485
            {
486
                // Is the verb supported by this context menu extension?
487
                string verbUTF = Marshal.PtrToStringUni(iciex.verbW);
488
                if (_items.ContainsKey(verbUTF))
489
                {
490
                    _items[verbUTF].MenuCommand(ici.hwnd);
491
                }
492
                else
493
                {
494
                    // If the verb is not recognized by the context menu handler, it 
495
                    // must return E_FAIL to allow it to be passed on to the other 
496
                    // context menu handlers that might implement that verb.
497
                    Marshal.ThrowExceptionForHR(WinError.E_FAIL);
498
                }
499
            }
500

    
501
                // If the command cannot be identified through the verb string, then 
502
            // check the identifier offset.
503
            else
504
            {
505
                // Is the command identifier offset supported by this context menu 
506
                // extension?
507
                int menuID = NativeMethods.LowWord(ici.verb.ToInt32());
508
                var menuItem = _items.FirstOrDefault(item => item.Value.MenuDisplayId == menuID).Value;
509
                if (menuItem != null)
510
                {
511
                    menuItem.MenuCommand(ici.hwnd);
512
                }
513
                else
514
                {
515
                    // If the verb is not recognized by the context menu handler, it 
516
                    // must return E_FAIL to allow it to be passed on to the other 
517
                    // context menu handlers that might implement that verb.
518
                    Marshal.ThrowExceptionForHR(WinError.E_FAIL);
519
                }
520
            }
521
        }
522

    
523
        /// <summary>
524
        /// Get information about a shortcut menu command, including the help string 
525
        /// and the language-independent, or canonical, name for the command.
526
        /// </summary>
527
        /// <param name="idCmd">Menu command identifier offset.</param>
528
        /// <param name="uFlags">
529
        /// Flags specifying the information to return. This parameter can have one 
530
        /// of the following values: GCS_HELPTEXTA, GCS_HELPTEXTW, GCS_VALIDATEA, 
531
        /// GCS_VALIDATEW, GCS_VERBA, GCS_VERBW.
532
        /// </param>
533
        /// <param name="pReserved">Reserved. Must be IntPtr.Zero</param>
534
        /// <param name="pszName">
535
        /// The address of the buffer to receive the null-terminated string being 
536
        /// retrieved.
537
        /// </param>
538
        /// <param name="cchMax">
539
        /// Size of the buffer, in characters, to receive the null-terminated string.
540
        /// </param>
541
        public void GetCommandString(
542
            UIntPtr idCmd,
543
            uint uFlags,
544
            IntPtr pReserved,
545
            StringBuilder pszName,
546
            uint cchMax)
547
        {
548
            uint menuID = idCmd.ToUInt32();
549
            var menuItem = _items.FirstOrDefault(item => item.Value.MenuDisplayId == menuID).Value;
550
            if (menuItem != null)
551
            {
552
                switch ((GCS)uFlags)
553
                {
554
                    case GCS.GCS_VERBW:
555
                        if (menuItem.VerbCanonicalName.Length > cchMax - 1)
556
                        {
557
                            Marshal.ThrowExceptionForHR(WinError.STRSAFE_E_INSUFFICIENT_BUFFER);
558
                        }
559
                        else
560
                        {
561
                            pszName.Clear();
562
                            pszName.Append(menuItem.VerbCanonicalName);
563
                        }
564
                        break;
565

    
566
                    case GCS.GCS_HELPTEXTW:
567
                        if (menuItem.VerbHelpText.Length > cchMax - 1)
568
                        {
569
                            Marshal.ThrowExceptionForHR(WinError.STRSAFE_E_INSUFFICIENT_BUFFER);
570
                        }
571
                        else
572
                        {
573
                            pszName.Clear();
574
                            pszName.Append(menuItem.VerbHelpText);
575
                        }
576
                        break;
577
                }
578
            }
579
        }
580

    
581
        #endregion
582
    }
583
}