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