UUID Changes
[pithos-ms-client] / trunk / Pithos.Client.WPF / Shell / ShellViewModel.cs
index 692f0ae..4a2a4c9 100644 (file)
-#region
-/* -----------------------------------------------------------------------
- * <copyright file="ShellViewModel.cs" company="GRNet">
- * 
- * Copyright 2011-2012 GRNET S.A. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- *   1. Redistributions of source code must retain the above
- *      copyright notice, this list of conditions and the following
- *      disclaimer.
- *
- *   2. Redistributions in binary form must reproduce the above
- *      copyright notice, this list of conditions and the following
- *      disclaimer in the documentation and/or other materials
- *      provided with the distribution.
- *
- *
- * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
- * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * The views and conclusions contained in the software and
- * documentation are those of the authors and should not be
- * interpreted as representing official policies, either expressed
- * or implied, of GRNET S.A.
- * </copyright>
- * -----------------------------------------------------------------------
- */
-#endregion
-using System.Collections.Concurrent;
-using System.Diagnostics;
-using System.Diagnostics.Contracts;
-using System.IO;
-using System.Net;
-using System.Reflection;
-using System.Runtime.InteropServices;
-using System.ServiceModel;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Windows;
-using System.Windows.Controls.Primitives;
-using System.Windows.Input;
-using AppLimit.NetSparkle;
-using Caliburn.Micro;
-using Hardcodet.Wpf.TaskbarNotification;
-using Pithos.Client.WPF.Configuration;
-using Pithos.Client.WPF.FileProperties;
-using Pithos.Client.WPF.Preferences;
-using Pithos.Client.WPF.SelectiveSynch;
-using Pithos.Client.WPF.Services;
-using Pithos.Client.WPF.Shell;
-using Pithos.Core;
-using Pithos.Core.Agents;
-using Pithos.Interfaces;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Pithos.Network;
-using StatusService = Pithos.Client.WPF.Services.StatusService;
-
-namespace Pithos.Client.WPF {
-       using System.ComponentModel.Composition;
-
-       public class ToggleStatusCommand:ICommand
-       {
-           private readonly ShellViewModel _model;
-           public ToggleStatusCommand(ShellViewModel model)
-           {
-               _model = model;
-           }
-           public void Execute(object parameter)
-           {
-               _model.CurrentSyncStatus();
-           }
-
-           public bool CanExecute(object parameter)
-           {
-               return true;
-           }
-
-           public event EventHandler CanExecuteChanged;
-       }
-
-
-       ///<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
-       ///</summary>
-       ///<remarks>
-       /// It is a strange "shell" as its main visible element is an icon instead of a window
-       /// The shell subscribes to the following events:
-       /// * Notification:  Raised by components that want to notify the user. Usually displayed in a balloon
-       /// * SelectiveSynchChanges: Notifies that the user made changes to the selective synch folders for an account. Raised by the Selective Synch dialog. Located here because the monitors are here
-       /// * 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)), Export(typeof(ShellViewModel)),Export(typeof(IStatusNotification))]
-       public class ShellViewModel : Screen, IStatusNotification, IShell,
-               IHandle<Notification>, IHandle<SelectiveSynchChanges>, IHandle<ShowFilePropertiesEvent>
-       {
-               
-               private readonly IEventAggregator _events;
-
-               public PithosSettings Settings { get; private set; }
-
-
-               private readonly ConcurrentDictionary<Uri, PithosMonitor> _monitors = new ConcurrentDictionary<Uri, PithosMonitor>();
-               ///<summary>
-               /// Dictionary of account monitors, keyed by account
-               ///</summary>
-               ///<remarks>
-               /// One monitor class is created for each account. The Shell needs access to the monitors to execute start/stop/pause commands,
-               /// retrieve account and boject info            
-               ///</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 ConcurrentDictionary<Uri, 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;
-
-               //Logging in the Pithos client is provided by log4net
-        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
-        [Import]
-           private PollAgent _pollAgent;
-
-        [Import]
-           private NetworkAgent _networkAgent;
-
-           [Import]
-           public Selectives Selectives { get; set; }
-
-
-           public ToggleStatusCommand ToggleMiniStatusCommand { get; set; }
-
-           private MiniStatusViewModel _miniStatus;
-
-           [Import]
-        public MiniStatusViewModel MiniStatus
-           {
-               get { return _miniStatus; }
-               set
-               {
-                   _miniStatus = value;
-                   _miniStatus.Shell = this;
-                   _miniStatus.Deactivated += (sender, arg) =>
-                                                  {
-                                                      _statusVisible = false;
-                                                   NotifyOfPropertyChange(()=>MiniStatusCaption);
-                                                  };
-               }
-           }
-
-           ///<summary>
-               /// The Shell depends on MEF to provide implementations for windowManager, events, the status checker service and the settings
-               ///</summary>
-               ///<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, PithosSettings settings/*,PollAgent pollAgent,NetworkAgent networkAgent*/)
-               {
-                       try
-                       {
-
-                               _windowManager = windowManager;
-                               //CHECK: Caliburn doesn't need explicit command construction
-                               //CurrentSyncStatusCommand = new PithosCommand(OpenPithosFolder);
-                               //The event subst
-                               _events = events;
-                               _events.Subscribe(this);
-
-/*
-                           _pollAgent = pollAgent;
-                           _networkAgent = networkAgent;
-*/
-                               Settings = settings;
-
-                               Proxy.SetFromSettings(settings);
-
-                StatusMessage = Settings.Accounts.Count==0 
-                    ? "No Accounts added\r\nPlease add an account" 
-                    : "Starting";
-
-                               _accounts.CollectionChanged += (sender, e) =>
-                                                                                                  {
-                                                                                                          NotifyOfPropertyChange(() => OpenFolderCaption);
-                                                                                                          NotifyOfPropertyChange(() => HasAccounts);
-                                                                                                  };
-
-                SetVersionMessage();
-
-                ToggleMiniStatusCommand=new ToggleStatusCommand(this);
-                       }
-                       catch (Exception exc)
-                       {
-                               Log.Error("Error while starting the ShellViewModel",exc);
-                               throw;
-                       }
-
-               }
-
-           private void SetVersionMessage()
-           {
-               Assembly assembly = Assembly.GetExecutingAssembly();
-               var fileVersion = FileVersionInfo.GetVersionInfo(assembly.Location);
-               VersionMessage = String.Format("Pithos+ {0}", fileVersion.FileVersion);
-           }
-
-        public void CurrentSyncStatus()
-        {
-            if (Accounts.Count == 0)
-            {
-                ShowPreferences("AccountTab");
-            }
-            else
-            {
-                if (!_statusVisible)
-                {
-                    _windowManager.ShowWindow(MiniStatus);
-                    _statusVisible = true;
-                }
-                else
-                {
-                    if (MiniStatus.IsActive)
-                        MiniStatus.TryClose();
-                    _statusVisible = false;
-                }
-
-                NotifyOfPropertyChange(() => MiniStatusCaption);
-            }
-        }
-
-           protected override void OnActivate()
-               {
-                       base.OnActivate();
-
-            InitializeSparkle();
-
-               //Must delay opening the upgrade window
-            //to avoid Windows Messages sent by the TaskbarIcon
-            TaskEx.Delay(5000).ContinueWith(_=>
-                Execute.OnUIThread(()=> _sparkle.StartLoop(true,Settings.UpdateForceCheck,Settings.UpdateCheckInterval)));
-
-
-                       StartMonitoring();                    
-               }
-
-
-           private void OnCheckFinished(object sender, bool updaterequired)
-           {
-            
-            Log.InfoFormat("Upgrade check finished. Need Upgrade: {0}", updaterequired);
-            if (_manualUpgradeCheck)
-            {
-                _manualUpgradeCheck = false;
-                if (!updaterequired)
-                    //Sparkle raises events on a background thread
-                    Execute.OnUIThread(()=>
-                        ShowBalloonFor(new Notification{Title="Pithos+ is up to date",Message="You have the latest Pithos+ version. No update is required"}));
-            }
-           }
-
-           private void OnUpgradeDetected(object sender, UpdateDetectedEventArgs e)
-           {            
-               Log.InfoFormat("Update detected {0}",e.LatestVersion);
-           }
-
-        public void CheckForUpgrade()
-        {
-            ShowBalloonFor(new Notification{Title="Checking for upgrades",Message="Contacting the server to retrieve the latest Pithos+ version."});
-            _sparkle.StopLoop();
-            _sparkle.updateDetected -= OnUpgradeDetected;
-            _sparkle.checkLoopFinished -= OnCheckFinished;
-            _sparkle.Dispose();
-
-            _manualUpgradeCheck = true;
-            InitializeSparkle();
-            _sparkle.StartLoop(true,true,Settings.UpdateCheckInterval);
-        }
-
-        private void InitializeSparkle()
-        {
-            _sparkle = new Sparkle(Settings.UpdateUrl);
-            _sparkle.updateDetected += OnUpgradeDetected;
-            _sparkle.checkLoopFinished += OnCheckFinished;
-            _sparkle.ShowDiagnosticWindow = Settings.UpdateDiagnostics;
-        }
-
-           private async void StartMonitoring()
-               {
-                       try
-                       {
-                if (Settings.IgnoreCertificateErrors)
-                {
-                    ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
-                }
-                    
-                               var accounts = Settings.Accounts.Select(MonitorAccount);
-                await TaskEx.WhenAll(accounts).ConfigureAwait(false);
-                               _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;
-                               var accountName = account.AccountName;
-
-                           MigrateFolders(account);
-
-                           Selectives.SetIsSelectiveEnabled(account.AccountKey, account.SelectiveSyncEnabled);
-
-                               if (Monitors.TryGetValue(account.AccountKey, out monitor))
-                               {
-                                       //If the account is active
-                    if (account.IsActive)
-                    {
-                        //The Api Key may have changed throuth the Preferences dialog
-                        monitor.ApiKey = account.ApiKey;
-                                               Debug.Assert(monitor.StatusNotification == this,"An existing monitor should already have a StatusNotification service object");
-                        monitor.RootPath = account.RootPath;
-                        //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(account.ServerUrl,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[account.AccountKey] = 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);
-                               }
-                       });
-               }
-
-           private void MigrateFolders(AccountSettings account)
-           {
-               var oldOthersFolder=Path.Combine(account.RootPath, FolderConstants.OldOthersFolder);
-               var newOthersFolder = Path.Combine(account.RootPath, FolderConstants.OthersFolder);
-               var oldFolder = new DirectoryInfo(oldOthersFolder);
-               var newFolder = new DirectoryInfo(newOthersFolder);
-
-            if (oldFolder.Exists && !newFolder.Exists)
-            {
-                oldFolder.MoveTo(newOthersFolder);
-            }
-           }
-
-
-           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);
-                NotifyOfPropertyChange(() => TooltipMessage);
-                       }
-               }
-
-        public string VersionMessage { get; set; }
-
-           public string TooltipMessage
-           {
-               get
-               {
-                   return String.Format("{0}\r\n{1}",VersionMessage,StatusMessage);
-               }
-           }
-
-        public string TooltipMiniStatus
-        {
-            get
-            {
-                return String.Format("{0}\r\n{1}", "Status Window", "Enable / Disable the status window");
-            }
-        }
-
-        /*public string ToggleStatusWindowMessage
-        {
-            get
-            {
-                return String.Format("{0}" + Environment.NewLine + "{1} Toggle Mini Status");
-            }
-        }*/
-
-           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 CancelCurrentOperation()
-        {
-            _networkAgent.CancelCurrentOperation();
-        }
-
-        public void ShowPreferences()
-        {
-            ShowPreferences(null);
-        }
-
-               public void ShowPreferences(string currentTab)
-               {
-                       //Settings.Reload();
-            
-                   var preferences = IoC.Get<PreferencesViewModel>();//??new PreferencesViewModel(_windowManager, _events, this, Settings,currentTab);
-            if (!String.IsNullOrWhiteSpace(currentTab))
-                preferences.SelectedTab = currentTab;
-            if (!preferences.IsActive)
-                       _windowManager.ShowWindow(preferences);
-            var view = (Window)preferences.GetView();
-            view.NullSafe(v=>v.Activate());
-               }
-
-               public void AboutPithos()
-               {
-                       var about = IoC.Get<AboutViewModel>();
-                   about.LatestVersion=_sparkle.LatestVersion;
-                       _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.ProductionServer;
-                       Process.Start(site);            
-               }
-
-
-               public void GoToSite(AccountInfo account)
-               {
-                   var uri = account.SiteUri.Replace("http://","https://");            
-                   Process.Start(uri);
-               }
-
-           private bool _statusVisible;
-
-           public string MiniStatusCaption
-           {
-               get
-               {
-                   return  _statusVisible ? "Hide Status Window" : "Show Status Window";
-               }
-           }
-
-           public bool HasConflicts
-           {
-            get { return true; }
-           }
-        public void ShowConflicts()
-        {
-            _windowManager.ShowWindow(IoC.Get<ConflictsViewModel>());            
-        }
-
-           /// <summary>
-        /// Open an explorer window to the target path's directory
-        /// and select the file
-        /// </summary>
-        /// <param name="entry"></param>
-        public void GoToFile(FileEntry entry)
-        {
-            var fullPath = entry.FullPath;
-            if (!File.Exists(fullPath) && !Directory.Exists(fullPath))
-                return;
-            Process.Start("explorer.exe","/select, " + fullPath);
-        }
-
-        public void OpenLogPath()
-        {
-            var pithosDataPath = PithosSettings.PithosDataPath;
-
-            Process.Start(pithosDataPath);
-        }
-        
-        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()
-               {
-                       _pollAgent.SynchNow();
-               }
-
-               public ObjectInfo RefreshObjectInfo(ObjectInfo currentInfo)
-               {
-                       if (currentInfo==null)
-                               throw new ArgumentNullException("currentInfo");
-                       Contract.EndContractBlock();                
-            var monitor = Monitors[currentInfo.AccountKey];
-                       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.AccountKey];
-                       var newInfo = monitor.CloudClient.GetContainerInfo(container.Account, container.Name);
-                       return newInfo;
-               }
-
-           private bool _isPaused;
-           public bool IsPaused
-           {
-               get { return _isPaused; }
-               set
-               {
-                   _isPaused = value;
-                PauseSyncCaption = IsPaused ? "Resume syncing" : "Pause syncing";
-                var iconKey = IsPaused ? "TraySyncPaused" : "TrayInSynch";
-                StatusIcon = String.Format(@"../Images/{0}.ico", iconKey);
-
-                NotifyOfPropertyChange(() => IsPaused);
-               }
-           }
-
-           public void ToggleSynching()
-               {
-                       IsPaused=!IsPaused;
-                       foreach (var monitor in Monitors.Values)
-                       {
-                           monitor.Pause = IsPaused ;
-                       }
-            _pollAgent.Pause = IsPaused;
-            _networkAgent.Pause = IsPaused;
-
-
-               }
-
-        public void ExitPithos()
-        {
-            try
-            {
-
-                foreach (var monitor in Monitors.Select(pair => pair.Value))
-                {
-                    monitor.Stop();
-                }
-
-                var view = GetView() as Window;
-                if (view != null)
-                    view.Close();
-            }
-            catch (Exception exc)
-            {
-                Log.Info("Exception while exiting", exc);                
-            }
-            finally
-            {
-                Application.Current.Shutdown();
-            }
-        }
-
-           #endregion
-
-
-               private readonly Dictionary<PithosStatus, StatusInfo> _iconNames = new List<StatusInfo>
-                       {
-                               new StatusInfo(PithosStatus.InSynch, "All files up to date", "TrayInSynch"),
-                               new StatusInfo(PithosStatus.PollSyncing, "Polling Files", "TraySynching"),
-                new StatusInfo(PithosStatus.LocalSyncing, "Syncing Files", "TraySynching"),
-                               new StatusInfo(PithosStatus.SyncPaused, "Sync Paused", "TraySyncPaused")
-                       }.ToDictionary(s => s.Status);
-
-               readonly IWindowManager _windowManager;
-               
-        //private int _syncCount=0;
-
-
-        private PithosStatus _pithosStatus = PithosStatus.Disconnected;
-
-        public void SetPithosStatus(PithosStatus status)
-        {
-            if (_pithosStatus == PithosStatus.LocalSyncing && status == PithosStatus.PollComplete)
-                return;
-            if (_pithosStatus == PithosStatus.PollSyncing && status == PithosStatus.LocalComplete)
-                return;
-            if (status == PithosStatus.LocalComplete || status == PithosStatus.PollComplete)
-                _pithosStatus = PithosStatus.InSynch;
-            else
-                _pithosStatus = status;
-            UpdateStatus();
-        }
-
-        public void SetPithosStatus(PithosStatus status,string message)
-        {
-            StatusMessage = message;
-            SetPithosStatus(status);
-        }
-
-         /*  public Notifier GetNotifier(Notification startNotification, Notification endNotification)
-           {
-               return new Notifier(this, startNotification, endNotification);
-           }*/
-
-           public Notifier GetNotifier(string startMessage, string endMessage, params object[] args)
-           {
-               return new Notifier(this, 
-                new StatusNotification(String.Format(startMessage,args)), 
-                new StatusNotification(String.Format(endMessage,args)));
-           }
-
-
-           ///<summary>
-               /// Updates the visual status indicators of the application depending on status changes, e.g. icon, stat                
-               ///</summary>
-               public void UpdateStatus()
-               {
-
-                       if (_iconNames.ContainsKey(_pithosStatus))
-                       {
-                               var info = _iconNames[_pithosStatus];
-                               StatusIcon = String.Format(@"../Images/{0}.ico", info.IconName);
-                       }
-
-            if (_pithosStatus == PithosStatus.InSynch)
-                StatusMessage = "All files up to date";
-               }
-
-
-          
-               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;
-
-                                               HttpStatusCode statusCode =HttpStatusCode.OK;
-                                               var response = exc.Response as HttpWebResponse;
-                                               if(response!=null)
-                                                       statusCode = response.StatusCode;
-
-                                               switch (statusCode)
-                                               {
-                                                       case HttpStatusCode.Unauthorized:
-                                                               var message = String.Format("API Key Expired for {0}. Starting Renewal",
-                                                                                                                       monitor.UserName);
-                                                               Log.Error(message, exc);
-                                var account = Settings.Accounts.Find(acc => acc.AccountKey == new Uri(new Uri(monitor.AuthenticationUrl), monitor.UserName));                                
-                                                       account.IsExpired = true;
-                                Notify(new ExpirationNotification(account));
-                                                               //TryAuthorize(monitor.UserName, retries).Wait();
-                                                               break;
-                                                       case HttpStatusCode.ProxyAuthenticationRequired:
-                                                               TryAuthenticateProxy(monitor,retries);
-                                                               break;
-                                                       default:
-                                                               TryLater(monitor, exc, retries);
-                                                               break;
-                                               }
-                                       }
-                                       catch (Exception exc)
-                                       {
-                                               if (AbandonRetry(monitor, retries)) 
-                                                       return;
-
-                                               TryLater(monitor,exc,retries);
-                                       }
-                               }
-                       });
-               }
-
-               private void TryAuthenticateProxy(PithosMonitor monitor,int retries)
-               {
-                       Execute.OnUIThread(() =>
-                                                                  {                                       
-                                                                          var proxyAccount = IoC.Get<ProxyAccountViewModel>();
-                                                                               proxyAccount.Settings = Settings;
-                                                                          if (true != _windowManager.ShowDialog(proxyAccount)) 
-                                                                                  return;
-                                                                          StartMonitor(monitor, retries);
-                                                                          NotifyOfPropertyChange(() => Accounts);
-                                                                  });
-               }
-
-               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 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)
-               {
-            if (RecentFiles.Any(e => e.FullPath == filePath))
-                return;
-            
-                       IProducerConsumerCollection<FileEntry> files=RecentFiles;
-                       FileEntry popped;
-                       while (files.Count > 5)
-                               files.TryTake(out popped);
-            var entry = new FileEntry { FullPath = filePath };
-                       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.EscapeDataString(account.Token),
-                               Uri.EscapeDataString(account.UserName));
-
-                       if (!Accounts.Any(item => item.UserName == account.UserName && item.SiteUri == account.SiteUri))
-                               Accounts.TryAdd(account);
-
-               }
-
-               public void NotifyConflicts(IEnumerable<FileSystemInfo> conflictFiles, string message)
-               {
-                       if (conflictFiles == null)
-                               return;
-                   //Convert to list to avoid multiple iterations
-            var files = conflictFiles.ToList();
-                       if (files.Count==0)
-                               return;
-
-                       UpdateStatus();
-                       //TODO: Create a more specific message. For now, just show a warning
-                       NotifyForFiles(files,message,TraceLevel.Warning);
-
-               }
-
-               public void NotifyForFiles(IEnumerable<FileSystemInfo> files, string message,TraceLevel level=TraceLevel.Info)
-               {
-                       if (files == null)
-                               return;
-                       if (!files.Any())
-                               return;
-
-                       StatusMessage = message;
-
-                       _events.Publish(new Notification { Title = "Pithos+", Message = message, Level = level});
-               }
-
-               public void Notify(Notification notification)
-               {
-            TaskEx.Run(()=> _events.Publish(notification));
-               }
-
-
-               public void RemoveMonitor(string serverUrl,string accountName)
-               {
-                       if (String.IsNullOrWhiteSpace(accountName))
-                               return;
-
-                       var accountInfo=_accounts.FirstOrDefault(account => account.UserName == accountName && account.StorageUri.ToString().StartsWith(serverUrl));
-            if (accountInfo != null)
-            {
-                _accounts.TryRemove(accountInfo);
-                _pollAgent.RemoveAccount(accountInfo);
-            }
-
-            var accountKey = new Uri(new Uri(serverUrl),accountName);
-                   PithosMonitor monitor;
-                       if (Monitors.TryRemove(accountKey, out monitor))
-                       {
-                               monitor.Stop();
-                //TODO: Also remove any pending actions for this account
-                //from the network queue                
-                       }
-               }
-
-               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)
-               {
-                   TaskEx.Run(() =>
-                   {
-                       PithosMonitor monitor;
-                       if (Monitors.TryGetValue(message.Account.AccountKey, out monitor))
-                       {
-                    Selectives.SetIsSelectiveEnabled(message.Account.AccountKey, message.Enabled);
-                           monitor.SetSelectivePaths(message.Uris, message.Added, message.Removed);
-                       }
-
-                       var account = Accounts.FirstOrDefault(acc => acc.AccountKey == message.Account.AccountKey);
-                       if (account != null)
-                       {
-                           var added=monitor.UrisToFilePaths(message.Added);
-                    _pollAgent.SynchNow(added);
-                       }
-                   });
-
-               }
-
-
-               private bool _pollStarted;
-           private Sparkle _sparkle;
-           private bool _manualUpgradeCheck;
-
-           //SMELL: Doing so much work for notifications in the shell is wrong
-               //The notifications should be moved to their own view/viewmodel pair
-               //and different templates should be used for different message types
-               //This will also allow the addition of extra functionality, eg. actions
-               //
-               public void Handle(Notification notification)
-               {
-                       UpdateStatus();
-
-                       if (!Settings.ShowDesktopNotifications)
-                               return;
-
-                       if (notification is PollNotification)
-                       {
-                               _pollStarted = true;
-                               return;
-                       }
-                       if (notification is CloudNotification)
-                       {
-                               if (!_pollStarted) 
-                                       return;
-                               _pollStarted= false;
-                               notification.Title = "Pithos+";
-                               notification.Message = "Start Synchronisation";
-                       }
-
-                   var deleteNotification = notification as CloudDeleteNotification;
-            if (deleteNotification != null)
-            {
-                StatusMessage = String.Format("Deleted {0}", deleteNotification.Data.Name);
-                return;
-            }
-
-                   var progress = notification as ProgressNotification;
-                   
-                   
-            if (progress != null)
-                   {
-                       StatusMessage = String.Format("{0} {1:p2} of {2} - {3}",                                                      
-                                              progress.Action,
-                                                     (progress.Block + progress.BlockPercentage/100.0)/(double)progress.TotalBlocks,
-                                                     progress.FileSize.ToByteSize(),
-                                                     progress.FileName);
-                       return;
-                   }
-
-                   var info = notification as StatusNotification;
-            if (info != null)
-            {
-                StatusMessage = info.Title;
-                return;
-            }
-                       if (String.IsNullOrWhiteSpace(notification.Message) && String.IsNullOrWhiteSpace(notification.Title))
-                               return;
-
-            if (notification.Level <= TraceLevel.Warning)
-                           ShowBalloonFor(notification);
-               }
-
-           private void ShowBalloonFor(Notification notification)
-           {
-            Contract.Requires(notification!=null);
-            
-            if (!Settings.ShowDesktopNotifications) 
-                return;
-            
-            BalloonIcon icon;
-               switch (notification.Level)
-               {
-                case TraceLevel.Verbose:
-                       return;
-                   case TraceLevel.Info:                   
-                       icon = BalloonIcon.Info;
-                       break;
-                case TraceLevel.Error:
-                    icon = BalloonIcon.Error;
-                    break;
-                case TraceLevel.Warning:
-                       icon = BalloonIcon.Warning;
-                       break;
-                   default:
-                       return;
-               }
-
-               var tv = (ShellView) GetView();
-               System.Action clickAction = null;
-               if (notification is ExpirationNotification)
-               {
-                   clickAction = () => ShowPreferences("AccountTab");
-               }
-               var balloon = new PithosBalloon
-                                 {
-                                     Title = notification.Title,
-                                     Message = notification.Message,
-                                     Icon = icon,
-                                     ClickAction = clickAction
-                                 };
-               tv.TaskbarView.ShowCustomBalloon(balloon, PopupAnimation.Fade, 4000);
-           }
-
-           #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();
-               }
-
-               public FileStatus GetFileStatus(string localFileName)
-               {
-                       if (String.IsNullOrWhiteSpace(localFileName))
-                               throw new ArgumentNullException("localFileName");
-                       Contract.EndContractBlock();
-                       
-                       var statusKeeper = IoC.Get<IStatusKeeper>();
-                       var status=statusKeeper.GetFileStatus(localFileName);
-                       return status;
-               }
-
-           public void RemoveAccountFromDatabase(AccountSettings account)
-           {
-            var statusKeeper = IoC.Get<IStatusKeeper>();
-            statusKeeper.ClearFolderStatus(account.RootPath);          
-           }
-       }
-}
+#region\r
+/* -----------------------------------------------------------------------\r
+ * <copyright file="ShellViewModel.cs" company="GRNet">\r
+ * \r
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or\r
+ * without modification, are permitted provided that the following\r
+ * conditions are met:\r
+ *\r
+ *   1. Redistributions of source code must retain the above\r
+ *      copyright notice, this list of conditions and the following\r
+ *      disclaimer.\r
+ *\r
+ *   2. Redistributions in binary form must reproduce the above\r
+ *      copyright notice, this list of conditions and the following\r
+ *      disclaimer in the documentation and/or other materials\r
+ *      provided with the distribution.\r
+ *\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS\r
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\r
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR\r
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\r
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF\r
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\r
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\r
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * The views and conclusions contained in the software and\r
+ * documentation are those of the authors and should not be\r
+ * interpreted as representing official policies, either expressed\r
+ * or implied, of GRNET S.A.\r
+ * </copyright>\r
+ * -----------------------------------------------------------------------\r
+ */\r
+#endregion\r
+using System.Collections.Concurrent;\r
+using System.Diagnostics;\r
+using System.Diagnostics.Contracts;\r
+using System.IO;\r
+using System.Net;\r
+using System.Net.Http;\r
+using System.Reflection;\r
+using System.Runtime.InteropServices;\r
+using System.ServiceModel;\r
+using System.Threading;\r
+using System.Threading.Tasks;\r
+using System.Windows;\r
+using System.Windows.Controls.Primitives;\r
+using System.Windows.Input;\r
+using AppLimit.NetSparkle;\r
+using Caliburn.Micro;\r
+using Hardcodet.Wpf.TaskbarNotification;\r
+using Pithos.Client.WPF.Configuration;\r
+using Pithos.Client.WPF.FileProperties;\r
+using Pithos.Client.WPF.Preferences;\r
+using Pithos.Client.WPF.SelectiveSynch;\r
+using Pithos.Client.WPF.Services;\r
+using Pithos.Client.WPF.Shell;\r
+using Pithos.Core;\r
+using Pithos.Core.Agents;\r
+using Pithos.Interfaces;\r
+using System;\r
+using System.Collections.Generic;\r
+using System.Linq;\r
+using Pithos.Network;\r
+using Pithos.OFM;\r
+using StatusService = Pithos.Client.WPF.Services.StatusService;\r
+\r
+\r
+namespace Pithos.Client.WPF {\r
+       using System.ComponentModel.Composition;\r
+\r
+       public class ToggleStatusCommand:ICommand\r
+       {\r
+           private readonly ShellViewModel _model;\r
+           public ToggleStatusCommand(ShellViewModel model)\r
+           {\r
+               _model = model;\r
+           }\r
+           public void Execute(object parameter)\r
+           {\r
+               _model.CurrentSyncStatus();\r
+           }\r
+\r
+           public bool CanExecute(object parameter)\r
+           {\r
+               return true;\r
+           }\r
+\r
+           public event EventHandler CanExecuteChanged;\r
+       }\r
+\r
+\r
+       ///<summary>\r
+       /// The "shell" of the Pithos application displays the taskbar  icon, menu and notifications.\r
+       /// The shell also hosts the status service called by shell extensions to retrieve file info\r
+       ///</summary>\r
+       ///<remarks>\r
+       /// It is a strange "shell" as its main visible element is an icon instead of a window\r
+       /// The shell subscribes to the following events:\r
+       /// * Notification:  Raised by components that want to notify the user. Usually displayed in a balloon\r
+       /// * SelectiveSynchChanges: Notifies that the user made changes to the selective synch folders for an account. Raised by the Selective Synch dialog. Located here because the monitors are here\r
+       /// * ShowFilePropertiesEvent: Raised when a shell command requests the display of the file/container properties dialog\r
+       ///</remarks>           \r
+       //TODO: CODE SMELL Why does the shell handle the SelectiveSynchChanges?\r
+    [Export(typeof(IShell)), Export(typeof(ShellViewModel)),Export(typeof(IStatusNotification))]\r
+       public class ShellViewModel : Screen, IStatusNotification, IShell,\r
+               IHandle<Notification>, IHandle<SelectiveSynchChanges>, IHandle<ShowFilePropertiesEvent>\r
+       {\r
+               \r
+               private readonly IEventAggregator _events;\r
+\r
+        \r
+               public PithosSettings Settings { get; private set; }\r
+\r
+\r
+               private readonly ConcurrentDictionary<Uri, PithosMonitor> _monitors = new ConcurrentDictionary<Uri, PithosMonitor>();\r
+               ///<summary>\r
+               /// Dictionary of account monitors, keyed by account\r
+               ///</summary>\r
+               ///<remarks>\r
+               /// One monitor class is created for each account. The Shell needs access to the monitors to execute start/stop/pause commands,\r
+               /// retrieve account and boject info            \r
+               ///</remarks>\r
+               // TODO: Does the Shell REALLY need access to the monitors? Could we achieve the same results with a better design?\r
+               // TODO: The monitors should be internal to Pithos.Core, even though exposing them makes coding of the Object and Container windows easier\r
+               public ConcurrentDictionary<Uri, PithosMonitor> Monitors\r
+               {\r
+                       get { return _monitors; }\r
+               }\r
+\r
+\r
+               ///<summary>\r
+               /// The status service is used by Shell extensions to retrieve file status information\r
+               ///</summary>\r
+               //TODO: CODE SMELL! This is the shell! While hosting in the shell makes executing start/stop commands easier, it is still a smell\r
+               private ServiceHost _statusService;\r
+\r
+               //Logging in the Pithos client is provided by log4net\r
+        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);\r
+\r
+        #pragma warning disable 649\r
+        [Import]\r
+           private PollAgent _pollAgent;\r
+\r
+        [Import]\r
+           private NetworkAgent _networkAgent;\r
+\r
+           [Import]\r
+           public Selectives Selectives { get; set; }\r
+\r
+        #pragma warning restore 649\r
+\r
+        public ToggleStatusCommand ToggleMiniStatusCommand { get; set; }\r
+\r
+           private MiniStatusViewModel _miniStatus;\r
+\r
+           [Import]\r
+        public MiniStatusViewModel MiniStatus\r
+           {\r
+               get { return _miniStatus; }\r
+               set\r
+               {\r
+                   _miniStatus = value;\r
+                   _miniStatus.Shell = this;\r
+                   _miniStatus.Deactivated += (sender, arg) =>\r
+                                                  {\r
+                                                      _statusVisible = false;\r
+                                                   NotifyOfPropertyChange(()=>MiniStatusCaption);\r
+                                                  };\r
+               }\r
+           }\r
+\r
+           ///<summary>\r
+               /// The Shell depends on MEF to provide implementations for windowManager, events, the status checker service and the settings\r
+               ///</summary>\r
+               ///<remarks>\r
+               /// The PithosSettings class encapsulates the app's settings to abstract their storage mechanism (App settings, a database or registry)\r
+               ///</remarks>\r
+               [ImportingConstructor]          \r
+               public ShellViewModel(IWindowManager windowManager, IEventAggregator events, PithosSettings settings/*,PollAgent pollAgent,NetworkAgent networkAgent*/)\r
+               {\r
+                       try\r
+                       {\r
+\r
+                               _windowManager = windowManager;\r
+                               //CHECK: Caliburn doesn't need explicit command construction\r
+                               //CurrentSyncStatusCommand = new PithosCommand(OpenPithosFolder);\r
+                               //The event subst\r
+                               _events = events;\r
+                               _events.Subscribe(this);\r
+\r
+/*\r
+                           _pollAgent = pollAgent;\r
+                           _networkAgent = networkAgent;\r
+*/\r
+                               Settings = settings;\r
+\r
+                               Proxy.SetFromSettings(settings);\r
+\r
+                StatusMessage = Settings.Accounts.Count==0 \r
+                    ? "No Accounts added\r\nPlease add an account" \r
+                    : "Starting";\r
+\r
+                               _accounts.CollectionChanged += (sender, e) =>\r
+                                                                                                  {\r
+                                                                                                          NotifyOfPropertyChange(() => OpenFolderCaption);\r
+                                                                                                          NotifyOfPropertyChange(() => HasAccounts);\r
+                                                                                                  };\r
+\r
+                SetVersionMessage();\r
+\r
+                ToggleMiniStatusCommand=new ToggleStatusCommand(this);\r
+                       }\r
+                       catch (Exception exc)\r
+                       {\r
+                               Log.Error("Error while starting the ShellViewModel",exc);\r
+                               throw;\r
+                       }\r
+\r
+               }\r
+\r
+           private void SetVersionMessage()\r
+           {\r
+               Assembly assembly = Assembly.GetExecutingAssembly();\r
+               var fileVersion = FileVersionInfo.GetVersionInfo(assembly.Location);\r
+               VersionMessage = String.Format("Pithos+ {0}", fileVersion.FileVersion);\r
+           }\r
+\r
+\r
+        public void openOFM()\r
+        {\r
+            var fileManager = IoC.Get<FileManagerViewModel>();\r
+            fileManager.Accounts = Settings.Accounts;\r
+            fileManager.CurrentAccount = fileManager.Accounts.First();// Settings.Accounts.First();\r
+            _windowManager.ShowWindow(fileManager);\r
+            //var ofm = new OFM.GUI.OFM();\r
+            //ofm.Show();\r
+        }\r
+\r
+        public void CurrentSyncStatus()\r
+        {\r
+            if (Accounts.Count == 0)\r
+            {\r
+                ShowPreferences("AccountTab");\r
+            }\r
+            else\r
+            {\r
+                if (!_statusVisible)\r
+                {\r
+                    _windowManager.ShowWindow(MiniStatus);\r
+                    _statusVisible = true;\r
+                }\r
+                else\r
+                {\r
+                    if (MiniStatus.IsActive)\r
+                        MiniStatus.TryClose();\r
+                    _statusVisible = false;\r
+                }\r
+\r
+                NotifyOfPropertyChange(() => MiniStatusCaption);\r
+            }\r
+        }\r
+\r
+           protected override void OnActivate()\r
+               {\r
+                       base.OnActivate();\r
+\r
+            InitializeSparkle();\r
+\r
+               //Must delay opening the upgrade window\r
+            //to avoid Windows Messages sent by the TaskbarIcon\r
+            TaskEx.Delay(5000).ContinueWith(_=>\r
+                Execute.OnUIThread(()=> _sparkle.StartLoop(true,Settings.UpdateForceCheck,Settings.UpdateCheckInterval)));\r
+\r
+\r
+                       StartMonitoring();                    \r
+               }\r
+\r
+\r
+           private void OnCheckFinished(object sender, bool updaterequired)\r
+           {\r
+            \r
+            Log.InfoFormat("Upgrade check finished. Need Upgrade: {0}", updaterequired);\r
+            if (_manualUpgradeCheck)\r
+            {\r
+                _manualUpgradeCheck = false;\r
+                if (!updaterequired)\r
+                    //Sparkle raises events on a background thread\r
+                    Execute.OnUIThread(()=>\r
+                        ShowBalloonFor(new Notification{Title="Pithos+ is up to date",Message="You have the latest Pithos+ version. No update is required"}));\r
+            }\r
+           }\r
+\r
+           private void OnUpgradeDetected(object sender, UpdateDetectedEventArgs e)\r
+           {            \r
+               Log.InfoFormat("Update detected {0}",e.LatestVersion);\r
+           }\r
+\r
+        public void CheckForUpgrade()\r
+        {\r
+            ShowBalloonFor(new Notification{Title="Checking for upgrades",Message="Contacting the server to retrieve the latest Pithos+ version."});\r
+            _sparkle.StopLoop();\r
+            _sparkle.updateDetected -= OnUpgradeDetected;\r
+            _sparkle.checkLoopFinished -= OnCheckFinished;\r
+            _sparkle.Dispose();\r
+\r
+            _manualUpgradeCheck = true;\r
+            InitializeSparkle();\r
+            _sparkle.StartLoop(true,true,Settings.UpdateCheckInterval);\r
+        }\r
+\r
+        private void InitializeSparkle()\r
+        {\r
+            _sparkle = new Sparkle(Settings.UpdateUrl);\r
+            _sparkle.updateDetected += OnUpgradeDetected;\r
+            _sparkle.checkLoopFinished += OnCheckFinished;\r
+            _sparkle.ShowDiagnosticWindow = Settings.UpdateDiagnostics;\r
+        }\r
+\r
+           private async void StartMonitoring()\r
+               {\r
+                       try\r
+                       {\r
+                if (Settings.IgnoreCertificateErrors)\r
+                {\r
+                    ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;\r
+                }\r
+                    \r
+                               var accounts = Settings.Accounts.Select(MonitorAccount);\r
+                await TaskEx.WhenAll(accounts).ConfigureAwait(false);\r
+                               _statusService = StatusService.Start();\r
+\r
+                       }\r
+                       catch (AggregateException exc)\r
+                       {\r
+                               exc.Handle(e =>\r
+                               {\r
+                                       Log.Error("Error while starting monitoring", e);\r
+                                       return true;\r
+                               });\r
+                               throw;\r
+                       }\r
+               }\r
+\r
+               protected override void OnDeactivate(bool close)\r
+               {\r
+                       base.OnDeactivate(close);\r
+                       if (close)\r
+                       {\r
+                               StatusService.Stop(_statusService);\r
+                               _statusService = null;\r
+                       }\r
+               }\r
+\r
+               public Task MonitorAccount(AccountSettings account)\r
+               {\r
+                       return Task.Factory.StartNew(() =>\r
+                       {                                                \r
+                               PithosMonitor monitor;\r
+                               var accountName = account.AccountName;\r
+\r
+                           MigrateFolders(account);\r
+\r
+                           Selectives.SetIsSelectiveEnabled(account.AccountKey, account.SelectiveSyncEnabled);\r
+\r
+                               if (Monitors.TryGetValue(account.AccountKey, out monitor))\r
+                               {\r
+                                       //If the account is active\r
+                    if (account.IsActive)\r
+                    {\r
+                        //The Api Key may have changed throuth the Preferences dialog\r
+                        monitor.ApiKey = account.ApiKey;\r
+                                               Debug.Assert(monitor.StatusNotification == this,"An existing monitor should already have a StatusNotification service object");\r
+                        monitor.RootPath = account.RootPath;\r
+                        //Start the monitor. It's OK to start an already started monitor,\r
+                        //it will just ignore the call                        \r
+                        StartMonitor(monitor).Wait();\r
+                    }\r
+                    else\r
+                    {\r
+                        //If the account is inactive\r
+                        //Stop and remove the monitor\r
+                        RemoveMonitor(account.ServerUrl,accountName);\r
+                    }\r
+                                       return;\r
+                               }\r
+\r
+                               \r
+                               //Create a new monitor/ Can't use MEF here, it would return a single instance for all monitors\r
+                               monitor = new PithosMonitor\r
+                                                         {\r
+                                                                 UserName = accountName,\r
+                                                                 ApiKey = account.ApiKey,                                  \r
+                                                                 StatusNotification = this,\r
+                                                                 RootPath = account.RootPath\r
+                                                         };\r
+                               //PithosMonitor uses MEF so we need to resolve it\r
+                               IoC.BuildUp(monitor);\r
+\r
+                               monitor.AuthenticationUrl = account.ServerUrl;\r
+\r
+                               Monitors[account.AccountKey] = monitor;\r
+\r
+                if (!Directory.Exists(account.RootPath))\r
+                {\r
+                    account.IsActive = false;\r
+                    Settings.Save();\r
+                    Notify(new Notification\r
+                    {\r
+                        Level = TraceLevel.Error,\r
+                        Title = "Missing account folder",\r
+                        Message = String.Format("Can't find the root folder for account {0} at {1}. The account was deactivated.\r" +\r
+                        "If the account's files were stored in a removable disk, please connect it and reactivate the account", account.AccountName, account.RootPath)\r
+                    });\r
+                }\r
+\r
+\r
+                               if (account.IsActive)\r
+                               {\r
+                                       //Don't start a monitor if it doesn't have an account and ApiKey\r
+                                       if (String.IsNullOrWhiteSpace(monitor.UserName) ||\r
+                                               String.IsNullOrWhiteSpace(monitor.ApiKey))\r
+                                               return;\r
+                                       StartMonitor(monitor);\r
+                               }\r
+                       });\r
+               }\r
+\r
+           private void MigrateFolders(AccountSettings account)\r
+           {\r
+               var oldOthersFolder=Path.Combine(account.RootPath, FolderConstants.OldOthersFolder);\r
+               var newOthersFolder = Path.Combine(account.RootPath, FolderConstants.OthersFolder);\r
+               var oldFolder = new DirectoryInfo(oldOthersFolder);\r
+               var newFolder = new DirectoryInfo(newOthersFolder);\r
+\r
+            if (oldFolder.Exists && !newFolder.Exists)\r
+            {\r
+                oldFolder.MoveTo(newOthersFolder);\r
+            }\r
+           }\r
+\r
+\r
+           protected override void OnViewLoaded(object view)\r
+               {\r
+                       UpdateStatus();\r
+                       var window = (Window)view;            \r
+                       TaskEx.Delay(1000).ContinueWith(t => Execute.OnUIThread(window.Hide));\r
+                       base.OnViewLoaded(view);\r
+               }\r
+\r
+\r
+               #region Status Properties\r
+\r
+               private string _statusMessage;\r
+               public string StatusMessage\r
+               {\r
+                       get { return _statusMessage; }\r
+                       set\r
+                       {\r
+                               _statusMessage = value;\r
+                               NotifyOfPropertyChange(() => StatusMessage);\r
+                NotifyOfPropertyChange(() => TooltipMessage);\r
+                       }\r
+               }\r
+\r
+        private double _ProgressBar;\r
+        public double ProgressBar\r
+        {\r
+            get { return _ProgressBar; }\r
+            set\r
+            {\r
+                _ProgressBar = value;\r
+                NotifyOfPropertyChange(() => ProgressBar);\r
+            }\r
+        }\r
+\r
+\r
+        public string VersionMessage { get; set; }\r
+\r
+           public string TooltipMessage\r
+           {\r
+               get\r
+               {\r
+                   return String.Format("{0}\r\n{1}",VersionMessage,StatusMessage);\r
+               }\r
+           }\r
+\r
+        public string TooltipMiniStatus\r
+        {\r
+            get\r
+            {\r
+                return String.Format("{0}\r\n{1}", "Status Window", "Enable / Disable the status window");\r
+            }\r
+        }\r
+\r
+        /*public string ToggleStatusWindowMessage\r
+        {\r
+            get\r
+            {\r
+                return String.Format("{0}" + Environment.NewLine + "{1} Toggle Mini Status");\r
+            }\r
+        }*/\r
+\r
+           private readonly ObservableConcurrentCollection<AccountInfo> _accounts = new ObservableConcurrentCollection<AccountInfo>();\r
+               public ObservableConcurrentCollection<AccountInfo> Accounts\r
+               {\r
+                       get { return _accounts; }\r
+               }\r
+\r
+               public bool HasAccounts\r
+               {\r
+                       get { return _accounts.Count > 0; }\r
+               }\r
+\r
+\r
+               public string OpenFolderCaption\r
+               {\r
+                       get\r
+                       {\r
+                               return (_accounts.Count == 0)\r
+                                               ? "No Accounts Defined"\r
+                                               : "Open Pithos Folder";\r
+                       }\r
+               }\r
+\r
+               private string _pauseSyncCaption="Pause Synching";\r
+               public string PauseSyncCaption\r
+               {\r
+                       get { return _pauseSyncCaption; }\r
+                       set\r
+                       {\r
+                               _pauseSyncCaption = value;\r
+                               NotifyOfPropertyChange(() => PauseSyncCaption);\r
+                       }\r
+               }\r
+\r
+               private readonly ObservableConcurrentCollection<FileEntry> _recentFiles = new ObservableConcurrentCollection<FileEntry>();\r
+               public ObservableConcurrentCollection<FileEntry> RecentFiles\r
+               {\r
+                       get { return _recentFiles; }\r
+               }\r
+\r
+\r
+               private string _statusIcon="../Images/Pithos.ico";\r
+               public string StatusIcon\r
+               {\r
+                       get { return _statusIcon; }\r
+                       set\r
+                       {\r
+                               //TODO: Ensure all status icons use the Pithos logo\r
+                               _statusIcon = value;\r
+                               NotifyOfPropertyChange(() => StatusIcon);\r
+                       }\r
+               }\r
+\r
+               #endregion\r
+\r
+               #region Commands\r
+\r
+        public void CancelCurrentOperation()\r
+        {\r
+            _pollAgent.CancelCurrentOperation();\r
+        }\r
+\r
+        public void ShowPreferences()\r
+        {\r
+            ShowPreferences(null);\r
+        }\r
+\r
+               public void ShowPreferences(string currentTab)\r
+               {\r
+                       //Settings.Reload();\r
+            \r
+                   var preferences = IoC.Get<PreferencesViewModel>();//??new PreferencesViewModel(_windowManager, _events, this, Settings,currentTab);\r
+            if (!String.IsNullOrWhiteSpace(currentTab))\r
+                preferences.SelectedTab = currentTab;\r
+            if (!preferences.IsActive)\r
+                       _windowManager.ShowWindow(preferences);\r
+            var view = (Window)preferences.GetView();\r
+            view.NullSafe(v=>v.Activate());\r
+               }\r
+\r
+               public void AboutPithos()\r
+               {\r
+                       var about = IoC.Get<AboutViewModel>();\r
+                   about.LatestVersion=_sparkle.LatestVersion;\r
+                       _windowManager.ShowWindow(about);\r
+               }\r
+\r
+               public void SendFeedback()\r
+               {\r
+                       var feedBack =  IoC.Get<FeedbackViewModel>();\r
+                       _windowManager.ShowWindow(feedBack);\r
+               }\r
+\r
+               //public PithosCommand OpenPithosFolderCommand { get; private set; }\r
+\r
+               public void OpenPithosFolder()\r
+               {\r
+                       var account = Settings.Accounts.FirstOrDefault(acc => acc.IsActive);\r
+                       if (account == null)\r
+                               return;\r
+                       Process.Start(account.RootPath);\r
+               }\r
+\r
+               public void OpenPithosFolder(AccountInfo account)\r
+               {\r
+                       Process.Start(account.AccountPath);\r
+               }\r
+\r
+               \r
+\r
+               public void GoToSite()\r
+               {            \r
+                       var site = Properties.Settings.Default.ProductionServer;\r
+                       Process.Start(site);            \r
+               }\r
+\r
+\r
+               public void GoToSite(AccountInfo account)\r
+               {\r
+                   var uri = account.SiteUri.Replace("http://","https://");            \r
+                   Process.Start(uri);\r
+               }\r
+\r
+           private bool _statusVisible;\r
+\r
+           public string MiniStatusCaption\r
+           {\r
+               get\r
+               {\r
+                   return  _statusVisible ? "Hide Status Window" : "Show Status Window";\r
+               }\r
+           }\r
+\r
+           public bool HasConflicts\r
+           {\r
+            get { return true; }\r
+           }\r
+        public void ShowConflicts()\r
+        {\r
+            _windowManager.ShowWindow(IoC.Get<ConflictsViewModel>());            \r
+        }\r
+\r
+           /// <summary>\r
+        /// Open an explorer window to the target path's directory\r
+        /// and select the file\r
+        /// </summary>\r
+        /// <param name="entry"></param>\r
+        public void GoToFile(FileEntry entry)\r
+        {\r
+            var fullPath = entry.FullPath;\r
+            if (!File.Exists(fullPath) && !Directory.Exists(fullPath))\r
+                return;\r
+            Process.Start("explorer.exe","/select, " + fullPath);\r
+        }\r
+\r
+        public void OpenLogPath()\r
+        {\r
+            var pithosDataPath = PithosSettings.PithosDataPath;\r
+\r
+            Process.Start(pithosDataPath);\r
+        }\r
+        \r
+        public void ShowFileProperties()\r
+               {\r
+                       var account = Settings.Accounts.First(acc => acc.IsActive);            \r
+                       var dir = new DirectoryInfo(account.RootPath + @"\pithos");\r
+                       var files=dir.GetFiles();\r
+                       var r=new Random();\r
+                       var idx=r.Next(0, files.Length);\r
+                       ShowFileProperties(files[idx].FullName);            \r
+               }\r
+\r
+               public void ShowFileProperties(string filePath)\r
+               {\r
+                       if (String.IsNullOrWhiteSpace(filePath))\r
+                               throw new ArgumentNullException("filePath");\r
+                       if (!File.Exists(filePath) && !Directory.Exists(filePath))\r
+                               throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");\r
+                       Contract.EndContractBlock();\r
+\r
+                       var pair=(from monitor in  Monitors\r
+                                                          where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)\r
+                                                                  select monitor).FirstOrDefault();\r
+                       var accountMonitor = pair.Value;\r
+\r
+                       if (accountMonitor == null)\r
+                               return;\r
+\r
+                       var infoTask=accountMonitor.GetObjectInfo(filePath);\r
+\r
+                       \r
+\r
+                       var fileProperties = new FilePropertiesViewModel(this, infoTask,filePath);\r
+                       _windowManager.ShowWindow(fileProperties);\r
+               } \r
+               \r
+               public void ShowContainerProperties()\r
+               {\r
+                       var account = Settings.Accounts.First(acc => acc.IsActive);            \r
+                       var dir = new DirectoryInfo(account.RootPath);\r
+                       var fullName = (from folder in dir.EnumerateDirectories()\r
+                                                       where (folder.Attributes & FileAttributes.Hidden) == 0\r
+                                                       select folder.FullName).First();\r
+                       ShowContainerProperties(fullName);            \r
+               }\r
+\r
+               public void ShowContainerProperties(string filePath)\r
+               {\r
+                       if (String.IsNullOrWhiteSpace(filePath))\r
+                               throw new ArgumentNullException("filePath");\r
+                       if (!Directory.Exists(filePath))\r
+                               throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath");\r
+                       Contract.EndContractBlock();\r
+\r
+                       var pair=(from monitor in  Monitors\r
+                                                          where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase)\r
+                                                                  select monitor).FirstOrDefault();\r
+                       var accountMonitor = pair.Value;            \r
+                       var info = accountMonitor.GetContainerInfo(filePath);\r
+\r
+                       \r
+\r
+                       var containerProperties = new ContainerPropertiesViewModel(this, info,filePath);\r
+                       _windowManager.ShowWindow(containerProperties);\r
+               }\r
+\r
+               public void SynchNow()\r
+               {\r
+                       _pollAgent.SynchNow();\r
+               }\r
+\r
+               public async Task<ObjectInfo> RefreshObjectInfo(ObjectInfo currentInfo)\r
+               {\r
+                       if (currentInfo==null)\r
+                               throw new ArgumentNullException("currentInfo");\r
+                       Contract.EndContractBlock();                \r
+            var monitor = Monitors[currentInfo.AccountKey];\r
+                       var newInfo=await monitor.CloudClient.GetObjectInfo(currentInfo.Account, currentInfo.Container, currentInfo.Name).ConfigureAwait(false);\r
+                       return newInfo;\r
+               }\r
+\r
+               public async Task<ContainerInfo> RefreshContainerInfo(ContainerInfo container)\r
+               {\r
+                       if (container == null)\r
+                               throw new ArgumentNullException("container");\r
+                       Contract.EndContractBlock();\r
+\r
+                       var monitor = Monitors[container.AccountKey];\r
+                       var newInfo = await monitor.CloudClient.GetContainerInfo(container.Account, container.Name).ConfigureAwait(false);\r
+                       return newInfo;\r
+               }\r
+\r
+           private bool _isPaused;\r
+           public bool IsPaused\r
+           {\r
+               get { return _isPaused; }\r
+               set\r
+               {\r
+                   _isPaused = value;\r
+                PauseSyncCaption = IsPaused ? "Resume syncing" : "Pause syncing";\r
+                var iconKey = IsPaused ? "TraySyncPaused" : "TrayInSynch";\r
+                StatusIcon = String.Format(@"../Images/{0}.ico", iconKey);\r
+\r
+                NotifyOfPropertyChange(() => IsPaused);\r
+               }\r
+           }\r
+\r
+           public void ToggleSynching()\r
+               {\r
+                       IsPaused=!IsPaused;\r
+                       foreach (var monitor in Monitors.Values)\r
+                       {\r
+                           monitor.Pause = IsPaused ;\r
+                       }\r
+            _pollAgent.Pause = IsPaused;\r
+            _networkAgent.Pause = IsPaused;\r
+\r
+\r
+               }\r
+\r
+        public void ExitPithos()\r
+        {\r
+            try\r
+            {\r
+\r
+                foreach (var monitor in Monitors.Select(pair => pair.Value))\r
+                {\r
+                    monitor.Stop();\r
+                }\r
+\r
+                var view = GetView() as Window;\r
+                if (view != null)\r
+                    view.Close();\r
+            }\r
+            catch (Exception exc)\r
+            {\r
+                Log.Info("Exception while exiting", exc);                \r
+            }\r
+            finally\r
+            {\r
+                Application.Current.Shutdown();\r
+            }\r
+        }\r
+\r
+           #endregion\r
+\r
+\r
+               private readonly Dictionary<PithosStatus, StatusInfo> _iconNames = new List<StatusInfo>\r
+                       {\r
+                               new StatusInfo(PithosStatus.InSynch, "All files up to date", "TrayInSynch"),\r
+                               new StatusInfo(PithosStatus.PollSyncing, "Polling Files", "TraySynching"),\r
+                new StatusInfo(PithosStatus.LocalSyncing, "Syncing Files", "TraySynching"),\r
+                               new StatusInfo(PithosStatus.SyncPaused, "Sync Paused", "TraySyncPaused")\r
+                       }.ToDictionary(s => s.Status);\r
+\r
+               readonly IWindowManager _windowManager;\r
+               \r
+        //private int _syncCount=0;\r
+\r
+\r
+        private PithosStatus _pithosStatus = PithosStatus.Disconnected;\r
+\r
+        public void SetPithosStatus(PithosStatus status)\r
+        {\r
+            if (_pithosStatus == PithosStatus.LocalSyncing && status == PithosStatus.PollComplete)\r
+                return;\r
+            if (_pithosStatus == PithosStatus.PollSyncing && status == PithosStatus.LocalComplete)\r
+                return;\r
+            if (status == PithosStatus.LocalComplete || status == PithosStatus.PollComplete)\r
+                _pithosStatus = PithosStatus.InSynch;\r
+            else\r
+                _pithosStatus = status;\r
+            UpdateStatus();\r
+        }\r
+\r
+        public void SetPithosStatus(PithosStatus status,string message)\r
+        {\r
+            StatusMessage = message;\r
+            SetPithosStatus(status);\r
+        }\r
+\r
+         /*  public Notifier GetNotifier(Notification startNotification, Notification endNotification)\r
+           {\r
+               return new Notifier(this, startNotification, endNotification);\r
+           }*/\r
+\r
+           public Notifier GetNotifier(string startMessage, string endMessage, bool isActive=true,params object[] args)\r
+           {\r
+               return isActive?new Notifier(this, \r
+                new StatusNotification(String.Format(startMessage,args)),\r
+                new StatusNotification(String.Format(endMessage,args)))\r
+                :new Notifier(this,(Notification) null,null);\r
+           }\r
+\r
+\r
+           ///<summary>\r
+               /// Updates the visual status indicators of the application depending on status changes, e.g. icon, stat                \r
+               ///</summary>\r
+               public void UpdateStatus()\r
+               {\r
+\r
+                       if (_iconNames.ContainsKey(_pithosStatus))\r
+                       {\r
+                               var info = _iconNames[_pithosStatus];\r
+                               StatusIcon = String.Format(@"../Images/{0}.ico", info.IconName);\r
+                       }\r
+\r
+            if (_pithosStatus == PithosStatus.InSynch)\r
+                StatusMessage = "All files up to date";\r
+               }\r
+\r
+\r
+          \r
+               public async Task StartMonitor(PithosMonitor monitor,int retries=0)\r
+               {\r
+                   using (log4net.ThreadContext.Stacks["Monitor"].Push("Start"))\r
+                   {\r
+                       try\r
+                       {\r
+                           Log.InfoFormat("Start Monitoring {0}", monitor.UserName);\r
+\r
+                           await monitor.Start();\r
+                       }\r
+                catch (HttpRequestWithStatusException exc)\r
+                       {\r
+                           if (AbandonRetry(monitor, retries))\r
+                               return;\r
+\r
+                           //HttpStatusCode statusCode = HttpStatusCode.OK;\r
+                           //var response =  as HttpWebResponse;\r
+                           //if (response != null)\r
+                           var statusCode = exc.StatusCode;//response.StatusCode;\r
+\r
+                           switch (statusCode)\r
+                           {\r
+                               case HttpStatusCode.Unauthorized:\r
+                                   var message = String.Format("API Key Expired for {0}. Starting Renewal",\r
+                                                               monitor.UserName);\r
+                                   Log.Error(message, exc);\r
+                                   var account =\r
+                                       Settings.Accounts.Find(\r
+                                           acc => acc.AccountKey == new Uri(monitor.AuthenticationUrl).Combine(monitor.UserName));\r
+                                   account.IsExpired = true;\r
+                            Settings.Save();\r
+                                   Notify(new ExpirationNotification(account));\r
+                                   //TryAuthorize(monitor.UserName, retries).Wait();\r
+                                   break;\r
+                               case HttpStatusCode.ProxyAuthenticationRequired:\r
+                                   TryAuthenticateProxy(monitor, retries);\r
+                                   break;\r
+                               default:\r
+                                   TryLater(monitor, exc, retries);\r
+                                   break;\r
+                           }\r
+                       }\r
+                       catch (Exception exc)\r
+                       {\r
+                           if (AbandonRetry(monitor, retries))\r
+                               return;\r
+\r
+                           TryLater(monitor, exc, retries);\r
+                       }\r
+                   }\r
+\r
+               }\r
+\r
+           private void TryAuthenticateProxy(PithosMonitor monitor,int retries)\r
+               {\r
+                       Execute.OnUIThread(() =>\r
+                                                                  {                                       \r
+                                                                          var proxyAccount = IoC.Get<ProxyAccountViewModel>();\r
+                                                                               proxyAccount.Settings = Settings;\r
+                                                                          if (true != _windowManager.ShowDialog(proxyAccount)) \r
+                                                                                  return;\r
+                                                                          StartMonitor(monitor, retries);\r
+                                                                          NotifyOfPropertyChange(() => Accounts);\r
+                                                                  });\r
+               }\r
+\r
+               private bool AbandonRetry(PithosMonitor monitor, int retries)\r
+               {\r
+                       if (retries > 3)\r
+                       {\r
+                               var message = String.Format("Monitoring of account {0} has failed too many times. Will not retry",\r
+                                                                                       monitor.UserName);\r
+                               _events.Publish(new Notification\r
+                                                                       {Title = "Account monitoring failed", Message = message, Level = TraceLevel.Error});\r
+                               return true;\r
+                       }\r
+                       return false;\r
+               }\r
+\r
+\r
+           private void TryLater(PithosMonitor monitor, Exception exc,int retries)\r
+               {\r
+                       var message = String.Format("An exception occured. Can't start monitoring\nWill retry in 10 seconds");\r
+                       Task.Factory.StartNewDelayed(10000, () => StartMonitor(monitor,retries+1));\r
+                       _events.Publish(new Notification\r
+                                                               {Title = "Error", Message = message, Level = TraceLevel.Error});\r
+                       Log.Error(message, exc);\r
+               }\r
+\r
+\r
+               public void NotifyChange(string status, TraceLevel level=TraceLevel.Info)\r
+               {\r
+                       StatusMessage = status;\r
+                       \r
+                       _events.Publish(new Notification { Title = "Pithos+", Message = status, Level = level });\r
+               }\r
+\r
+               public void NotifyChangedFile(string filePath)\r
+               {\r
+            if (RecentFiles.Any(e => e.FullPath == filePath))\r
+                return;\r
+            \r
+                       IProducerConsumerCollection<FileEntry> files=RecentFiles;\r
+                       FileEntry popped;\r
+                       while (files.Count > 5)\r
+                               files.TryTake(out popped);\r
+            var entry = new FileEntry { FullPath = filePath };\r
+                       files.TryAdd(entry);\r
+               }\r
+\r
+               public void NotifyAccount(AccountInfo account)\r
+               {\r
+                       if (account== null)\r
+                               return;\r
+                       //TODO: What happens to an existing account whose Token has changed?\r
+                       account.SiteUri= String.Format("{0}/ui/?token={1}&user={2}",\r
+                               account.SiteUri, Uri.EscapeDataString(account.Token),\r
+                               Uri.EscapeDataString(account.UserName));\r
+\r
+                       if (!Accounts.Any(item => item.UserName == account.UserName && item.SiteUri == account.SiteUri))\r
+                               Accounts.TryAdd(account);\r
+\r
+               }\r
+\r
+               public void NotifyConflicts(IEnumerable<FileSystemInfo> conflictFiles, string message)\r
+               {\r
+                       if (conflictFiles == null)\r
+                               return;\r
+                   //Convert to list to avoid multiple iterations\r
+            var files = conflictFiles.ToList();\r
+                       if (files.Count==0)\r
+                               return;\r
+\r
+                       UpdateStatus();\r
+                       //TODO: Create a more specific message. For now, just show a warning\r
+                       NotifyForFiles(files,message,TraceLevel.Warning);\r
+\r
+               }\r
+\r
+               public void NotifyForFiles(IEnumerable<FileSystemInfo> files, string message,TraceLevel level=TraceLevel.Info)\r
+               {\r
+                       if (files == null)\r
+                               return;\r
+                       if (!files.Any())\r
+                               return;\r
+\r
+                       StatusMessage = message;\r
+\r
+                       _events.Publish(new Notification { Title = "Pithos+", Message = message, Level = level});\r
+               }\r
+\r
+               public void Notify(Notification notification)\r
+               {\r
+            TaskEx.Run(()=> _events.Publish(notification));\r
+               }\r
+\r
+\r
+               public void RemoveMonitor(string serverUrl,string accountName)\r
+               {\r
+                       if (String.IsNullOrWhiteSpace(accountName))\r
+                               return;\r
+\r
+                       var accountInfo=_accounts.FirstOrDefault(account => account.UserName == accountName && account.StorageUri.ToString().StartsWith(serverUrl));\r
+            if (accountInfo != null)\r
+            {\r
+                _accounts.TryRemove(accountInfo);\r
+                _pollAgent.RemoveAccount(accountInfo);\r
+            }\r
+\r
+            var accountKey = new Uri(serverUrl).Combine(accountName);\r
+                   PithosMonitor monitor;\r
+                       if (Monitors.TryRemove(accountKey, out monitor))\r
+                       {\r
+                               monitor.Stop();\r
+                //TODO: Also remove any pending actions for this account\r
+                //from the network queue                \r
+                       }\r
+               }\r
+\r
+               public void RefreshOverlays()\r
+               {\r
+                       foreach (var pair in Monitors)\r
+                       {\r
+                               var monitor = pair.Value;\r
+\r
+                               var path = monitor.RootPath;\r
+\r
+                               if (String.IsNullOrWhiteSpace(path))\r
+                                       continue;\r
+\r
+                               if (!Directory.Exists(path) && !File.Exists(path))\r
+                                       continue;\r
+\r
+                               IntPtr pathPointer = Marshal.StringToCoTaskMemAuto(path);\r
+\r
+                               try\r
+                               {\r
+                                       NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_UPDATEITEM,\r
+                                                                                                HChangeNotifyFlags.SHCNF_PATHW | HChangeNotifyFlags.SHCNF_FLUSHNOWAIT,\r
+                                                                                                pathPointer, IntPtr.Zero);\r
+                               }\r
+                               finally\r
+                               {\r
+                                       Marshal.FreeHGlobal(pathPointer);\r
+                               }\r
+                       }\r
+               }\r
+\r
+               #region Event Handlers\r
+               \r
+               public void Handle(SelectiveSynchChanges message)\r
+               {\r
+                   TaskEx.Run(() =>\r
+                   {\r
+                       PithosMonitor monitor;\r
+                       if (Monitors.TryGetValue(message.Account.AccountKey, out monitor))\r
+                       {\r
+                    Selectives.SetIsSelectiveEnabled(message.Account.AccountKey, message.Enabled);\r
+                           monitor.SetSelectivePaths(message.Uris, message.Added, message.Removed);\r
+                       }\r
+\r
+                       var account = Accounts.FirstOrDefault(acc => acc.AccountKey == message.Account.AccountKey);\r
+                       if (account != null)\r
+                       {\r
+                           var added=monitor.UrisToFilePaths(message.Added);\r
+                    _pollAgent.SynchNow(added);\r
+                       }\r
+                   });\r
+\r
+               }\r
+\r
+\r
+               private bool _pollStarted;\r
+           private Sparkle _sparkle;\r
+           private bool _manualUpgradeCheck;\r
+\r
+           //SMELL: Doing so much work for notifications in the shell is wrong\r
+               //The notifications should be moved to their own view/viewmodel pair\r
+               //and different templates should be used for different message types\r
+               //This will also allow the addition of extra functionality, eg. actions\r
+               //\r
+               public void Handle(Notification notification)\r
+               {\r
+                       UpdateStatus();\r
+\r
+                       if (!Settings.ShowDesktopNotifications)\r
+                               return;\r
+\r
+                       if (notification is PollNotification)\r
+                       {\r
+                               _pollStarted = true;\r
+                               return;\r
+                       }\r
+                       if (notification is CloudNotification)\r
+                       {\r
+                               if (!_pollStarted) \r
+                                       return;\r
+                               _pollStarted= false;\r
+                               notification.Title = "Pithos+";\r
+                               notification.Message = "Start Synchronisation";\r
+                       }\r
+\r
+                   var deleteNotification = notification as CloudDeleteNotification;\r
+            if (deleteNotification != null)\r
+            {\r
+                StatusMessage = String.Format("Deleted {0}", deleteNotification.Data.Name);\r
+                return;\r
+            }\r
+\r
+                   var progress = notification as ProgressNotification;\r
+                   \r
+                   \r
+            if (progress != null)\r
+            {\r
+                double percentage = (progress.TotalBlocks == progress.Block) ? 1\r
+                    :(progress.Block + progress.BlockPercentage / 100.0) / (double)progress.TotalBlocks;\r
+                       StatusMessage = String.Format("{0} {1:p2} of {2} - {3}",                                                      \r
+                                              progress.Action,\r
+                                                     percentage,\r
+                                                     progress.FileSize.ToByteSize(),\r
+                                                     progress.FileName);\r
+                ProgressBar = percentage;\r
+                       return;\r
+                   }\r
+\r
+                   var info = notification as StatusNotification;\r
+            if (info != null)\r
+            {\r
+                StatusMessage = info.Title;\r
+                return;\r
+            }\r
+                       if (String.IsNullOrWhiteSpace(notification.Message) && String.IsNullOrWhiteSpace(notification.Title))\r
+                               return;\r
+\r
+            if (notification.Level <= TraceLevel.Warning)\r
+                           ShowBalloonFor(notification);\r
+               }\r
+\r
+           private void ShowBalloonFor(Notification notification)\r
+           {\r
+            Contract.Requires(notification!=null);\r
+            \r
+            if (!Settings.ShowDesktopNotifications) \r
+                return;\r
+            \r
+            BalloonIcon icon;\r
+               switch (notification.Level)\r
+               {\r
+                case TraceLevel.Verbose:\r
+                       return;\r
+                   case TraceLevel.Info:                   \r
+                       icon = BalloonIcon.Info;\r
+                       break;\r
+                case TraceLevel.Error:\r
+                    icon = BalloonIcon.Error;\r
+                    break;\r
+                case TraceLevel.Warning:\r
+                       icon = BalloonIcon.Warning;\r
+                       break;\r
+                   default:\r
+                       return;\r
+               }\r
+\r
+               var tv = (ShellView) GetView();\r
+               System.Action clickAction = null;\r
+               if (notification is ExpirationNotification)\r
+               {\r
+                   clickAction = () => ShowPreferences("AccountTab");\r
+               }\r
+               var balloon = new PithosBalloon\r
+                                 {\r
+                                     Title = notification.Title,\r
+                                     Message = notification.Message,\r
+                                     Icon = icon,\r
+                                     ClickAction = clickAction\r
+                                 };\r
+               tv.TaskbarView.ShowCustomBalloon(balloon, PopupAnimation.Fade, 4000);\r
+           }\r
+\r
+           #endregion\r
+\r
+               public void Handle(ShowFilePropertiesEvent message)\r
+               {\r
+                       if (message == null)\r
+                               throw new ArgumentNullException("message");\r
+                       if (String.IsNullOrWhiteSpace(message.FileName) )\r
+                               throw new ArgumentException("message");\r
+                       Contract.EndContractBlock();\r
+\r
+                       var fileName = message.FileName;\r
+                       //TODO: Display file properties for non-container folders\r
+                       if (File.Exists(fileName))\r
+                               //Retrieve the full name with exact casing. Pithos names are case sensitive                             \r
+                               ShowFileProperties(FileInfoExtensions.GetProperFilePathCapitalization(fileName));\r
+                       else if (Directory.Exists(fileName))\r
+                               //Retrieve the full name with exact casing. Pithos names are case sensitive\r
+                       {\r
+                               var path = FileInfoExtensions.GetProperDirectoryCapitalization(fileName);\r
+                               if (IsContainer(path))\r
+                                       ShowContainerProperties(path);\r
+                               else\r
+                                       ShowFileProperties(path);\r
+                       }\r
+               }\r
+\r
+               private bool IsContainer(string path)\r
+               {\r
+                       var matchingFolders = from account in _accounts\r
+                                                                 from rootFolder in Directory.GetDirectories(account.AccountPath)\r
+                                                                 where rootFolder.Equals(path, StringComparison.InvariantCultureIgnoreCase)\r
+                                                                 select rootFolder;\r
+                       return matchingFolders.Any();\r
+               }\r
+\r
+               public FileStatus GetFileStatus(string localFileName)\r
+               {\r
+                       if (String.IsNullOrWhiteSpace(localFileName))\r
+                               throw new ArgumentNullException("localFileName");\r
+                       Contract.EndContractBlock();\r
+                       \r
+                       var statusKeeper = IoC.Get<IStatusKeeper>();\r
+                       var status=statusKeeper.GetFileStatus(localFileName);\r
+                       return status;\r
+               }\r
+\r
+           public void RemoveAccountFromDatabase(AccountSettings account)\r
+           {\r
+            var statusKeeper = IoC.Get<IStatusKeeper>();\r
+            statusKeeper.ClearFolderStatus(account.RootPath);          \r
+           }\r
+       }\r
+}\r