Revision 4f6d51d4 trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs
b/trunk/Pithos.Client.WPF/Shell/ShellViewModel.cs | ||
---|---|---|
1 | 1 |
using System.Collections.Concurrent; |
2 |
using System.ComponentModel; |
|
3 |
using System.ComponentModel.Composition; |
|
4 | 2 |
using System.Diagnostics; |
5 | 3 |
using System.Diagnostics.Contracts; |
6 | 4 |
using System.IO; |
... | ... | |
8 | 6 |
using System.Reflection; |
9 | 7 |
using System.Runtime.InteropServices; |
10 | 8 |
using System.ServiceModel; |
11 |
using System.ServiceModel.Description; |
|
12 | 9 |
using System.Threading.Tasks; |
13 | 10 |
using System.Windows; |
14 | 11 |
using Caliburn.Micro; |
15 | 12 |
using Hardcodet.Wpf.TaskbarNotification; |
16 | 13 |
using Pithos.Client.WPF.Configuration; |
17 | 14 |
using Pithos.Client.WPF.FileProperties; |
18 |
using Pithos.Client.WPF.Properties; |
|
19 | 15 |
using Pithos.Client.WPF.SelectiveSynch; |
20 | 16 |
using Pithos.Client.WPF.Services; |
21 | 17 |
using Pithos.Client.WPF.Shell; |
... | ... | |
24 | 20 |
using System; |
25 | 21 |
using System.Collections.Generic; |
26 | 22 |
using System.Linq; |
27 |
using System.Text; |
|
28 | 23 |
using Pithos.Network; |
29 | 24 |
using StatusService = Pithos.Client.WPF.Services.StatusService; |
30 | 25 |
|
... | ... | |
50 | 45 |
{ |
51 | 46 |
//The Status Checker provides the current synch state |
52 | 47 |
//TODO: Could we remove the status checker and use events in its place? |
53 |
private IStatusChecker _statusChecker; |
|
54 |
private IEventAggregator _events; |
|
48 |
private readonly IStatusChecker _statusChecker;
|
|
49 |
private readonly IEventAggregator _events;
|
|
55 | 50 |
|
56 | 51 |
public PithosSettings Settings { get; private set; } |
57 | 52 |
|
58 | 53 |
|
59 |
private ConcurrentDictionary<string, PithosMonitor> _monitors = new ConcurrentDictionary<string, PithosMonitor>(); |
|
54 |
private readonly ConcurrentDictionary<string, PithosMonitor> _monitors = new ConcurrentDictionary<string, PithosMonitor>();
|
|
60 | 55 |
///<summary> |
61 | 56 |
/// Dictionary of account monitors, keyed by account |
62 | 57 |
///</summary> |
... | ... | |
72 | 67 |
} |
73 | 68 |
|
74 | 69 |
|
75 |
///<summary>
|
|
76 |
/// The status service is used by Shell extensions to retrieve file status information
|
|
77 |
///</summary>
|
|
78 |
//TODO: CODE SMELL! This is the shell! While hosting in the shell makes executing start/stop commands easier, it is still a smell
|
|
79 |
private ServiceHost _statusService { get; set; }
|
|
70 |
///<summary>
|
|
71 |
/// The status service is used by Shell extensions to retrieve file status information
|
|
72 |
///</summary>
|
|
73 |
//TODO: CODE SMELL! This is the shell! While hosting in the shell makes executing start/stop commands easier, it is still a smell
|
|
74 |
private ServiceHost _statusService;
|
|
80 | 75 |
|
81 | 76 |
//Logging in the Pithos client is provided by log4net |
82 | 77 |
private static readonly log4net.ILog Log = log4net.LogManager.GetLogger("Pithos"); |
... | ... | |
128 | 123 |
} |
129 | 124 |
|
130 | 125 |
|
131 |
private async Task StartMonitoring()
|
|
126 |
private async void StartMonitoring()
|
|
132 | 127 |
{ |
133 | 128 |
try |
134 | 129 |
{ |
... | ... | |
169 | 164 |
{ |
170 | 165 |
return Task.Factory.StartNew(() => |
171 | 166 |
{ |
172 |
PithosMonitor monitor = null;
|
|
167 |
PithosMonitor monitor; |
|
173 | 168 |
var accountName = account.AccountName; |
174 | 169 |
|
175 | 170 |
if (_monitors.TryGetValue(accountName, out monitor)) |
... | ... | |
199 | 194 |
//PithosMonitor uses MEF so we need to resolve it |
200 | 195 |
IoC.BuildUp(monitor); |
201 | 196 |
|
202 |
var appSettings = Properties.Settings.Default; |
|
203 |
monitor.AuthenticationUrl = account.ServerUrl; |
|
197 |
monitor.AuthenticationUrl = account.ServerUrl; |
|
204 | 198 |
|
205 | 199 |
_monitors[accountName] = monitor; |
206 | 200 |
|
... | ... | |
284 | 278 |
get { return _statusIcon; } |
285 | 279 |
set |
286 | 280 |
{ |
287 |
//_statusIcon = value; |
|
281 |
//TODO: Ensure all status icons use the Pithos logo |
|
282 |
_statusIcon = value; |
|
288 | 283 |
NotifyOfPropertyChange(() => StatusIcon); |
289 | 284 |
} |
290 | 285 |
} |
... | ... | |
359 | 354 |
{ |
360 | 355 |
if (String.IsNullOrWhiteSpace(filePath)) |
361 | 356 |
throw new ArgumentNullException("filePath"); |
362 |
if (!File.Exists(filePath)) |
|
357 |
if (!File.Exists(filePath) && !Directory.Exists(filePath))
|
|
363 | 358 |
throw new ArgumentException(String.Format("Non existent file {0}",filePath),"filePath"); |
364 | 359 |
Contract.EndContractBlock(); |
365 | 360 |
|
366 | 361 |
var pair=(from monitor in Monitors |
367 | 362 |
where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase) |
368 | 363 |
select monitor).FirstOrDefault(); |
369 |
var account = pair.Key; |
|
370 |
var accountMonitor = pair.Value; |
|
364 |
var accountMonitor = pair.Value; |
|
371 | 365 |
|
372 | 366 |
if (accountMonitor == null) |
373 | 367 |
return; |
... | ... | |
401 | 395 |
var pair=(from monitor in Monitors |
402 | 396 |
where filePath.StartsWith(monitor.Value.RootPath, StringComparison.InvariantCultureIgnoreCase) |
403 | 397 |
select monitor).FirstOrDefault(); |
404 |
var account = pair.Key; |
|
405 |
var accountMonitor = pair.Value; |
|
398 |
var accountMonitor = pair.Value; |
|
406 | 399 |
var info = accountMonitor.GetContainerInfo(filePath); |
407 | 400 |
|
408 | 401 |
|
... | ... | |
462 | 455 |
#endregion |
463 | 456 |
|
464 | 457 |
|
465 |
private Dictionary<PithosStatus, StatusInfo> iconNames = new List<StatusInfo>
|
|
458 |
private readonly Dictionary<PithosStatus, StatusInfo> _iconNames = new List<StatusInfo>
|
|
466 | 459 |
{ |
467 | 460 |
new StatusInfo(PithosStatus.InSynch, "All files up to date", "TrayInSynch"), |
468 | 461 |
new StatusInfo(PithosStatus.Syncing, "Syncing Files", "TraySynching"), |
... | ... | |
479 | 472 |
{ |
480 | 473 |
var pithosStatus = _statusChecker.GetPithosStatus(); |
481 | 474 |
|
482 |
if (iconNames.ContainsKey(pithosStatus)) |
|
475 |
if (_iconNames.ContainsKey(pithosStatus))
|
|
483 | 476 |
{ |
484 |
var info = iconNames[pithosStatus]; |
|
477 |
var info = _iconNames[pithosStatus];
|
|
485 | 478 |
StatusIcon = String.Format(@"../Images/{0}.ico", info.IconName); |
486 | 479 |
|
487 | 480 |
Assembly assembly = Assembly.GetExecutingAssembly(); |
... | ... | |
558 | 551 |
|
559 | 552 |
var credentials = await PithosAccount.RetrieveCredentials(Settings.PithosLoginUrl); |
560 | 553 |
|
561 |
var account = Settings.Accounts.FirstOrDefault(act => act.AccountName == credentials.UserName);
|
|
554 |
var account = Settings.Accounts.First(act => act.AccountName == credentials.UserName); |
|
562 | 555 |
account.ApiKey = credentials.Password; |
563 | 556 |
monitor.ApiKey = credentials.Password; |
564 | 557 |
Settings.Save(); |
... | ... | |
571 | 564 |
string message = String.Format("API Key retrieval for {0} failed", monitor.UserName); |
572 | 565 |
Log.Error(message, exc.InnerException); |
573 | 566 |
_events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error }); |
574 |
return; |
|
575 | 567 |
} |
576 | 568 |
catch (Exception exc) |
577 | 569 |
{ |
578 | 570 |
string message = String.Format("API Key retrieval for {0} failed", monitor.UserName); |
579 | 571 |
Log.Error(message, exc); |
580 | 572 |
_events.Publish(new Notification { Title = "Authorization failed", Message = message, Level = TraceLevel.Error }); |
581 |
return; |
|
582 |
|
|
583 | 573 |
} |
584 | 574 |
|
585 | 575 |
} |
... | ... | |
608 | 598 |
|
609 | 599 |
public void NotifyChange(string status, TraceLevel level=TraceLevel.Info) |
610 | 600 |
{ |
611 |
this.StatusMessage = status;
|
|
601 |
StatusMessage = status; |
|
612 | 602 |
|
613 | 603 |
_events.Publish(new Notification { Title = "Pithos", Message = status, Level = level }); |
614 | 604 |
} |
... | ... | |
616 | 606 |
public void NotifyChangedFile(string filePath) |
617 | 607 |
{ |
618 | 608 |
var entry = new FileEntry {FullPath=filePath}; |
619 |
IProducerConsumerCollection<FileEntry> files=this.RecentFiles;
|
|
609 |
IProducerConsumerCollection<FileEntry> files=RecentFiles; |
|
620 | 610 |
FileEntry popped; |
621 | 611 |
while (files.Count > 5) |
622 | 612 |
files.TryTake(out popped); |
... | ... | |
702 | 692 |
{ |
703 | 693 |
if (!Settings.ShowDesktopNotifications) |
704 | 694 |
return; |
705 |
BalloonIcon icon = BalloonIcon.None;
|
|
695 |
BalloonIcon icon; |
|
706 | 696 |
switch (notification.Level) |
707 | 697 |
{ |
708 | 698 |
case TraceLevel.Error: |
... | ... | |
722 | 712 |
|
723 | 713 |
if (Settings.ShowDesktopNotifications) |
724 | 714 |
{ |
725 |
var tv = (ShellView) this.GetView();
|
|
715 |
var tv = (ShellView) GetView(); |
|
726 | 716 |
tv.TaskbarView.ShowBalloonTip(notification.Title, notification.Message, icon); |
727 | 717 |
} |
728 | 718 |
} |
... | ... | |
737 | 727 |
Contract.EndContractBlock(); |
738 | 728 |
|
739 | 729 |
var fileName = message.FileName; |
740 |
|
|
730 |
//TODO: Display file properties for non-container folders |
|
741 | 731 |
if (File.Exists(fileName)) |
742 |
this.ShowFileProperties(fileName); |
|
732 |
//Retrieve the full name with exact casing. Pithos names are case sensitive |
|
733 |
ShowFileProperties(GetProperFilePathCapitalization(fileName)); |
|
743 | 734 |
else if (Directory.Exists(fileName)) |
744 |
this.ShowContainerProperties(fileName); |
|
735 |
//Retrieve the full name with exact casing. Pithos names are case sensitive |
|
736 |
{ |
|
737 |
var path = GetProperDirectoryCapitalization(fileName); |
|
738 |
if (IsContainer(path)) |
|
739 |
ShowContainerProperties(path); |
|
740 |
else |
|
741 |
ShowFileProperties(path); |
|
742 |
} |
|
745 | 743 |
} |
744 |
|
|
745 |
private bool IsContainer(string path) |
|
746 |
{ |
|
747 |
var matchingFolders = from account in _accounts |
|
748 |
from rootFolder in Directory.GetDirectories(account.AccountPath) |
|
749 |
where rootFolder.Equals(path, StringComparison.InvariantCultureIgnoreCase) |
|
750 |
select rootFolder; |
|
751 |
return matchingFolders.Any(); |
|
752 |
} |
|
753 |
|
|
754 |
static string GetProperDirectoryCapitalization(string fileName) |
|
755 |
{ |
|
756 |
var dirInfo = new DirectoryInfo(fileName); |
|
757 |
var parentDirInfo = dirInfo.Parent; |
|
758 |
if (null == parentDirInfo) |
|
759 |
return dirInfo.Name; |
|
760 |
return Path.Combine(GetProperDirectoryCapitalization(parentDirInfo.FullName), |
|
761 |
parentDirInfo.GetDirectories(dirInfo.Name)[0].Name); |
|
762 |
} |
|
763 |
|
|
764 |
static string GetProperFilePathCapitalization(string fileName) |
|
765 |
{ |
|
766 |
if (String.IsNullOrWhiteSpace(fileName)) |
|
767 |
throw new ArgumentNullException("fileName"); |
|
768 |
if (!Path.IsPathRooted(fileName)) |
|
769 |
throw new ArgumentException("fileName must be an absolute path","fileName"); |
|
770 |
Contract.EndContractBlock(); |
|
771 |
|
|
772 |
|
|
773 |
var fileInfo = new FileInfo(fileName); |
|
774 |
var dirInfo = fileInfo.Directory; |
|
775 |
|
|
776 |
//Directory will not be null for an absolute path |
|
777 |
Contract.Assume(dirInfo!=null); |
|
778 |
|
|
779 |
return Path.Combine(GetProperDirectoryCapitalization(dirInfo.FullName), |
|
780 |
dirInfo.GetFiles(fileInfo.Name)[0].Name); |
|
781 |
} |
|
746 | 782 |
} |
747 | 783 |
} |
Also available in: Unified diff