Statistics
| Branch: | Revision:

root / trunk / Pithos.Client.WPF / ShellViewModel.cs @ c53aa229

History | View | Annotate | Download (13.3 kB)

1
using System.Collections.Concurrent;
2
using System.ComponentModel.Composition;
3
using System.Diagnostics;
4
using System.IO;
5
using System.Runtime.InteropServices;
6
using System.ServiceModel;
7
using System.ServiceModel.Description;
8
using System.Threading.Tasks;
9
using System.Windows;
10
using Caliburn.Micro;
11
using Hardcodet.Wpf.TaskbarNotification;
12
using Pithos.Client.WPF.Configuration;
13
using Pithos.Client.WPF.Properties;
14
using Pithos.Core;
15
using Pithos.Interfaces;
16
using System;
17
using System.Collections.Generic;
18
using System.Linq;
19
using System.Text;
20
using StatusService = Pithos.Client.WPF.Services.StatusService;
21

    
22
namespace Pithos.Client.WPF {
23
    using System.ComponentModel.Composition;
24

    
25
    [Export(typeof(IShell))]
26
    public class ShellViewModel : ViewAware, IStatusNotification, IShell, IHandle<Notification>
27
    {
28
       
29
        private IStatusChecker _statusChecker;
30
        private IEventAggregator _events;
31

    
32
        public PithosSettings Settings { get; private set; }
33

    
34
        public IScreen Parent { get; set; }
35

    
36

    
37
        private Dictionary<string, PithosMonitor> _monitors = new Dictionary<string, PithosMonitor>();
38
        public Dictionary<string, PithosMonitor> Monitors
39
        {
40
            get { return _monitors; }
41
        }
42

    
43
        private ServiceHost _statusService { get; set; }
44

    
45
        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger("Pithos");
46

    
47
        [ImportingConstructor]
48
        public ShellViewModel(IWindowManager windowManager, IEventAggregator events, IStatusChecker statusChecker, PithosSettings settings)
49
        {
50
            _windowManager = windowManager;
51
            OpenPithosFolderCommand = new PithosCommand(OpenPithosFolder);
52
            _statusChecker = statusChecker;
53
            _events = events;            
54
            Settings = settings;
55
            
56
            
57

    
58
            UsageMessage = "Using 15% of 50 GB";
59
            StatusMessage = "In Synch";
60

    
61
            foreach (var account in settings.Accounts)
62
            {
63

    
64
                MonitorAccount(account);
65
            }
66

    
67
            StartStatusService();
68
        }
69
        
70

    
71
        
72
        public void MonitorAccount(AccountSettings account)
73
        {
74
            PithosMonitor monitor = null;
75
            var accountName = account.AccountName;
76

    
77
            if (_monitors.TryGetValue(accountName,out monitor))
78
            {
79
                //If the account is active
80
                if (account.IsActive)
81
                    //Start the monitor. It's OK to start an already started monitor,
82
                    //it will just ignore the call
83
                    monitor.Start();
84
                else
85
                {
86
                    //If the account is inactive
87
                    //Stop and remove the monitor
88
                    RemoveMonitor(accountName);
89
                }
90
                return;
91
            }
92

    
93
            //PithosMonitor uses MEF so we need to resolve it
94
            monitor = new PithosMonitor
95
                          {
96
                              UserName = accountName,
97
                              ApiKey = account.ApiKey,
98
                              UsePithos = account.UsePithos,
99
                              StatusNotification = this,
100
                              RootPath=account.RootPath                             
101
                          };          
102
            IoC.BuildUp(monitor);
103

    
104
            var appSettings = Properties.Settings.Default;
105
            monitor.AuthenticationUrl = account.UsePithos
106
                                            ? appSettings.PithosAuthenticationUrl
107
                                            : appSettings.CloudfilesAuthenticationUrl;
108

    
109
            _monitors[accountName] = monitor;
110

    
111
            if (account.IsActive)
112
            {                
113
                //Don't start a monitor if it doesn't have an account and ApiKey
114
                if (String.IsNullOrWhiteSpace(monitor.UserName) || String.IsNullOrWhiteSpace(monitor.ApiKey))
115
                    return;
116
                StartMonitor(monitor);
117
            }
118
        }
119

    
120

    
121
        protected override void OnViewLoaded(object view)
122
        {
123
            var window = (Window)view;
124
            window.Hide();
125
            UpdateStatus();
126
            base.OnViewLoaded(view);
127
        }
128

    
129

    
130
        #region Status Properties
131

    
132
        private string _statusMessage;
133
        public string StatusMessage
134
        {
135
            get { return _statusMessage; }
136
            set
137
            {
138
                _statusMessage = value;
139
                NotifyOfPropertyChange(() => StatusMessage);
140
            }
141
        }
142

    
143
        private string _usageMessage;
144
        public string UsageMessage
145
        {
146
            get { return _usageMessage; }
147
            set
148
            {
149
                _usageMessage = value;
150
                NotifyOfPropertyChange(() => UsageMessage);
151
            }
152
        }
153

    
154

    
155
        private string _pauseSyncCaption="Pause Syncing";
156
        public string PauseSyncCaption
157
        {
158
            get { return _pauseSyncCaption; }
159
            set
160
            {
161
                _pauseSyncCaption = value;
162
                NotifyOfPropertyChange(() => PauseSyncCaption);
163
            }
164
        }
165

    
166
        private readonly ObservableConcurrentCollection<FileEntry> _recentFiles = new ObservableConcurrentCollection<FileEntry>();
167
        public ObservableConcurrentCollection<FileEntry> RecentFiles
168
        {
169
            get { return _recentFiles; }
170
        }
171

    
172

    
173
        private string _statusIcon="Images/Tray.ico";
174
        public string StatusIcon
175
        {
176
            get { return _statusIcon; }
177
            set
178
            {
179
                _statusIcon = value;
180
                NotifyOfPropertyChange(() => StatusIcon);
181
            }
182
        }
183

    
184
        #endregion
185

    
186
        #region Commands
187

    
188
        public void ShowPreferences()
189
        {
190
            Settings.Reload();
191
            var preferences = new PreferencesViewModel(_events, this,Settings);            
192
            _windowManager.ShowDialog(preferences);
193
        }
194

    
195

    
196
        public PithosCommand OpenPithosFolderCommand { get; private set; }
197

    
198
        public void OpenPithosFolder()
199
        {
200
            Process.Start(Settings.PithosPath);
201
        }
202

    
203
        public void GoToSite()
204
        {
205
            var activeAccount=Settings.Accounts.FirstOrDefault(account => account.IsActive);
206
            if (activeAccount == null)
207
                return;            
208

    
209
            var site = String.Format("http://{0}/ui/?token={1}&user={2}",
210
                Properties.Settings.Default.PithosSite,activeAccount.ApiKey,
211
                activeAccount.AccountName);
212
            Process.Start(site);
213
        }
214

    
215

    
216
        public void ToggleSynching()
217
        {
218
            bool isPaused=false;
219
            foreach (var pair in Monitors)
220
            {
221
                var monitor = pair.Value;
222
                monitor.Pause = !monitor.Pause;
223
                isPaused = monitor.Pause;
224
            }
225

    
226
            PauseSyncCaption = isPaused ? "Resume syncing" : "Pause syncing";
227
            var iconKey = isPaused? "TraySyncPaused" : "TrayInSynch";
228
            StatusIcon = String.Format(@"Images/{0}.ico", iconKey);
229
        }
230

    
231
        public void ExitPithos()
232
        {
233
            foreach (var pair in Monitors)
234
            {
235
                var monitor = pair.Value;
236
                monitor.Stop();
237
            }
238

    
239
            ((Window)GetView()).Close();
240
        }
241
        #endregion
242

    
243

    
244
        private Dictionary<PithosStatus, StatusInfo> iconNames = new List<StatusInfo>
245
            {
246
                new StatusInfo(PithosStatus.InSynch, "All files up to date", "TrayInSynch"),
247
                new StatusInfo(PithosStatus.Syncing, "Syncing Files", "TraySynching"),
248
                new StatusInfo(PithosStatus.SyncPaused, "Sync Paused", "TraySyncPaused")
249
            }.ToDictionary(s => s.Status);
250

    
251
        readonly IWindowManager _windowManager;
252

    
253

    
254
        public void UpdateStatus()
255
        {
256
            var pithosStatus = _statusChecker.GetPithosStatus();
257

    
258
            if (iconNames.ContainsKey(pithosStatus))
259
            {
260
                var info = iconNames[pithosStatus];
261
                StatusIcon = String.Format(@"Images/{0}.ico", info.IconName);
262
                StatusMessage = String.Format("Pithos 1.0\r\n{0}", info.StatusText);
263
            }
264

    
265
            var tv=this.GetView();
266
            _events.Publish(new Notification { Title = "Start", Message = "Start Monitoring", Level = TraceLevel.Info});
267
        }
268

    
269

    
270
       
271
        private Task StartMonitor(PithosMonitor monitor)
272
        {
273
            return Task.Factory.StartNew(() =>
274
            {
275
                using (log4net.ThreadContext.Stacks["Monitor"].Push("Start"))
276
                {
277
                    try
278
                    {
279
                        Log.InfoFormat("Start Monitoring {0}", monitor.UserName);
280
                        monitor.Start();
281
                    }
282
                    catch (Exception exc)
283
                    {
284
                        var message =
285
                            String.Format("An exception occured. Can't start monitoring\nWill retry in 10 seconds");
286
                        Task.Factory.StartNewDelayed(10000, () => StartMonitor(monitor));
287
                        _events.Publish(new Notification {Title = "Error", Message = message, Level = TraceLevel.Error});
288
                        Log.Error(message, exc);
289
                    }
290
                }
291
            });
292
        }
293

    
294

    
295
        public void NotifyChange(string status, TraceLevel level=TraceLevel.Info)
296
        {
297
            this.StatusMessage = status;
298
            
299
            _events.Publish(new Notification { Title = "Pithos", Message = status, Level = level });
300
        }
301

    
302
        public void NotifyChangedFile(string filePath)
303
        {
304
            var entry = new FileEntry {FullPath=filePath};
305
            IProducerConsumerCollection<FileEntry> files=this.RecentFiles;
306
            FileEntry popped;
307
            while (files.Count > 5)
308
                files.TryTake(out popped);
309
            files.TryAdd(entry);
310
        }
311

    
312

    
313
        public void Handle(Notification notification)
314
        {
315
            if (!Settings.ShowDesktopNotifications)
316
                return;
317
            BalloonIcon icon = BalloonIcon.None;
318
            switch (notification.Level)
319
            {
320
                case TraceLevel.Error:
321
                    icon = BalloonIcon.Error;
322
                    break;
323
                case TraceLevel.Info:
324
                case TraceLevel.Verbose:
325
                    icon = BalloonIcon.Info;
326
                    break;
327
                case TraceLevel.Warning:
328
                    icon = BalloonIcon.Warning;
329
                    break;
330
                default:
331
                    icon = BalloonIcon.None;
332
                    break;
333
            }
334

    
335
            var tv = (ShellView)this.GetView();
336
            tv.TaskbarView.ShowBalloonTip(notification.Title, notification.Message, icon);
337
        }
338

    
339
        public void RemoveMonitor(string accountName)
340
        {
341
            if (String.IsNullOrWhiteSpace(accountName))
342
                return;
343

    
344
            PithosMonitor monitor;
345
            if (Monitors.TryGetValue(accountName, out monitor))
346
            {
347
                Monitors.Remove(accountName);
348
                monitor.Stop();
349
            }
350
        }
351

    
352
        public void RefreshOverlays()
353
        {
354
            foreach (var pair in Monitors)
355
            {
356
                var monitor = pair.Value;
357

    
358
                var path = monitor.RootPath;
359

    
360
                if (String.IsNullOrWhiteSpace(path))
361
                    continue;
362

    
363
                if (!Directory.Exists(path) && !File.Exists(path))
364
                    continue;
365

    
366
                IntPtr pathPointer = Marshal.StringToCoTaskMemAuto(path);
367

    
368
                try
369
                {
370
                    NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_UPDATEITEM,
371
                                                 HChangeNotifyFlags.SHCNF_PATHW | HChangeNotifyFlags.SHCNF_FLUSHNOWAIT,
372
                                                 pathPointer, IntPtr.Zero);
373
                }
374
                finally
375
                {
376
                    Marshal.FreeHGlobal(pathPointer);
377
                }
378
            }
379
        }
380

    
381
        private void StartStatusService()
382
        {
383
            // Create a ServiceHost for the CalculatorService type and provide the base address.
384
            var baseAddress = new Uri("net.pipe://localhost/pithos");
385
            _statusService = new ServiceHost(typeof(StatusService), baseAddress);
386

    
387
            var binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
388

    
389
            _statusService.AddServiceEndpoint(typeof(IStatusService), binding, "net.pipe://localhost/pithos/statuscache");
390
            _statusService.AddServiceEndpoint(typeof(ISettingsService), binding, "net.pipe://localhost/pithos/settings");
391

    
392

    
393
            //// Add a mex endpoint
394
            var smb = new ServiceMetadataBehavior
395
            {
396
                HttpGetEnabled = true,
397
                HttpGetUrl = new Uri("http://localhost:30000/pithos/mex")
398
            };
399
            _statusService.Description.Behaviors.Add(smb);
400

    
401

    
402
            _statusService.Open();
403
        }
404

    
405
        private void StopStatusService()
406
        {
407
            if (_statusService == null)
408
                return;
409

    
410
            if (_statusService.State == CommunicationState.Faulted)
411
                _statusService.Abort();
412
            else if (_statusService.State != CommunicationState.Closed)
413
                _statusService.Close();
414
            _statusService = null;
415

    
416
        }
417
    }
418
}