PageRenderTime 22ms CodeModel.GetById 8ms app.highlight 10ms RepoModel.GetById 1ms app.codeStats 1ms

/SparkleShare/SparkleStatusIconController.cs

http://github.com/hbons/SparkleShare
C# | 408 lines | 282 code | 106 blank | 20 comment | 52 complexity | 58fb33fe0d5a80e56a3153b16934801f MD5 | raw file
  1//   SparkleShare, a collaboration and sharing tool.
  2//   Copyright (C) 2010  Hylke Bons <hylkebons@gmail.com>
  3//
  4//   This program is free software: you can redistribute it and/or modify
  5//   it under the terms of the GNU General Public License as published by
  6//   the Free Software Foundation, either version 3 of the License, or
  7//   (at your option) any later version.
  8//
  9//   This program is distributed in the hope that it will be useful,
 10//   but WITHOUT ANY WARRANTY; without even the implied warranty of
 11//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 12//   GNU General Public License for more details.
 13//
 14//   You should have received a copy of the GNU General Public License
 15//   along with this program. If not, see <http://www.gnu.org/licenses/>.
 16
 17
 18using System;
 19using System.Collections.Generic;
 20using System.Threading;
 21using Timers = System.Timers;
 22
 23using SparkleLib;
 24
 25namespace SparkleShare {
 26
 27    public enum IconState {
 28        Idle,
 29        SyncingUp,
 30        SyncingDown,
 31        Syncing,
 32        Error
 33    }
 34
 35
 36    public class ProjectInfo {
 37
 38        private SparkleRepoBase repo;
 39
 40        public string Name { get { return this.repo.Name; }}
 41        public string Path { get { return this.repo.LocalPath; }}
 42
 43        public bool IsPaused { get { return this.repo.Status == SyncStatus.Paused; }}
 44        public bool HasError { get { return this.repo.Status == SyncStatus.Error; }}
 45
 46
 47        public string StatusMessage {
 48            get {
 49                string status_message = "Waiting to sync";
 50                
 51                if (!this.repo.LastSync.Equals (DateTime.MinValue))
 52                    status_message = string.Format ("Synced {0}", this.repo.LastSync.ToPrettyDate ());
 53
 54                if (this.repo.Status == SyncStatus.SyncUp)
 55                    status_message = "Sending changes… " + this.repo.ProgressPercentage + "%";
 56            
 57                if (this.repo.Status == SyncStatus.SyncDown)
 58                    status_message = "Receiving changes… " + this.repo.ProgressPercentage + "%";
 59
 60                if (this.repo.Status == SyncStatus.SyncUp || this.repo.Status == SyncStatus.SyncDown) {
 61                    if (this.repo.ProgressSpeed > 0)
 62                        status_message += " " + this.repo.ProgressSpeed.ToSize () + "/s";
 63                }
 64
 65                if (IsPaused) {
 66                    return "Paused";
 67
 68                } else if (HasError) {
 69                    switch (this.repo.Error) {
 70                    case ErrorStatus.HostUnreachable: return "Can’t reach the host";
 71                    case ErrorStatus.HostIdentityChanged: return "The host’s identity has changed";
 72                    case ErrorStatus.AuthenticationFailed: return "Authentication failed";
 73                    case ErrorStatus.DiskSpaceExceeded: return "Host is out of disk space";
 74                    case ErrorStatus.UnreadableFiles: return "Some local files are unreadable or in use";
 75                    case ErrorStatus.NotFound: return "Project doesn’t exist on host";
 76                    case ErrorStatus.IncompatibleClientServer: return "Incompatible client/server versions";
 77                    }
 78                }
 79
 80                return status_message;
 81            }
 82        }
 83
 84
 85        public string MoreUnsyncedChanges = "";
 86
 87        public Dictionary<string, string> UnsyncedChangesInfo {
 88            get { 
 89                Dictionary<string, string> changes_info = new Dictionary<string, string> ();
 90            
 91                int changes_count = 0;
 92                foreach (SparkleChange change in repo.UnsyncedChanges) {
 93                    changes_count++;
 94
 95                    if (changes_count > 10)
 96                        continue;
 97
 98                    switch (change.Type) {
 99                    case SparkleChangeType.Added:   changes_info [change.Path] = "document-added-12.png"; break;
100                    case SparkleChangeType.Edited:  changes_info [change.Path] = "document-edited-12.png"; break;
101                    case SparkleChangeType.Deleted: changes_info [change.Path] = "document-deleted-12.png"; break;
102                    case SparkleChangeType.Moved:   changes_info [change.MovedToPath] = "document-moved-12.png"; break;
103                    }
104                }
105
106                if (changes_count > 10)
107                    MoreUnsyncedChanges = string.Format ("and {0} more", changes_count - 10);
108
109                return changes_info;
110            }
111        }
112
113
114        public ProjectInfo (SparkleRepoBase repo)
115        {
116            this.repo = repo;
117        }
118    }
119
120
121    public class SparkleStatusIconController {
122
123        public event UpdateIconEventHandler UpdateIconEvent = delegate { };
124        public delegate void UpdateIconEventHandler (IconState state);
125
126        public event UpdateMenuEventHandler UpdateMenuEvent = delegate { };
127        public delegate void UpdateMenuEventHandler (IconState state);
128
129        public event UpdateStatusItemEventHandler UpdateStatusItemEvent = delegate { };
130        public delegate void UpdateStatusItemEventHandler (string state_text);
131
132        public event UpdateQuitItemEventHandler UpdateQuitItemEvent = delegate { };
133        public delegate void UpdateQuitItemEventHandler (bool quit_item_enabled);
134
135        public IconState CurrentState = IconState.Idle;
136        public string StateText       = "Welcome to SparkleShare!";
137
138        public ProjectInfo [] Projects = new ProjectInfo [0];
139
140
141        public int ProgressPercentage {
142            get {
143                return (int) Program.Controller.ProgressPercentage;
144            }
145        }
146
147        public string ProgressSpeed {
148            get {
149                string progress_speed = "";
150
151                if (Program.Controller.ProgressSpeedDown == 0 && Program.Controller.ProgressSpeedUp > 0) {
152                    progress_speed = Program.Controller.ProgressSpeedUp.ToSize () + "/s ";
153
154                } else if (Program.Controller.ProgressSpeedUp == 0 && Program.Controller.ProgressSpeedDown > 0) {
155                    progress_speed = Program.Controller.ProgressSpeedDown.ToSize () + "/s ";
156                        
157                } else if (Program.Controller.ProgressSpeedUp   > 0 &&
158                           Program.Controller.ProgressSpeedDown > 0) {
159
160                    progress_speed = "Up: " + Program.Controller.ProgressSpeedUp.ToSize () + "/s " +
161                        "Down: " + Program.Controller.ProgressSpeedDown.ToSize () + "/s";
162                }
163
164                return progress_speed;
165            }
166        }
167
168        public bool RecentEventsItemEnabled {
169            get {
170                return (Program.Controller.Repositories.Length > 0);
171            }
172        }
173
174        public bool LinkCodeItemEnabled {
175            get {
176                return !string.IsNullOrEmpty (Program.Controller.CurrentUser.PublicKey);
177            }
178        }
179
180        public bool QuitItemEnabled {
181            get {
182                return (CurrentState == IconState.Idle || CurrentState == IconState.Error);
183            }
184        }
185
186
187        public SparkleStatusIconController ()
188        {
189            UpdateFolders ();
190
191            Program.Controller.FolderListChanged += delegate {
192                if (CurrentState != IconState.Error) {
193                    CurrentState = IconState.Idle;
194
195                    UpdateStateText ();
196                }
197
198                UpdateFolders ();
199
200                UpdateStatusItemEvent (StateText);
201                UpdateMenuEvent (CurrentState);
202            };
203
204            Program.Controller.OnIdle += delegate {
205                if (CurrentState != IconState.Error) {
206                    CurrentState = IconState.Idle;
207
208                    UpdateStateText ();
209                }
210
211                UpdateFolders ();
212
213                UpdateIconEvent (CurrentState);
214                UpdateStatusItemEvent (StateText);
215                UpdateQuitItemEvent (QuitItemEnabled);
216                UpdateMenuEvent (CurrentState);
217            };
218
219            Program.Controller.OnSyncing += delegate {
220				int repos_syncing_up   = 0;
221				int repos_syncing_down = 0;
222				
223				foreach (SparkleRepoBase repo in Program.Controller.Repositories) {
224					if (repo.Status == SyncStatus.SyncUp)
225						repos_syncing_up++;
226					
227					if (repo.Status == SyncStatus.SyncDown)
228						repos_syncing_down++;
229				}
230				
231				if (repos_syncing_up > 0 &&
232				    repos_syncing_down > 0) {
233					
234					CurrentState = IconState.Syncing;
235                    StateText    = "Syncing changes…";
236				
237				} else if (repos_syncing_down == 0) {
238					CurrentState = IconState.SyncingUp;
239                    StateText    = "Sending changes…";
240					
241				} else {
242					CurrentState = IconState.SyncingDown;
243                    StateText    = "Receiving changes…";
244				}
245
246                if (ProgressPercentage > 0)
247                    StateText += " " + ProgressPercentage + "%  " + ProgressSpeed;
248
249                UpdateIconEvent (CurrentState);
250                UpdateStatusItemEvent (StateText);
251                UpdateQuitItemEvent (QuitItemEnabled);
252            };
253
254            Program.Controller.OnError += delegate {
255                CurrentState = IconState.Error;
256                StateText    = "Some changes weren’t synced";
257
258                UpdateFolders ();
259                
260                UpdateIconEvent (CurrentState);
261                UpdateStatusItemEvent (StateText);
262                UpdateQuitItemEvent (QuitItemEnabled);
263                UpdateMenuEvent (CurrentState);
264            };
265
266
267            // FIXME: Work around a race condition causing
268            // the icon to not always show the right state
269            Timers.Timer timer = new Timers.Timer () { Interval = 30 * 1000 };
270
271            timer.Elapsed += delegate {
272                UpdateIconEvent (CurrentState);
273                UpdateStatusItemEvent (StateText);
274            };
275
276            timer.Start ();
277        }
278
279
280        private string UpdateStateText ()
281        {
282            if (Projects.Length == 0)
283                return StateText = "Welcome to SparkleShare!";
284            else
285                return StateText = "Projects up to date " + GetPausedCount ();
286        }
287
288
289        private string GetPausedCount ()
290        {
291            int paused_projects = 0;
292            
293            foreach (ProjectInfo project in Projects)
294                if (project.IsPaused)
295                    paused_projects++;
296
297            if (paused_projects > 0) 
298                return string.Format ("— {0} paused", paused_projects);
299            else
300                return "";
301        }
302
303
304        // Main menu items
305        public void RecentEventsClicked ()
306        {
307            new Thread (() => {
308                while (!Program.Controller.RepositoriesLoaded)
309                    Thread.Sleep (100);
310
311                Program.Controller.ShowEventLogWindow ();
312            
313            }).Start ();
314        }
315
316        public void AddHostedProjectClicked ()
317        {
318            new Thread (() => Program.Controller.ShowSetupWindow (PageType.Add)).Start ();
319        }
320
321        public void CopyToClipboardClicked ()
322        {
323            Program.Controller.CopyToClipboard (Program.Controller.CurrentUser.PublicKey);
324        }
325
326        public void AboutClicked ()
327        {
328            Program.Controller.ShowAboutWindow ();
329        }
330
331        public void QuitClicked ()
332        {
333            Program.Controller.Quit ();
334        }
335
336
337        // Project items
338        public void ProjectClicked (string project)
339        {
340            Program.Controller.OpenSparkleShareFolder (project);
341        }
342
343        public void PauseClicked (string project)
344        {
345            Program.Controller.GetRepoByName (project).Pause ();
346            UpdateStateText ();
347            UpdateMenuEvent (CurrentState);
348        }
349
350        public void ResumeClicked (string project)
351        {
352            if (Program.Controller.GetRepoByName (project).UnsyncedChanges.Count > 0) {
353                Program.Controller.ShowNoteWindow (project);
354            
355            } else {
356              new Thread (() => {
357                    Program.Controller.GetRepoByName (project).Resume ("");
358                    
359                    UpdateStateText ();
360                    UpdateMenuEvent (CurrentState);
361
362                }).Start ();
363            }
364        }
365
366        public void TryAgainClicked (string project)
367        {
368            new Thread (() => Program.Controller.GetRepoByName (project).ForceRetry ()).Start ();
369        }
370
371
372        // Helper delegates
373        public EventHandler OpenFolderDelegate (string project)
374        {
375            return delegate { ProjectClicked (project); };
376        }
377        
378        public EventHandler TryAgainDelegate (string project)
379        {
380            return delegate { TryAgainClicked (project); };
381        }
382        
383        public EventHandler PauseDelegate (string project)
384        {
385            return delegate { PauseClicked (project); };
386        }
387        
388        public EventHandler ResumeDelegate (string project)
389        {
390            return delegate { ResumeClicked (project); };
391        }
392
393
394        private Object projects_lock = new Object ();
395
396        private void UpdateFolders ()
397        {
398            lock (this.projects_lock) {
399                List<ProjectInfo> projects = new List<ProjectInfo> ();
400
401                foreach (SparkleRepoBase repo in Program.Controller.Repositories)
402                    projects.Add (new ProjectInfo (repo));
403            
404                Projects = projects.ToArray ();
405            }
406        }
407    }
408}