Added Polling interval property and setting
[pithos-ms-client] / trunk / Pithos.Client.WPF / Shell / ShellViewModel.cs
index 4a9881c..d0ca919 100644 (file)
@@ -1,6 +1,4 @@
 using System.Collections.Concurrent;
-using System.ComponentModel;
-using System.ComponentModel.Composition;
 using System.Diagnostics;
 using System.Diagnostics.Contracts;
 using System.IO;
@@ -8,14 +6,12 @@ using System.Net;
 using System.Reflection;
 using System.Runtime.InteropServices;
 using System.ServiceModel;
-using System.ServiceModel.Description;
 using System.Threading.Tasks;
 using System.Windows;
 using Caliburn.Micro;
 using Hardcodet.Wpf.TaskbarNotification;
 using Pithos.Client.WPF.Configuration;
 using Pithos.Client.WPF.FileProperties;
-using Pithos.Client.WPF.Properties;
 using Pithos.Client.WPF.SelectiveSynch;
 using Pithos.Client.WPF.Services;
 using Pithos.Client.WPF.Shell;
@@ -24,14 +20,13 @@ using Pithos.Interfaces;
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Text;
 using Pithos.Network;
 using StatusService = Pithos.Client.WPF.Services.StatusService;
 
 namespace Pithos.Client.WPF {
-    using System.ComponentModel.Composition;
+       using System.ComponentModel.Composition;
 
-    
+       
        ///<summary>
        /// The "shell" of the Pithos application displays the taskbar  icon, menu and notifications.
        /// The shell also hosts the status service called by shell extensions to retrieve file info
@@ -44,19 +39,19 @@ namespace Pithos.Client.WPF {
        /// * ShowFilePropertiesEvent: Raised when a shell command requests the display of the file/container properties dialog
        ///</remarks>           
        //TODO: CODE SMELL Why does the shell handle the SelectiveSynchChanges?
-    [Export(typeof(IShell))]
-    public class ShellViewModel : Screen, IStatusNotification, IShell,
-        IHandle<Notification>, IHandle<SelectiveSynchChanges>, IHandle<ShowFilePropertiesEvent>
-    {
+       [Export(typeof(IShell))]
+       public class ShellViewModel : Screen, IStatusNotification, IShell,
+               IHandle<Notification>, IHandle<SelectiveSynchChanges>, IHandle<ShowFilePropertiesEvent>
+       {
                //The Status Checker provides the current synch state
                //TODO: Could we remove the status checker and use events in its place?
-        private IStatusChecker _statusChecker;
-        private IEventAggregator _events;
+               private readonly IStatusChecker _statusChecker;
+               private readonly IEventAggregator _events;
 
-        public PithosSettings Settings { get; private set; }        
+               public PithosSettings Settings { get; private set; }
 
-               
-        private Dictionary<string, PithosMonitor> _monitors = new Dictionary<string, PithosMonitor>();
+
+        private readonly ConcurrentDictionary<string, PithosMonitor> _monitors = new ConcurrentDictionary<string, PithosMonitor>();
                ///<summary>
                /// Dictionary of account monitors, keyed by account
                ///</summary>
@@ -66,20 +61,20 @@ namespace Pithos.Client.WPF {
                ///</remarks>
                // TODO: Does the Shell REALLY need access to the monitors? Could we achieve the same results with a better design?
                // TODO: The monitors should be internal to Pithos.Core, even though exposing them makes coding of the Object and Container windows easier
-        public Dictionary<string, PithosMonitor> Monitors
-        {
-            get { return _monitors; }
-        }
+        public ConcurrentDictionary<string, PithosMonitor> Monitors
+               {
+                       get { return _monitors; }
+               }
 
 
-               ///<summary>
-               /// The status service is used by Shell extensions to retrieve file status information
-               ///</summary>
-               //TODO: CODE SMELL! This is the shell! While hosting in the shell makes executing start/stop commands easier, it is still a smell
-        private ServiceHost _statusService { get; set; }
+           ///<summary>
+           /// The status service is used by Shell extensions to retrieve file status information
+           ///</summary>
+           //TODO: CODE SMELL! This is the shell! While hosting in the shell makes executing start/stop commands easier, it is still a smell
+           private ServiceHost _statusService;
 
                //Logging in the Pithos client is provided by log4net
-        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger("Pithos");
+               private static readonly log4net.ILog Log = log4net.LogManager.GetLogger("Pithos");
 
                ///<summary>
                /// The Shell depends on MEF to provide implementations for windowManager, events, the status checker service and the settings
@@ -87,674 +82,677 @@ namespace Pithos.Client.WPF {
                ///<remarks>
                /// The PithosSettings class encapsulates the app's settings to abstract their storage mechanism (App settings, a database or registry)
                ///</remarks>
-        [ImportingConstructor]         
-        public ShellViewModel(IWindowManager windowManager, IEventAggregator events, IStatusChecker statusChecker, PithosSettings settings)
-        {
-            try
-            {
+               [ImportingConstructor]          
+               public ShellViewModel(IWindowManager windowManager, IEventAggregator events, IStatusChecker statusChecker, PithosSettings settings)
+               {
+                       try
+                       {
 
-                _windowManager = windowManager;
+                               _windowManager = windowManager;
                                //CHECK: Caliburn doesn't need explicit command construction
-                //OpenPithosFolderCommand = new PithosCommand(OpenPithosFolder);
-                _statusChecker = statusChecker;
+                               //OpenPithosFolderCommand = new PithosCommand(OpenPithosFolder);
+                               _statusChecker = statusChecker;
                                //The event subst
-                _events = events;
-                _events.Subscribe(this);
-
-                Settings = settings;
-
-                StatusMessage = "In Synch";
-
-                _accounts.CollectionChanged += (sender, e) =>
-                                                   {
-                                                       NotifyOfPropertyChange(() => OpenFolderCaption);
-                                                       NotifyOfPropertyChange(() => HasAccounts);
-                                                   };
-
-            }
-            catch (Exception exc)
-            {
-                Log.Error("Error while starting the ShellViewModel",exc);
-                throw;
-            }
-        }
-
-
-        protected override void OnActivate()
-        {
-            base.OnActivate();
-
-            StartMonitoring();                    
-        }
-
-
-        private async Task StartMonitoring()
-        {
-            try
-            {
-                if (Settings.Accounts == null)
-                {
-                    Settings.Accounts=new AccountsCollection();
-                    Settings.Save();
-                    return;
-                }
-                  
-                foreach (var account in Settings.Accounts)
-                {
-                    await MonitorAccount(account);
-                }
-                _statusService = StatusService.Start();
-            }
-            catch (AggregateException exc)
-            {
-                exc.Handle(e =>
-                {
-                    Log.Error("Error while starting monitoring", e);
-                    return true;
-                });
-                throw;
-            }
-        }
-
-        protected override void OnDeactivate(bool close)
-        {
-            base.OnDeactivate(close);
-            if (close)
-            {
-                StatusService.Stop(_statusService);
-                _statusService = null;
-            }
-        }
-
-        public Task MonitorAccount(AccountSettings account)
-        {
-            return Task.Factory.StartNew(() =>
-            {                                                
-                PithosMonitor monitor = null;
-                var accountName = account.AccountName;
-
-                if (_monitors.TryGetValue(accountName, out monitor))
-                {
-                    //If the account is active
-                    if (account.IsActive)
-                        //Start the monitor. It's OK to start an already started monitor,
-                        //it will just ignore the call                        
-                        StartMonitor(monitor).Wait();                        
-                    else
-                    {
-                        //If the account is inactive
-                        //Stop and remove the monitor
-                        RemoveMonitor(accountName);
-                    }
-                    return;
-                }
-
-                //Create a new monitor/ Can't use MEF here, it would return a single instance for all monitors
-                monitor = new PithosMonitor
-                              {
-                                  UserName = accountName,
-                                  ApiKey = account.ApiKey,                                  
-                                  StatusNotification = this,
-                                  RootPath = account.RootPath
-                              };
-                //PithosMonitor uses MEF so we need to resolve it
-                IoC.BuildUp(monitor);
-
-                var appSettings = Properties.Settings.Default;
-                monitor.AuthenticationUrl = account.ServerUrl;
-
-                _monitors[accountName] = monitor;
-
-                if (account.IsActive)
-                {
-                    //Don't start a monitor if it doesn't have an account and ApiKey
-                    if (String.IsNullOrWhiteSpace(monitor.UserName) ||
-                        String.IsNullOrWhiteSpace(monitor.ApiKey))
-                        return;
-                    StartMonitor(monitor);
-                }
-            });
-        }
-
-
-        protected override void OnViewLoaded(object view)
-        {
-            UpdateStatus();
-            var window = (Window)view;            
-            TaskEx.Delay(1000).ContinueWith(t => Execute.OnUIThread(window.Hide));
-            base.OnViewLoaded(view);
-        }
-
-
-        #region Status Properties
-
-        private string _statusMessage;
-        public string StatusMessage
-        {
-            get { return _statusMessage; }
-            set
-            {
-                _statusMessage = value;
-                NotifyOfPropertyChange(() => StatusMessage);
-            }
-        }
-
-        private readonly ObservableConcurrentCollection<AccountInfo> _accounts = new ObservableConcurrentCollection<AccountInfo>();
-        public ObservableConcurrentCollection<AccountInfo> Accounts
-        {
-            get { return _accounts; }
-        }
-
-           public bool HasAccounts
-           {
-            get { return _accounts.Count > 0; }
-           }
+                               _events = events;
+                               _events.Subscribe(this);
+
+                               Settings = settings;
+
+                               StatusMessage = "In Synch";
+
+                               _accounts.CollectionChanged += (sender, e) =>
+                                                                                                  {
+                                                                                                          NotifyOfPropertyChange(() => OpenFolderCaption);
+                                                                                                          NotifyOfPropertyChange(() => HasAccounts);
+                                                                                                  };
+
+                       }
+                       catch (Exception exc)
+                       {
+                               Log.Error("Error while starting the ShellViewModel",exc);
+                               throw;
+                       }
+               }
+
+
+               protected override void OnActivate()
+               {
+                       base.OnActivate();
 
+                       StartMonitoring();                    
+               }
+
+
+               private async void StartMonitoring()
+               {
+                       try
+                       {
+                               var accounts = Settings.Accounts.Select(MonitorAccount);
+                               await TaskEx.WhenAll(accounts);
+                               _statusService = StatusService.Start();
 
-        public string OpenFolderCaption
-        {
-            get
-            {
-                return (_accounts.Count == 0)
-                        ? "No Accounts Defined"
-                        : "Open Pithos Folder";
-            }
-        }
-
-        private string _pauseSyncCaption="Pause Synching";
-        public string PauseSyncCaption
-        {
-            get { return _pauseSyncCaption; }
-            set
-            {
-                _pauseSyncCaption = value;
-                NotifyOfPropertyChange(() => PauseSyncCaption);
-            }
-        }
-
-        private readonly ObservableConcurrentCollection<FileEntry> _recentFiles = new ObservableConcurrentCollection<FileEntry>();
-        public ObservableConcurrentCollection<FileEntry> RecentFiles
-        {
-            get { return _recentFiles; }
-        }
-
-
-        private string _statusIcon="../Images/Pithos.ico";
-        public string StatusIcon
-        {
-            get { return _statusIcon; }
-            set
-            {
-                //_statusIcon = value;
-                NotifyOfPropertyChange(() => StatusIcon);
-            }
-        }
-
-        #endregion
-
-        #region Commands
-
-        public void ShowPreferences()
-        {
-            Settings.Reload();
-            var preferences = new PreferencesViewModel(_windowManager,_events, this,Settings);            
-            _windowManager.ShowDialog(preferences);
-            
-        }
-
-        public void AboutPithos()
-        {
-            var about = new AboutViewModel();
-            _windowManager.ShowWindow(about);
-        }
-
-        public void SendFeedback()
-        {
-            var feedBack =  IoC.Get<FeedbackViewModel>();
-            _windowManager.ShowWindow(feedBack);
-        }
-
-        //public PithosCommand OpenPithosFolderCommand { get; private set; }
-
-        public void OpenPithosFolder()
-        {
-            var account = Settings.Accounts.FirstOrDefault(acc => acc.IsActive);
-            if (account == null)
-                return;
-            Process.Start(account.RootPath);
-        }
-
-        public void OpenPithosFolder(AccountInfo account)
-        {
-            Process.Start(account.AccountPath);
-        }
-
-        
 /*
-        public void GoToSite()
-        {            
-            var site = Properties.Settings.Default.PithosSite;
-            Process.Start(site);            
-        }
+                               foreach (var account in Settings.Accounts)
+                               {
+                                       await MonitorAccount(account);
+                               }
 */
+                               
+                       }
+                       catch (AggregateException exc)
+                       {
+                               exc.Handle(e =>
+                               {
+                                       Log.Error("Error while starting monitoring", e);
+                                       return true;
+                               });
+                               throw;
+                       }
+               }
+
+               protected override void OnDeactivate(bool close)
+               {
+                       base.OnDeactivate(close);
+                       if (close)
+                       {
+                               StatusService.Stop(_statusService);
+                               _statusService = null;
+                       }
+               }
+
+               public Task MonitorAccount(AccountSettings account)
+               {
+                       return Task.Factory.StartNew(() =>
+                       {                                                
+                               PithosMonitor monitor;
+                               var accountName = account.AccountName;
+
+                               if (_monitors.TryGetValue(accountName, out monitor))
+                               {
+                                       //If the account is active
+                                       if (account.IsActive)
+                                               //Start the monitor. It's OK to start an already started monitor,
+                                               //it will just ignore the call                        
+                                               StartMonitor(monitor).Wait();                        
+                                       else
+                                       {
+                                               //If the account is inactive
+                                               //Stop and remove the monitor
+                                               RemoveMonitor(accountName);
+                                       }
+                                       return;
+                               }
+
+                               //Create a new monitor/ Can't use MEF here, it would return a single instance for all monitors
+                               monitor = new PithosMonitor
+                                                         {
+                                                                 UserName = accountName,
+                                                                 ApiKey = account.ApiKey,                                  
+                                                                 StatusNotification = this,
+                                                                 RootPath = account.RootPath
+                                                         };
+                               //PithosMonitor uses MEF so we need to resolve it
+                               IoC.BuildUp(monitor);
+
+                           monitor.AuthenticationUrl = account.ServerUrl;
+
+                               _monitors[accountName] = monitor;
+
+                               if (account.IsActive)
+                               {
+                                       //Don't start a monitor if it doesn't have an account and ApiKey
+                                       if (String.IsNullOrWhiteSpace(monitor.UserName) ||
+                                               String.IsNullOrWhiteSpace(monitor.ApiKey))
+                                               return;
+                                       StartMonitor(monitor);
+                               }
+                       });
+               }
+
+
+               protected override void OnViewLoaded(object view)
+               {
+                       UpdateStatus();
+                       var window = (Window)view;            
+                       TaskEx.Delay(1000).ContinueWith(t => Execute.OnUIThread(window.Hide));
+                       base.OnViewLoaded(view);
+               }
+
+
+               #region Status Properties
+
+               private string _statusMessage;
+               public string StatusMessage
+               {
+                       get { return _statusMessage; }
+                       set
+                       {
+                               _statusMessage = value;
+                               NotifyOfPropertyChange(() => StatusMessage);
+                       }
+               }
+
+               private readonly ObservableConcurrentCollection<AccountInfo> _accounts = new ObservableConcurrentCollection<AccountInfo>();
+               public ObservableConcurrentCollection<AccountInfo> Accounts
+               {
+                       get { return _accounts; }
+               }
+
+               public bool HasAccounts
+               {
+                       get { return _accounts.Count > 0; }
+               }
+
+
+               public string OpenFolderCaption
+               {
+                       get
+                       {
+                               return (_accounts.Count == 0)
+                                               ? "No Accounts Defined"
+                                               : "Open Pithos Folder";
+                       }
+               }
+
+               private string _pauseSyncCaption="Pause Synching";
+               public string PauseSyncCaption
+               {
+                       get { return _pauseSyncCaption; }
+                       set
+                       {
+                               _pauseSyncCaption = value;
+                               NotifyOfPropertyChange(() => PauseSyncCaption);
+                       }
+               }
+
+               private readonly ObservableConcurrentCollection<FileEntry> _recentFiles = new ObservableConcurrentCollection<FileEntry>();
+               public ObservableConcurrentCollection<FileEntry> RecentFiles
+               {
+                       get { return _recentFiles; }
+               }
+
+
+               private string _statusIcon="../Images/Pithos.ico";
+               public string StatusIcon
+               {
+                       get { return _statusIcon; }
+                       set
+                       {
+                //TODO: Ensure all status icons use the Pithos logo
+                               _statusIcon = value;
+                               NotifyOfPropertyChange(() => StatusIcon);
+                       }
+               }
+
+               #endregion
+
+               #region Commands
+
+               public void ShowPreferences()
+               {
+                       Settings.Reload();
+                       var preferences = new PreferencesViewModel(_windowManager,_events, this,Settings);            
+                       _windowManager.ShowDialog(preferences);
+                       
+               }
+
+               public void AboutPithos()
+               {
+                       var about = new AboutViewModel();
+                       _windowManager.ShowWindow(about);
+               }
+
+               public void SendFeedback()
+               {
+                       var feedBack =  IoC.Get<FeedbackViewModel>();
+                       _windowManager.ShowWindow(feedBack);
+               }
+
+               //public PithosCommand OpenPithosFolderCommand { get; private set; }
+
+               public void OpenPithosFolder()
+               {
+                       var account = Settings.Accounts.FirstOrDefault(acc => acc.IsActive);
+                       if (account == null)
+                               return;
+                       Process.Start(account.RootPath);
+               }
+
+               public void OpenPithosFolder(AccountInfo account)
+               {
+                       Process.Start(account.AccountPath);
+               }
 
-        public void GoToSite(AccountInfo account)
-        {
-            var site = String.Format("{0}/ui/?token={1}&user={2}",
-                account.SiteUri,account.Token,
-                account.UserName);
-            Process.Start(site);
-        }
-
-        public void ShowFileProperties()
-        {
-            var account = Settings.Accounts.First(acc => acc.IsActive);            
-            var dir = new DirectoryInfo(account.RootPath + @"\pithos");
-            var files=dir.GetFiles();
-            var r=new Random();
-            var idx=r.Next(0, files.Length);
-            ShowFileProperties(files[idx].FullName);            
-        }
-
-        public void ShowFileProperties(string filePath)
-        {
-            if (String.IsNullOrWhiteSpace(filePath))
-                throw new ArgumentNullException("filePath");
-            if (!File.Exists(filePath))
-                throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");
-            Contract.EndContractBlock();
-
-            var pair=(from monitor in  Monitors
-                               where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)
-                                   select monitor).FirstOrDefault();
-            var account = pair.Key;
-            var accountMonitor = pair.Value;
-
-            if (accountMonitor == null)
-                return;
-
-            var infoTask=Task.Factory.StartNew(()=>accountMonitor.GetObjectInfo(filePath));
-
-            
-
-            var fileProperties = new FilePropertiesViewModel(this, infoTask,filePath);
-            _windowManager.ShowWindow(fileProperties);
-        } 
-        
-        public void ShowContainerProperties()
-        {
-            var account = Settings.Accounts.First(acc => acc.IsActive);            
-            var dir = new DirectoryInfo(account.RootPath);
-            var fullName = (from folder in dir.EnumerateDirectories()
-                            where (folder.Attributes & FileAttributes.Hidden) == 0
-                            select folder.FullName).First();
-            ShowContainerProperties(fullName);            
-        }
-
-        public void ShowContainerProperties(string filePath)
-        {
-            if (String.IsNullOrWhiteSpace(filePath))
-                throw new ArgumentNullException("filePath");
-            if (!Directory.Exists(filePath))
-                throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");
-            Contract.EndContractBlock();
-
-            var pair=(from monitor in  Monitors
-                               where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)
-                                   select monitor).FirstOrDefault();
-            var account = pair.Key;
-            var accountMonitor = pair.Value;            
-            var info = accountMonitor.GetContainerInfo(filePath);
-
-            
-
-            var containerProperties = new ContainerPropertiesViewModel(this, info,filePath);
-            _windowManager.ShowWindow(containerProperties);
-        }
-
-        public ObjectInfo RefreshObjectInfo(ObjectInfo currentInfo)
-        {
-            if (currentInfo==null)
-                throw new ArgumentNullException("currentInfo");
-            Contract.EndContractBlock();
-
-            var monitor = Monitors[currentInfo.Account];
-            var newInfo=monitor.CloudClient.GetObjectInfo(currentInfo.Account, currentInfo.Container, currentInfo.Name);
-            return newInfo;
-        }
-
-        public ContainerInfo RefreshContainerInfo(ContainerInfo container)
-        {
-            if (container == null)
-                throw new ArgumentNullException("container");
-            Contract.EndContractBlock();
-
-            var monitor = Monitors[container.Account];
-            var newInfo = monitor.CloudClient.GetContainerInfo(container.Account, container.Name);
-            return newInfo;
-        }
-
-
-        public void ToggleSynching()
-        {
-            bool isPaused=false;
-            foreach (var pair in Monitors)
-            {
-                var monitor = pair.Value;
-                monitor.Pause = !monitor.Pause;
-                isPaused = monitor.Pause;
-            }
-
-            PauseSyncCaption = isPaused ? "Resume syncing" : "Pause syncing";
-            var iconKey = isPaused? "TraySyncPaused" : "TrayInSynch";
-            StatusIcon = String.Format(@"../Images/{0}.ico", iconKey);
-        }
-
-        public void ExitPithos()
-        {
-            foreach (var pair in Monitors)
-            {
-                var monitor = pair.Value;
-                monitor.Stop();
-            }
-
-            ((Window)GetView()).Close();
-        }
-        #endregion
-
-
-        private Dictionary<PithosStatus, StatusInfo> iconNames = new List<StatusInfo>
-            {
-                new StatusInfo(PithosStatus.InSynch, "All files up to date", "TrayInSynch"),
-                new StatusInfo(PithosStatus.Syncing, "Syncing Files", "TraySynching"),
-                new StatusInfo(PithosStatus.SyncPaused, "Sync Paused", "TraySyncPaused")
-            }.ToDictionary(s => s.Status);
-
-        readonly IWindowManager _windowManager;
+               
+/*
+               public void GoToSite()
+               {            
+                       var site = Properties.Settings.Default.PithosSite;
+                       Process.Start(site);            
+               }
+*/
+
+               public void GoToSite(AccountInfo account)
+               {
+                       /*var site = String.Format("{0}/ui/?token={1}&user={2}",
+                               account.SiteUri,account.Token,
+                               account.UserName);*/
+                       Process.Start(account.SiteUri);
+               }
+
+               public void ShowFileProperties()
+               {
+                       var account = Settings.Accounts.First(acc => acc.IsActive);            
+                       var dir = new DirectoryInfo(account.RootPath + @"\pithos");
+                       var files=dir.GetFiles();
+                       var r=new Random();
+                       var idx=r.Next(0, files.Length);
+                       ShowFileProperties(files[idx].FullName);            
+               }
+
+               public void ShowFileProperties(string filePath)
+               {
+                       if (String.IsNullOrWhiteSpace(filePath))
+                               throw new ArgumentNullException("filePath");
+                       if (!File.Exists(filePath) && !Directory.Exists(filePath))
+                               throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");
+                       Contract.EndContractBlock();
+
+                       var pair=(from monitor in  Monitors
+                                                          where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)
+                                                                  select monitor).FirstOrDefault();
+                   var accountMonitor = pair.Value;
+
+                       if (accountMonitor == null)
+                               return;
+
+                       var infoTask=Task.Factory.StartNew(()=>accountMonitor.GetObjectInfo(filePath));
+
+                       
+
+                       var fileProperties = new FilePropertiesViewModel(this, infoTask,filePath);
+                       _windowManager.ShowWindow(fileProperties);
+               } 
+               
+               public void ShowContainerProperties()
+               {
+                       var account = Settings.Accounts.First(acc => acc.IsActive);            
+                       var dir = new DirectoryInfo(account.RootPath);
+                       var fullName = (from folder in dir.EnumerateDirectories()
+                                                       where (folder.Attributes & FileAttributes.Hidden) == 0
+                                                       select folder.FullName).First();
+                       ShowContainerProperties(fullName);            
+               }
+
+               public void ShowContainerProperties(string filePath)
+               {
+                       if (String.IsNullOrWhiteSpace(filePath))
+                               throw new ArgumentNullException("filePath");
+                       if (!Directory.Exists(filePath))
+                               throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");
+                       Contract.EndContractBlock();
+
+                       var pair=(from monitor in  Monitors
+                                                          where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)
+                                                                  select monitor).FirstOrDefault();
+                   var accountMonitor = pair.Value;            
+                       var info = accountMonitor.GetContainerInfo(filePath);
+
+                       
+
+                       var containerProperties = new ContainerPropertiesViewModel(this, info,filePath);
+                       _windowManager.ShowWindow(containerProperties);
+               }
+
+        public void SynchNow()
+        {}
+
+               public ObjectInfo RefreshObjectInfo(ObjectInfo currentInfo)
+               {
+                       if (currentInfo==null)
+                               throw new ArgumentNullException("currentInfo");
+                       Contract.EndContractBlock();
+
+                       var monitor = Monitors[currentInfo.Account];
+                       var newInfo=monitor.CloudClient.GetObjectInfo(currentInfo.Account, currentInfo.Container, currentInfo.Name);
+                       return newInfo;
+               }
+
+               public ContainerInfo RefreshContainerInfo(ContainerInfo container)
+               {
+                       if (container == null)
+                               throw new ArgumentNullException("container");
+                       Contract.EndContractBlock();
+
+                       var monitor = Monitors[container.Account];
+                       var newInfo = monitor.CloudClient.GetContainerInfo(container.Account, container.Name);
+                       return newInfo;
+               }
+
+
+               public void ToggleSynching()
+               {
+                       bool isPaused=false;
+                       foreach (var pair in Monitors)
+                       {
+                               var monitor = pair.Value;
+                               monitor.Pause = !monitor.Pause;
+                               isPaused = monitor.Pause;
+                       }
+
+                       PauseSyncCaption = isPaused ? "Resume syncing" : "Pause syncing";
+                       var iconKey = isPaused? "TraySyncPaused" : "TrayInSynch";
+                       StatusIcon = String.Format(@"../Images/{0}.ico", iconKey);
+               }
+
+               public void ExitPithos()
+               {
+                       foreach (var pair in Monitors)
+                       {
+                               var monitor = pair.Value;
+                               monitor.Stop();
+                       }
+
+                       ((Window)GetView()).Close();
+               }
+               #endregion
+
+
+               private readonly Dictionary<PithosStatus, StatusInfo> _iconNames = new List<StatusInfo>
+                       {
+                               new StatusInfo(PithosStatus.InSynch, "All files up to date", "TrayInSynch"),
+                               new StatusInfo(PithosStatus.Syncing, "Syncing Files", "TraySynching"),
+                               new StatusInfo(PithosStatus.SyncPaused, "Sync Paused", "TraySyncPaused")
+                       }.ToDictionary(s => s.Status);
+
+               readonly IWindowManager _windowManager;
 
 
                ///<summary>
                /// Updates the visual status indicators of the application depending on status changes, e.g. icon, stat                
                ///</summary>
-        public void UpdateStatus()
-        {
-            var pithosStatus = _statusChecker.GetPithosStatus();
-
-            if (iconNames.ContainsKey(pithosStatus))
-            {
-                var info = iconNames[pithosStatus];
-                StatusIcon = String.Format(@"../Images/{0}.ico", info.IconName);
-
-                Assembly assembly = Assembly.GetExecutingAssembly();                               
-                var fileVersion = FileVersionInfo.GetVersionInfo(assembly.Location);
-
-
-                StatusMessage = String.Format("Pithos {0}\r\n{1}", fileVersion.FileVersion,info.StatusText);
-            }
-            
-            _events.Publish(new Notification { Title = "Start", Message = "Start Monitoring", Level = TraceLevel.Info});
-        }
-
-
-       
-        private Task StartMonitor(PithosMonitor monitor,int retries=0)
-        {
-            return Task.Factory.StartNew(() =>
-            {
-                using (log4net.ThreadContext.Stacks["Monitor"].Push("Start"))
-                {
-                    try
-                    {
-                        Log.InfoFormat("Start Monitoring {0}", monitor.UserName);
-
-                        monitor.Start();
-                    }
-                    catch (WebException exc)
-                    {
-                        if (AbandonRetry(monitor, retries))
-                            return;
-
-                        if (IsUnauthorized(exc))
-                        {
-                            var message = String.Format("API Key Expired for {0}. Starting Renewal",monitor.UserName);                            
-                            Log.Error(message,exc);
-                            TryAuthorize(monitor,retries).Wait();
-                        }
-                        else
-                        {
-                            TryLater(monitor, exc,retries);
-                        }
-                    }
-                    catch (Exception exc)
-                    {
-                        if (AbandonRetry(monitor, retries)) 
-                            return;
-
-                        TryLater(monitor,exc,retries);
-                    }
-                }
-            });
-        }
-
-        private bool AbandonRetry(PithosMonitor monitor, int retries)
-        {
-            if (retries > 1)
-            {
-                var message = String.Format("Monitoring of account {0} has failed too many times. Will not retry",
-                                            monitor.UserName);
-                _events.Publish(new Notification
-                                    {Title = "Account monitoring failed", Message = message, Level = TraceLevel.Error});
-                return true;
-            }
-            return false;
-        }
-
-
-        private async Task TryAuthorize(PithosMonitor monitor,int retries)
-        {
-            _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 });
-
-            try
-            {
-
-                var credentials = await PithosAccount.RetrieveCredentials(Settings.PithosLoginUrl);
-
-                var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);
-                account.ApiKey = credentials.Password;
-                monitor.ApiKey = credentials.Password;
-                Settings.Save();
-                await TaskEx.Delay(10000);
-                StartMonitor(monitor, retries + 1);
-                NotifyOfPropertyChange(()=>Accounts);
-            }
-            catch (AggregateException exc)
-            {
-                string message = String.Format("API Key retrieval for {0} failed", monitor.UserName);
-                Log.Error(message, exc.InnerException);
-                _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
-                return;
-            }
-            catch (Exception exc)
-            {
-                string message = String.Format("API Key retrieval for {0} failed", monitor.UserName);
-                Log.Error(message, exc);
-                _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
-                return;
-                
-            }
-
-        }
-
-        private static bool IsUnauthorized(WebException exc)
-        {
-            if (exc==null)
-                throw new ArgumentNullException("exc");
-            Contract.EndContractBlock();
-
-            var response = exc.Response as HttpWebResponse;
-            if (response == null)
-                return false;
-            return (response.StatusCode == HttpStatusCode.Unauthorized);
-        }
-
-        private void TryLater(PithosMonitor monitor, Exception exc,int retries)
-        {
-            var message = String.Format("An exception occured. Can't start monitoring\nWill retry in 10 seconds");
-            Task.Factory.StartNewDelayed(10000, () => StartMonitor(monitor,retries+1));
-            _events.Publish(new Notification
-                                {Title = "Error", Message = message, Level = TraceLevel.Error});
-            Log.Error(message, exc);
-        }
-
-
-        public void NotifyChange(string status, TraceLevel level=TraceLevel.Info)
-        {
-            this.StatusMessage = status;
-            
-            _events.Publish(new Notification { Title = "Pithos", Message = status, Level = level });
-        }
-
-        public void NotifyChangedFile(string filePath)
-        {
-            var entry = new FileEntry {FullPath=filePath};
-            IProducerConsumerCollection<FileEntry> files=this.RecentFiles;
-            FileEntry popped;
-            while (files.Count > 5)
-                files.TryTake(out popped);
-            files.TryAdd(entry);
-        }
-
-        public void NotifyAccount(AccountInfo account)
-        {
-            if (account== null)
-                return;
-
-            account.SiteUri= String.Format("{0}/ui/?token={1}&user={2}",
-                account.SiteUri, account.Token,
-                account.UserName);
-
-            IProducerConsumerCollection<AccountInfo> accounts = Accounts;
-            for (var i = 0; i < _accounts.Count; i++)
-            {
-                AccountInfo item;
-                if (accounts.TryTake(out item))
-                {
-                    if (item.UserName!=account.UserName)
-                    {
-                        accounts.TryAdd(item);
-                    }
-                }
-            }
-
-            accounts.TryAdd(account);
-        }
-
-
-        public void RemoveMonitor(string accountName)
-        {
-            if (String.IsNullOrWhiteSpace(accountName))
-                return;
-
-            var accountInfo=_accounts.FirstOrDefault(account => account.UserName == accountName);
-            _accounts.TryRemove(accountInfo);
-
-            PithosMonitor monitor;
-            if (Monitors.TryGetValue(accountName, out monitor))
-            {
-                Monitors.Remove(accountName);
-                monitor.Stop();
-            }
-        }
-
-        public void RefreshOverlays()
-        {
-            foreach (var pair in Monitors)
-            {
-                var monitor = pair.Value;
-
-                var path = monitor.RootPath;
-
-                if (String.IsNullOrWhiteSpace(path))
-                    continue;
-
-                if (!Directory.Exists(path) && !File.Exists(path))
-                    continue;
-
-                IntPtr pathPointer = Marshal.StringToCoTaskMemAuto(path);
-
-                try
-                {
-                    NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_UPDATEITEM,
-                                                 HChangeNotifyFlags.SHCNF_PATHW | HChangeNotifyFlags.SHCNF_FLUSHNOWAIT,
-                                                 pathPointer, IntPtr.Zero);
-                }
-                finally
-                {
-                    Marshal.FreeHGlobal(pathPointer);
-                }
-            }
-        }
-
-        #region Event Handlers
-        
-        public void Handle(SelectiveSynchChanges message)
-        {
-            var accountName = message.Account.AccountName;
-            PithosMonitor monitor;
-            if (_monitors.TryGetValue(accountName, out monitor))
-            {
-                monitor.AddSelectivePaths(message.Added);
-                monitor.RemoveSelectivePaths(message.Removed);
-
-            }
-            
-        }
-
-
-        public void Handle(Notification notification)
-        {
-            if (!Settings.ShowDesktopNotifications)
-                return;
-            BalloonIcon icon = BalloonIcon.None;
-            switch (notification.Level)
-            {
-                case TraceLevel.Error:
-                    icon = BalloonIcon.Error;
-                    break;
-                case TraceLevel.Info:
-                case TraceLevel.Verbose:
-                    icon = BalloonIcon.Info;
-                    break;
-                case TraceLevel.Warning:
-                    icon = BalloonIcon.Warning;
-                    break;
-                default:
-                    icon = BalloonIcon.None;
-                    break;
-            }
-
-            if (Settings.ShowDesktopNotifications)
-            {
-                var tv = (ShellView) this.GetView();
-                tv.TaskbarView.ShowBalloonTip(notification.Title, notification.Message, icon);
-            }
-        }
-        #endregion
-
-        public void Handle(ShowFilePropertiesEvent message)
-        {
-            if (message == null)
-                throw new ArgumentNullException("message");
-            if (String.IsNullOrWhiteSpace(message.FileName) )
-                throw new ArgumentException("message");
-            Contract.EndContractBlock();
-
-            var fileName = message.FileName;
-
-            if (File.Exists(fileName))
-                this.ShowFileProperties(fileName);
-            else if (Directory.Exists(fileName))
-                this.ShowContainerProperties(fileName);
-        }
-    }
+               public void UpdateStatus()
+               {
+                       var pithosStatus = _statusChecker.GetPithosStatus();
+
+                       if (_iconNames.ContainsKey(pithosStatus))
+                       {
+                               var info = _iconNames[pithosStatus];
+                               StatusIcon = String.Format(@"../Images/{0}.ico", info.IconName);
+
+                               Assembly assembly = Assembly.GetExecutingAssembly();                               
+                               var fileVersion = FileVersionInfo.GetVersionInfo(assembly.Location);
+
+
+                               StatusMessage = String.Format("Pithos {0}\r\n{1}", fileVersion.FileVersion,info.StatusText);
+                       }
+                       
+                       _events.Publish(new Notification { Title = "Start", Message = "Start Monitoring", Level = TraceLevel.Info});
+               }
+
+
+          
+               private Task StartMonitor(PithosMonitor monitor,int retries=0)
+               {
+                       return Task.Factory.StartNew(() =>
+                       {
+                               using (log4net.ThreadContext.Stacks["Monitor"].Push("Start"))
+                               {
+                                       try
+                                       {
+                                               Log.InfoFormat("Start Monitoring {0}", monitor.UserName);
+
+                                               monitor.Start();
+                                       }
+                                       catch (WebException exc)
+                                       {
+                                               if (AbandonRetry(monitor, retries))
+                                                       return;
+
+                                               if (IsUnauthorized(exc))
+                                               {
+                                                       var message = String.Format("API Key Expired for {0}. Starting Renewal",monitor.UserName);                            
+                                                       Log.Error(message,exc);
+                                                       TryAuthorize(monitor,retries).Wait();
+                                               }
+                                               else
+                                               {
+                                                       TryLater(monitor, exc,retries);
+                                               }
+                                       }
+                                       catch (Exception exc)
+                                       {
+                                               if (AbandonRetry(monitor, retries)) 
+                                                       return;
+
+                                               TryLater(monitor,exc,retries);
+                                       }
+                               }
+                       });
+               }
+
+               private bool AbandonRetry(PithosMonitor monitor, int retries)
+               {
+                       if (retries > 1)
+                       {
+                               var message = String.Format("Monitoring of account {0} has failed too many times. Will not retry",
+                                                                                       monitor.UserName);
+                               _events.Publish(new Notification
+                                                                       {Title = "Account monitoring failed", Message = message, Level = TraceLevel.Error});
+                               return true;
+                       }
+                       return false;
+               }
+
+
+               private async Task TryAuthorize(PithosMonitor monitor,int retries)
+               {
+                       _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 });
+
+                       try
+                       {
+
+                               var credentials = await PithosAccount.RetrieveCredentials(Settings.PithosLoginUrl);
+
+                               var account = Settings.Accounts.First(act => act.AccountName == credentials.UserName);
+                               account.ApiKey = credentials.Password;
+                               monitor.ApiKey = credentials.Password;
+                               Settings.Save();
+                               await TaskEx.Delay(10000);
+                               StartMonitor(monitor, retries + 1);
+                               NotifyOfPropertyChange(()=>Accounts);
+                       }
+                       catch (AggregateException exc)
+                       {
+                               string message = String.Format("API Key retrieval for {0} failed", monitor.UserName);
+                               Log.Error(message, exc.InnerException);
+                               _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
+                       }
+                       catch (Exception exc)
+                       {
+                               string message = String.Format("API Key retrieval for {0} failed", monitor.UserName);
+                               Log.Error(message, exc);
+                               _events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error });
+                       }
+
+               }
+
+               private static bool IsUnauthorized(WebException exc)
+               {
+                       if (exc==null)
+                               throw new ArgumentNullException("exc");
+                       Contract.EndContractBlock();
+
+                       var response = exc.Response as HttpWebResponse;
+                       if (response == null)
+                               return false;
+                       return (response.StatusCode == HttpStatusCode.Unauthorized);
+               }
+
+               private void TryLater(PithosMonitor monitor, Exception exc,int retries)
+               {
+                       var message = String.Format("An exception occured. Can't start monitoring\nWill retry in 10 seconds");
+                       Task.Factory.StartNewDelayed(10000, () => StartMonitor(monitor,retries+1));
+                       _events.Publish(new Notification
+                                                               {Title = "Error", Message = message, Level = TraceLevel.Error});
+                       Log.Error(message, exc);
+               }
+
+
+               public void NotifyChange(string status, TraceLevel level=TraceLevel.Info)
+               {
+                       StatusMessage = status;
+                       
+                       _events.Publish(new Notification { Title = "Pithos", Message = status, Level = level });
+               }
+
+               public void NotifyChangedFile(string filePath)
+               {
+                       var entry = new FileEntry {FullPath=filePath};
+                       IProducerConsumerCollection<FileEntry> files=RecentFiles;
+                       FileEntry popped;
+                       while (files.Count > 5)
+                               files.TryTake(out popped);
+                       files.TryAdd(entry);
+               }
+
+               public void NotifyAccount(AccountInfo account)
+               {
+                       if (account== null)
+                               return;
+                       //TODO: What happens to an existing account whose Token has changed?
+                       account.SiteUri= String.Format("{0}/ui/?token={1}&user={2}",
+                               account.SiteUri, Uri.EscapeUriString(account.Token),
+                               Uri.EscapeUriString(account.UserName));
+
+                       if (Accounts.All(item => item.UserName != account.UserName))
+                               Accounts.TryAdd(account);
+
+               }
+
+
+               public void RemoveMonitor(string accountName)
+               {
+                       if (String.IsNullOrWhiteSpace(accountName))
+                               return;
+
+                       var accountInfo=_accounts.FirstOrDefault(account => account.UserName == accountName);
+                       _accounts.TryRemove(accountInfo);
+
+                       PithosMonitor monitor;
+                       if (Monitors.TryRemove(accountName, out monitor))
+                       {
+                               monitor.Stop();
+                       }
+               }
+
+               public void RefreshOverlays()
+               {
+                       foreach (var pair in Monitors)
+                       {
+                               var monitor = pair.Value;
+
+                               var path = monitor.RootPath;
+
+                               if (String.IsNullOrWhiteSpace(path))
+                                       continue;
+
+                               if (!Directory.Exists(path) && !File.Exists(path))
+                                       continue;
+
+                               IntPtr pathPointer = Marshal.StringToCoTaskMemAuto(path);
+
+                               try
+                               {
+                                       NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_UPDATEITEM,
+                                                                                                HChangeNotifyFlags.SHCNF_PATHW | HChangeNotifyFlags.SHCNF_FLUSHNOWAIT,
+                                                                                                pathPointer, IntPtr.Zero);
+                               }
+                               finally
+                               {
+                                       Marshal.FreeHGlobal(pathPointer);
+                               }
+                       }
+               }
+
+               #region Event Handlers
+               
+               public void Handle(SelectiveSynchChanges message)
+               {
+                       var accountName = message.Account.AccountName;
+                       PithosMonitor monitor;
+                       if (_monitors.TryGetValue(accountName, out monitor))
+                       {
+                               monitor.AddSelectivePaths(message.Added);
+                               monitor.RemoveSelectivePaths(message.Removed);
+
+                       }
+                       
+               }
+
+
+               public void Handle(Notification notification)
+               {
+                       if (!Settings.ShowDesktopNotifications)
+                               return;
+                       BalloonIcon icon;
+                       switch (notification.Level)
+                       {
+                               case TraceLevel.Error:
+                                       icon = BalloonIcon.Error;
+                                       break;
+                               case TraceLevel.Info:
+                               case TraceLevel.Verbose:
+                                       icon = BalloonIcon.Info;
+                                       break;
+                               case TraceLevel.Warning:
+                                       icon = BalloonIcon.Warning;
+                                       break;
+                               default:
+                                       icon = BalloonIcon.None;
+                                       break;
+                       }
+
+                       if (Settings.ShowDesktopNotifications)
+                       {
+                               var tv = (ShellView) GetView();
+                               tv.TaskbarView.ShowBalloonTip(notification.Title, notification.Message, icon);
+                       }
+               }
+               #endregion
+
+               public void Handle(ShowFilePropertiesEvent message)
+               {
+                       if (message == null)
+                               throw new ArgumentNullException("message");
+                       if (String.IsNullOrWhiteSpace(message.FileName) )
+                               throw new ArgumentException("message");
+                       Contract.EndContractBlock();
+
+                       var fileName = message.FileName;
+            //TODO: Display file properties for non-container folders
+                       if (File.Exists(fileName))
+                //Retrieve the full name with exact casing. Pithos names are case sensitive                            
+                ShowFileProperties(FileInfoExtensions.GetProperFilePathCapitalization(fileName));
+                       else if (Directory.Exists(fileName))
+                //Retrieve the full name with exact casing. Pithos names are case sensitive
+                       {
+                var path = FileInfoExtensions.GetProperDirectoryCapitalization(fileName);
+                if (IsContainer(path))
+                               ShowContainerProperties(path);
+                else
+                    ShowFileProperties(path);
+                       }
+               }
+
+           private bool IsContainer(string path)
+           {
+               var matchingFolders = from account in _accounts
+                                     from rootFolder in Directory.GetDirectories(account.AccountPath)
+                                     where rootFolder.Equals(path, StringComparison.InvariantCultureIgnoreCase)
+                                     select rootFolder;
+               return matchingFolders.Any();
+           }
+
+       }
 }