Additional fix for starting tab. Preferences now comes into focus whenever it is...
[pithos-ms-client] / trunk / Pithos.Client.WPF / Preferences / PreferencesViewModel.cs
1 #region\r
2 /* -----------------------------------------------------------------------\r
3  * <copyright file="PreferencesViewModel.cs" company="GRNet">\r
4  * \r
5  * Copyright 2011-2012 GRNET S.A. All rights reserved.\r
6  *\r
7  * Redistribution and use in source and binary forms, with or\r
8  * without modification, are permitted provided that the following\r
9  * conditions are met:\r
10  *\r
11  *   1. Redistributions of source code must retain the above\r
12  *      copyright notice, this list of conditions and the following\r
13  *      disclaimer.\r
14  *\r
15  *   2. Redistributions in binary form must reproduce the above\r
16  *      copyright notice, this list of conditions and the following\r
17  *      disclaimer in the documentation and/or other materials\r
18  *      provided with the distribution.\r
19  *\r
20  *\r
21  * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS\r
22  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
23  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
24  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR\r
25  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
28  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\r
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
32  * POSSIBILITY OF SUCH DAMAGE.\r
33  *\r
34  * The views and conclusions contained in the software and\r
35  * documentation are those of the authors and should not be\r
36  * interpreted as representing official policies, either expressed\r
37  * or implied, of GRNET S.A.\r
38  * </copyright>\r
39  * -----------------------------------------------------------------------\r
40  */\r
41 #endregion\r
42 \r
43 \r
44 using System.Collections.Concurrent;\r
45 using System.Collections.Generic;\r
46 using System.Collections.Specialized;\r
47 using System.ComponentModel.Composition;\r
48 using System.Diagnostics;\r
49 using System.IO;\r
50 using System.Net;\r
51 using System.Reflection;\r
52 using System.Threading.Tasks;\r
53 using System.Windows;\r
54 using System.Windows.Forms;\r
55 using Caliburn.Micro;\r
56 using Pithos.Client.WPF.Configuration;\r
57 using Pithos.Client.WPF.Properties;\r
58 using Pithos.Client.WPF.SelectiveSynch;\r
59 using Pithos.Client.WPF.Utils;\r
60 using Pithos.Core;\r
61 using Pithos.Core.Agents;\r
62 using Pithos.Interfaces;\r
63 using System;\r
64 using System.Linq;\r
65 using Pithos.Network;\r
66 using MessageBox = System.Windows.MessageBox;\r
67 using Screen = Caliburn.Micro.Screen;\r
68 \r
69 namespace Pithos.Client.WPF.Preferences\r
70 {\r
71     /// <summary>\r
72     /// The preferences screen displays user and account settings and updates the PithosMonitor\r
73     /// classes when account settings change.\r
74     /// </summary>\r
75     /// <remarks>\r
76     /// The class is a single ViewModel for all Preferences tabs. It can be broken in separate\r
77     /// ViewModels, one for each tab.\r
78     /// </remarks>\r
79     [Export, PartCreationPolicy(CreationPolicy.Shared)]\r
80     public class PreferencesViewModel : Screen\r
81     {\r
82         private readonly IEventAggregator _events;\r
83 \r
84         //Logging in the Pithos client is provided by log4net\r
85         private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);\r
86 \r
87         private PithosSettings _settings;\r
88         public PithosSettings Settings\r
89         {\r
90             get { return _settings; }\r
91             set\r
92             {\r
93                 _settings = value;\r
94                 NotifyOfPropertyChange(()=>Settings);\r
95             }\r
96         }\r
97 \r
98         private ObservableConcurrentCollection<AccountViewModel> _accounts;\r
99         public ObservableConcurrentCollection<AccountViewModel> Accounts\r
100         {\r
101             get { return _accounts; }\r
102             set \r
103             { \r
104                 _accounts = value;\r
105                 NotifyOfPropertyChange(()=>Accounts);\r
106             }\r
107         }\r
108         \r
109         public bool StartOnSystemStartup { get; set; }\r
110 \r
111         public ShellViewModel Shell { get;  set; }\r
112         //ShellExtensionController _extensionController=new ShellExtensionController();\r
113 \r
114         [ImportingConstructor]\r
115         public PreferencesViewModel(IWindowManager windowManager, IEventAggregator events, ShellViewModel shell, PithosSettings settings)\r
116             :this(windowManager,events,shell,settings,null)\r
117         {\r
118             this.DisplayName = "Pithos+ Preferences";\r
119         }\r
120 \r
121         public PreferencesViewModel(IWindowManager windowManager, IEventAggregator events, ShellViewModel shell, PithosSettings settings, string currentTab)\r
122         {\r
123             // ReSharper disable DoNotCallOverridableMethodsInConstructor\r
124             //Caliburn.Micro uses DisplayName for the view's title\r
125             DisplayName = "Pithos+ Preferences";\r
126             // ReSharper restore DoNotCallOverridableMethodsInConstructor\r
127 \r
128             _windowManager = windowManager;\r
129             _events = events;\r
130 \r
131             Shell = shell;\r
132             \r
133             Settings=settings;\r
134             Accounts = new ObservableConcurrentCollection<AccountViewModel>();\r
135             if (settings.Accounts == null)\r
136             {\r
137                 settings.Accounts=new AccountsCollection();\r
138                 settings.Save();\r
139             }\r
140             var accountVMs = from account in settings.Accounts\r
141                              select new AccountViewModel(account);\r
142 \r
143             Accounts.AddFromEnumerable(accountVMs);\r
144             \r
145             var startupPath = Environment.GetFolderPath(Environment.SpecialFolder.Startup);\r
146             _shortcutPath = Path.Combine(startupPath, "Pithos.lnk");\r
147 \r
148 \r
149             StartOnSystemStartup = File.Exists(_shortcutPath);\r
150 \r
151             SelectedTab = currentTab;\r
152         }\r
153 \r
154         private string _selectedTab="General";\r
155         public string SelectedTab\r
156         {\r
157             get { return _selectedTab; }\r
158             set\r
159             {\r
160                 _selectedTab = value??"GeneralTab";\r
161                 NotifyOfPropertyChange(()=>SelectedTab);\r
162                 NotifyOfPropertyChange(() => AccountTabSelected);\r
163             }\r
164         }\r
165 \r
166 \r
167         public bool AccountTabSelected\r
168         {\r
169             get { return _selectedTab == "AccountTab"; }\r
170         }\r
171         #region Preferences Properties\r
172 \r
173         private bool _noProxy;\r
174         public bool NoProxy\r
175         {\r
176             get { return _noProxy; }\r
177             set\r
178             {\r
179                 _noProxy = value;\r
180                 NotifyOfPropertyChange(()=>NoProxy);\r
181             }\r
182         }\r
183 \r
184 \r
185         private bool _defaultProxy;\r
186 \r
187         public bool DefaultProxy\r
188         {\r
189             get { return _defaultProxy; }\r
190             set\r
191             {\r
192                 _defaultProxy = value;\r
193                 NotifyOfPropertyChange(() => DefaultProxy);\r
194             }\r
195         }\r
196 \r
197 \r
198         private bool _manualProxy;\r
199 \r
200         public bool ManualProxy\r
201         {\r
202             get { return _manualProxy; }\r
203             set\r
204             {\r
205                 _manualProxy = value;\r
206                 NotifyOfPropertyChange(() => ManualProxy);\r
207             }\r
208         }\r
209         #endregion\r
210 \r
211 \r
212         public int StartupDelay\r
213         {\r
214             get { return (int) Settings.StartupDelay.TotalMinutes; }\r
215             set\r
216             {\r
217                 if (value<0)\r
218                     throw new ArgumentOutOfRangeException("value",Resources.PreferencesViewModel_StartupDelay_Greater_or_equal_to_0);\r
219                 Settings.StartupDelay = TimeSpan.FromMinutes(value);\r
220                 NotifyOfPropertyChange(()=>StartupDelay);\r
221             }\r
222         }\r
223        \r
224         #region Commands\r
225         \r
226         public bool CanSelectiveSyncFolders\r
227         {\r
228             get { return CurrentAccount != null && CurrentAccount.SelectiveSyncEnabled; }\r
229         }\r
230 \r
231         public void SelectiveSyncFolders()\r
232         {\r
233             //var monitor = Shell.Monitors[CurrentAccount.AccountKey];\r
234             \r
235 \r
236             var model = new SelectiveSynchViewModel(_events,CurrentAccount.Account,CurrentAccount.ApiKey,false);            \r
237             if (_windowManager.ShowDialog(model) == true)\r
238             {\r
239                 \r
240             }\r
241         }\r
242 \r
243        /* private bool _networkTracing;\r
244         public bool NetworkTracing\r
245         {\r
246             get { return _networkTracing; }\r
247             set\r
248             {\r
249                 _networkTracing = value;\r
250                 \r
251             }\r
252         }*/\r
253 \r
254         public void RefreshApiKey()\r
255         {\r
256             //_events.Publish(new Notification { Title = "Authorization failed", Message = "Your API Key has probably expired. You will be directed to a page where you can renew it", Level = TraceLevel.Error });\r
257             if (CurrentAccount == null)\r
258                 return;\r
259             try\r
260             {\r
261 \r
262                 var name = CurrentAccount.AccountName;\r
263 \r
264                 var loginUri = new Uri(new Uri(CurrentAccount.ServerUrl), "login");\r
265                 var credentials = PithosAccount.RetrieveCredentials(loginUri.ToString(),name);\r
266                 if (credentials==null)\r
267                     return;\r
268                 //The server will return credentials for a different account, not just the current account\r
269                 //We need to find the correct account first\r
270                 var account = Accounts.First(act => act.AccountName == credentials.UserName && act.ServerUrl == CurrentAccount.ServerUrl);\r
271                 account.ApiKey = credentials.Password;                \r
272                 account.IsExpired = false;\r
273                 Settings.Save();\r
274                 TaskEx.Delay(10000).ContinueWith(_ =>Shell.MonitorAccount(account.Account));\r
275                 NotifyOfPropertyChange(() => Accounts);\r
276             }\r
277             catch (AggregateException exc)\r
278             {\r
279                 string message = String.Format("API Key retrieval failed");\r
280                 Log.Error(message, exc.InnerException);\r
281                 _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });\r
282             }\r
283             catch (Exception exc)\r
284             {\r
285                 string message = String.Format("API Key retrieval failed");\r
286                 Log.Error(message, exc);\r
287                 _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });\r
288             }\r
289 \r
290         }\r
291 \r
292     \r
293         public void OpenLogPath()\r
294         {\r
295             Shell.OpenLogPath();\r
296         }\r
297 \r
298         public void OpenLogConsole()\r
299         {\r
300             var logView=IoC.Get<LogConsole.LogConsoleViewModel>();            \r
301             _windowManager.ShowWindow(logView);\r
302         }\r
303 \r
304         public void SaveChanges()\r
305         {\r
306             DoSave();\r
307             TryClose(/*true*/);\r
308         }\r
309 \r
310         public void RejectChanges()\r
311         {\r
312             Settings.Reload();\r
313             TryClose(/*false*/);\r
314         }\r
315 \r
316         public void ApplyChanges()\r
317         {\r
318             DoSave();\r
319         }\r
320 \r
321         private void DoSave()\r
322         {\r
323             //SetStartupMode();            \r
324 \r
325             //Ensure we save the settings changes first\r
326             foreach (var account in _accountsToRemove)\r
327             {\r
328                 Settings.Accounts.Remove(account);\r
329             }\r
330 \r
331             foreach (var account in _accountsToAdd)\r
332             {\r
333                 Settings.Accounts.Add(account);    \r
334             }\r
335 \r
336             Settings.Save();\r
337 \r
338 \r
339             try\r
340             {\r
341                 foreach (var account in _accountsToRemove)\r
342                 {\r
343                     Shell.RemoveMonitor(account.ServerUrl, account.AccountName);\r
344                     Shell.RemoveAccountFromDatabase(account);\r
345                 }\r
346 \r
347                 foreach (var account in Settings.Accounts)\r
348                 {\r
349                     Shell.MonitorAccount(account);\r
350                 }\r
351 \r
352                 var poller=IoC.Get<PollAgent>();\r
353                 poller.SynchNow();\r
354             }                \r
355             finally\r
356             {\r
357                 _accountsToRemove.Clear();\r
358                 _accountsToAdd.Clear();\r
359             }\r
360 \r
361             NotifyOfPropertyChange(()=>Settings);\r
362 \r
363             if (IgnoreCertificateErrors)\r
364                 ServicePointManager.ServerCertificateValidationCallback= (sender,certificate,chain,errors)=> true;\r
365             else\r
366             {\r
367                 ServicePointManager.ServerCertificateValidationCallback = null;\r
368             }\r
369         }\r
370 \r
371      /*   public void ChangePithosFolder()\r
372         {\r
373             var browser = new FolderBrowserDialog();\r
374             browser.SelectedPath = Settings.PithosPath;\r
375             var result = browser.ShowDialog((IWin32Window)GetView());\r
376             if (result == DialogResult.OK)\r
377             {\r
378                 var newPath = browser.SelectedPath;\r
379                 var accountName = CurrentAccount.AccountName;\r
380                 var monitor = Shell.Monitors[accountName];\r
381                 monitor.Stop();\r
382                 \r
383                 Shell.Monitors.Remove(accountName);\r
384 \r
385                 Directory.Move(Settings.PithosPath, newPath);\r
386                 Settings.PithosPath = newPath;\r
387                 Settings.Save();\r
388 \r
389                 Shell.MonitorAccount(CurrentAccount);\r
390 \r
391                 NotifyOfPropertyChange(() => Settings);                \r
392             }\r
393         }\r
394 */\r
395 \r
396 \r
397         readonly List<AccountSettings> _accountsToAdd=new List<AccountSettings>();\r
398        public void AddAccount()\r
399        {\r
400            var wizard = new AddAccountViewModel();\r
401            if (_windowManager.ShowDialog(wizard) == true)\r
402            {\r
403                string selectedPath = wizard.AccountPath;\r
404                var initialRootPath = wizard.ShouldCreateOkeanosFolder?\r
405                    Path.Combine(selectedPath, "Okeanos")\r
406                    :selectedPath;\r
407                var actualRootPath= initialRootPath;\r
408                if (wizard.ShouldCreateOkeanosFolder)\r
409                {\r
410                    int attempt = 1;\r
411                    while (Directory.Exists(actualRootPath) || File.Exists(actualRootPath))\r
412                    {\r
413                        actualRootPath = String.Format("{0} {1}", initialRootPath, attempt++);\r
414                    }\r
415                }\r
416 \r
417 \r
418 \r
419                var account = Accounts.FirstOrDefault(act => act.AccountName == wizard.AccountName && act.ServerUrl == wizard.CurrentServer);\r
420                if (account != null)\r
421                {\r
422                    if (MessageBox.Show("The account you specified already exists. Do you want to update it?", "The account exists") == MessageBoxResult.Yes)\r
423                    {\r
424                        account.ApiKey = wizard.Token;\r
425                        account.IsExpired = false;\r
426                        CurrentAccount = account;\r
427                    }\r
428                }\r
429                else\r
430                {\r
431                    var newAccount = new AccountSettings\r
432                                         {\r
433                                             AccountName = wizard.AccountName,\r
434                                             ServerUrl = wizard.CurrentServer,\r
435                                             ApiKey = wizard.Token,\r
436                                             RootPath = actualRootPath,\r
437                                             IsActive = wizard.IsAccountActive,\r
438                                         };\r
439 \r
440 \r
441                    var client = new CloudFilesClient(newAccount.AccountName, newAccount.ApiKey)\r
442                                     {\r
443                                         AuthenticationUrl = newAccount.ServerUrl, UsePithos = true\r
444                                     };\r
445                    client.Authenticate();\r
446 \r
447 \r
448                    var containers = client.ListContainers(newAccount.AccountName);\r
449                    var containerUris = from container in containers\r
450                                        select String.Format(@"{0}/v1/{1}/{2}",\r
451                                             newAccount.ServerUrl, newAccount.AccountName, container.Name);\r
452                    \r
453                    newAccount.SelectiveFolders.AddRange(containerUris.ToArray());\r
454 \r
455                    var objectInfos = (from container in containers\r
456                                       from dir in client.ListObjects(newAccount.AccountName, container.Name)\r
457                                       where container.Name != "trash"\r
458                                       select dir).ToList();\r
459                    var tree = objectInfos.ToTree();\r
460 \r
461                    var selected = (from root in tree \r
462                                    from child in root \r
463                                    select child.Uri.ToString()).ToArray();\r
464                    newAccount.SelectiveFolders.AddRange(selected);\r
465 \r
466                    \r
467                    //TODO:Add the "pithos" container as a default selection                   \r
468 \r
469                    _accountsToAdd.Add(newAccount);\r
470                    var accountVm = new AccountViewModel(newAccount);\r
471                    (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVm);\r
472                    CurrentAccount = accountVm;\r
473                }\r
474                NotifyOfPropertyChange(() => Accounts);\r
475                NotifyOfPropertyChange(() => Settings);   \r
476            }\r
477 \r
478 \r
479             \r
480        }\r
481 \r
482 /*\r
483         public void AddPithosAccount()\r
484        {\r
485             var credentials=PithosAccount.RetrieveCredentials(null);\r
486             if (credentials == null)\r
487                 return;\r
488             var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);\r
489             var accountVM = new AccountViewModel(account);\r
490             if (account == null)\r
491             {\r
492                 account=new AccountSettings{\r
493                     AccountName=credentials.UserName,\r
494                     ApiKey=credentials.Password\r
495                 };\r
496                 Settings.Accounts.Add(account);\r
497                 accountVM = new AccountViewModel(account);\r
498                 (Accounts as IProducerConsumerCollection<AccountViewModel>).TryAdd(accountVM);\r
499             }\r
500             else\r
501             {\r
502                 account.ApiKey=credentials.Password;\r
503             }\r
504             //SelectedAccountIndex= Settings.Accounts.IndexOf(account);\r
505             CurrentAccount = accountVM;\r
506             NotifyOfPropertyChange(() => Accounts);\r
507             NotifyOfPropertyChange(()=>Settings);                       \r
508        }\r
509 */\r
510 \r
511 \r
512         readonly List<AccountSettings> _accountsToRemove = new List<AccountSettings>();\r
513         public void RemoveAccount()\r
514         {\r
515             Accounts.TryRemove(CurrentAccount);\r
516             _accountsToRemove.Add(CurrentAccount.Account);\r
517 \r
518             CurrentAccount = null;\r
519             NotifyOfPropertyChange(() => Accounts);\r
520 \r
521             \r
522             //NotifyOfPropertyChange("Settings.Accounts");\r
523         }\r
524 \r
525         public bool CanRemoveAccount\r
526         {\r
527             get { return (CurrentAccount != null); }\r
528         }\r
529 \r
530         public bool CanClearAccountCache\r
531         {\r
532             get { return (CurrentAccount != null); }\r
533         }\r
534 \r
535         public void ClearAccountCache()\r
536         {\r
537             if (MessageBoxResult.Yes == MessageBox.Show("You are about to delete all partially downloaded files from the account's cache.\n" +\r
538                             " You will have to download all partially downloaded data again\n" +\r
539                             "This change can not be undone\n\n" +\r
540             "Do you wish to delete all partially downloaded data?", "Warning! Clearing account cache",\r
541                             MessageBoxButton.YesNo,MessageBoxImage.Question,MessageBoxResult.No))\r
542             {\r
543 \r
544                 var cachePath = Path.Combine(CurrentAccount.RootPath, FolderConstants.CacheFolder);\r
545                 var dir = new DirectoryInfo(cachePath);\r
546                 //The file may not exist if we just created the account\r
547                 if (!dir.Exists)\r
548                     return;\r
549                 dir.EnumerateFiles().Apply(file=>file.Delete());\r
550                 dir.EnumerateDirectories().Apply(folder => folder.Delete(true));\r
551             }\r
552         }\r
553 \r
554 \r
555         public bool ExtensionsActivated\r
556         {\r
557             get { return Settings.ExtensionsActivated; }\r
558             set\r
559             {\r
560                 if (Settings.ExtensionsActivated == value)\r
561                     return;\r
562 \r
563                 Settings.ExtensionsActivated = value;\r
564 \r
565 /*\r
566                 if (value)\r
567                     _extensionController.RegisterExtensions();\r
568                 else\r
569                 {\r
570                     _extensionController.UnregisterExtensions();\r
571                 }\r
572 */\r
573                 NotifyOfPropertyChange(() => ExtensionsActivated);\r
574             }\r
575         }\r
576 \r
577         public bool DebugLoggingEnabled\r
578         {\r
579             get { return Settings.DebugLoggingEnabled; }\r
580             set { \r
581                 Settings.DebugLoggingEnabled = value;\r
582                 NotifyOfPropertyChange(()=>DebugLoggingEnabled);\r
583             }\r
584         }\r
585 \r
586         public bool IgnoreCertificateErrors\r
587         {\r
588             get { return Settings.IgnoreCertificateErrors; }\r
589             set {\r
590                 Settings.IgnoreCertificateErrors = value;\r
591                 NotifyOfPropertyChange(() => IgnoreCertificateErrors);\r
592             }\r
593         }\r
594        \r
595         #endregion\r
596 \r
597        /* private int _selectedAccountIndex;\r
598         public int SelectedAccountIndex\r
599         {\r
600             get { return _selectedAccountIndex; }\r
601             set\r
602             {\r
603                 //var accountCount=Settings.Accounts.Count;\r
604                 //if (accountCount == 0)\r
605                 //    return;\r
606                 //if (0 <= value && value < accountCount)\r
607                 //    _selectedAccountIndex = value;\r
608                 //else\r
609                 //    _selectedAccountIndex = 0;\r
610                 _selectedAccountIndex = value;\r
611                 NotifyOfPropertyChange(() => CurrentAccount);\r
612                 NotifyOfPropertyChange(() => CanRemoveAccount);\r
613                 NotifyOfPropertyChange(()=>SelectedAccountIndex);\r
614             }\r
615         }*/\r
616 \r
617         private AccountViewModel _currentAccount;\r
618         private readonly IWindowManager _windowManager;\r
619         private readonly string _shortcutPath;\r
620 \r
621 \r
622         \r
623         public AccountViewModel CurrentAccount\r
624         {\r
625             get { return _currentAccount; }\r
626             set\r
627             {\r
628                 _currentAccount = value;\r
629 \r
630                 if (_currentAccount!=null)\r
631                     _currentAccount.PropertyChanged += (o, e) => NotifyOfPropertyChange(() => CanSelectiveSyncFolders);\r
632 \r
633                 NotifyOfPropertyChange(() => CurrentAccount);\r
634                 NotifyOfPropertyChange(() => CanRemoveAccount);\r
635                 NotifyOfPropertyChange(() => CanSelectiveSyncFolders);\r
636                 NotifyOfPropertyChange(() => CanMoveAccountFolder);\r
637                 NotifyOfPropertyChange(() => CanClearAccountCache);\r
638             }\r
639         }\r
640 \r
641 /*\r
642         public AccountSettings CurrentAccount\r
643         {\r
644             get {\r
645                 if (0 <= SelectedAccountIndex && SelectedAccountIndex < Settings.Accounts.Count)                    \r
646                     return Settings.Accounts[SelectedAccountIndex];\r
647                 return null;\r
648             }\r
649 \r
650         }\r
651 */\r
652 \r
653 \r
654         public bool CanMoveAccountFolder\r
655         {\r
656             get { return CurrentAccount != null; }\r
657         }\r
658 \r
659     public void MoveAccountFolder()\r
660     {\r
661 \r
662         using (var dlg = new FolderBrowserDialog())\r
663         {\r
664             var currentFolder = CurrentAccount.RootPath;\r
665             dlg.SelectedPath = currentFolder;\r
666             //Ask the user to select a folder\r
667             //Note: We need a parent window here, which we retrieve with GetView            \r
668             var view = (Window)GetView();            \r
669             if (DialogResult.OK != dlg.ShowDialog(new Wpf32Window(view)))\r
670                 return;            \r
671 \r
672             var newPath= dlg.SelectedPath;                \r
673             //Find the account's monitor and stop it\r
674             PithosMonitor monitor;\r
675             if (Shell.Monitors.TryGetValue(CurrentAccount.AccountKey, out monitor))\r
676             {\r
677                 monitor.Stop();\r
678 \r
679 \r
680                 var oldPath = monitor.RootPath;\r
681                 //The old directory may not exist eg. if we create an account for the first time\r
682                 if (Directory.Exists(oldPath))\r
683                 {\r
684                     //If it does, do the move\r
685 \r
686                     //Now Create all of the directories\r
687                     foreach (string dirPath in Directory.EnumerateDirectories(oldPath, "*",\r
688                                                            SearchOption.AllDirectories))\r
689                         Directory.CreateDirectory(dirPath.Replace(oldPath, newPath));\r
690 \r
691                     //Copy all the files\r
692                     foreach (string newFilePath in Directory.EnumerateFiles(oldPath, "*.*",\r
693                                                                             SearchOption.AllDirectories))\r
694                         File.Copy(newFilePath, newFilePath.Replace(oldPath, newPath));\r
695 \r
696                     Log.InfoFormat("Deleting account folder {0}",oldPath);\r
697                     Directory.Delete(oldPath, true);\r
698 \r
699                     //We also need to change the path of the existing file states\r
700                     monitor.MoveFileStates(oldPath, newPath);\r
701                 }\r
702             }\r
703             //Replace the old rootpath with the new\r
704             CurrentAccount.RootPath = newPath;\r
705             //TODO: This will save all settings changes. Too coarse grained, need to fix at a later date\r
706             Settings.Save();            \r
707             //And start the monitor on the new RootPath            \r
708             if (monitor != null)\r
709             {\r
710                 monitor.RootPath = newPath;\r
711                 if (CurrentAccount.IsActive)\r
712                     monitor.Start();\r
713             }\r
714             else\r
715                 Shell.MonitorAccount(CurrentAccount.Account);\r
716             //Finally, notify that the Settings, CurrentAccount have changed\r
717             NotifyOfPropertyChange(() => CurrentAccount);\r
718             NotifyOfPropertyChange(() => Settings);\r
719 \r
720         }\r
721     }\r
722     }\r
723 }\r