diff --git a/README b/README index b8b921cb..c3f265d1 100644 --- a/README +++ b/README @@ -63,13 +63,18 @@ Note: Build on Linux: =============== -Installing the build dependencies on Debian: +Installing the build dependencies on Debian or Ubuntu: $ sudo apt-get install gtk-sharp2 mono-runtime mono-devel monodevelop \ libndesk-dbus1.0-cil-dev nant libnotify-cil-dev libgtk2.0-cil-dev \ libwebkit-cil-dev intltool libtool python-nautilus libndesk-dbus-glib1.0-cil-dev -Or on Fedora: +For Ubuntu libappindicator support, run the following before building: + + $ sudo apt-get install libappindicator0.1-cil-dev + + +On Fedora: $ sudo yum install gtk-sharp2-devel mono-core mono-devel monodevelop \ ndesk-dbus-devel ndesk-dbus-glib-devel nautilus-python-devel nant \ @@ -79,13 +84,13 @@ Or on Fedora: You can build and install SparkleShare like this: - $ ./configure (or ./autogen if you got SparkleShare from the repository) + $ ./configure --prefix=/usr (or ./autogen.sh if you build from the repository) $ make $ sudo make install Note: - Use './configure --prefix=/usr' if you want the Nautilus extension to work. + Use '--prefix=/usr' if you want the Nautilus extension to work. Run on Mac: diff --git a/SparkleLib/Git/SparkleRepoGit.cs b/SparkleLib/Git/SparkleRepoGit.cs index 3a611caa..d6d1e578 100644 --- a/SparkleLib/Git/SparkleRepoGit.cs +++ b/SparkleLib/Git/SparkleRepoGit.cs @@ -198,7 +198,7 @@ namespace SparkleLib { if (!AnyDifferences) return; - SparkleGit git = new SparkleGit (LocalPath, "commit -m '" + message + "'"); + SparkleGit git = new SparkleGit (LocalPath, "commit -m \"" + message + "\""); git.Start (); git.WaitForExit (); @@ -405,18 +405,17 @@ namespace SparkleLib { if (match.Success) { SparkleChangeSet change_set = new SparkleChangeSet (); + change_set.Folder = Name; change_set.Revision = match.Groups [1].Value; change_set.UserName = match.Groups [2].Value; change_set.UserEmail = match.Groups [3].Value; change_set.IsMerge = is_merge_commit; - change_set.Timestamp = new DateTime (int.Parse (match.Groups [4].Value), int.Parse (match.Groups [5].Value), int.Parse (match.Groups [6].Value), int.Parse (match.Groups [7].Value), int.Parse (match.Groups [8].Value), int.Parse (match.Groups [9].Value)); - string time_zone = match.Groups [10].Value; int our_offset = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).Hours; int their_offset = int.Parse (time_zone.Substring (1, 2)); @@ -445,10 +444,13 @@ namespace SparkleLib { if (change_type.Equals ("A")) { change_set.Added.Add (file_path); + } else if (change_type.Equals ("M")) { change_set.Edited.Add (file_path); + } else if (change_type.Equals ("D")) { change_set.Deleted.Add (file_path); + } else if (change_type.Equals ("R")) { int tab_pos = entry_line.LastIndexOf ("\t"); file_path = entry_line.Substring (42, tab_pos - 42); @@ -531,6 +533,7 @@ namespace SparkleLib { return message + "..." + n; } + message = message.Replace ("\"", ""); return message.TrimEnd (); } diff --git a/SparkleLib/SparkleChangeSet.cs b/SparkleLib/SparkleChangeSet.cs index 70baabb1..9bcd66b9 100644 --- a/SparkleLib/SparkleChangeSet.cs +++ b/SparkleLib/SparkleChangeSet.cs @@ -24,6 +24,7 @@ namespace SparkleLib { public string UserName; public string UserEmail; + public string Folder; public string Revision; public DateTime Timestamp; public bool IsMerge = false; diff --git a/SparkleLib/SparkleConfig.cs b/SparkleLib/SparkleConfig.cs index 9995316c..88f99835 100644 --- a/SparkleLib/SparkleConfig.cs +++ b/SparkleLib/SparkleConfig.cs @@ -56,18 +56,23 @@ namespace SparkleLib { private void CreateInitialConfig () { - string user_name = Environment.UserName; + string user_name = "Unknown"; if (SparkleBackend.Platform == PlatformID.Unix || SparkleBackend.Platform == PlatformID.MacOSX) { user_name = new UnixUserInfo (UnixEnvironment.UserName).RealName; + if (string.IsNullOrEmpty (user_name)) + user_name = UnixEnvironment.UserName; + else + user_name = user_name.TrimEnd (",".ToCharArray()); + + } else { + user_name = Environment.UserName; } if (string.IsNullOrEmpty (user_name)) - user_name = Environment.UserName; - else - user_name = user_name.TrimEnd (",".ToCharArray()); + user_name = "Unknown"; TextWriter writer = new StreamWriter (Path); string n = Environment.NewLine; diff --git a/SparkleLib/SparkleRepoBase.cs b/SparkleLib/SparkleRepoBase.cs index cf9d5ac6..31791649 100644 --- a/SparkleLib/SparkleRepoBase.cs +++ b/SparkleLib/SparkleRepoBase.cs @@ -34,19 +34,21 @@ namespace SparkleLib { public abstract class SparkleRepoBase { + private TimeSpan short_interval = new TimeSpan (0, 0, 3, 0); + private TimeSpan long_interval = new TimeSpan (0, 0, 10, 0); - - private Timer local_timer = new Timer () { Interval = 250 }; - private Timer remote_timer = new Timer () { Interval = 60000 }; private FileSystemWatcher watcher; private SparkleListenerBase listener; + private TimeSpan poll_interval; + private Timer local_timer = new Timer () { Interval = 0.25 * 1000 }; + private Timer remote_timer = new Timer () { Interval = 10 * 1000 }; + private DateTime last_poll = DateTime.Now; private List sizebuffer = new List (); - private bool has_changed = false; - private Object change_lock = new Object (); + private bool has_changed = false; + private Object change_lock = new Object (); protected SyncStatus status; protected bool is_buffering = false; - protected bool is_polling = true; protected bool server_online = true; public readonly SparkleBackend Backend; @@ -75,9 +77,10 @@ namespace SparkleLib { public SparkleRepoBase (string path, SparkleBackend backend) { - LocalPath = path; - Name = Path.GetFileName (LocalPath); - Backend = backend; + LocalPath = path; + Name = Path.GetFileName (LocalPath); + Backend = backend; + this.poll_interval = this.short_interval; SyncStatusChanged += delegate (SyncStatus status) { this.status = status; @@ -96,7 +99,12 @@ namespace SparkleLib { }; this.remote_timer.Elapsed += delegate { - if (this.is_polling) { + bool time_to_poll = (DateTime.Compare (this.last_poll, + DateTime.Now.Subtract (this.poll_interval)) < 0); + + if (time_to_poll) { + this.last_poll = DateTime.Now; + if (CheckForRemoteChanges ()) SyncDownBase (); } @@ -192,13 +200,6 @@ namespace SparkleLib { } - public bool IsPolling { - get { - return this.is_polling; - } - } - - // Disposes all resourses of this object public void Dispose () { @@ -230,7 +231,8 @@ namespace SparkleLib { // Stop polling when the connection to the irc channel is succesful this.listener.Connected += delegate { - this.is_polling = false; + this.poll_interval = this.long_interval; + this.last_poll = DateTime.Now; // Check for changes manually one more time if (CheckForRemoteChanges ()) @@ -243,8 +245,8 @@ namespace SparkleLib { // Start polling when the connection to the irc channel is lost this.listener.Disconnected += delegate { + this.poll_interval = this.short_interval; SparkleHelpers.DebugInfo (Name, "Falling back to polling"); - this.is_polling = true; }; // Fetch changes when there is a message in the irc channel @@ -262,12 +264,11 @@ namespace SparkleLib { } } }; - + // Start listening - if (!this.listener.IsConnected && !this.listener.IsConnecting) + if (!this.listener.IsConnected && !this.listener.IsConnecting) { this.listener.Connect (); - else - this.is_polling = false; + } } @@ -302,7 +303,7 @@ namespace SparkleLib { // Starts a timer when something changes public void OnFileActivity (object o, FileSystemEventArgs args) { - if (args.FullPath.Contains ("/.")) + if (args.FullPath.Contains (Path.DirectorySeparatorChar + ".")) return; WatcherChangeTypes wct = args.ChangeType; diff --git a/SparkleShare/Mac/MainMenu.xib b/SparkleShare/Mac/MainMenu.xib index adc6dd89..cdc80211 100644 --- a/SparkleShare/Mac/MainMenu.xib +++ b/SparkleShare/Mac/MainMenu.xib @@ -56,25 +56,6 @@ SparkleShare YES - - - About SparkleShare - - 2147483647 - - - - - - YES - YES - - - 1048576 - 2147483647 - - - Services @@ -949,14 +930,6 @@ 534 - - - delegate - - - - 538 - @@ -1184,23 +1157,16 @@ YES - - - - 58 - - - 134 @@ -1221,11 +1187,6 @@ - - 236 - - - 131 @@ -1548,8 +1509,6 @@ 221.ImportedFromIB2 23.IBPluginDependency 23.ImportedFromIB2 - 236.IBPluginDependency - 236.ImportedFromIB2 239.IBPluginDependency 239.ImportedFromIB2 24.IBEditorWindowLastContentRect @@ -1600,8 +1559,6 @@ 57.IBPluginDependency 57.ImportedFromIB2 57.editorWindowContentRectSynchronizationRect - 58.IBPluginDependency - 58.ImportedFromIB2 92.IBPluginDependency 92.ImportedFromIB2 @@ -1693,8 +1650,6 @@ com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - {{514, 649}, {194, 73}} com.apple.InterfaceBuilder.CocoaPlugin @@ -1739,14 +1694,12 @@ com.apple.InterfaceBuilder.CocoaPlugin - {{358, 569}, {223, 153}} + {{358, 599}, {213, 123}} com.apple.InterfaceBuilder.CocoaPlugin {{23, 794}, {245, 183}} com.apple.InterfaceBuilder.CocoaPlugin - com.apple.InterfaceBuilder.CocoaPlugin - diff --git a/SparkleShare/Mac/SparkleEventLog.cs b/SparkleShare/Mac/SparkleEventLog.cs new file mode 100644 index 00000000..3cc23774 --- /dev/null +++ b/SparkleShare/Mac/SparkleEventLog.cs @@ -0,0 +1,219 @@ +// SparkleShare, a collaboration and sharing tool. +// Copyright (C) 2010 Hylke Bons +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Threading; + +using MonoMac.Foundation; +using MonoMac.AppKit; +using MonoMac.ObjCRuntime; +using MonoMac.WebKit; +using SparkleLib; // Only used for SparkleChangeSet + +namespace SparkleShare { + + public class SparkleEventLog : NSWindow { + + private WebView WebView; + private NSBox Separator; + private string HTML; + private NSPopUpButton popup_button; + private NSProgressIndicator ProgressIndicator; + private List change_sets; + private string selected_log = null; + + + public SparkleEventLog (IntPtr handle) : base (handle) { } + + public SparkleEventLog () : base () + { + Title = "Recent Events"; + Delegate = new SparkleEventsDelegate (); + + SetFrame (new RectangleF (0, 0, 480, 640), true); + Center (); + + StyleMask = (NSWindowStyle.Closable | + NSWindowStyle.Miniaturizable | + NSWindowStyle.Titled); + + MaxSize = new SizeF (480, 640); + MinSize = new SizeF (480, 640); + HasShadow = true; + BackingType = NSBackingStore.Buffered; + + CreateEvents (); + UpdateEvents (false); + UpdateChooser (); + + OrderFrontRegardless (); + } + + + private void CreateEvents () + { + Separator = new NSBox (new RectangleF (0, 573, 480, 1)) { + BorderColor = NSColor.LightGray, + BoxType = NSBoxType.NSBoxCustom + }; + + ContentView.AddSubview (Separator); + + WebView = new WebView (new RectangleF (0, 0, 480, 573 ), "", ""){ + PolicyDelegate = new SparkleWebPolicyDelegate () + }; + + ProgressIndicator = new NSProgressIndicator () { + Style = NSProgressIndicatorStyle.Spinning, + Frame = new RectangleF (WebView.Frame.Width / 2 - 10, WebView.Frame.Height / 2 + 10, 20, 20) + }; + + ProgressIndicator.StartAnimation (this); + Update (); + } + + + public void UpdateChooser () + { + if (this.popup_button != null) + this.popup_button.RemoveFromSuperview (); + + this.popup_button = new NSPopUpButton () { + Frame = new RectangleF (480 - 156 - 8, 640 - 31 - 26, 156, 26), + PullsDown = false + }; + + this.popup_button.AddItem ("All Folders"); + this.popup_button.Menu.AddItem (NSMenuItem.SeparatorItem); + this.popup_button.AddItems (SparkleShare.Controller.Folders.ToArray ()); + + this.popup_button.Activated += delegate { + if (popup_button.IndexOfSelectedItem == 0) + this.selected_log = null; + else + this.selected_log = this.popup_button.SelectedItem.Title; + + UpdateEvents (false); + }; + + ContentView.AddSubview (this.popup_button); + } + + + public void UpdateEvents () + { + UpdateEvents (true); + } + + + public void UpdateEvents (bool silent) + { + if (!silent) { + InvokeOnMainThread (delegate { + if (WebView.Superview == ContentView) + WebView.RemoveFromSuperview (); + + ContentView.AddSubview (ProgressIndicator); + }); + } + + Thread thread = new Thread (new ThreadStart (delegate { + using (NSAutoreleasePool pool = new NSAutoreleasePool ()) { + Stopwatch watch = new Stopwatch (); + watch.Start (); + this.change_sets = SparkleShare.Controller.GetLog (this.selected_log); + GenerateHTML (); + watch.Stop (); + + // A short delay is less annoying than + // a flashing window + if (watch.ElapsedMilliseconds < 500 && !silent) + Thread.Sleep (500 - (int) watch.ElapsedMilliseconds); + + AddHTML (); + } + })); + + thread.Start (); + } + + + private void GenerateHTML () + { + HTML = SparkleShare.Controller.GetHTMLLog (this.change_sets); + + HTML = HTML.Replace ("", "Lucida Grande"); + HTML = HTML.Replace ("", "13.6px"); + HTML = HTML.Replace ("", "13.4px"); + HTML = HTML.Replace ("", "#bbb"); + HTML = HTML.Replace ("", "#ddd"); + HTML = HTML.Replace ("", "#f5f5f5"); + HTML = HTML.Replace ("", "#0085cf"); + HTML = HTML.Replace ("", "#009ff8"); + HTML = HTML.Replace ("", + "file://" + Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "avatar-default.png")); + HTML = HTML.Replace ("", + "file://" + Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "document-added-12.png")); + HTML = HTML.Replace ("", + "file://" + Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "document-deleted-12.png")); + HTML = HTML.Replace ("", + "file://" + Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "document-edited-12.png")); + HTML = HTML.Replace ("", + "file://" + Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "document-moved-12.png")); + } + + + private void AddHTML () + { + InvokeOnMainThread (delegate { + if (ProgressIndicator.Superview == ContentView) + ProgressIndicator.RemoveFromSuperview (); + + WebView.MainFrame.LoadHtmlString (HTML, new NSUrl ("")); + ContentView.AddSubview (WebView); + Update (); + }); + } + } + + + public class SparkleEventsDelegate : NSWindowDelegate { + + public override bool WindowShouldClose (NSObject sender) + { + (sender as SparkleEventLog).OrderOut (this); + return false; + } + } + + + public class SparkleWebPolicyDelegate : WebPolicyDelegate { + + public override void DecidePolicyForNavigation (WebView web_view, NSDictionary action_info, + NSUrlRequest request, WebFrame frame, NSObject decision_token) + { + string file_path = request.Url.ToString (); + file_path = file_path.Replace ("%20", " "); + + NSWorkspace.SharedWorkspace.OpenFile (file_path); + } + } +} diff --git a/SparkleShare/Mac/SparkleLog.cs b/SparkleShare/Mac/SparkleLog.cs deleted file mode 100644 index 5bab464a..00000000 --- a/SparkleShare/Mac/SparkleLog.cs +++ /dev/null @@ -1,207 +0,0 @@ -// SparkleShare, a collaboration and sharing tool. -// Copyright (C) 2010 Hylke Bons -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - - -using System; -using System.Drawing; -using System.IO; -using System.Threading; - -using MonoMac.Foundation; -using MonoMac.AppKit; -using MonoMac.ObjCRuntime; -using MonoMac.WebKit; - -namespace SparkleShare { - - public class SparkleLog : NSWindow { - - public readonly string LocalPath; - - private WebView WebView; - private NSButton CloseButton; - private NSButton OpenFolderButton; - private NSBox Separator; - private string HTML; - private NSProgressIndicator ProgressIndicator; - - - public SparkleLog (IntPtr handle) : base (handle) { } - - public SparkleLog (string path) : base () - { - LocalPath = path; - - Delegate = new SparkleLogDelegate (); - - SetFrame (new RectangleF (0, 0, 480, 640), true); - Center (); - - // Open slightly off center for each consecutive window - if (SparkleUI.OpenLogs.Count > 0) { - RectangleF offset = new RectangleF (Frame.X + (SparkleUI.OpenLogs.Count * 20), - Frame.Y - (SparkleUI.OpenLogs.Count * 20), Frame.Width, Frame.Height); - - SetFrame (offset, true); - } - - StyleMask = (NSWindowStyle.Closable | - NSWindowStyle.Miniaturizable | - NSWindowStyle.Titled); - - MaxSize = new SizeF (480, 640); - MinSize = new SizeF (480, 640); - HasShadow = true; - BackingType = NSBackingStore.Buffered; - - CreateEventLog (); - UpdateEventLog (); - - OrderFrontRegardless (); - } - - - private void CreateEventLog () - { - OpenFolderButton = new NSButton (new RectangleF (16, 12, 120, 32)) { - Title = "Open Folder", - BezelStyle = NSBezelStyle.Rounded , - Font = SparkleUI.Font - }; - - OpenFolderButton.Activated += delegate { - SparkleShare.Controller.OpenSparkleShareFolder (LocalPath); - }; - - ContentView.AddSubview (OpenFolderButton); - - - CloseButton = new NSButton (new RectangleF (480 - 120 - 16, 12, 120, 32)) { - Title = "Close", - BezelStyle = NSBezelStyle.Rounded, - Font = SparkleUI.Font - }; - - CloseButton.Activated += delegate { - InvokeOnMainThread (delegate { - PerformClose (this); - }); - }; - - ContentView.AddSubview (CloseButton); - - - string name = Path.GetFileName (LocalPath); - Title = String.Format ("Events in ‘{0}’", name); - - Separator = new NSBox (new RectangleF (0, 58, 480, 1)) { - BorderColor = NSColor.LightGray, - BoxType = NSBoxType.NSBoxCustom - }; - - ContentView.AddSubview (Separator); - - - ProgressIndicator = new NSProgressIndicator () { - Style = NSProgressIndicatorStyle.Spinning, - Frame = new RectangleF (Frame.Width / 2 - 10, Frame.Height / 2 + 10, 20, 20) - }; - - ProgressIndicator.StartAnimation (this); - - WebView = new WebView (new RectangleF (0, 59, 480, 559), "", ""){ - PolicyDelegate = new SparkleWebPolicyDelegate () - }; - - Update (); - } - - - public void UpdateEventLog () - { - InvokeOnMainThread (delegate { - - if (HTML == null) - ContentView.AddSubview (ProgressIndicator); - }); - - Thread thread = new Thread (new ThreadStart (delegate { - using (NSAutoreleasePool pool = new NSAutoreleasePool ()) { - GenerateHTML (); - AddHTML (); - } - })); - - thread.Start (); - } - - - private void GenerateHTML () - { - string folder_name = Path.GetFileName (LocalPath); - HTML = SparkleShare.Controller.GetHTMLLog (folder_name); - - HTML = HTML.Replace ("", "Lucida Grande"); - HTML = HTML.Replace ("", "13.6px"); - HTML = HTML.Replace ("", "13.4px"); - HTML = HTML.Replace ("", "#bbb"); - HTML = HTML.Replace ("", "#ddd"); - HTML = HTML.Replace ("", "#f5f5f5"); - HTML = HTML.Replace ("", "#0085cf"); - HTML = HTML.Replace ("", "#009ff8"); - HTML = HTML.Replace ("", - "file://" + Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "avatar-default.png")); - } - - - private void AddHTML () - { - InvokeOnMainThread (delegate { - if (ProgressIndicator.Superview == ContentView) - ProgressIndicator.RemoveFromSuperview (); - - WebView.MainFrame.LoadHtmlString (HTML, new NSUrl ("")); - - ContentView.AddSubview (WebView); - Update (); - - }); - } - } - - - public class SparkleLogDelegate : NSWindowDelegate { - - public override bool WindowShouldClose (NSObject sender) - { - (sender as SparkleLog).OrderOut (this); - return false; - } - } - - - public class SparkleWebPolicyDelegate : WebPolicyDelegate { - - public override void DecidePolicyForNavigation (WebView web_view, NSDictionary action_info, - NSUrlRequest request, WebFrame frame, NSObject decision_token) - { - string file_path = request.Url.ToString (); - file_path = file_path.Replace ("%20", " "); - - NSWorkspace.SharedWorkspace.OpenFile (file_path); - } - } -} diff --git a/SparkleShare/Mac/SparkleShare.csproj b/SparkleShare/Mac/SparkleShare.csproj index 89c1e10f..08c5bec8 100644 --- a/SparkleShare/Mac/SparkleShare.csproj +++ b/SparkleShare/Mac/SparkleShare.csproj @@ -24,7 +24,7 @@ false - + @@ -55,10 +55,6 @@ False - - False - ..\..\bin\SparkleLib.dll - False @@ -66,6 +62,10 @@ False ..\..\bin\Meebey.SmartIrc4net.dll + + False + ..\..\bin\SparkleLib.dll + @@ -79,7 +79,6 @@ - @@ -90,6 +89,7 @@ + @@ -158,6 +158,18 @@ Pixmaps\error-active.png + + Pixmaps\document-added-12.png + + + Pixmaps\document-edited-12.png + + + Pixmaps\document-deleted-12.png + + + Pixmaps\document-moved-12.png + diff --git a/SparkleShare/Mac/SparkleStatusIcon.cs b/SparkleShare/Mac/SparkleStatusIcon.cs index 357c3f40..bd2950b0 100644 --- a/SparkleShare/Mac/SparkleStatusIcon.cs +++ b/SparkleShare/Mac/SparkleStatusIcon.cs @@ -43,6 +43,8 @@ namespace SparkleShare { private NSMenuItem SyncMenuItem; private NSMenuItem AboutMenuItem; private NSMenuItem NotificationsMenuItem; + private NSMenuItem RecentEventsMenuItem; + private NSMenuItem QuitMenuItem; private delegate void Task (); private EventHandler [] Tasks; @@ -149,7 +151,7 @@ namespace SparkleShare { Title = StateText }; - Menu.AddItem (StateMenuItem); + Menu.AddItem (StateMenuItem); Menu.AddItem (NSMenuItem.SeparatorItem); FolderMenuItem = new NSMenuItem () { @@ -176,7 +178,7 @@ namespace SparkleShare { foreach (string folder_name in SparkleShare.Controller.Folders) { NSMenuItem item = new NSMenuItem (); - item.Title = folder_name; + item.Title = folder_name; if (SparkleShare.Controller.UnsyncedFolders.Contains (folder_name)) item.Image = NSImage.ImageNamed ("NSCaution"); @@ -184,7 +186,7 @@ namespace SparkleShare { item.Image = NSImage.ImageNamed ("NSFolder"); item.Image.Size = new SizeF (16, 16); - Tasks [i] = OpenEventLogDelegate (folder_name); + Tasks [i] = OpenFolderDelegate (folder_name); FolderMenuItems [i] = item; FolderMenuItems [i].Activated += Tasks [i]; @@ -233,6 +235,25 @@ namespace SparkleShare { Menu.AddItem (SyncMenuItem); Menu.AddItem (NSMenuItem.SeparatorItem); + RecentEventsMenuItem = new NSMenuItem () { + Title = "Show Recent Events" + }; + + if (SparkleShare.Controller.Folders.Count > 0) { + RecentEventsMenuItem.Activated += delegate { + InvokeOnMainThread (delegate { + NSApplication.SharedApplication.ActivateIgnoringOtherApps (true); + + if (SparkleUI.EventLog == null) + SparkleUI.EventLog = new SparkleEventLog (); + + SparkleUI.EventLog.OrderFrontRegardless (); + SparkleUI.EventLog.MakeKeyAndOrderFront (this); + }); + }; + } + + Menu.AddItem (RecentEventsMenuItem); NotificationsMenuItem = new NSMenuItem (); @@ -255,28 +276,28 @@ namespace SparkleShare { Menu.AddItem (NotificationsMenuItem); Menu.AddItem (NSMenuItem.SeparatorItem); - AboutMenuItem = new NSMenuItem () { Title = "About SparkleShare" }; - AboutMenuItem.Activated += delegate { - InvokeOnMainThread (delegate { - NSApplication.SharedApplication.ActivateIgnoringOtherApps (true); + AboutMenuItem.Activated += delegate { + InvokeOnMainThread (delegate { + NSApplication.SharedApplication.ActivateIgnoringOtherApps (true); - if (SparkleUI.About == null) - SparkleUI.About = new SparkleAbout (); + if (SparkleUI.About == null) + SparkleUI.About = new SparkleAbout (); - SparkleUI.About.OrderFrontRegardless (); - SparkleUI.About.MakeKeyAndOrderFront (this); - SparkleUI.About.CheckForNewVersion (); - }); - }; + SparkleUI.About.OrderFrontRegardless (); + SparkleUI.About.MakeKeyAndOrderFront (this); + SparkleUI.About.CheckForNewVersion (); + }); + }; Menu.AddItem (AboutMenuItem); + StatusItem.Menu = Menu; StatusItem.Menu.Update (); } @@ -284,26 +305,10 @@ namespace SparkleShare { // A method reference that makes sure that opening the // event log for each repository works correctly - private EventHandler OpenEventLogDelegate (string path) + private EventHandler OpenFolderDelegate (string name) { return delegate { - InvokeOnMainThread (delegate { - NSApplication.SharedApplication.ActivateIgnoringOtherApps (true); - - SparkleLog log = SparkleUI.OpenLogs.Find (delegate (SparkleLog l) { - return l.LocalPath.Equals (path); - }); - - // Check whether the log is already open, create a new one if - // that's not the case or present it to the user if it is - if (log == null) { - SparkleUI.OpenLogs.Add (new SparkleLog (path)); - SparkleUI.OpenLogs [SparkleUI.OpenLogs.Count - 1].MakeKeyAndOrderFront (this); - } else { - log.OrderFrontRegardless (); - log.MakeKeyAndOrderFront (this); - } - }); + SparkleShare.Controller.OpenSparkleShareFolder (name); }; } diff --git a/SparkleShare/Mac/SparkleUI.cs b/SparkleShare/Mac/SparkleUI.cs index a923bce2..66ba5aa6 100644 --- a/SparkleShare/Mac/SparkleUI.cs +++ b/SparkleShare/Mac/SparkleUI.cs @@ -51,7 +51,7 @@ namespace SparkleShare { public class SparkleUI : AppDelegate { public static SparkleStatusIcon StatusIcon; - public static List OpenLogs; + public static SparkleEventLog EventLog; public static SparkleIntro Intro; public static SparkleAbout About; public static NSFont Font; @@ -63,6 +63,7 @@ namespace SparkleShare { { string content_path = Directory.GetParent ( System.AppDomain.CurrentDomain.BaseDirectory).ToString (); + string app_path = Directory.GetParent (content_path).ToString (); string growl_path = Path.Combine (app_path, "Frameworks", "Growl.framework", "Growl"); @@ -88,18 +89,15 @@ namespace SparkleShare { Font = NSFontManager.SharedFontManager.FontWithFamily ("Lucida Grande", NSFontTraitMask.Condensed, 0, 13); - - OpenLogs = new List (); + StatusIcon = new SparkleStatusIcon (); } SparkleShare.Controller.NotificationRaised += delegate (string user_name, string user_email, string message, string repository_path) { InvokeOnMainThread (delegate { - foreach (SparkleLog log in OpenLogs) { - if (log.LocalPath.Equals (repository_path)) - log.UpdateEventLog (); - } + if (EventLog != null) + EventLog.UpdateEvents (); if (SparkleShare.Controller.NotificationsEnabled) { if (NSApplication.SharedApplication.DockTile.BadgeLabel == null) @@ -134,20 +132,28 @@ namespace SparkleShare { SparkleShare.Controller.AvatarFetched += delegate { InvokeOnMainThread (delegate { - foreach (SparkleLog log in SparkleUI.OpenLogs) - log.UpdateEventLog (); + if (EventLog != null) + EventLog.UpdateEvents (); }); }; - SparkleShare.Controller.OnIdle += delegate { - InvokeOnMainThread (delegate { - foreach (SparkleLog log in SparkleUI.OpenLogs) - log.UpdateEventLog (); - }); - }; - - + SparkleShare.Controller.OnIdle += delegate { + InvokeOnMainThread (delegate { + if (EventLog != null) + EventLog.UpdateEvents (); + }); + }; + + + SparkleShare.Controller.FolderListChanged += delegate { + InvokeOnMainThread (delegate { + if (EventLog != null) + EventLog.UpdateChooser (); + }); + }; + + if (SparkleShare.Controller.FirstRun) { Intro = new SparkleIntro (); Intro.ShowAccountForm (); @@ -179,5 +185,5 @@ namespace SparkleShare { string path = NSBundle.MainBundle.PathForResource ("Growl", "plist"); return NSDictionary.FromFile (path); } - } + } } diff --git a/SparkleShare/Makefile.am b/SparkleShare/Makefile.am index fd3ba6e2..c757da22 100644 --- a/SparkleShare/Makefile.am +++ b/SparkleShare/Makefile.am @@ -7,6 +7,9 @@ TARGET = exe LINK = $(REF_SPARKLESHARE) $(NOTIFY_SHARP_LIBS) $(WEBKIT_SHARP_LIBS) +if HAVE_APP_INDICATOR +BUILD_DEFINES="-define:HAVE_APP_INDICATOR" +endif SOURCES = \ SparkleAbout.cs \ @@ -16,7 +19,7 @@ SOURCES = \ SparkleInfobar.cs \ SparkleIntro.cs \ SparkleLinController.cs \ - SparkleLog.cs \ + SparkleEventLog.cs \ SparkleShare.cs \ SparkleSpinner.cs \ SparkleStatusIcon.cs \ diff --git a/SparkleShare/SparkleController.cs b/SparkleShare/SparkleController.cs index e6be1fc5..663497ba 100644 --- a/SparkleShare/SparkleController.cs +++ b/SparkleShare/SparkleController.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Net; using System.Security.Cryptography; using System.Text; @@ -222,7 +223,9 @@ namespace SparkleShare { public List Folders { get { - return SparkleConfig.DefaultConfig.Folders; + List folders = SparkleConfig.DefaultConfig.Folders; + folders.Sort (); + return folders; } } @@ -240,11 +243,31 @@ namespace SparkleShare { } } - - public List GetLog (string name) + + public List GetLog () { + List list = new List (); + + foreach (SparkleRepoBase repo in Repositories) + list.AddRange (repo.GetChangeSets (50)); + + list.Sort ((x, y) => (x.Timestamp.CompareTo (y.Timestamp))); + list.Reverse (); + + if (list.Count > 100) + return list.GetRange (0, 100); + else + return list.GetRange (0, list.Count); + } + + + public List GetLog (string name) + { + if (name == null) + return GetLog (); + string path = Path.Combine (SparklePaths.SparklePath, name); - int log_size = 30; + int log_size = 50; foreach (SparkleRepoBase repo in Repositories) { if (repo.LocalPath.Equals (path)) @@ -260,10 +283,9 @@ namespace SparkleShare { public abstract string EventEntryHTML { get; } - public string GetHTMLLog (string name) + public string GetHTMLLog (List change_sets) { - List change_sets = GetLog (name); - List activity_days = new List (); + List activity_days = new List (); if (change_sets.Count == 0) return null; @@ -302,68 +324,58 @@ namespace SparkleShare { string event_entry = "
"; if (change_set.IsMerge) { - event_entry += "
Merged a branch
"; + event_entry += "
Did something magical
"; } else { if (change_set.Edited.Count > 0) { - event_entry += "
Edited
"; - foreach (string file_path in change_set.Edited) { string absolute_file_path = SparkleHelpers.CombineMore (SparklePaths.SparklePath, - name, file_path); + change_set.Folder, file_path); if (File.Exists (absolute_file_path)) - event_entry += "
" + file_path + "
"; + event_entry += "
" + file_path + "
"; else - event_entry += "
" + file_path + "
"; + event_entry += "
" + file_path + "
"; } } if (change_set.Added.Count > 0) { - event_entry += "
Added
"; - foreach (string file_path in change_set.Added) { string absolute_file_path = SparkleHelpers.CombineMore (SparklePaths.SparklePath, - name, file_path); + change_set.Folder, file_path); if (File.Exists (absolute_file_path)) - event_entry += "
" + file_path + "
"; + event_entry += "
" + file_path + "
"; else - event_entry += "
" + file_path + "
"; + event_entry += "
" + file_path + "
"; } } if (change_set.Deleted.Count > 0) { - event_entry += "
Deleted
"; - foreach (string file_path in change_set.Deleted) { string absolute_file_path = SparkleHelpers.CombineMore (SparklePaths.SparklePath, - name, file_path); + change_set.Folder, file_path); if (File.Exists (absolute_file_path)) - event_entry += "
" + file_path + "
"; + event_entry += "
" + file_path + "
"; else - event_entry += "
" + file_path + "
"; + event_entry += "
" + file_path + "
"; } } if (change_set.MovedFrom.Count > 0) { - event_entry += "
Moved
"; - int i = 0; foreach (string file_path in change_set.MovedFrom) { string to_file_path = change_set.MovedTo [i]; string absolute_file_path = SparkleHelpers.CombineMore (SparklePaths.SparklePath, - name, file_path); + change_set.Folder, file_path); string absolute_to_file_path = SparkleHelpers.CombineMore (SparklePaths.SparklePath, - name, to_file_path); + change_set.Folder, to_file_path); if (File.Exists (absolute_file_path)) - event_entry += "
" + file_path + "
" + - " "; + event_entry += "
" + file_path + "
"; else - event_entry += "
" + file_path + "
" + - " "; + event_entry += "
" + file_path + "
"; if (File.Exists (absolute_to_file_path)) event_entry += "" + to_file_path + "
"; @@ -378,8 +390,10 @@ namespace SparkleShare { event_entry += "
"; event_entries += event_entry_html.Replace ("", event_entry) .Replace ("", change_set.UserName) - .Replace ("", "file://" + GetAvatar (change_set.UserEmail, 36) ) - .Replace ("", change_set.Timestamp.ToString ("H:mm")); + .Replace ("", "file://" + GetAvatar (change_set.UserEmail, 36)) + .Replace ("", change_set.Timestamp.ToString ("H:mm")) + .Replace ("", change_set.Folder) + .Replace ("", AssignColor (change_set.Folder)); } string day_entry = ""; @@ -1015,6 +1029,20 @@ namespace SparkleShare { web_client.DownloadStringAsync (uri); } + + + private string [] tango_palette = new string [] {"#eaab00", "#e37222", + "#3892ab", "#33c2cb", "#19b271", "#9eab05", "#8599a8", "#9ca696", + "#b88454", "#cc0033", "#8f6678", "#8c6cd0", "#796cbf", "#4060af", + "#aa9c8f", "#818a8f"}; + + private string AssignColor (string s) + { + string hash = GetMD5 (s).Substring (0, 8); + string numbers = Regex.Replace (hash, "[a-z]", ""); + int number = 3 + int.Parse (numbers); + return this.tango_palette [number % this.tango_palette.Length]; + } } diff --git a/SparkleShare/SparkleLog.cs b/SparkleShare/SparkleEventLog.cs similarity index 61% rename from SparkleShare/SparkleLog.cs rename to SparkleShare/SparkleEventLog.cs index e920571c..2720c93b 100644 --- a/SparkleShare/SparkleLog.cs +++ b/SparkleShare/SparkleEventLog.cs @@ -28,9 +28,7 @@ using WebKit; namespace SparkleShare { - public class SparkleLog : Window { - - public readonly string LocalPath; + public class SparkleEventLog : Window { private ScrolledWindow ScrolledWindow; private MenuBar MenuBar; @@ -39,6 +37,10 @@ namespace SparkleShare { private SparkleSpinner Spinner; private string HTML; private EventBox LogContent; + private List change_sets; + private string selected_log = null; + private ComboBox combo_box; + private HBox layout_horizontal; // Short alias for the translations @@ -48,48 +50,35 @@ namespace SparkleShare { } - public SparkleLog (string path) : base ("") + public SparkleEventLog () : base ("") { - LocalPath = path; - - string name = System.IO.Path.GetFileName (LocalPath); SetSizeRequest (480, 640); - - Resizable = false; - - BorderWidth = 0; SetPosition (WindowPosition.Center); - // Open slightly off center for each consecutive window - if (SparkleUI.OpenLogs.Count > 0) { + Resizable = false; + BorderWidth = 0; - int x, y; - GetPosition (out x, out y); - Move (x + SparkleUI.OpenLogs.Count * 20, y + SparkleUI.OpenLogs.Count * 20); - - } - - // TRANSLATORS: {0} is a folder name, and {1} is a server address - Title = String.Format(_("Events in ‘{0}’"), name); + Title = _("Recent Events"); IconName = "folder-sparkleshare"; - DeleteEvent += Close; + DeleteEvent += Close; - CreateEventLog (); - UpdateEventLog (); + CreateEvents (); + UpdateEvents (false); + UpdateChooser (); } - private void CreateEventLog () + private void CreateEvents () { - LogContent = new EventBox (); VBox layout_vertical = new VBox (false, 0); + LogContent = new EventBox (); - ScrolledWindow = new ScrolledWindow (); + ScrolledWindow = new ScrolledWindow (); - WebView = new WebView () { - Editable = false - }; + WebView = new WebView () { + Editable = false + }; WebView.HoveringOverLink += delegate (object o, WebKit.HoveringOverLinkArgs args) { LinkStatus = args.Link; @@ -103,50 +92,83 @@ namespace SparkleShare { process.StartInfo.Arguments = args.Request.Uri.Replace (" ", "\\ "); // Escape space-characters process.Start (); - UpdateEventLog (); + UpdateEvents (); } }; - ScrolledWindow.Add (WebView); - LogContent.Add (ScrolledWindow); + ScrolledWindow.Add (WebView); + LogContent.Add (ScrolledWindow); + this.layout_horizontal = new HBox (true, 0); + this.layout_horizontal.PackStart (new Label (""), true, true, 0); + this.layout_horizontal.PackStart (new Label (""), true, true, 0); + + layout_vertical.PackStart (layout_horizontal, false, false, 0); layout_vertical.PackStart (LogContent, true, true, 0); - HButtonBox dialog_buttons = new HButtonBox { - Layout = ButtonBoxStyle.Edge, - BorderWidth = 12 - }; - - Button open_folder_button = new Button (_("_Open Folder")) { - UseUnderline = true - }; - - open_folder_button.Clicked += delegate (object o, EventArgs args) { - SparkleShare.Controller.OpenSparkleShareFolder (LocalPath); - }; - - Button close_button = new Button (Stock.Close); - - close_button.Clicked += delegate { - HideAll (); - }; - - dialog_buttons.Add (open_folder_button); - dialog_buttons.Add (close_button); - // We have to hide the menubar somewhere... layout_vertical.PackStart (CreateShortcutsBar (), false, false, 0); - layout_vertical.PackStart (dialog_buttons, false, false, 0); Add (layout_vertical); - ShowAll (); } - public void UpdateEventLog () + public void UpdateChooser () { - if (HTML == null) { // TODO: there may be a race condition here + if (this.combo_box != null && this.combo_box.Parent != null) + this.layout_horizontal.Remove (this.combo_box); + + this.combo_box = new ComboBox (); + this.layout_horizontal.BorderWidth = 9; + + CellRendererText cell = new CellRendererText(); + this.combo_box.PackStart (cell, false); + this.combo_box.AddAttribute (cell, "text", 0); + ListStore store = new ListStore (typeof (string)); + this.combo_box.Model = store; + + store.AppendValues (_("All Folders")); + store.AppendValues ("---"); + + foreach (string folder_name in SparkleShare.Controller.Folders) + store.AppendValues (folder_name); + + this.combo_box.Active = 0; + + this.combo_box.RowSeparatorFunc = delegate (TreeModel model, TreeIter iter) { + string item = (string) this.combo_box.Model.GetValue (iter, 0); + return (item == "---"); + }; + + this.combo_box.Changed += delegate { + TreeIter iter; + this.combo_box.GetActiveIter (out iter); + + string selection = (string) this.combo_box.Model.GetValue (iter, 0); + + if (selection.Equals (_("All Folders"))) + this.selected_log = null; + else + this.selected_log = selection; + + UpdateEvents (false); + }; + + this.layout_horizontal.PackStart (this.combo_box, true, true, 0); + this.layout_horizontal.ShowAll (); + } + + + public void UpdateEvents () + { + UpdateEvents (true); + } + + + public void UpdateEvents (bool silent) + { + if (!silent) { LogContent.Remove (LogContent.Child); Spinner = new SparkleSpinner (22); LogContent.Add (Spinner); @@ -154,7 +176,17 @@ namespace SparkleShare { } Thread thread = new Thread (new ThreadStart (delegate { + Stopwatch watch = new Stopwatch (); + watch.Start (); + this.change_sets = SparkleShare.Controller.GetLog (this.selected_log); GenerateHTML (); + watch.Stop (); + + // A short delay is less annoying than + // a flashing window + if (watch.ElapsedMilliseconds < 500 && !silent) + Thread.Sleep (500 - (int) watch.ElapsedMilliseconds); + AddHTML (); })); @@ -164,7 +196,7 @@ namespace SparkleShare { private void GenerateHTML () { - HTML = SparkleShare.Controller.GetHTMLLog (System.IO.Path.GetFileName (LocalPath)); + HTML = SparkleShare.Controller.GetHTMLLog (this.change_sets); HTML = HTML.Replace ("", (double) (Style.FontDescription.Size / 1024 + 3) + "px"); HTML = HTML.Replace ("", (Style.FontDescription.Size / 1024 + 3) + "px"); @@ -179,6 +211,18 @@ namespace SparkleShare { HTML = HTML.Replace ("", "file://" + SparkleHelpers.CombineMore (Defines.PREFIX, "share", "sparkleshare", "icons", "hicolor", "32x32", "status", "avatar-default.png")); + HTML = HTML.Replace ("", "file://" + + SparkleHelpers.CombineMore (Defines.PREFIX, "share", "sparkleshare", "icons", + "hicolor", "12x12", "status", "document-added.png")); + HTML = HTML.Replace ("", "file://" + + SparkleHelpers.CombineMore (Defines.PREFIX, "share", "sparkleshare", "icons", + "hicolor", "12x12", "status", "document-edited.png")); + HTML = HTML.Replace ("", "file://" + + SparkleHelpers.CombineMore (Defines.PREFIX, "share", "sparkleshare", "icons", + "hicolor", "12x12", "status", "document-deleted.png")); + HTML = HTML.Replace ("", "file://" + + SparkleHelpers.CombineMore (Defines.PREFIX, "share", "sparkleshare", "icons", + "hicolor", "12x12", "status", "document-moved.png")); } @@ -248,3 +292,4 @@ namespace SparkleShare { } } } + diff --git a/SparkleShare/SparkleStatusIcon.cs b/SparkleShare/SparkleStatusIcon.cs index 5707bb7e..3c29c866 100644 --- a/SparkleShare/SparkleStatusIcon.cs +++ b/SparkleShare/SparkleStatusIcon.cs @@ -19,6 +19,9 @@ using System; using System.IO; using System.Timers; +#if HAVE_APP_INDICATOR +using AppIndicator; +#endif using Gtk; using Mono.Unix; @@ -26,7 +29,7 @@ namespace SparkleShare { // The statusicon that stays in the // user's notification area - public class SparkleStatusIcon : StatusIcon { + public class SparkleStatusIcon { private Timer Animation; private Gdk.Pixbuf [] AnimationFrames; @@ -34,6 +37,12 @@ namespace SparkleShare { private string StateText; private Menu Menu; + #if HAVE_APP_INDICATOR + private ApplicationIndicator indicator; + #else + private StatusIcon status_icon; + #endif + // Short alias for the translations public static string _ (string s) { @@ -41,13 +50,23 @@ namespace SparkleShare { } - public SparkleStatusIcon () : base () + public SparkleStatusIcon () { AnimationFrames = CreateAnimationFrames (); Animation = CreateAnimation (); - Activate += ShowMenu; // Primary mouse button click - PopupMenu += ShowMenu; // Secondary mouse button click + #if HAVE_APP_INDICATOR + this.indicator = new ApplicationIndicator ("sparkleshare", + "process-syncing-sparkleshare-i", Category.ApplicationStatus) { + + Status = Status.Attention + }; + #else + this.status_icon = new StatusIcon (); + + this.status_icon.Activate += ShowMenu; // Primary mouse button click + this.status_icon.PopupMenu += ShowMenu; // Secondary mouse button click + #endif SetNormalState (); CreateMenu (); @@ -120,8 +139,17 @@ namespace SparkleShare { else FrameNumber = 0; + string icon_name = "process-syncing-sparkleshare-"; + + for (int i = 0; i <= FrameNumber; i++) + icon_name += "i"; + Application.Invoke (delegate { - Pixbuf = AnimationFrames [FrameNumber]; + #if HAVE_APP_INDICATOR + this.indicator.IconName = icon_name; + #else + this.status_icon.Pixbuf = SparkleUIHelpers.GetIcon (icon_name, 24); + #endif }); }; @@ -156,20 +184,23 @@ namespace SparkleShare { if (SparkleShare.Controller.Folders.Count > 0) { // Creates a menu item for each repository with a link to their logs - foreach (string path in SparkleShare.Controller.Folders) { + foreach (string folder_name in SparkleShare.Controller.Folders) { + Gdk.Pixbuf folder_icon; - Gdk.Pixbuf folder_icon = IconTheme.Default.LoadIcon ("folder", 16, - IconLookupFlags.GenericFallback); - - ImageMenuItem subfolder_item = new SparkleMenuItem (Path.GetFileName (path)) { + if (SparkleShare.Controller.UnsyncedFolders.Contains (folder_name)) { + folder_icon = IconTheme.Default.LoadIcon ("dialog-error", 16, + IconLookupFlags.GenericFallback); + + } else { + folder_icon = IconTheme.Default.LoadIcon ("folder", 16, + IconLookupFlags.GenericFallback); + } + + ImageMenuItem subfolder_item = new SparkleMenuItem (folder_name) { Image = new Image (folder_icon) }; -// if (repo.HasUnsyncedChanges) -// folder_action.IconName = "dialog-error"; - - subfolder_item.Activated += OpenEventLogDelegate (path); - + subfolder_item.Activated += OpenFolderDelegate (folder_name); Menu.Add (subfolder_item); } @@ -181,6 +212,8 @@ namespace SparkleShare { Menu.Add (no_folders_item); } + Menu.Add (new SeparatorMenuItem ()); + // Opens the wizard to add a new remote folder MenuItem sync_item = new MenuItem (_("Add Remote Folder…")); @@ -206,7 +239,24 @@ namespace SparkleShare { Menu.Add (sync_item); Menu.Add (new SeparatorMenuItem ()); - MenuItem notify_item; + MenuItem recent_events_item = new MenuItem (_("Show Recent Events")); + + if (SparkleShare.Controller.Folders.Count < 1) + recent_events_item.Sensitive = false; + + recent_events_item.Activated += delegate { + Application.Invoke (delegate { + if (SparkleUI.EventLog == null) + SparkleUI.EventLog = new SparkleEventLog (); + + SparkleUI.EventLog.ShowAll (); + SparkleUI.EventLog.Present (); + }); + }; + + Menu.Add (recent_events_item); + + MenuItem notify_item; if (SparkleShare.Controller.NotificationsEnabled) notify_item = new MenuItem (_("Turn Notifications Off")); @@ -241,34 +291,30 @@ namespace SparkleShare { Menu.Add (quit_item); Menu.ShowAll (); + + #if HAVE_APP_INDICATOR + this.indicator.Menu = Menu; + #endif } // A method reference that makes sure that opening the // event log for each repository works correctly - private EventHandler OpenEventLogDelegate (string path) + private EventHandler OpenFolderDelegate (string name) { return delegate { - SparkleShare.UI.AddEventLog (path); + SparkleShare.Controller.OpenSparkleShareFolder (name); }; } public void UpdateMenu () { - Menu.Remove (Menu.Children [0]); - - MenuItem status_menu_item = new MenuItem (StateText) { - Sensitive = false - }; - - Menu.Add (status_menu_item); - Menu.ReorderChild (status_menu_item, 0); - + ((Menu.Children [0] as MenuItem).Child as Label).Text = StateText; Menu.ShowAll (); } - + #if !HAVE_APP_INDICATOR // Makes the menu visible private void ShowMenu (object o, EventArgs args) { @@ -279,9 +325,9 @@ namespace SparkleShare { // Makes sure the menu pops up in the right position private void SetPosition (Menu menu, out int x, out int y, out bool push_in) { - PositionMenu (menu, out x, out y, out push_in, Handle); + StatusIcon.PositionMenu (menu, out x, out y, out push_in, this.status_icon.Handle); } - + #endif // The state when there's nothing going on private void SetNormalState () @@ -299,19 +345,32 @@ namespace SparkleShare { StateText = _("Welcome to SparkleShare!"); Application.Invoke (delegate { - Pixbuf = AnimationFrames [0]; + #if HAVE_APP_INDICATOR + this.indicator.IconName = "process-syncing-sparkleshare-i"; + #else + this.status_icon.Pixbuf = AnimationFrames [0]; + #endif }); + } else { if (error) { StateText = _("Not everything is synced"); Application.Invoke (delegate { - Pixbuf = SparkleUIHelpers.GetIcon ("sparkleshare-syncing-error", 24); + #if HAVE_APP_INDICATOR + this.indicator.IconName = "sparkleshare-syncing-error"; + #else + this.status_icon.Pixbuf = SparkleUIHelpers.GetIcon ("sparkleshare-syncing-error", 24); + #endif }); } else { StateText = _("Up to date") + " (" + SparkleShare.Controller.FolderSize + ")"; Application.Invoke (delegate { - Pixbuf = AnimationFrames [0]; + #if HAVE_APP_INDICATOR + this.indicator.IconName = "process-syncing-sparkleshare-i"; + #else + this.status_icon.Pixbuf = AnimationFrames [0]; + #endif }); } } diff --git a/SparkleShare/SparkleUI.cs b/SparkleShare/SparkleUI.cs index abc506f7..51dad981 100644 --- a/SparkleShare/SparkleUI.cs +++ b/SparkleShare/SparkleUI.cs @@ -33,7 +33,7 @@ namespace SparkleShare { public class SparkleUI { public static SparkleStatusIcon StatusIcon; - public static List OpenLogs; + public static SparkleEventLog EventLog; public static SparkleIntro Intro; @@ -52,9 +52,6 @@ namespace SparkleShare { // Create the statusicon StatusIcon = new SparkleStatusIcon (); - // Keep track of which event logs are open - OpenLogs = new List (); - if (SparkleShare.Controller.FirstRun) { Intro = new SparkleIntro (); Intro.ShowAccountForm (); @@ -75,10 +72,8 @@ namespace SparkleShare { SparkleShare.Controller.NotificationRaised += delegate (string user_name, string user_email, string message, string repository_path) { Application.Invoke (delegate { - foreach (SparkleLog log in OpenLogs) { - if (log.LocalPath.Equals (repository_path)) - log.UpdateEventLog (); - } + if (EventLog != null) + EventLog.UpdateEvents (); if (!SparkleShare.Controller.NotificationsEnabled) return; @@ -91,10 +86,6 @@ namespace SparkleShare { else bubble.Icon = SparkleUIHelpers.GetIcon ("avatar-default", 32); - bubble.AddAction ("", "Show Events", delegate { - AddEventLog (repository_path); - }); - bubble.Show (); }); }; @@ -111,38 +102,25 @@ namespace SparkleShare { SparkleShare.Controller.AvatarFetched += delegate { Application.Invoke (delegate { - foreach (SparkleLog log in OpenLogs) - log.UpdateEventLog (); + if (EventLog != null) + EventLog.UpdateEvents (); }); }; SparkleShare.Controller.OnIdle += delegate { Application.Invoke (delegate { - foreach (SparkleLog log in OpenLogs) - log.UpdateEventLog (); + if (EventLog != null) + EventLog.UpdateEvents (); }); }; - } - - public void AddEventLog (string path) - { - SparkleLog log = SparkleUI.OpenLogs.Find (delegate (SparkleLog l) { - return l.LocalPath.Equals (path); - }); - - // Check whether the log is already open, create a new one if - // that's not the case or present it to the user if it is - if (log == null) { - OpenLogs.Add (new SparkleLog (path)); - OpenLogs [OpenLogs.Count - 1].ShowAll (); - OpenLogs [OpenLogs.Count - 1].Present (); - } else { - log.ShowAll (); - log.Present (); - } - } - + SparkleShare.Controller.FolderListChanged += delegate { + Application.Invoke (delegate { + if (EventLog != null) + EventLog.UpdateChooser (); + }); + }; + } // Runs the application public void Run () diff --git a/build/build.environment.mk b/build/build.environment.mk index 806d0f13..a76b32ff 100644 --- a/build/build.environment.mk +++ b/build/build.environment.mk @@ -17,6 +17,7 @@ LINK_DBUS = $(NDESK_DBUS_LIBS) $(NDESK_DBUS_GLIB_LIBS) LINK_DBUS_NO_GLIB = $(NDESK_DBUS_LIBS) LINK_SMARTIRC4NET_SYSTEM = $(SMARTIRC4NET_LIBS) LINK_SMARTIRC4NET_LOCAL = -r:$(abs_top_builddir)/$(SMARTIRC4NET_ASSEMBLY) +LINK_APP_INDICATOR = $(APP_INDICATOR_LIBS) REF_NOTIFY_SHARP = $(LINK_SYSTEM) $(LINK_DBUS) $(GTKSHARP_LIBS) $(GLIBSHARP_LIBS) LINK_NOTIFY_SHARP = -r:$(DIR_BIN)/NotifySharp.dll @@ -30,7 +31,7 @@ REF_SPARKLELIB = $(LINK_SYSTEM) $(LINK_MONO_POSIX) LINK_SPARKLELIB = -r:$(DIR_BIN)/SparkleLib.dll LINK_SPARKLELIB_DEPS = $(REF_SPARKLELIB) $(LINK_SPARKLELIB) -REF_SPARKLESHARE = $(LINK_DBUS) $(LINK_GTK) $(LINK_SPARKLELIB_DEPS) +REF_SPARKLESHARE = $(LINK_DBUS) $(LINK_GTK) $(LINK_SPARKLELIB_DEPS) $(LINK_APP_INDICATOR) REF_SPARKLEDIFF = $(LINK_FRIENDFACE_DEPS) $(LINK_GTK) $(LINK_SPARKLELIB_DEPS) diff --git a/configure.ac b/configure.ac index b89c6aba..dc8a97c6 100644 --- a/configure.ac +++ b/configure.ac @@ -96,6 +96,30 @@ AC_SUBST(WEBKIT_SHARP_LIBS) SHAMROCK_CHECK_NUNIT +APPINDICATOR_REQUIRED=0.0.7 + +AC_ARG_ENABLE(appindicator, + AS_HELP_STRING([--enable-appindicator[=@<:@no/auto/yes@:>@]],[Build support for application indicators ]), + [enable_appindicator=$enableval], + [enable_appindicator="auto"]) + +if test x$enable_appindicator = xauto ; then + PKG_CHECK_EXISTS([appindicator-sharp-0.1 >= $APPINDICATOR_REQUIRED], + enable_appindicator="yes", + enable_appindicator="no") +fi + +if test x$enable_appindicator = xyes ; then + PKG_CHECK_EXISTS([appindicator-sharp-0.1 >= $APPINDICATOR_REQUIRED],, + AC_MSG_ERROR([appindicator-sharp-0.1 is not installed])) + PKG_CHECK_MODULES(APP_INDICATOR, + appindicator-sharp-0.1 >= $APPINDICATOR_REQUIRED) + AC_SUBST(APP_INDICATOR_CFLAGS) + AC_SUBST(APP_INDICATOR_LIBS) + AC_DEFINE(HAVE_APP_INDICATOR, 1, [Have AppIndicator]) +fi +AM_CONDITIONAL(HAVE_APP_INDICATOR, test x"$enable_appindicator" = xyes) + dnl Get nautilus extensions directory SPARKLESHARE_NAUTILUS_PYTHON diff --git a/data/actions.svg b/data/actions.svg new file mode 100644 index 00000000..c349857c --- /dev/null +++ b/data/actions.svg @@ -0,0 +1,1960 @@ + + + + + Text Editor + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Jakub Steiner + + + http://jimmac.musichall.cz + + Text Editor + + + text + editor + gedit + + + + + + + + + + + + + + + + + + + + + Lapo Calamandrei + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/html/event-entry.html b/data/html/event-entry.html index fbeed413..1ede4727 100644 --- a/data/html/event-entry.html +++ b/data/html/event-entry.html @@ -1,20 +1,16 @@
-
- -
-
-
-
- - +
+
+
+
+
+ +
+
- -
- +
-
-
diff --git a/data/html/event-log.html b/data/html/event-log.html index a595ea83..fc3f6252 100644 --- a/data/html/event-log.html +++ b/data/html/event-log.html @@ -4,7 +4,7 @@ SparkleShare Event Log diff --git a/data/icons/Makefile.am b/data/icons/Makefile.am index fd621831..79026af3 100644 --- a/data/icons/Makefile.am +++ b/data/icons/Makefile.am @@ -8,7 +8,12 @@ system_theme_icons = \ apps,folder-sparkleshare-24.png \ apps,folder-sparkleshare-256.png \ apps,folder-sparkleshare-32.png \ - apps,folder-sparkleshare-48.png + apps,folder-sparkleshare-48.png \ + status,process-syncing-sparkleshare-i-24.png \ + status,process-syncing-sparkleshare-ii-24.png \ + status,process-syncing-sparkleshare-iii-24.png \ + status,process-syncing-sparkleshare-iiii-24.png \ + status,process-syncing-sparkleshare-iiiii-24.png app_theme_icons = \ animations,process-syncing-sparkleshare-24.png \ @@ -25,10 +30,10 @@ app_theme_icons = \ status,avatar-default-24.png \ status,avatar-default-32.png \ status,avatar-default-48.png \ - status,document-added-16.png \ - status,document-edited-16.png \ - status,document-moved-16.png \ - status,document-removed-16.png \ + status,document-added-12.png \ + status,document-edited-12.png \ + status,document-deleted-12.png \ + status,document-moved-12.png \ status,dialog-error-16.png \ status,dialog-error-24.png diff --git a/data/icons/document-added-12.png b/data/icons/document-added-12.png new file mode 100644 index 00000000..409867db Binary files /dev/null and b/data/icons/document-added-12.png differ diff --git a/data/icons/document-added-16.png b/data/icons/document-added-16.png deleted file mode 100644 index 14fa8a36..00000000 Binary files a/data/icons/document-added-16.png and /dev/null differ diff --git a/data/icons/document-deleted-12.png b/data/icons/document-deleted-12.png new file mode 100644 index 00000000..f5585742 Binary files /dev/null and b/data/icons/document-deleted-12.png differ diff --git a/data/icons/document-edited-12.png b/data/icons/document-edited-12.png new file mode 100644 index 00000000..57b50968 Binary files /dev/null and b/data/icons/document-edited-12.png differ diff --git a/data/icons/document-edited-16.png b/data/icons/document-edited-16.png deleted file mode 100644 index e0a9f978..00000000 Binary files a/data/icons/document-edited-16.png and /dev/null differ diff --git a/data/icons/document-moved-12.png b/data/icons/document-moved-12.png new file mode 100644 index 00000000..50f4fb32 Binary files /dev/null and b/data/icons/document-moved-12.png differ diff --git a/data/icons/document-moved-16.png b/data/icons/document-moved-16.png deleted file mode 100644 index 834f6a72..00000000 Binary files a/data/icons/document-moved-16.png and /dev/null differ diff --git a/data/icons/document-removed-16.png b/data/icons/document-removed-16.png deleted file mode 100644 index 4695e878..00000000 Binary files a/data/icons/document-removed-16.png and /dev/null differ diff --git a/data/icons/process-syncing-sparkleshare-i-24.png b/data/icons/process-syncing-sparkleshare-i-24.png new file mode 100644 index 00000000..286e79d6 Binary files /dev/null and b/data/icons/process-syncing-sparkleshare-i-24.png differ diff --git a/data/icons/process-syncing-sparkleshare-ii-24.png b/data/icons/process-syncing-sparkleshare-ii-24.png new file mode 100644 index 00000000..83dd2cf2 Binary files /dev/null and b/data/icons/process-syncing-sparkleshare-ii-24.png differ diff --git a/data/icons/process-syncing-sparkleshare-iii-24.png b/data/icons/process-syncing-sparkleshare-iii-24.png new file mode 100644 index 00000000..6f6b9e58 Binary files /dev/null and b/data/icons/process-syncing-sparkleshare-iii-24.png differ diff --git a/data/icons/process-syncing-sparkleshare-iiii-24.png b/data/icons/process-syncing-sparkleshare-iiii-24.png new file mode 100644 index 00000000..31f9a53e Binary files /dev/null and b/data/icons/process-syncing-sparkleshare-iiii-24.png differ diff --git a/data/icons/process-syncing-sparkleshare-iiiii-24.png b/data/icons/process-syncing-sparkleshare-iiiii-24.png new file mode 100644 index 00000000..a0e2476a Binary files /dev/null and b/data/icons/process-syncing-sparkleshare-iiiii-24.png differ diff --git a/po/de.po b/po/de.po index 92d0590e..3d849b10 100644 --- a/po/de.po +++ b/po/de.po @@ -69,15 +69,15 @@ msgstr "Über SparkleShare" #: ../SparkleShare/SparkleAbout.cs:54 msgid "A newer version is available" -msgstr "" +msgstr "Eine neuere Version ist verfügbar" #: ../SparkleShare/SparkleAbout.cs:61 msgid "You are running the latest version." -msgstr "" +msgstr "Sie verwenden die aktuelle Version." #: ../SparkleShare/SparkleAbout.cs:88 msgid "Checking for updates..." -msgstr "" +msgstr "Suche Aktualisierungen..." #: ../SparkleShare/SparkleAbout.cs:117 msgid "_Show Credits" @@ -103,7 +103,7 @@ msgstr "‘{0}’ hinzugefügt" #: ../SparkleShare/SparkleController.cs:613 #, csharp-format msgid "moved ‘{0}’" -msgstr "" +msgstr "‘{0}’ verschoben" #: ../SparkleShare/SparkleController.cs:618 #, csharp-format @@ -132,7 +132,7 @@ msgid "" "bits of information from you." msgstr "" "Bevor wir einen SparkleShare-Ordner auf diesem Computer einrichten können, " -"benötigen wir ein paar Informationen von Ihnen." +"benötigen wir einige Informationen von Ihnen." #: ../SparkleShare/SparkleIntro.cs:81 msgid "Full Name:" @@ -178,7 +178,7 @@ msgstr "Das GNOME Project" #: ../SparkleShare/SparkleIntro.cs:195 msgid "GNOME is an easy to understand interface to your computer." -msgstr "GNOME ist ein einfach verständliches Interface zu Ihrem Computer" +msgstr "GNOME ist ein einfach verständliches Interface für Ihrem Computer" #: ../SparkleShare/SparkleIntro.cs:196 msgid "Select this option if you’re a developer or designer working on GNOME." @@ -356,7 +356,7 @@ msgstr "Diesen Hilfetext anzeigen" #: ../SparkleShare/SparkleShare.cs:114 msgid "SparkleShare, a collaboration and sharing tool." -msgstr "" +msgstr "SparkleShare, ein Werkzeug für verteilte Zusammenarbeit." #: ../SparkleShare/SparkleShare.cs:115 msgid "Copyright (C) 2010 Hylke Bons" @@ -434,6 +434,6 @@ msgstr "" #: ../SparkleShare/SparkleWindow.cs:41 msgid "SparkleShare Setup" -msgstr "" +msgstr "SparkleShare Konfiguration"