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