From 66b7f0c58dd5fdd0f4f919feaa3a5b9d2657d01a Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Sun, 3 Jul 2011 18:43:22 +0100 Subject: [PATCH 01/16] Add a event log controller --- SparkleShare/Mac/SparkleEventLog.cs | 152 ++++++++---------- SparkleShare/Mac/SparkleEventLogController.cs | 109 +++++++++++++ SparkleShare/Mac/SparkleShare.csproj | 3 +- SparkleShare/Mac/SparkleUI.cs | 25 --- SparkleShare/SparkleController.cs | 1 - 5 files changed, 174 insertions(+), 116 deletions(-) create mode 100644 SparkleShare/Mac/SparkleEventLogController.cs diff --git a/SparkleShare/Mac/SparkleEventLog.cs b/SparkleShare/Mac/SparkleEventLog.cs index 9c8bd92a..074938dd 100644 --- a/SparkleShare/Mac/SparkleEventLog.cs +++ b/SparkleShare/Mac/SparkleEventLog.cs @@ -33,13 +33,12 @@ namespace SparkleShare { public class SparkleEventLog : NSWindow { + private SparkleEventLogController controller; + 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) { } @@ -61,16 +60,7 @@ namespace SparkleShare { HasShadow = true; BackingType = NSBackingStore.Buffered; - CreateEvents (); - UpdateEvents (false); - UpdateChooser (); - OrderFrontRegardless (); - } - - - private void CreateEvents () - { Separator = new NSBox (new RectangleF (0, 579, 480, 1)) { BorderColor = NSColor.LightGray, BoxType = NSBoxType.NSBoxCustom @@ -78,21 +68,42 @@ namespace SparkleShare { ContentView.AddSubview (Separator); + WebView = new WebView (new RectangleF (0, 0, 480, 579), "", "") { 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 (); + + UpdateContent (null, false); + UpdateChooser (this.controller.Folders); + OrderFrontRegardless (); + + // Hook up the controller events + this.controller = new SparkleEventLogController (); + + this.controller.UpdateChooserEvent += delegate (string [] folders) { + InvokeOnMainThread (delegate { + UpdateChooser (folders); + }); + }; + + this.controller.UpdateContentEvent += delegate (string html, bool silently) { + InvokeOnMainThread (delegate { + UpdateContent (html, true); + }); + }; } - public void UpdateChooser () + + + public void UpdateChooser (string [] folders) { if (this.popup_button != null) this.popup_button.RemoveFromSuperview (); @@ -104,108 +115,71 @@ namespace SparkleShare { this.popup_button.Cell.ControlSize = NSControlSize.Small; this.popup_button.Font = NSFontManager.SharedFontManager.FontWithFamily - ("Lucida Grande", NSFontTraitMask.Condensed, 0, NSFont.SmallSystemFontSize); + ("Lucida Grande", NSFontTraitMask.Condensed, 0, NSFont.SmallSystemFontSize); this.popup_button.AddItem ("All Folders"); this.popup_button.Menu.AddItem (NSMenuItem.SeparatorItem); - this.popup_button.AddItems (SparkleShare.Controller.Folders.ToArray ()); - - if (this.selected_log != null && - !SparkleShare.Controller.Folders.Contains (this.selected_log)) { - - this.selected_log = null; - } + this.popup_button.AddItems (folders); this.popup_button.Activated += delegate { if (this.popup_button.IndexOfSelectedItem == 0) - this.selected_log = null; + this.controller.SelectedFolder = null; else - this.selected_log = this.popup_button.SelectedItem.Title; - - UpdateEvents (false); + this.controller.SelectedFolder = this.popup_button.SelectedItem.Title; }; ContentView.AddSubview (this.popup_button); } - public void UpdateEvents () + public void UpdateContent (string html, bool silently) { - UpdateEvents (true); - } + if (!silently) { + if (WebView.Superview == ContentView) + WebView.RemoveFromSuperview (); - - public void UpdateEvents (bool silent) - { - if (!silent) { - InvokeOnMainThread (delegate { - if (WebView.Superview == ContentView) - WebView.RemoveFromSuperview (); - - ContentView.AddSubview (ProgressIndicator); - }); + 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 (); + if (html == null) + html = this.controller.HTML; - // A short delay is less annoying than - // a flashing window - if (watch.ElapsedMilliseconds < 500 && !silent) - Thread.Sleep (500 - (int) watch.ElapsedMilliseconds); + 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")); - AddHTML (); + InvokeOnMainThread (delegate { + if (ProgressIndicator.Superview == ContentView) + ProgressIndicator.RemoveFromSuperview (); + + WebView.MainFrame.LoadHtmlString (html, new NSUrl ("")); + ContentView.AddSubview (WebView); + Update (); + }); } })); 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) diff --git a/SparkleShare/Mac/SparkleEventLogController.cs b/SparkleShare/Mac/SparkleEventLogController.cs new file mode 100644 index 00000000..1dd9be43 --- /dev/null +++ b/SparkleShare/Mac/SparkleEventLogController.cs @@ -0,0 +1,109 @@ +// 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.Threading; + +using SparkleLib; + +namespace SparkleShare { + + public class SparkleEventLogController { + + public event UpdateContentEventEventHandler UpdateContentEvent; + public delegate void UpdateContentEventEventHandler (string html, bool silently); + + public event UpdateChooserEventHandler UpdateChooserEvent; + public delegate void UpdateChooserEventHandler (string [] folders); + + public string SelectedFolder { + get { + return this.selected_folder; + } + + set { + this.selected_folder = value; + + if (UpdateContentEvent != null) + UpdateContentEvent (HTML, true); + } + } + + public string HTML { + get { + Stopwatch watch = new Stopwatch (); + watch.Start (); + + List change_sets = SparkleShare.Controller.GetLog (SelectedFolder); + string html = SparkleShare.Controller.GetHTMLLog (change_sets); + + watch.Stop (); + + // A short delay is less annoying than + // a flashing window + if (watch.ElapsedMilliseconds < 500 /* && !silent */) + Thread.Sleep (500 - (int) watch.ElapsedMilliseconds); + + return html; + } + } + + public string [] Folders { + get { + return SparkleShare.Controller.Folders.ToArray (); + } + } + + + private string selected_folder; + + + public SparkleEventLogController () + { + SparkleShare.Controller.AvatarFetched += delegate { + if (UpdateContentEvent != null) + UpdateContentEvent (HTML, true); + }; + + SparkleShare.Controller.OnIdle += delegate { + if (UpdateContentEvent != null) + UpdateContentEvent (HTML, true); + }; + + SparkleShare.Controller.FolderListChanged += delegate { + if (this.selected_folder != null && + !SparkleShare.Controller.Folders.Contains (this.selected_folder)) { + + this.selected_folder = null; + } + + if (UpdateChooserEvent != null) + UpdateChooserEvent (Folders); + + if (UpdateContentEvent != null) + UpdateContentEvent (HTML, true); + }; + + SparkleShare.Controller.NotificationRaised += delegate { + if (UpdateContentEvent != null) + UpdateContentEvent (HTML, true); + }; + } + } +} diff --git a/SparkleShare/Mac/SparkleShare.csproj b/SparkleShare/Mac/SparkleShare.csproj index d5fc5b16..4da38ecf 100644 --- a/SparkleShare/Mac/SparkleShare.csproj +++ b/SparkleShare/Mac/SparkleShare.csproj @@ -62,7 +62,7 @@ False ..\..\bin\Meebey.SmartIrc4net.dll - + False ..\..\bin\SparkleLib.dll @@ -91,6 +91,7 @@ + diff --git a/SparkleShare/Mac/SparkleUI.cs b/SparkleShare/Mac/SparkleUI.cs index d4e24aeb..efd46f7f 100644 --- a/SparkleShare/Mac/SparkleUI.cs +++ b/SparkleShare/Mac/SparkleUI.cs @@ -96,8 +96,6 @@ namespace SparkleShare { SparkleShare.Controller.NotificationRaised += delegate (string user_name, string user_email, string message, string repository_path) { InvokeOnMainThread (delegate { - if (EventLog != null) - EventLog.UpdateEvents (); if (SparkleShare.Controller.NotificationsEnabled) { if (NSApplication.SharedApplication.DockTile.BadgeLabel == null) @@ -130,30 +128,7 @@ namespace SparkleShare { }; - SparkleShare.Controller.AvatarFetched += delegate { - InvokeOnMainThread (delegate { - if (EventLog != null) - EventLog.UpdateEvents (); - }); - }; - - SparkleShare.Controller.OnIdle += delegate { - InvokeOnMainThread (delegate { - if (EventLog != null) - EventLog.UpdateEvents (); - }); - }; - - - SparkleShare.Controller.FolderListChanged += delegate { - InvokeOnMainThread (delegate { - if (EventLog != null) { - EventLog.UpdateChooser (); - EventLog.UpdateEvents (); - } - }); - }; if (SparkleShare.Controller.FirstRun) { diff --git a/SparkleShare/SparkleController.cs b/SparkleShare/SparkleController.cs index 1ed7642e..1c4377b2 100644 --- a/SparkleShare/SparkleController.cs +++ b/SparkleShare/SparkleController.cs @@ -1006,7 +1006,6 @@ namespace SparkleShare { fetcher.Failed += delegate { - if (FolderFetchError != null) FolderFetchError (); From 8980daa265531d848d3a56d4c7900b58775fec5c Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Mon, 4 Jul 2011 02:54:43 +0100 Subject: [PATCH 02/16] Make event log work with its controller --- SparkleShare/Mac/SparkleEventLog.cs | 94 +++++++++---------- SparkleShare/Mac/SparkleEventLogController.cs | 54 ++++++----- 2 files changed, 79 insertions(+), 69 deletions(-) diff --git a/SparkleShare/Mac/SparkleEventLog.cs b/SparkleShare/Mac/SparkleEventLog.cs index 074938dd..6a2dc440 100644 --- a/SparkleShare/Mac/SparkleEventLog.cs +++ b/SparkleShare/Mac/SparkleEventLog.cs @@ -16,8 +16,6 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; using System.Drawing; using System.IO; using System.Text.RegularExpressions; @@ -27,18 +25,24 @@ 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 SparkleEventLogController controller; + private SparkleEventLogController controller = new SparkleEventLogController (); + + private WebView web_view = new WebView (new RectangleF (0, 0, 480, 579), "", "") { + PolicyDelegate = new SparkleWebPolicyDelegate () + }; + + private NSBox Separator = new NSBox (new RectangleF (0, 579, 480, 1)) { + BorderColor = NSColor.LightGray, + BoxType = NSBoxType.NSBoxCustom + }; - private WebView WebView; - private NSBox Separator; private NSPopUpButton popup_button; - private NSProgressIndicator ProgressIndicator; + private NSProgressIndicator progress_indicator; public SparkleEventLog (IntPtr handle) : base (handle) { } @@ -60,51 +64,52 @@ namespace SparkleShare { HasShadow = true; BackingType = NSBackingStore.Buffered; - - Separator = new NSBox (new RectangleF (0, 579, 480, 1)) { - BorderColor = NSColor.LightGray, - BoxType = NSBoxType.NSBoxCustom - }; - ContentView.AddSubview (Separator); - WebView = new WebView (new RectangleF (0, 0, 480, 579), "", "") { - PolicyDelegate = new SparkleWebPolicyDelegate () + this.progress_indicator = new NSthis.progress_indicator () { + Style = NSthis.progress_indicatorStyle.Spinning, + Frame = new RectangleF (this.web_view.Frame.Width / 2 - 10, this.web_view.Frame.Height / 2 + 10, 20, 20) }; - - ProgressIndicator = new NSProgressIndicator () { - Style = NSProgressIndicatorStyle.Spinning, - Frame = new RectangleF (WebView.Frame.Width / 2 - 10, WebView.Frame.Height / 2 + 10, 20, 20) - }; + this.progress_indicator.StartAnimation (this); + ContentView.AddSubview (this.progress_indicator); - UpdateContent (null, false); - UpdateChooser (this.controller.Folders); + UpdateContent (null); + UpdateChooser (null); OrderFrontRegardless (); - // Hook up the controller events - this.controller = new SparkleEventLogController (); + // Hook up the controller events this.controller.UpdateChooserEvent += delegate (string [] folders) { InvokeOnMainThread (delegate { UpdateChooser (folders); }); }; - this.controller.UpdateContentEvent += delegate (string html, bool silently) { + this.controller.UpdateContentEvent += delegate (string html) { InvokeOnMainThread (delegate { - UpdateContent (html, true); + UpdateContent (html); + }); + }; + + this.controller.ContentLoadingEvent += delegate { + InvokeOnMainThread (delegate { + if (this.web_view.Superview == ContentView) + this.web_view.RemoveFromSuperview (); + + ContentView.AddSubview (this.progress_indicator); }); }; } - - public void UpdateChooser (string [] folders) { + if (folders == null) + folders = this.controller.Folders; + if (this.popup_button != null) this.popup_button.RemoveFromSuperview (); @@ -132,20 +137,13 @@ namespace SparkleShare { } - public void UpdateContent (string html, bool silently) + public void UpdateContent (string html) { - if (!silently) { - if (WebView.Superview == ContentView) - WebView.RemoveFromSuperview (); - - ContentView.AddSubview (ProgressIndicator); - } - - Thread thread = new Thread (new ThreadStart (delegate { - using (NSAutoreleasePool pool = new NSAutoreleasePool ()) { + using (NSAutoreleasePool pool = new NSAutoreleasePool ()) { + Thread thread = new Thread (new ThreadStart (delegate { if (html == null) html = this.controller.HTML; - + html = html.Replace ("", "Lucida Grande"); html = html.Replace ("", "13.6px"); html = html.Replace ("", "13.4px"); @@ -164,22 +162,24 @@ namespace SparkleShare { "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")); - + InvokeOnMainThread (delegate { - if (ProgressIndicator.Superview == ContentView) - ProgressIndicator.RemoveFromSuperview (); + if (this.progress_indicator.Superview == ContentView) + this.progress_indicator.RemoveFromSuperview (); + + this.web_view.MainFrame.LoadHtmlString (html, new NSUrl ("")); + ContentView.AddSubview (this.web_view); - WebView.MainFrame.LoadHtmlString (html, new NSUrl ("")); - ContentView.AddSubview (WebView); Update (); }); - } - })); + })); - thread.Start (); + thread.Start (); + } } } + public class SparkleEventsDelegate : NSWindowDelegate { public override bool WindowShouldClose (NSObject sender) diff --git a/SparkleShare/Mac/SparkleEventLogController.cs b/SparkleShare/Mac/SparkleEventLogController.cs index 1dd9be43..fdc99f3d 100644 --- a/SparkleShare/Mac/SparkleEventLogController.cs +++ b/SparkleShare/Mac/SparkleEventLogController.cs @@ -27,11 +27,15 @@ namespace SparkleShare { public class SparkleEventLogController { public event UpdateContentEventEventHandler UpdateContentEvent; - public delegate void UpdateContentEventEventHandler (string html, bool silently); - + public delegate void UpdateContentEventEventHandler (string html); + public event UpdateChooserEventHandler UpdateChooserEvent; public delegate void UpdateChooserEventHandler (string [] folders); + public event ContentLoadingEventHandler ContentLoadingEvent; + public delegate void ContentLoadingEventHandler (); + + public string SelectedFolder { get { return this.selected_folder; @@ -40,27 +44,33 @@ namespace SparkleShare { set { this.selected_folder = value; - if (UpdateContentEvent != null) - UpdateContentEvent (HTML, true); + if (ContentLoadingEvent != null) + ContentLoadingEvent (); + + Stopwatch watch = new Stopwatch (); + watch.Start (); + + Thread thread = new Thread (new ThreadStart (delegate { + string html = HTML; + watch.Stop (); + + // A short delay is less annoying than + // a flashing window + if (watch.ElapsedMilliseconds < 500) + Thread.Sleep (500 - (int) watch.ElapsedMilliseconds); + + if (UpdateContentEvent != null) + UpdateContentEvent (html); + })); + + thread.Start (); } } public string HTML { get { - Stopwatch watch = new Stopwatch (); - watch.Start (); - - List change_sets = SparkleShare.Controller.GetLog (SelectedFolder); - string html = SparkleShare.Controller.GetHTMLLog (change_sets); - - watch.Stop (); - - // A short delay is less annoying than - // a flashing window - if (watch.ElapsedMilliseconds < 500 /* && !silent */) - Thread.Sleep (500 - (int) watch.ElapsedMilliseconds); - - return html; + List change_sets = SparkleShare.Controller.GetLog (this.selected_folder); + return SparkleShare.Controller.GetHTMLLog (change_sets); } } @@ -78,12 +88,12 @@ namespace SparkleShare { { SparkleShare.Controller.AvatarFetched += delegate { if (UpdateContentEvent != null) - UpdateContentEvent (HTML, true); + UpdateContentEvent (HTML); }; SparkleShare.Controller.OnIdle += delegate { if (UpdateContentEvent != null) - UpdateContentEvent (HTML, true); + UpdateContentEvent (HTML); }; SparkleShare.Controller.FolderListChanged += delegate { @@ -97,12 +107,12 @@ namespace SparkleShare { UpdateChooserEvent (Folders); if (UpdateContentEvent != null) - UpdateContentEvent (HTML, true); + UpdateContentEvent (HTML); }; SparkleShare.Controller.NotificationRaised += delegate { if (UpdateContentEvent != null) - UpdateContentEvent (HTML, true); + UpdateContentEvent (HTML); }; } } From 1d8867ce4478a96bcd7335efae73e7d853f3fc47 Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Mon, 4 Jul 2011 03:37:35 +0100 Subject: [PATCH 03/16] Add controller for bubbles --- SparkleShare/Mac/SparkleBubble.cs | 58 --------------------- SparkleShare/Mac/SparkleBubbleController.cs | 44 ++++++++++++++++ SparkleShare/Mac/SparkleBubbles.cs | 56 ++++++++++++++++++++ SparkleShare/Mac/SparkleEventLog.cs | 8 +-- SparkleShare/Mac/SparkleShare.csproj | 3 +- SparkleShare/Mac/SparkleUI.cs | 18 +------ 6 files changed, 107 insertions(+), 80 deletions(-) delete mode 100644 SparkleShare/Mac/SparkleBubble.cs create mode 100644 SparkleShare/Mac/SparkleBubbleController.cs create mode 100644 SparkleShare/Mac/SparkleBubbles.cs diff --git a/SparkleShare/Mac/SparkleBubble.cs b/SparkleShare/Mac/SparkleBubble.cs deleted file mode 100644 index 05523f1f..00000000 --- a/SparkleShare/Mac/SparkleBubble.cs +++ /dev/null @@ -1,58 +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.IO; - -using MonoMac.AppKit; -using MonoMac.Foundation; -using MonoMac.Growl; - -namespace SparkleShare { - - public class SparkleBubble : NSObject { - - public string ImagePath; - - private string title; - private string subtext; - - - public SparkleBubble (string title, string subtext) - { - this.title = title; - this.subtext = subtext; - } - - - public void Show () - { - InvokeOnMainThread (delegate { - if (ImagePath != null && File.Exists (ImagePath)) { - NSData image_data = NSData.FromFile (ImagePath); - - GrowlApplicationBridge.Notify (this.title, this.subtext, - "Start", image_data, 0, false, null); - - } else { - GrowlApplicationBridge.Notify (this.title, this.subtext, - "Start", null, 0, false, null); - } - }); - } - } -} diff --git a/SparkleShare/Mac/SparkleBubbleController.cs b/SparkleShare/Mac/SparkleBubbleController.cs new file mode 100644 index 00000000..811b8154 --- /dev/null +++ b/SparkleShare/Mac/SparkleBubbleController.cs @@ -0,0 +1,44 @@ +// 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; + +namespace SparkleShare { + + public class SparkleBubblesController { + + public event ShowBubbleEventHandler ShowBubbleEvent; + public delegate void ShowBubbleEventHandler (string title, string subtext, string image_path); + + + public SparkleBubblesController () + { + SparkleShare.Controller.ConflictNotificationRaised += delegate { + if (ShowBubbleEvent != null) + ShowBubbleEvent ("Ouch! Mid-air collision!", + "Don't worry, SparkleShare made a copy of each conflicting file.", null); + }; + + SparkleShare.Controller.NotificationRaised += delegate (string user_name, string user_email, + string message, string folder_path) { + if (ShowBubbleEvent != null) + ShowBubbleEvent (user_name, message, + SparkleShare.Controller.GetAvatar (user_email, 36)); + }; + } + } +} diff --git a/SparkleShare/Mac/SparkleBubbles.cs b/SparkleShare/Mac/SparkleBubbles.cs new file mode 100644 index 00000000..7581b537 --- /dev/null +++ b/SparkleShare/Mac/SparkleBubbles.cs @@ -0,0 +1,56 @@ +// 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.IO; + +using MonoMac.AppKit; +using MonoMac.Foundation; +using MonoMac.Growl; + +namespace SparkleShare { + + public class SparkleBubbles : NSObject { + + private SparkleBubblesController controller = new SparkleBubblesController (); + + + public SparkleBubbles () + { + this.controller.ShowBubbleEvent += delegate(string title, string subtext, string image_path) { + InvokeOnMainThread (delegate { + if (!GrowlApplicationBridge.IsGrowlRunning ()) { + NSApplication.SharedApplication.RequestUserAttention ( + NSRequestUserAttentionType.InformationalRequest); + + return; + } + + if (image_path != null && File.Exists (image_path)) { + NSData image_data = NSData.FromFile (image_path); + GrowlApplicationBridge.Notify (title, subtext, + "Start", image_data, 0, false, null); + + } else { + GrowlApplicationBridge.Notify (title, subtext, + "Start", null, 0, false, null); + } + }); + }; + } + } +} diff --git a/SparkleShare/Mac/SparkleEventLog.cs b/SparkleShare/Mac/SparkleEventLog.cs index 6a2dc440..88df1f37 100644 --- a/SparkleShare/Mac/SparkleEventLog.cs +++ b/SparkleShare/Mac/SparkleEventLog.cs @@ -47,6 +47,7 @@ namespace SparkleShare { public SparkleEventLog (IntPtr handle) : base (handle) { } + // TODO: Window needs to be made resizable public SparkleEventLog () : base () { Title = "Recent Events"; @@ -67,8 +68,8 @@ namespace SparkleShare { ContentView.AddSubview (Separator); - this.progress_indicator = new NSthis.progress_indicator () { - Style = NSthis.progress_indicatorStyle.Spinning, + this.progress_indicator = new NSProgressIndicator () { + Style = NSProgressIndicatorStyle.Spinning, Frame = new RectangleF (this.web_view.Frame.Width / 2 - 10, this.web_view.Frame.Height / 2 + 10, 20, 20) }; @@ -167,10 +168,9 @@ namespace SparkleShare { if (this.progress_indicator.Superview == ContentView) this.progress_indicator.RemoveFromSuperview (); + // TODO: still causes some flashes this.web_view.MainFrame.LoadHtmlString (html, new NSUrl ("")); ContentView.AddSubview (this.web_view); - - Update (); }); })); diff --git a/SparkleShare/Mac/SparkleShare.csproj b/SparkleShare/Mac/SparkleShare.csproj index 4da38ecf..f8d67e36 100644 --- a/SparkleShare/Mac/SparkleShare.csproj +++ b/SparkleShare/Mac/SparkleShare.csproj @@ -87,11 +87,12 @@ - + + diff --git a/SparkleShare/Mac/SparkleUI.cs b/SparkleShare/Mac/SparkleUI.cs index efd46f7f..e96a246f 100644 --- a/SparkleShare/Mac/SparkleUI.cs +++ b/SparkleShare/Mac/SparkleUI.cs @@ -104,28 +104,12 @@ namespace SparkleShare { NSApplication.SharedApplication.DockTile.BadgeLabel = (int.Parse (NSApplication.SharedApplication.DockTile.BadgeLabel) + 1).ToString (); - if (GrowlApplicationBridge.IsGrowlRunning ()) { - SparkleBubble bubble = new SparkleBubble (user_name, message) { - ImagePath = SparkleShare.Controller.GetAvatar (user_email, 36) - }; - - bubble.Show (); - - } else { - NSApplication.SharedApplication.RequestUserAttention ( - NSRequestUserAttentionType.InformationalRequest); - } - } + } }); }; - SparkleShare.Controller.ConflictNotificationRaised += delegate { - string title = "Ouch! Mid-air collision!"; - string subtext = "Don't worry, SparkleShare made a copy of each conflicting file."; - new SparkleBubble (title, subtext).Show (); - }; From c3db3a3b4eabc57444f410718198b3074fa23ba8 Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Mon, 4 Jul 2011 04:07:07 +0100 Subject: [PATCH 04/16] Move UI events to controllers --- SparkleShare/Mac/Growl.plist | 24 +++---- SparkleShare/Mac/SparkleBubbles.cs | 14 +++- ...troller.cs => SparkleBubblesController.cs} | 5 +- SparkleShare/Mac/SparkleShare.csproj | 2 +- SparkleShare/Mac/SparkleUI.cs | 65 +++++++------------ 5 files changed, 47 insertions(+), 63 deletions(-) rename SparkleShare/Mac/{SparkleBubbleController.cs => SparkleBubblesController.cs} (90%) diff --git a/SparkleShare/Mac/Growl.plist b/SparkleShare/Mac/Growl.plist index cb8e7130..af7016e0 100644 --- a/SparkleShare/Mac/Growl.plist +++ b/SparkleShare/Mac/Growl.plist @@ -2,19 +2,15 @@ - TicketVersion - 1 - AllNotifications - - Start - Stop - Info - - DefaultNotifications - - Start - Stop - Info - + TicketVersion + 1 + AllNotifications + + Event + + DefaultNotifications + + Event + diff --git a/SparkleShare/Mac/SparkleBubbles.cs b/SparkleShare/Mac/SparkleBubbles.cs index 7581b537..95487186 100644 --- a/SparkleShare/Mac/SparkleBubbles.cs +++ b/SparkleShare/Mac/SparkleBubbles.cs @@ -31,7 +31,7 @@ namespace SparkleShare { public SparkleBubbles () { - this.controller.ShowBubbleEvent += delegate(string title, string subtext, string image_path) { + this.controller.ShowBubbleEvent += delegate (string title, string subtext, string image_path) { InvokeOnMainThread (delegate { if (!GrowlApplicationBridge.IsGrowlRunning ()) { NSApplication.SharedApplication.RequestUserAttention ( @@ -40,14 +40,22 @@ namespace SparkleShare { return; } + if (NSApplication.SharedApplication.DockTile.BadgeLabel == null) { + NSApplication.SharedApplication.DockTile.BadgeLabel = "1"; + + } else { + int events = int.Parse (NSApplication.SharedApplication.DockTile.BadgeLabel); + NSApplication.SharedApplication.DockTile.BadgeLabel = (events + 1).ToString (); + } + if (image_path != null && File.Exists (image_path)) { NSData image_data = NSData.FromFile (image_path); GrowlApplicationBridge.Notify (title, subtext, - "Start", image_data, 0, false, null); + "Event", image_data, 0, false, null); } else { GrowlApplicationBridge.Notify (title, subtext, - "Start", null, 0, false, null); + "Event", null, 0, false, null); } }); }; diff --git a/SparkleShare/Mac/SparkleBubbleController.cs b/SparkleShare/Mac/SparkleBubblesController.cs similarity index 90% rename from SparkleShare/Mac/SparkleBubbleController.cs rename to SparkleShare/Mac/SparkleBubblesController.cs index 811b8154..56ffd3a9 100644 --- a/SparkleShare/Mac/SparkleBubbleController.cs +++ b/SparkleShare/Mac/SparkleBubblesController.cs @@ -28,14 +28,15 @@ namespace SparkleShare { public SparkleBubblesController () { SparkleShare.Controller.ConflictNotificationRaised += delegate { - if (ShowBubbleEvent != null) + if (ShowBubbleEvent != null && SparkleShare.Controller.NotificationsEnabled) ShowBubbleEvent ("Ouch! Mid-air collision!", "Don't worry, SparkleShare made a copy of each conflicting file.", null); }; SparkleShare.Controller.NotificationRaised += delegate (string user_name, string user_email, string message, string folder_path) { - if (ShowBubbleEvent != null) + + if (ShowBubbleEvent != null && SparkleShare.Controller.NotificationsEnabled) ShowBubbleEvent (user_name, message, SparkleShare.Controller.GetAvatar (user_email, 36)); }; diff --git a/SparkleShare/Mac/SparkleShare.csproj b/SparkleShare/Mac/SparkleShare.csproj index f8d67e36..8c3a898c 100644 --- a/SparkleShare/Mac/SparkleShare.csproj +++ b/SparkleShare/Mac/SparkleShare.csproj @@ -91,8 +91,8 @@ - + diff --git a/SparkleShare/Mac/SparkleUI.cs b/SparkleShare/Mac/SparkleUI.cs index e96a246f..1ac366ef 100644 --- a/SparkleShare/Mac/SparkleUI.cs +++ b/SparkleShare/Mac/SparkleUI.cs @@ -29,25 +29,12 @@ using MonoMac.Growl; namespace SparkleShare { - public partial class AppDelegate : NSApplicationDelegate { - - public override void WillBecomeActive (NSNotification notification) - { - NSApplication.SharedApplication.DockTile.BadgeLabel = null; - } - - public override void WillTerminate (NSNotification notification) - { - SparkleShare.Controller.Quit (); - } - } - - public class SparkleUI : AppDelegate { public static SparkleStatusIcon StatusIcon; public static SparkleEventLog EventLog; public static SparkleIntro Intro; + public static SparkleBubbles Bubbles; public static SparkleAbout About; public static NSFont Font; @@ -69,7 +56,6 @@ namespace SparkleShare { // Use translations Catalog.Init ("sparkleshare", Path.Combine (NSBundle.MainBundle.ResourcePath, "Translations")); - using (NSAutoreleasePool pool = new NSAutoreleasePool ()) { @@ -79,45 +65,24 @@ namespace SparkleShare { NSApplication.SharedApplication.ApplicationIconImage = NSImage.ImageNamed ("sparkleshare.icns"); - SetFolderIcon (); - if (!SparkleShare.Controller.BackendIsPresent) { this.alert = new SparkleAlert (); this.alert.RunModal (); return; } + + SetFolderIcon (); Font = NSFontManager.SharedFontManager.FontWithFamily ("Lucida Grande", NSFontTraitMask.Condensed, 0, 13); StatusIcon = new SparkleStatusIcon (); - } + Bubbles = new SparkleBubbles (); - SparkleShare.Controller.NotificationRaised += delegate (string user_name, string user_email, - string message, string repository_path) { - InvokeOnMainThread (delegate { - - if (SparkleShare.Controller.NotificationsEnabled) { - if (NSApplication.SharedApplication.DockTile.BadgeLabel == null) - NSApplication.SharedApplication.DockTile.BadgeLabel = "1"; - else - NSApplication.SharedApplication.DockTile.BadgeLabel = - (int.Parse (NSApplication.SharedApplication.DockTile.BadgeLabel) + 1).ToString (); - - } - }); - }; - - - - - - - - - if (SparkleShare.Controller.FirstRun) { - Intro = new SparkleIntro (); - Intro.ShowAccountForm (); + if (SparkleShare.Controller.FirstRun) { + Intro = new SparkleIntro (); + Intro.ShowAccountForm (); + } } } @@ -147,4 +112,18 @@ namespace SparkleShare { return NSDictionary.FromFile (path); } } + + + public partial class AppDelegate : NSApplicationDelegate { + + public override void WillBecomeActive (NSNotification notification) + { + NSApplication.SharedApplication.DockTile.BadgeLabel = null; + } + + public override void WillTerminate (NSNotification notification) + { + SparkleShare.Controller.Quit (); + } + } } From 6f494ba384fabadbe5a03b741218c0ad5372db9f Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Mon, 4 Jul 2011 18:52:46 +0100 Subject: [PATCH 05/16] Rename intro to setup and add a controller --- SparkleShare/Mac/SparkleIntro.cs | 445 --------------------- SparkleShare/Mac/SparkleSetup.cs | 369 +++++++++++++++++ SparkleShare/Mac/SparkleSetupController.cs | 142 +++++++ SparkleShare/Mac/SparkleShare.csproj | 3 +- SparkleShare/Mac/SparkleStatusIcon.cs | 14 +- SparkleShare/Mac/SparkleUI.cs | 6 +- SparkleShare/SparkleController.cs | 2 +- 7 files changed, 524 insertions(+), 457 deletions(-) delete mode 100644 SparkleShare/Mac/SparkleIntro.cs create mode 100644 SparkleShare/Mac/SparkleSetup.cs create mode 100644 SparkleShare/Mac/SparkleSetupController.cs diff --git a/SparkleShare/Mac/SparkleIntro.cs b/SparkleShare/Mac/SparkleIntro.cs deleted file mode 100644 index 37d25c93..00000000 --- a/SparkleShare/Mac/SparkleIntro.cs +++ /dev/null @@ -1,445 +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.Timers; - -using Mono.Unix; -using MonoMac.Foundation; -using MonoMac.AppKit; -using MonoMac.ObjCRuntime; -using MonoMac.WebKit; - -namespace SparkleShare { - - public class SparkleIntro : SparkleWindow { - - private NSButton ContinueButton; - private NSButton SyncButton; - private NSButton TryAgainButton; - private NSButton CancelButton; - private NSButton SkipButton; - private NSButton OpenFolderButton; - private NSButton FinishButton; - private NSForm UserInfoForm; - private NSProgressIndicator ProgressIndicator; - private NSTextField AddressTextField; - private NSTextField FolderNameTextField; - private NSTextField ServerTypeLabel; - private NSTextField AddressLabel; - private NSTextField FolderNameLabel; - private NSTextField FolderNameHelpLabel; - private NSButtonCell ButtonCellProto; - private NSMatrix Matrix; - private int ServerType; - private bool ServerFormOnly; - - - public SparkleIntro () : base () - { - ServerFormOnly = false; - } - - - public void ShowAccountForm () - { - Reset (); - - Header = "Welcome to SparkleShare!"; - Description = "Before we can create a SparkleShare folder on this " + - "computer, we need some information from you."; - - UserInfoForm = new NSForm (new RectangleF (250, 115, 350, 64)); - UserInfoForm.AddEntry ("Full Name:"); - UserInfoForm.AddEntry ("Email Address:"); - UserInfoForm.CellSize = new SizeF (280, 22); - UserInfoForm.IntercellSpacing = new SizeF (4, 4); - UserInfoForm.Cells [0].LineBreakMode = NSLineBreakMode.TruncatingTail; - UserInfoForm.Cells [1].LineBreakMode = NSLineBreakMode.TruncatingTail; - - UserInfoForm.Cells [0].StringValue = SparkleShare.Controller.UserName; - UserInfoForm.Cells [1].StringValue = SparkleShare.Controller.UserEmail; - - ContinueButton = new NSButton () { - Title = "Continue", - Enabled = false - }; - - ContinueButton.Activated += delegate { - SparkleShare.Controller.UserName = UserInfoForm.Cells [0].StringValue.Trim (); - SparkleShare.Controller.UserEmail = UserInfoForm.Cells [1].StringValue.Trim (); - SparkleShare.Controller.GenerateKeyPair (); - SparkleUI.StatusIcon.CreateMenu (); - - InvokeOnMainThread (delegate { - ShowServerForm (); - }); - }; - - // TODO: Ugly hack, do properly with events - Timer timer = new Timer () { - Interval = 50 - }; - - timer.Elapsed += delegate { - InvokeOnMainThread (delegate { - bool name_is_correct = - !UserInfoForm.Cells [0].StringValue.Trim ().Equals (""); - - bool email_is_correct = SparkleShare.Controller.IsValidEmail ( - UserInfoForm.Cells [1].StringValue.Trim ()); - - ContinueButton.Enabled = (name_is_correct && email_is_correct); - }); - }; - - timer.Start (); - - ContentView.AddSubview (UserInfoForm); - Buttons.Add (ContinueButton); - - ShowAll (); - } - - - public void ShowServerForm (bool server_form_only) - { - ServerFormOnly = server_form_only; - ShowServerForm (); - } - - - public void ShowServerForm () - { - Reset (); - - Header = "Where is your remote folder?"; - Description = ""; - - ServerTypeLabel = new NSTextField () { - Alignment = NSTextAlignment.Right, - BackgroundColor = NSColor.WindowBackground, - Bordered = false, - Editable = false, - Frame = new RectangleF (150, Frame.Height - 139 , 160, 17), - StringValue = "Server Type:", - Font = SparkleUI.Font - }; - - AddressLabel = new NSTextField () { - Alignment = NSTextAlignment.Right, - BackgroundColor = NSColor.WindowBackground, - Bordered = false, - Editable = false, - Frame = new RectangleF (150, Frame.Height - 237 , 160, 17), - StringValue = "Address:", - Font = SparkleUI.Font - }; - - FolderNameLabel = new NSTextField () { - Alignment = NSTextAlignment.Right, - BackgroundColor = NSColor.WindowBackground, - Bordered = false, - Editable = false, - Frame = new RectangleF (150, Frame.Height - 264 , 160, 17), - StringValue = "Folder Name:", - Font = SparkleUI.Font - }; - - - AddressTextField = new NSTextField () { - Frame = new RectangleF (320, Frame.Height - 240 , 256, 22), - Font = SparkleUI.Font - }; - - AddressTextField.Cell.LineBreakMode = NSLineBreakMode.TruncatingTail; - - FolderNameTextField = new NSTextField () { - Frame = new RectangleF (320, Frame.Height - (240 + 22 + 4) , 256, 22), - StringValue = "" - }; - - FolderNameTextField.Cell.LineBreakMode = NSLineBreakMode.TruncatingTail; - - FolderNameHelpLabel = new NSTextField () { - BackgroundColor = NSColor.WindowBackground, - Bordered = false, - TextColor = NSColor.DisabledControlText, - Editable = false, - Frame = new RectangleF (320, Frame.Height - 285 , 200, 17), - StringValue = "e.g. ‘rupert/website-design’" - }; - - - ServerType = 0; - - ButtonCellProto = new NSButtonCell (); - ButtonCellProto.SetButtonType (NSButtonType.Radio) ; - - Matrix = new NSMatrix (new RectangleF (315, 180, 256, 78), - NSMatrixMode.Radio, ButtonCellProto, 4, 1); - - Matrix.CellSize = new SizeF (256, 18); - - Matrix.Cells [0].Title = "My own server"; - Matrix.Cells [1].Title = "Github"; - Matrix.Cells [2].Title = "Gitorious"; - Matrix.Cells [3].Title = "The GNOME Project"; - - foreach (NSCell cell in Matrix.Cells) - cell.Font = SparkleUI.Font; - - // TODO: Ugly hack, do properly with events - Timer timer = new Timer () { - Interval = 50 - }; - - timer.Elapsed += delegate { - - InvokeOnMainThread (delegate { - - if (Matrix.SelectedRow != ServerType) { - ServerType = Matrix.SelectedRow; - - AddressTextField.Enabled = (ServerType == 0); - - switch (ServerType) { - case 0: - AddressTextField.StringValue = ""; - FolderNameHelpLabel.StringValue = "e.g. ‘rupert/website-design’"; - break; - case 1: - AddressTextField.StringValue = "ssh://git@github.com/"; - FolderNameHelpLabel.StringValue = "e.g. ‘rupert/website-design’"; - break; - case 2: - AddressTextField.StringValue = "ssh://git@gitorious.org/"; - FolderNameHelpLabel.StringValue = "e.g. ‘project/website-design’"; - break; - case 3: - AddressTextField.StringValue = "ssh://git@gnome.org/git/"; - FolderNameHelpLabel.StringValue = "e.g. ‘gnome-icon-theme’"; - break; - } - } - - - if (ServerType == 0 && !AddressTextField.StringValue.Trim ().Equals ("") - && !FolderNameTextField.StringValue.Trim ().Equals ("")) { - - SyncButton.Enabled = true; - - } else if (ServerType != 0 && - !FolderNameTextField.StringValue.Trim ().Equals ("")) { - - SyncButton.Enabled = true; - - } else { - SyncButton.Enabled = false; - } - }); - - }; - - timer.Start (); - - ContentView.AddSubview (ServerTypeLabel); - ContentView.AddSubview (Matrix); - - ContentView.AddSubview (AddressLabel); - ContentView.AddSubview (AddressTextField); - - ContentView.AddSubview (FolderNameLabel); - ContentView.AddSubview (FolderNameTextField); - ContentView.AddSubview (FolderNameHelpLabel); - - SyncButton = new NSButton () { - Title = "Sync", - Enabled = false - }; - - SyncButton.Activated += delegate { - string folder_name = FolderNameTextField.StringValue; - string server = AddressTextField.StringValue; - string canonical_name = Path.GetFileNameWithoutExtension (folder_name); - - ShowSyncingPage (canonical_name); - - SparkleShare.Controller.FolderFetched += delegate { - InvokeOnMainThread (delegate { - ShowSuccessPage (canonical_name); - }); - }; - - SparkleShare.Controller.FolderFetchError += delegate { - InvokeOnMainThread (delegate { - ShowErrorPage (); - }); - }; - - SparkleShare.Controller.FetchFolder (server, folder_name); - }; - - Buttons.Add (SyncButton); - - if (ServerFormOnly) { - CancelButton = new NSButton () { - Title = "Cancel" - }; - - CancelButton.Activated += delegate { - InvokeOnMainThread (delegate { - PerformClose (this); - }); - }; - - Buttons.Add (CancelButton); - } else { - SkipButton = new NSButton () { - Title = "Skip" - }; - - SkipButton.Activated += delegate { - InvokeOnMainThread (delegate { - ShowCompletedPage (); - }); - }; - - Buttons.Add (SkipButton); - } - - ShowAll (); - } - - - public void ShowErrorPage () - { - Reset (); - - Header = "Something went wrong…"; - Description = ""; - - TryAgainButton = new NSButton () { - Title = "Try again…" - }; - - TryAgainButton.Activated += delegate { - InvokeOnMainThread (delegate { - ShowServerForm (); - }); - }; - - Buttons.Add (TryAgainButton); - - ShowAll (); - } - - - private void ShowSyncingPage (string name) - { - Reset (); - - Header = "Syncing folder ‘" + name + "’…"; - Description = "This may take a while.\n" + - "Are you sure it’s not coffee o'clock?"; - - ProgressIndicator = new NSProgressIndicator () { - Frame = new RectangleF (190, Frame.Height - 200, 640 - 150 - 80, 20), - Style = NSProgressIndicatorStyle.Bar - }; - - ProgressIndicator.StartAnimation (this); - - ContentView.AddSubview (ProgressIndicator); - - FinishButton = new NSButton () { - Title = "Finish", - Enabled = false - }; - - Buttons.Add (FinishButton); - - ShowAll (); - } - - - public void ShowSuccessPage (string folder_name) - { - Reset (); - - Header = "Folder synced succesfully!"; - Description = "Now you can access the synced files from ‘" + folder_name + "’ in " + - "your SparkleShare folder."; - - FinishButton = new NSButton () { - Title = "Finish" - }; - - FinishButton.Activated += delegate { - InvokeOnMainThread (delegate { - SparkleUI.StatusIcon.CreateMenu (); - PerformClose (this); - }); - }; - - OpenFolderButton = new NSButton () { - Title = "Open Folder" - }; - - OpenFolderButton.Activated += delegate { - SparkleShare.Controller.OpenSparkleShareFolder (folder_name); - }; - - Buttons.Add (FinishButton); - Buttons.Add (OpenFolderButton); - - ShowAll (); - - NSApplication.SharedApplication.RequestUserAttention - (NSRequestUserAttentionType.CriticalRequest); - } - - - private void ShowCompletedPage () - { - Reset (); - - Header = "SparkleShare is ready to go!"; - Description = "Now you can start accepting invitations from others. " + - "Just click on invitations you get by email and " + - "we will take care of the rest."; - - FinishButton = new NSButton () { - Title = "Finish" - }; - - FinishButton.Activated += delegate { - InvokeOnMainThread (delegate { - SparkleUI.StatusIcon.CreateMenu (); - PerformClose (this); - }); - - }; - - Buttons.Add (FinishButton); - - ShowAll (); - } - } -} diff --git a/SparkleShare/Mac/SparkleSetup.cs b/SparkleShare/Mac/SparkleSetup.cs new file mode 100644 index 00000000..9860d1d7 --- /dev/null +++ b/SparkleShare/Mac/SparkleSetup.cs @@ -0,0 +1,369 @@ +// 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.Timers; + +using Mono.Unix; +using MonoMac.Foundation; +using MonoMac.AppKit; +using MonoMac.ObjCRuntime; +using MonoMac.WebKit; + +namespace SparkleShare { + + public class SparkleSetup : SparkleWindow { + + public SparkleSetupController Controller = new SparkleSetupController (); + + private NSButton ContinueButton; + private NSButton SyncButton; + private NSButton TryAgainButton; + private NSButton CancelButton; + private NSButton SkipButton; + private NSButton OpenFolderButton; + private NSButton FinishButton; + private NSForm UserInfoForm; + private NSProgressIndicator ProgressIndicator; + private NSTextField AddressTextField; + private NSTextField FolderNameTextField; + private NSTextField ServerTypeLabel; + private NSTextField AddressLabel; + private NSTextField FolderNameLabel; + private NSTextField FolderNameHelpLabel; + private NSButtonCell ButtonCellProto; + private NSMatrix Matrix; + private int ServerType; + private Timer timer; + + + public SparkleSetup () : base () + { + Controller.ChangePageEvent += delegate (PageType type) { + InvokeOnMainThread (delegate { + Reset (); + + switch (type) { + case PageType.Setup: + + Header = "Welcome to SparkleShare!"; + Description = "Before we can create a SparkleShare folder on this " + + "computer, we need some information from you."; + + UserInfoForm = new NSForm (new RectangleF (250, 115, 350, 64)); + + UserInfoForm.AddEntry ("Full Name:"); + UserInfoForm.AddEntry ("Email Address:"); + + UserInfoForm.CellSize = new SizeF (280, 22); + UserInfoForm.IntercellSpacing = new SizeF (4, 4); + UserInfoForm.Cells [0].LineBreakMode = NSLineBreakMode.TruncatingTail; + UserInfoForm.Cells [1].LineBreakMode = NSLineBreakMode.TruncatingTail; + + UserInfoForm.Cells [0].StringValue = SparkleShare.Controller.UserName; + UserInfoForm.Cells [1].StringValue = SparkleShare.Controller.UserEmail; + + // TODO: Ugly hack, do properly with events + timer = new Timer () { + Interval = 50 + }; + + ContinueButton = new NSButton () { + Title = "Continue", + Enabled = false + }; + + ContinueButton.Activated += delegate { + timer.Stop (); + timer = null; + + string full_name = UserInfoForm.Cells [0].StringValue.Trim (); + string email = UserInfoForm.Cells [1].StringValue.Trim (); + + Controller.SetupPageCompleted (full_name, email); + }; + + timer.Elapsed += delegate { + InvokeOnMainThread (delegate { + bool name_is_valid = !UserInfoForm.Cells [0].StringValue.Trim ().Equals (""); + + bool email_is_valid = SparkleShare.Controller.IsValidEmail ( + UserInfoForm.Cells [1].StringValue.Trim ()); + + ContinueButton.Enabled = (name_is_valid && email_is_valid); + }); + }; + + timer.Start (); + + ContentView.AddSubview (UserInfoForm); + Buttons.Add (ContinueButton); + + break; + + case PageType.Add: + + Header = "Where is your remote folder?"; + Description = ""; + + ServerTypeLabel = new NSTextField () { + Alignment = NSTextAlignment.Right, + BackgroundColor = NSColor.WindowBackground, + Bordered = false, + Editable = false, + Frame = new RectangleF (150, Frame.Height - 139 , 160, 17), + StringValue = "Server Type:", + Font = SparkleUI.Font + }; + + AddressLabel = new NSTextField () { + Alignment = NSTextAlignment.Right, + BackgroundColor = NSColor.WindowBackground, + Bordered = false, + Editable = false, + Frame = new RectangleF (150, Frame.Height - 237 , 160, 17), + StringValue = "Address:", + Font = SparkleUI.Font + }; + + FolderNameLabel = new NSTextField () { + Alignment = NSTextAlignment.Right, + BackgroundColor = NSColor.WindowBackground, + Bordered = false, + Editable = false, + Frame = new RectangleF (150, Frame.Height - 264 , 160, 17), + StringValue = "Folder Name:", + Font = SparkleUI.Font + }; + + + AddressTextField = new NSTextField () { + Frame = new RectangleF (320, Frame.Height - 240 , 256, 22), + Font = SparkleUI.Font, + StringValue = Controller.PreviousServer + }; + + AddressTextField.Cell.LineBreakMode = NSLineBreakMode.TruncatingTail; + + FolderNameTextField = new NSTextField () { + Frame = new RectangleF (320, Frame.Height - (240 + 22 + 4) , 256, 22), + StringValue = Controller.PreviousFolder + }; + + FolderNameTextField.Cell.LineBreakMode = NSLineBreakMode.TruncatingTail; + + FolderNameHelpLabel = new NSTextField () { + BackgroundColor = NSColor.WindowBackground, + Bordered = false, + TextColor = NSColor.DisabledControlText, + Editable = false, + Frame = new RectangleF (320, Frame.Height - 285 , 200, 17), + StringValue = "e.g. ‘rupert/website-design’" + }; + + ServerType = 0; + + ButtonCellProto = new NSButtonCell (); + ButtonCellProto.SetButtonType (NSButtonType.Radio) ; + + Matrix = new NSMatrix (new RectangleF (315, 180, 256, 78), + NSMatrixMode.Radio, ButtonCellProto, 4, 1); + + Matrix.CellSize = new SizeF (256, 18); + + Matrix.Cells [0].Title = "My own server"; + Matrix.Cells [1].Title = "Github"; + Matrix.Cells [2].Title = "Gitorious"; + Matrix.Cells [3].Title = "The GNOME Project"; + + foreach (NSCell cell in Matrix.Cells) + cell.Font = SparkleUI.Font; + + // TODO: Ugly hack, do properly with events + timer = new Timer () { + Interval = 50 + }; + + timer.Elapsed += delegate { + InvokeOnMainThread (delegate { + if (Matrix.SelectedRow != ServerType) { + ServerType = Matrix.SelectedRow; + + AddressTextField.Enabled = (ServerType == 0); + + switch (ServerType) { + case 0: + AddressTextField.StringValue = ""; + FolderNameHelpLabel.StringValue = "e.g. ‘rupert/website-design’"; + break; + case 1: + AddressTextField.StringValue = "ssh://git@github.com/"; + FolderNameHelpLabel.StringValue = "e.g. ‘rupert/website-design’"; + break; + case 2: + AddressTextField.StringValue = "ssh://git@gitorious.org/"; + FolderNameHelpLabel.StringValue = "e.g. ‘project/website-design’"; + break; + case 3: + AddressTextField.StringValue = "ssh://git@gnome.org/git/"; + FolderNameHelpLabel.StringValue = "e.g. ‘gnome-icon-theme’"; + break; + } + } + + + if (ServerType == 0 && !AddressTextField.StringValue.Trim ().Equals ("") + && !FolderNameTextField.StringValue.Trim ().Equals ("")) { + + SyncButton.Enabled = true; + + } else if (ServerType != 0 && + !FolderNameTextField.StringValue.Trim ().Equals ("")) { + + SyncButton.Enabled = true; + + } else { + SyncButton.Enabled = false; + } + }); + + }; + + timer.Start (); + + ContentView.AddSubview (ServerTypeLabel); + ContentView.AddSubview (Matrix); + + ContentView.AddSubview (AddressLabel); + ContentView.AddSubview (AddressTextField); + + ContentView.AddSubview (FolderNameLabel); + ContentView.AddSubview (FolderNameTextField); + ContentView.AddSubview (FolderNameHelpLabel); + + SyncButton = new NSButton () { + Title = "Sync", + Enabled = false + }; + + SyncButton.Activated += delegate { + timer.Stop (); + timer = null; + + string folder_name = FolderNameTextField.StringValue; + string server = AddressTextField.StringValue; + Controller.AddPageCompleted (server, folder_name); + }; + + Buttons.Add (SyncButton); + + CancelButton = new NSButton () { + Title = "Cancel" + }; + + CancelButton.Activated += delegate { + InvokeOnMainThread (delegate { + PerformClose (this); + }); + }; + + Buttons.Add (CancelButton); + + break; + + case PageType.Syncing: + + Header = "Syncing folder ‘" + Controller.SyncingFolder + "’…"; + Description = "This may take a while.\n" + + "Are you sure it’s not coffee o'clock?"; + + ProgressIndicator = new NSProgressIndicator () { + Frame = new RectangleF (190, Frame.Height - 200, 640 - 150 - 80, 20), + Style = NSProgressIndicatorStyle.Bar + }; + + ProgressIndicator.StartAnimation (this); + ContentView.AddSubview (ProgressIndicator); + + FinishButton = new NSButton () { + Title = "Finish", + Enabled = false + }; + + Buttons.Add (FinishButton); + + break; + + case PageType.Error: + + Header = "Something went wrong…"; + Description = ""; + + TryAgainButton = new NSButton () { + Title = "Try again…" + }; + + TryAgainButton.Activated += delegate { + Controller.ErrorPageCompleted (); + }; + + Buttons.Add (TryAgainButton); + + break; + + case PageType.Finished: + + Header = "Folder synced succesfully!"; + Description = "Now you can access the synced files from " + + "‘" + Controller.SyncingFolder + "’ in " + + "your SparkleShare folder."; + + FinishButton = new NSButton () { + Title = "Finish" + }; + + FinishButton.Activated += delegate { + InvokeOnMainThread (delegate { + PerformClose (this); + }); + }; + + OpenFolderButton = new NSButton () { + Title = "Open Folder" + }; + + OpenFolderButton.Activated += delegate { + SparkleShare.Controller.OpenSparkleShareFolder (Controller.SyncingFolder); + }; + + Buttons.Add (FinishButton); + Buttons.Add (OpenFolderButton); + + NSApplication.SharedApplication.RequestUserAttention + (NSRequestUserAttentionType.CriticalRequest); + + break; + } + + ShowAll (); + }); + }; + } + } +} diff --git a/SparkleShare/Mac/SparkleSetupController.cs b/SparkleShare/Mac/SparkleSetupController.cs new file mode 100644 index 00000000..02cd880e --- /dev/null +++ b/SparkleShare/Mac/SparkleSetupController.cs @@ -0,0 +1,142 @@ +// 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.IO; + +namespace SparkleShare { + + public enum PageType { + Setup, + Add, + Syncing, + Error, + Finished + } + + + public class SparkleSetupController { + + public event ChangePageEventHandler ChangePageEvent; + public delegate void ChangePageEventHandler (PageType page); + + public string PreviousServer { + get { + return this.previous_server; + } + } + + public string PreviousFolder { + get { + return this.previous_folder; + } + } + + public string SyncingFolder { + get { + return this.syncing_folder; + } + } + + public PageType PreviousPage { + get { + return this.previous_page; + } + } + + private string previous_server = ""; + private string previous_folder = ""; + private string syncing_folder = ""; + private PageType previous_page; + + public SparkleSetupController () + { + ChangePageEvent += delegate (PageType page) { + this.previous_page = page; + }; + } + + + public void ShowAddPage () + { + if (ChangePageEvent != null) + ChangePageEvent (PageType.Add); + } + + + public void ShowSetupPage () + { + if (ChangePageEvent != null) + ChangePageEvent (PageType.Setup); + } + + + public void SetupPageCompleted (string full_name, string email) + { + SparkleShare.Controller.UserName = full_name; + SparkleShare.Controller.UserEmail = email; + + SparkleShare.Controller.GenerateKeyPair (); + SparkleShare.Controller.UpdateState (); + + if (ChangePageEvent != null) + ChangePageEvent (PageType.Add); + } + + + public void AddPageCompleted (string server, string folder_name) + { + this.syncing_folder = Path.GetFileNameWithoutExtension (folder_name); + this.previous_server = server; + this.previous_folder = folder_name; + + if (ChangePageEvent != null) + ChangePageEvent (PageType.Syncing); + + SparkleShare.Controller.FolderFetched += delegate { + if (ChangePageEvent != null) + ChangePageEvent (PageType.Finished); + + this.syncing_folder = ""; + }; + + SparkleShare.Controller.FolderFetchError += delegate { + if (ChangePageEvent != null) + ChangePageEvent (PageType.Error); + + this.syncing_folder = ""; + }; + + SparkleShare.Controller.FetchFolder (server, this.syncing_folder); + } + + + public void ErrorPageCompleted () + { + if (ChangePageEvent != null) + ChangePageEvent (PageType.Add); + } + + + public void FinishedPageCompleted () + { + this.previous_server = ""; + this.previous_folder = ""; + SparkleShare.Controller.UpdateState (); + } + } +} diff --git a/SparkleShare/Mac/SparkleShare.csproj b/SparkleShare/Mac/SparkleShare.csproj index 8c3a898c..d1c3a5a7 100644 --- a/SparkleShare/Mac/SparkleShare.csproj +++ b/SparkleShare/Mac/SparkleShare.csproj @@ -78,7 +78,6 @@ SparkleController.cs - @@ -93,6 +92,8 @@ + + diff --git a/SparkleShare/Mac/SparkleStatusIcon.cs b/SparkleShare/Mac/SparkleStatusIcon.cs index 4d4b7265..b7c59b6a 100644 --- a/SparkleShare/Mac/SparkleStatusIcon.cs +++ b/SparkleShare/Mac/SparkleStatusIcon.cs @@ -217,16 +217,16 @@ namespace SparkleShare { InvokeOnMainThread (delegate { NSApplication.SharedApplication.ActivateIgnoringOtherApps (true); - if (SparkleUI.Intro == null) { - SparkleUI.Intro = new SparkleIntro (); - SparkleUI.Intro.ShowServerForm (true); + if (SparkleUI.Setup == null) { + SparkleUI.Setup = new SparkleSetup (); + SparkleUI.Setup.Controller.ShowAddPage (); } - if (!SparkleUI.Intro.IsVisible) - SparkleUI.Intro.ShowServerForm (true); + if (!SparkleUI.Setup.IsVisible) + SparkleUI.Setup.Controller.ShowAddPage (); - SparkleUI.Intro.OrderFrontRegardless (); - SparkleUI.Intro.MakeKeyAndOrderFront (this); + SparkleUI.Setup.OrderFrontRegardless (); + SparkleUI.Setup.MakeKeyAndOrderFront (this); }); }; } diff --git a/SparkleShare/Mac/SparkleUI.cs b/SparkleShare/Mac/SparkleUI.cs index 1ac366ef..eb3721aa 100644 --- a/SparkleShare/Mac/SparkleUI.cs +++ b/SparkleShare/Mac/SparkleUI.cs @@ -33,7 +33,7 @@ namespace SparkleShare { public static SparkleStatusIcon StatusIcon; public static SparkleEventLog EventLog; - public static SparkleIntro Intro; + public static SparkleSetup Setup; public static SparkleBubbles Bubbles; public static SparkleAbout About; public static NSFont Font; @@ -80,8 +80,8 @@ namespace SparkleShare { Bubbles = new SparkleBubbles (); if (SparkleShare.Controller.FirstRun) { - Intro = new SparkleIntro (); - Intro.ShowAccountForm (); + Setup = new SparkleSetup (); + Setup.Controller.ShowSetupPage (); } } } diff --git a/SparkleShare/SparkleController.cs b/SparkleShare/SparkleController.cs index 1c4377b2..b940d5c6 100644 --- a/SparkleShare/SparkleController.cs +++ b/SparkleShare/SparkleController.cs @@ -498,7 +498,7 @@ namespace SparkleShare { // Fires events for the current syncing state - private void UpdateState () + public void UpdateState () { foreach (SparkleRepoBase repo in Repositories) { if (repo.Status == SyncStatus.SyncDown || From 9f09d2b8c721cb7fb165ece773d9bdc3004b9b09 Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Mon, 4 Jul 2011 20:44:48 +0100 Subject: [PATCH 06/16] Add statusicon UI controller --- SparkleShare/Mac/SparkleSetup.cs | 2 +- ...SparkleWindow.cs => SparkleSetupWindow.cs} | 4 +- SparkleShare/Mac/SparkleShare.csproj | 3 +- SparkleShare/Mac/SparkleStatusIcon.cs | 500 ++++++++---------- .../Mac/SparkleStatusIconController.cs | 87 +++ 5 files changed, 317 insertions(+), 279 deletions(-) rename SparkleShare/Mac/{SparkleWindow.cs => SparkleSetupWindow.cs} (98%) create mode 100644 SparkleShare/Mac/SparkleStatusIconController.cs diff --git a/SparkleShare/Mac/SparkleSetup.cs b/SparkleShare/Mac/SparkleSetup.cs index 9860d1d7..495d6eb8 100644 --- a/SparkleShare/Mac/SparkleSetup.cs +++ b/SparkleShare/Mac/SparkleSetup.cs @@ -28,7 +28,7 @@ using MonoMac.WebKit; namespace SparkleShare { - public class SparkleSetup : SparkleWindow { + public class SparkleSetup : SparkleSetupWindow { public SparkleSetupController Controller = new SparkleSetupController (); diff --git a/SparkleShare/Mac/SparkleWindow.cs b/SparkleShare/Mac/SparkleSetupWindow.cs similarity index 98% rename from SparkleShare/Mac/SparkleWindow.cs rename to SparkleShare/Mac/SparkleSetupWindow.cs index 868203ff..08ef66ef 100644 --- a/SparkleShare/Mac/SparkleWindow.cs +++ b/SparkleShare/Mac/SparkleSetupWindow.cs @@ -28,7 +28,7 @@ using Mono.Unix; namespace SparkleShare { - public class SparkleWindow : NSWindow { + public class SparkleSetupWindow : NSWindow { public List Buttons; public string Header; @@ -40,7 +40,7 @@ namespace SparkleShare { private NSTextField DescriptionTextField; - public SparkleWindow () : base () + public SparkleSetupWindow () : base () { SetFrame (new RectangleF (0, 0, 640, 380), true); diff --git a/SparkleShare/Mac/SparkleShare.csproj b/SparkleShare/Mac/SparkleShare.csproj index d1c3a5a7..68b49e8e 100644 --- a/SparkleShare/Mac/SparkleShare.csproj +++ b/SparkleShare/Mac/SparkleShare.csproj @@ -77,7 +77,6 @@ SparkleController.cs - @@ -94,6 +93,8 @@ + + diff --git a/SparkleShare/Mac/SparkleStatusIcon.cs b/SparkleShare/Mac/SparkleStatusIcon.cs index b7c59b6a..1830bb67 100644 --- a/SparkleShare/Mac/SparkleStatusIcon.cs +++ b/SparkleShare/Mac/SparkleStatusIcon.cs @@ -31,6 +31,8 @@ namespace SparkleShare { // user's notification area public class SparkleStatusIcon : NSObject { + public SparkleStatusIconController Controller = new SparkleStatusIconController (); + private Timer Animation; private int FrameNumber; private string StateText; @@ -58,51 +60,236 @@ namespace SparkleShare { public SparkleStatusIcon () : base () { - Animation = CreateAnimation (); + using (var a = new NSAutoreleasePool ()) { + Animation = CreateAnimation (); - StatusItem = NSStatusBar.SystemStatusBar.CreateStatusItem (28); - StatusItem.HighlightMode = true; - - SetNormalState (); - CreateMenu (); - - Menu.Delegate = new SparkleStatusIconMenuDelegate (); + StatusItem = NSStatusBar.SystemStatusBar.CreateStatusItem (28); + StatusItem.HighlightMode = true; + + StateText = _("Up to date") + " (" + Controller.FolderSize + ")"; + CreateMenu (); + + Menu.Delegate = new SparkleStatusIconMenuDelegate (); + } - SparkleShare.Controller.FolderSizeChanged += delegate { + Controller.UpdateMenuEvent += delegate (IconState state) { InvokeOnMainThread (delegate { - if (!Animation.Enabled) - SetNormalState (); - - UpdateMenu (); - }); - }; - - SparkleShare.Controller.FolderListChanged += delegate { - InvokeOnMainThread (delegate { - SetNormalState (); - CreateMenu (); + using (var a = new NSAutoreleasePool ()) { + switch (state) { + case IconState.Idle: + + Animation.Stop (); + + if (Controller.Folders.Length == 0) + StateText = _("Welcome to SparkleShare!"); + else + StateText = _("Up to date") + " (" + Controller.FolderSize + ")"; + + StateMenuItem.Title = StateText; + CreateMenu (); + + break; + + case IconState.Syncing: + + StateText = _("Syncing…"); + StateMenuItem.Title = StateText; + + if (!Animation.Enabled) + Animation.Start (); + + break; + + case IconState.Error: + + StateText = _("Not everything is synced"); + StateMenuItem.Title = StateText; + CreateMenu (); + + InvokeOnMainThread (delegate { + StatusItem.Image = new NSImage (NSBundle.MainBundle.ResourcePath + "/Pixmaps/error.png"); + StatusItem.AlternateImage = new NSImage (NSBundle.MainBundle.ResourcePath + "/Pixmaps/error-active.png"); + StatusItem.Image.Size = new SizeF (16, 16); + StatusItem.AlternateImage.Size = new SizeF (16, 16); + }); + break; + } + } }); }; - SparkleShare.Controller.OnIdle += delegate { - InvokeOnMainThread (delegate { - SetNormalState (); - CreateMenu (); - }); - }; - SparkleShare.Controller.OnSyncing += delegate { - InvokeOnMainThread (delegate { - SetAnimationState (); - UpdateMenu (); - }); - }; - SparkleShare.Controller.OnError += delegate { - InvokeOnMainThread (delegate { - SetNormalState (true); - CreateMenu (); - }); + } + + + public void CreateMenu () + { + using (NSAutoreleasePool a = new NSAutoreleasePool ()) { + StatusItem.Image = new NSImage (NSBundle.MainBundle.ResourcePath + "/Pixmaps/idle0.png"); + StatusItem.AlternateImage = new NSImage (NSBundle.MainBundle.ResourcePath + "/Pixmaps/idle0-active.png"); + StatusItem.Image.Size = new SizeF (16, 16); + StatusItem.AlternateImage.Size = new SizeF (16, 16); + + Menu = new NSMenu (); + + StateMenuItem = new NSMenuItem () { + Title = StateText + }; + + Menu.AddItem (StateMenuItem); + Menu.AddItem (NSMenuItem.SeparatorItem); + + FolderMenuItem = new NSMenuItem () { + Title = "SparkleShare" + }; + + FolderMenuItem.Activated += delegate { + SparkleShare.Controller.OpenSparkleShareFolder (); + }; + + FolderMenuItem.Image = NSImage.ImageNamed ("sparkleshare-mac"); + FolderMenuItem.Image.Size = new SizeF (16, 16); + + Menu.AddItem (FolderMenuItem); + + FolderMenuItems = new NSMenuItem [SparkleShare.Controller.Folders.Count]; + + if (Controller.Folders.Length > 0) { + Tasks = new EventHandler [SparkleShare.Controller.Folders.Count]; + + int i = 0; + foreach (string folder_name in SparkleShare.Controller.Folders) { + NSMenuItem item = new NSMenuItem (); + + item.Title = folder_name; + + if (SparkleShare.Controller.UnsyncedFolders.Contains (folder_name)) + item.Image = NSImage.ImageNamed ("NSCaution"); + else + item.Image = NSImage.ImageNamed ("NSFolder"); + + item.Image.Size = new SizeF (16, 16); + Tasks [i] = OpenFolderDelegate (folder_name); + + FolderMenuItems [i] = item; + FolderMenuItems [i].Activated += Tasks [i]; + + i++; + }; + + } else { + FolderMenuItems = new NSMenuItem [1]; + + FolderMenuItems [0] = new NSMenuItem () { + Title = "No Remote Folders Yet" + }; + } + + foreach (NSMenuItem item in FolderMenuItems) + Menu.AddItem (item); + + Menu.AddItem (NSMenuItem.SeparatorItem); + + SyncMenuItem = new NSMenuItem () { + Title = "Add Remote Folder…" + }; + + if (!SparkleShare.Controller.FirstRun) { + SyncMenuItem.Activated += delegate { + InvokeOnMainThread (delegate { + NSApplication.SharedApplication.ActivateIgnoringOtherApps (true); + + if (SparkleUI.Setup == null) { + SparkleUI.Setup = new SparkleSetup (); + SparkleUI.Setup.Controller.ShowAddPage (); + } + + if (!SparkleUI.Setup.IsVisible) + SparkleUI.Setup.Controller.ShowAddPage (); + + SparkleUI.Setup.OrderFrontRegardless (); + SparkleUI.Setup.MakeKeyAndOrderFront (this); + }); + }; + } + + Menu.AddItem (SyncMenuItem); + Menu.AddItem (NSMenuItem.SeparatorItem); + + RecentEventsMenuItem = new NSMenuItem () { + Title = "Show Recent Events" + }; + + if (Controller.Folders.Length > 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 (); + + if (SparkleShare.Controller.NotificationsEnabled) + NotificationsMenuItem.Title = "Turn Notifications Off"; + else + NotificationsMenuItem.Title = "Turn Notifications On"; + + NotificationsMenuItem.Activated += delegate { + SparkleShare.Controller.ToggleNotifications (); + + InvokeOnMainThread (delegate { + if (SparkleShare.Controller.NotificationsEnabled) + NotificationsMenuItem.Title = "Turn Notifications Off"; + else + NotificationsMenuItem.Title = "Turn Notifications On"; + }); + }; + + Menu.AddItem (NotificationsMenuItem); + Menu.AddItem (NSMenuItem.SeparatorItem); + + AboutMenuItem = new NSMenuItem () { + Title = "About SparkleShare" + }; + + AboutMenuItem.Activated += delegate { + InvokeOnMainThread (delegate { + NSApplication.SharedApplication.ActivateIgnoringOtherApps (true); + + if (SparkleUI.About == null) + SparkleUI.About = new SparkleAbout (); + + SparkleUI.About.OrderFrontRegardless (); + SparkleUI.About.MakeKeyAndOrderFront (this); + SparkleUI.About.CheckForNewVersion (); + }); + }; + + + Menu.AddItem (AboutMenuItem); + + StatusItem.Menu = Menu; + StatusItem.Menu.Update (); + } + } + + + // A method reference that makes sure that opening the + // event log for each repository works correctly + private EventHandler OpenFolderDelegate (string name) + { + return delegate { + SparkleShare.Controller.OpenSparkleShareFolder (name); }; } @@ -125,7 +312,7 @@ namespace SparkleShare { InvokeOnMainThread (delegate { string image_path = Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "idle" + FrameNumber + ".png"); - + string alternate_image_path = Path.Combine (NSBundle.MainBundle.ResourcePath, "Pixmaps", "idle" + FrameNumber + "-active.png"); @@ -139,243 +326,6 @@ namespace SparkleShare { return Animation; } - - // Creates the menu that is popped up when the - // user clicks the status icon - public void CreateMenu () - { - Menu = new NSMenu (); - - StateMenuItem = new NSMenuItem () { - Title = StateText - }; - - Menu.AddItem (StateMenuItem); - Menu.AddItem (NSMenuItem.SeparatorItem); - - FolderMenuItem = new NSMenuItem () { - Title = "SparkleShare" - }; - - FolderMenuItem.Activated += delegate { - SparkleShare.Controller.OpenSparkleShareFolder (); - }; - - string folder_icon_path = Path.Combine (NSBundle.MainBundle.ResourcePath, - "sparkleshare-mac.icns"); - - FolderMenuItem.Image = new NSImage (folder_icon_path); - FolderMenuItem.Image.Size = new SizeF (16, 16); - - Menu.AddItem (FolderMenuItem); - - if (SparkleShare.Controller.Folders.Count > 0) { - FolderMenuItems = new NSMenuItem [SparkleShare.Controller.Folders.Count]; - Tasks = new EventHandler [SparkleShare.Controller.Folders.Count]; - - int i = 0; - foreach (string folder_name in SparkleShare.Controller.Folders) { - NSMenuItem item = new NSMenuItem (); - - item.Title = folder_name; - - if (SparkleShare.Controller.UnsyncedFolders.Contains (folder_name)) - item.Image = NSImage.ImageNamed ("NSCaution"); - else - item.Image = NSImage.ImageNamed ("NSFolder"); - - item.Image.Size = new SizeF (16, 16); - Tasks [i] = OpenFolderDelegate (folder_name); - - FolderMenuItems [i] = item; - FolderMenuItems [i].Activated += Tasks [i]; - Menu.AddItem (FolderMenuItems [i]); - - i++; - }; - - - - } else { - FolderMenuItems = new NSMenuItem [1]; - - FolderMenuItems [0] = new NSMenuItem () { - Title = "No Remote Folders Yet" - }; - - Menu.AddItem (FolderMenuItems [0]); - } - - Menu.AddItem (NSMenuItem.SeparatorItem); - - SyncMenuItem = new NSMenuItem () { - Title = "Add Remote Folder…" - }; - - if (!SparkleShare.Controller.FirstRun) { - SyncMenuItem.Activated += delegate { - InvokeOnMainThread (delegate { - NSApplication.SharedApplication.ActivateIgnoringOtherApps (true); - - if (SparkleUI.Setup == null) { - SparkleUI.Setup = new SparkleSetup (); - SparkleUI.Setup.Controller.ShowAddPage (); - } - - if (!SparkleUI.Setup.IsVisible) - SparkleUI.Setup.Controller.ShowAddPage (); - - SparkleUI.Setup.OrderFrontRegardless (); - SparkleUI.Setup.MakeKeyAndOrderFront (this); - }); - }; - } - - 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 (); - - if (SparkleShare.Controller.NotificationsEnabled) - NotificationsMenuItem.Title = "Turn Notifications Off"; - else - NotificationsMenuItem.Title = "Turn Notifications On"; - - NotificationsMenuItem.Activated += delegate { - SparkleShare.Controller.ToggleNotifications (); - - InvokeOnMainThread (delegate { - if (SparkleShare.Controller.NotificationsEnabled) - NotificationsMenuItem.Title = "Turn Notifications Off"; - else - NotificationsMenuItem.Title = "Turn Notifications On"; - }); - }; - - Menu.AddItem (NotificationsMenuItem); - Menu.AddItem (NSMenuItem.SeparatorItem); - - AboutMenuItem = new NSMenuItem () { - Title = "About SparkleShare" - }; - - AboutMenuItem.Activated += delegate { - InvokeOnMainThread (delegate { - NSApplication.SharedApplication.ActivateIgnoringOtherApps (true); - - if (SparkleUI.About == null) - SparkleUI.About = new SparkleAbout (); - - SparkleUI.About.OrderFrontRegardless (); - SparkleUI.About.MakeKeyAndOrderFront (this); - SparkleUI.About.CheckForNewVersion (); - }); - }; - - - Menu.AddItem (AboutMenuItem); - - - - StatusItem.Menu = Menu; - StatusItem.Menu.Update (); - } - - - // A method reference that makes sure that opening the - // event log for each repository works correctly - private EventHandler OpenFolderDelegate (string name) - { - return delegate { - SparkleShare.Controller.OpenSparkleShareFolder (name); - }; - } - - - public void UpdateMenu () - { - StateMenuItem.Title = StateText; - } - - - // The state when there's nothing going on - private void SetNormalState () - { - if (SparkleShare.Controller.UnsyncedFolders.Count > 0) - SetNormalState (true); - else - SetNormalState (false); - } - - - // The state when there's nothing going on - private void SetNormalState (bool error) - { - Animation.Stop (); - - if (SparkleShare.Controller.Folders.Count == 0) { - StateText = _("Welcome to SparkleShare!"); - - InvokeOnMainThread (delegate { - StatusItem.Image = new NSImage (NSBundle.MainBundle.ResourcePath + "/Pixmaps/idle0.png"); - StatusItem.AlternateImage = new NSImage (NSBundle.MainBundle.ResourcePath + "/Pixmaps/idle0-active.png"); - StatusItem.Image.Size = new SizeF (16, 16); - StatusItem.AlternateImage.Size = new SizeF (16, 16); - }); - - } else { - if (error) { - StateText = _("Not everything is synced"); - - InvokeOnMainThread (delegate { - StatusItem.Image = new NSImage (NSBundle.MainBundle.ResourcePath + "/Pixmaps/error.png"); - StatusItem.AlternateImage = new NSImage (NSBundle.MainBundle.ResourcePath + "/Pixmaps/error-active.png"); - StatusItem.Image.Size = new SizeF (16, 16); - StatusItem.AlternateImage.Size = new SizeF (16, 16); - }); - - } else { - StateText = _("Up to date") + " (" + SparkleShare.Controller.FolderSize + ")"; - - InvokeOnMainThread (delegate { - StatusItem.Image = new NSImage (NSBundle.MainBundle.ResourcePath + "/Pixmaps/idle0.png"); - StatusItem.AlternateImage = new NSImage (NSBundle.MainBundle.ResourcePath + "/Pixmaps/idle0-active.png"); - StatusItem.Image.Size = new SizeF (16, 16); - StatusItem.AlternateImage.Size = new SizeF (16, 16); - }); - } - } - } - - - // The state when animating - private void SetAnimationState () - { - StateText = _("Syncing…"); - - if (!Animation.Enabled) - Animation.Start (); - } } diff --git a/SparkleShare/Mac/SparkleStatusIconController.cs b/SparkleShare/Mac/SparkleStatusIconController.cs new file mode 100644 index 00000000..b3805adb --- /dev/null +++ b/SparkleShare/Mac/SparkleStatusIconController.cs @@ -0,0 +1,87 @@ +// 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.IO; + +namespace SparkleShare { + + public enum IconState { + Idle, + Syncing, + Error + } + + + public class SparkleStatusIconController { + + public event UpdateStatusLineEventHandler UpdateStatusLineEvent; + public delegate void UpdateStatusLineEventHandler (); + + public event UpdateMenuEventHandler UpdateMenuEvent; + public delegate void UpdateMenuEventHandler (IconState state); + + public IconState CurrentState = IconState.Idle; + + public string [] Folders { + get { + return SparkleShare.Controller.Folders.ToArray (); + } + } + + public string FolderSize { + get { + return SparkleShare.Controller.FolderSize; + } + } + + public SparkleStatusIconController () + { + SparkleShare.Controller.FolderSizeChanged += delegate { + if (UpdateMenuEvent != null) + UpdateMenuEvent (CurrentState); + }; + + SparkleShare.Controller.FolderListChanged += delegate { + if (UpdateMenuEvent != null) + UpdateMenuEvent (CurrentState); + }; + + SparkleShare.Controller.OnIdle += delegate { + if (CurrentState != IconState.Error) + CurrentState = IconState.Idle; + + if (UpdateMenuEvent != null) + UpdateMenuEvent (CurrentState); + }; + + SparkleShare.Controller.OnSyncing += delegate { + CurrentState = IconState.Syncing; + + if (UpdateMenuEvent != null) + UpdateMenuEvent (IconState.Syncing); + }; + + SparkleShare.Controller.OnError += delegate { + CurrentState = IconState.Error; + + if (UpdateMenuEvent != null) + UpdateMenuEvent (IconState.Error); + }; + } + } +} From 4b74b09f75b1840d7bc53ba3882ef559bc151add Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Mon, 4 Jul 2011 21:14:44 +0100 Subject: [PATCH 07/16] Update project and make files --- SparkleLib/Makefile.am | 2 +- SparkleShare/Mac/SparkleShare.csproj | 16 ++++++++++++---- SparkleShare/Makefile.am | 6 +++++- .../{Mac => }/SparkleBubblesController.cs | 0 .../{Mac => }/SparkleEventLogController.cs | 0 SparkleShare/{Mac => }/SparkleSetupController.cs | 0 SparkleShare/SparkleShare.csproj | 4 ++++ .../{Mac => }/SparkleStatusIconController.cs | 0 8 files changed, 22 insertions(+), 6 deletions(-) rename SparkleShare/{Mac => }/SparkleBubblesController.cs (100%) rename SparkleShare/{Mac => }/SparkleEventLogController.cs (100%) rename SparkleShare/{Mac => }/SparkleSetupController.cs (100%) rename SparkleShare/{Mac => }/SparkleStatusIconController.cs (100%) diff --git a/SparkleLib/Makefile.am b/SparkleLib/Makefile.am index 0c928dc4..e5fdcd51 100644 --- a/SparkleLib/Makefile.am +++ b/SparkleLib/Makefile.am @@ -22,7 +22,7 @@ SOURCES = \ SparkleHelpers.cs \ SparkleListenerBase.cs \ SparkleListenerIrc.cs \ - SparkleListenerTcp.cs \ + SparkleListenerTcp.cs \ SparkleOptions.cs \ SparklePaths.cs \ SparkleRepoBase.cs \ diff --git a/SparkleShare/Mac/SparkleShare.csproj b/SparkleShare/Mac/SparkleShare.csproj index 68b49e8e..848b4282 100644 --- a/SparkleShare/Mac/SparkleShare.csproj +++ b/SparkleShare/Mac/SparkleShare.csproj @@ -88,13 +88,21 @@ - - - - + + SparkleBubblesController.cs + + + SparkleEventLogController.cs + + + SparkleSetupController.cs + + + SparkleStatusIconController.cs + diff --git a/SparkleShare/Makefile.am b/SparkleShare/Makefile.am index c757da22..6dcb8645 100644 --- a/SparkleShare/Makefile.am +++ b/SparkleShare/Makefile.am @@ -14,15 +14,19 @@ endif SOURCES = \ SparkleAbout.cs \ SparkleBubble.cs \ + SparkleBubblesController.cs \ SparkleController.cs \ SparkleEntry.cs \ + SparkleEventLog.cs \ + SparkleEventLogController.cs \ SparkleInfobar.cs \ SparkleIntro.cs \ SparkleLinController.cs \ - SparkleEventLog.cs \ + SparkleSetupController.cs \ SparkleShare.cs \ SparkleSpinner.cs \ SparkleStatusIcon.cs \ + SparkleStatusIconController.cs \ SparkleUI.cs \ SparkleUIHelpers.cs \ SparkleWindow.cs diff --git a/SparkleShare/Mac/SparkleBubblesController.cs b/SparkleShare/SparkleBubblesController.cs similarity index 100% rename from SparkleShare/Mac/SparkleBubblesController.cs rename to SparkleShare/SparkleBubblesController.cs diff --git a/SparkleShare/Mac/SparkleEventLogController.cs b/SparkleShare/SparkleEventLogController.cs similarity index 100% rename from SparkleShare/Mac/SparkleEventLogController.cs rename to SparkleShare/SparkleEventLogController.cs diff --git a/SparkleShare/Mac/SparkleSetupController.cs b/SparkleShare/SparkleSetupController.cs similarity index 100% rename from SparkleShare/Mac/SparkleSetupController.cs rename to SparkleShare/SparkleSetupController.cs diff --git a/SparkleShare/SparkleShare.csproj b/SparkleShare/SparkleShare.csproj index 839d9223..bfb9ee63 100644 --- a/SparkleShare/SparkleShare.csproj +++ b/SparkleShare/SparkleShare.csproj @@ -52,6 +52,10 @@ + + + + diff --git a/SparkleShare/Mac/SparkleStatusIconController.cs b/SparkleShare/SparkleStatusIconController.cs similarity index 100% rename from SparkleShare/Mac/SparkleStatusIconController.cs rename to SparkleShare/SparkleStatusIconController.cs From 763dcd1200cc881c01c48e88acac06dd22500fa3 Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Fri, 8 Jul 2011 22:52:55 +0100 Subject: [PATCH 08/16] save --- SparkleShare/Makefile.am | 9 ++-- .../{SparkleBubble.cs => SparkleBubbles.cs} | 35 +++++++++---- SparkleShare/SparkleInfobar.cs | 50 ------------------- .../{SparkleIntro.cs => SparkleSetup.cs} | 0 ...SparkleWindow.cs => SparkleSetupWindow.cs} | 0 SparkleShare/SparkleShare.csproj | 7 ++- 6 files changed, 32 insertions(+), 69 deletions(-) rename SparkleShare/{SparkleBubble.cs => SparkleBubbles.cs} (52%) delete mode 100644 SparkleShare/SparkleInfobar.cs rename SparkleShare/{SparkleIntro.cs => SparkleSetup.cs} (100%) rename SparkleShare/{SparkleWindow.cs => SparkleSetupWindow.cs} (100%) diff --git a/SparkleShare/Makefile.am b/SparkleShare/Makefile.am index 6dcb8645..1b4f1754 100644 --- a/SparkleShare/Makefile.am +++ b/SparkleShare/Makefile.am @@ -13,23 +13,22 @@ endif SOURCES = \ SparkleAbout.cs \ - SparkleBubble.cs \ + SparkleBubbles.cs \ SparkleBubblesController.cs \ SparkleController.cs \ SparkleEntry.cs \ SparkleEventLog.cs \ SparkleEventLogController.cs \ - SparkleInfobar.cs \ - SparkleIntro.cs \ SparkleLinController.cs \ + SparkleSetup.cs \ SparkleSetupController.cs \ + SparkleSetupWindow.cs \ SparkleShare.cs \ SparkleSpinner.cs \ SparkleStatusIcon.cs \ SparkleStatusIconController.cs \ SparkleUI.cs \ - SparkleUIHelpers.cs \ - SparkleWindow.cs + SparkleUIHelpers.cs include $(top_srcdir)/build/build.mk diff --git a/SparkleShare/SparkleBubble.cs b/SparkleShare/SparkleBubbles.cs similarity index 52% rename from SparkleShare/SparkleBubble.cs rename to SparkleShare/SparkleBubbles.cs index 61275449..1b2aa9fd 100644 --- a/SparkleShare/SparkleBubble.cs +++ b/SparkleShare/SparkleBubbles.cs @@ -16,26 +16,41 @@ using System; + +using Gtk; using Notifications; namespace SparkleShare { - public class SparkleBubble : Notification { + public class SparkleBubbles { - public SparkleBubble (string title, string subtext) : base (title, subtext) + public SparkleBubblesController Controller = new SparkleBubblesController (); + + + public SparkleBubbles () { - IconName = "folder-sparkleshare"; - Timeout = 4500; - Urgency = Urgency.Low; + Controller.ShowBubbleEvent += delegate (string title, string subtext, string image_path) { + Notification notification = new Notification () { + Timeout = 5 * 1000; + Urgency = Urgency.Low; + } + + if (image_path != null) + Icon = new Gdk.Pixbuf (image_path); + else + IconName = "folder-sparkleshare"; + + notification.Show (); + }; } // Checks whether the system allows adding buttons to a notification, // prevents error messages in Ubuntu. - new public void AddAction (string action, string label, ActionHandler handler) - { - if (Array.IndexOf (Notifications.Global.Capabilities, "actions") > -1) - base.AddAction (action, label, handler); - } +// new public void AddAction (string action, string label, ActionHandler handler) +// { +// if (Array.IndexOf (Notifications.Global.Capabilities, "actions") > -1) +// base.AddAction (action, label, handler); +// } } } diff --git a/SparkleShare/SparkleInfobar.cs b/SparkleShare/SparkleInfobar.cs deleted file mode 100644 index b211771f..00000000 --- a/SparkleShare/SparkleInfobar.cs +++ /dev/null @@ -1,50 +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 Gtk; - -namespace SparkleShare { - - // An infobar - public class SparkleInfobar : EventBox { - - public SparkleInfobar (string icon_name, string title, string text) - { - Window window = new Window (WindowType.Popup) { - Name = "gtk-tooltip" - }; - - window.EnsureStyle (); - Style = window.Style; - - Label label = new Label () { - Markup = "" + title + "\n" + text - }; - - HBox hbox = new HBox (false, 12) { - BorderWidth = 12 - }; - - hbox.PackStart (new Image (SparkleUIHelpers.GetIcon (icon_name, 24)), - false, false, 0); - - hbox.PackStart (label, false, false, 0); - - Add (hbox); - } - } -} diff --git a/SparkleShare/SparkleIntro.cs b/SparkleShare/SparkleSetup.cs similarity index 100% rename from SparkleShare/SparkleIntro.cs rename to SparkleShare/SparkleSetup.cs diff --git a/SparkleShare/SparkleWindow.cs b/SparkleShare/SparkleSetupWindow.cs similarity index 100% rename from SparkleShare/SparkleWindow.cs rename to SparkleShare/SparkleSetupWindow.cs diff --git a/SparkleShare/SparkleShare.csproj b/SparkleShare/SparkleShare.csproj index bfb9ee63..814ce518 100644 --- a/SparkleShare/SparkleShare.csproj +++ b/SparkleShare/SparkleShare.csproj @@ -38,17 +38,13 @@ - - - - @@ -56,6 +52,9 @@ + + + From bf2078b396548a87b4dbd0f9a4f06a1638dc8071 Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Fri, 8 Jul 2011 23:42:05 +0100 Subject: [PATCH 09/16] save --- SparkleShare/Mac/SparkleSetup.cs | 1 - SparkleShare/SparkleController.cs | 9 --------- SparkleShare/SparkleStatusIconController.cs | 1 - 3 files changed, 11 deletions(-) diff --git a/SparkleShare/Mac/SparkleSetup.cs b/SparkleShare/Mac/SparkleSetup.cs index 495d6eb8..126ce302 100644 --- a/SparkleShare/Mac/SparkleSetup.cs +++ b/SparkleShare/Mac/SparkleSetup.cs @@ -36,7 +36,6 @@ namespace SparkleShare { private NSButton SyncButton; private NSButton TryAgainButton; private NSButton CancelButton; - private NSButton SkipButton; private NSButton OpenFolderButton; private NSButton FinishButton; private NSForm UserInfoForm; diff --git a/SparkleShare/SparkleController.cs b/SparkleShare/SparkleController.cs index b940d5c6..8e32ef17 100644 --- a/SparkleShare/SparkleController.cs +++ b/SparkleShare/SparkleController.cs @@ -230,15 +230,6 @@ namespace SparkleShare { } - public List PreviousHosts { - get { - List hosts = SparkleConfig.DefaultConfig.Hosts; - hosts.Sort (); - return hosts; - } - } - - public List UnsyncedFolders { get { List unsynced_folders = new List (); diff --git a/SparkleShare/SparkleStatusIconController.cs b/SparkleShare/SparkleStatusIconController.cs index b3805adb..0353c214 100644 --- a/SparkleShare/SparkleStatusIconController.cs +++ b/SparkleShare/SparkleStatusIconController.cs @@ -29,7 +29,6 @@ namespace SparkleShare { public class SparkleStatusIconController { - public event UpdateStatusLineEventHandler UpdateStatusLineEvent; public delegate void UpdateStatusLineEventHandler (); public event UpdateMenuEventHandler UpdateMenuEvent; From 5db31e9f8eaf9542aafbff12bb78810b28effa51 Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Sat, 9 Jul 2011 01:42:43 +0100 Subject: [PATCH 10/16] add a controller for the about dialog --- SparkleShare/Mac/SparkleAbout.cs | 110 +++++++++------------ SparkleShare/Mac/SparkleAboutController.cs | 95 ++++++++++++++++++ SparkleShare/Mac/SparkleShare.csproj | 4 + SparkleShare/Mac/SparkleStatusIcon.cs | 4 - SparkleShare/SparkleController.cs | 34 ------- data/about.png | Bin 0 -> 51174 bytes 6 files changed, 145 insertions(+), 102 deletions(-) create mode 100644 SparkleShare/Mac/SparkleAboutController.cs create mode 100644 data/about.png diff --git a/SparkleShare/Mac/SparkleAbout.cs b/SparkleShare/Mac/SparkleAbout.cs index 8177789e..e145b120 100644 --- a/SparkleShare/Mac/SparkleAbout.cs +++ b/SparkleShare/Mac/SparkleAbout.cs @@ -24,14 +24,15 @@ using MonoMac.AppKit; using MonoMac.ObjCRuntime; using MonoMac.WebKit; + namespace SparkleShare { public class SparkleAbout : NSWindow { - private NSButton WebsiteButton; - private NSButton CreditsButton; - private NSBox Box; - private NSTextField HeaderTextField; + public SparkleAboutController Controller = new SparkleAboutController (); + + private NSImage AboutImage; + private NSImageView AboutImageView; private NSTextField VersionTextField; private NSTextField UpdatesTextField; private NSTextField CreditsTextField; @@ -41,21 +42,22 @@ namespace SparkleShare { public SparkleAbout () : base () { - SetFrame (new RectangleF (0, 0, 360, 288), true); + SetFrame (new RectangleF (0, 0, 640, 281), true); Center (); Delegate = new SparkleAboutDelegate (); StyleMask = (NSWindowStyle.Closable | NSWindowStyle.Titled); Title = "About SparkleShare"; - MaxSize = new SizeF (360, 288); - MinSize = new SizeF (360, 288); + MaxSize = new SizeF (640, 281); + MinSize = new SizeF (640, 281); HasShadow = true; BackingType = NSBackingStore.Buffered; CreateAbout (); + OrderFrontRegardless (); MakeKeyAndOrderFront (this); - SparkleShare.Controller.NewVersionAvailable += delegate (string new_version) { + Controller.NewVersionEvent += delegate (string new_version) { InvokeOnMainThread (delegate { UpdatesTextField.StringValue = "A newer version (" + new_version + ") is available!"; UpdatesTextField.TextColor = @@ -63,7 +65,7 @@ namespace SparkleShare { }); }; - SparkleShare.Controller.VersionUpToDate += delegate { + Controller.VersionUpToDateEvent += delegate { InvokeOnMainThread (delegate { UpdatesTextField.StringValue = "You are running the latest version."; UpdatesTextField.TextColor = @@ -71,103 +73,83 @@ namespace SparkleShare { }); }; - CheckForNewVersion (); - } - - - public void CheckForNewVersion () - { - SparkleShare.Controller.CheckForNewVersion (); + Controller.CheckingForNewVersionEvent += delegate { + InvokeOnMainThread (delegate { + UpdatesTextField.StringValue = "Checking for updates..."; + UpdatesTextField.TextColor = NSColor.DisabledControlText; + }); + }; } private void CreateAbout () { - Box = new NSBox () { - FillColor = NSColor.White, - Frame = new RectangleF (-1, Frame.Height - 105, Frame.Width + 2, 105), - BoxType = NSBoxType.NSBoxCustom + string about_image_path = Path.Combine (NSBundle.MainBundle.ResourcePath, + "Pixmaps", "about.png"); + + AboutImage = new NSImage (about_image_path) { + Size = new SizeF (640, 260) }; - HeaderTextField = new NSTextField () { - StringValue = "SparkleShare", - Frame = new RectangleF (22, Frame.Height - 89, 318, 48), - BackgroundColor = NSColor.White, - Bordered = false, - Editable = false, - Font = NSFontManager.SharedFontManager.FontWithFamily - ("Lucida Grande", NSFontTraitMask.Condensed, 0, 24) + AboutImageView = new NSImageView () { + Image = AboutImage, + Frame = new RectangleF (0, 0, 640, 260) }; + VersionTextField = new NSTextField () { - StringValue = SparkleShare.Controller.Version, - Frame = new RectangleF (22, Frame.Height - 94, 318, 22), + StringValue = "version " + Controller.RunningVersion, + Frame = new RectangleF (295, 140, 318, 22), BackgroundColor = NSColor.White, Bordered = false, Editable = false, + DrawsBackground = false, + TextColor = NSColor.White, Font = NSFontManager.SharedFontManager.FontWithFamily - ("Lucida Grande", NSFontTraitMask.Unbold, 0, 11), - TextColor = NSColor.DisabledControlText + ("Lucida Grande", NSFontTraitMask.Unbold, 0, 11) }; UpdatesTextField = new NSTextField () { StringValue = "Checking for updates...", - Frame = new RectangleF (22, Frame.Height - 222, 318, 98), - BackgroundColor = NSColor.WindowBackground, + Frame = new RectangleF (295, Frame.Height - 232, 318, 98), Bordered = false, Editable = false, + DrawsBackground = false, Font = NSFontManager.SharedFontManager.FontWithFamily ("Lucida Grande", NSFontTraitMask.Unbold, 0, 11), TextColor = NSColor.DisabledControlText }; CreditsTextField = new NSTextField () { - StringValue = @"Copyright © 2010–" + DateTime.Now.Year + " Hylke Bons and others" + + StringValue = @"Copyright © 2010–" + DateTime.Now.Year + " Hylke Bons and others." + "\n" + "\n" + "SparkleShare is Free and Open Source Software. You are free to use, modify, and redistribute it " + "under the GNU General Public License version 3 or later.", - Frame = new RectangleF (22, Frame.Height - 250, 318, 98), - BackgroundColor = NSColor.WindowBackground, + Frame = new RectangleF (295, Frame.Height - 260, 318, 98), + TextColor = NSColor.White, + DrawsBackground = false, Bordered = false, Editable = false, Font = NSFontManager.SharedFontManager.FontWithFamily ("Lucida Grande", NSFontTraitMask.Unbold, 0, 11), }; - WebsiteButton = new NSButton () { - Frame = new RectangleF (12, 12, 120, 32), - Title = "Visit Website", - BezelStyle = NSBezelStyle.Rounded, - Font = SparkleUI.Font - }; +// WebsiteButton.Activated += delegate { +// NSUrl url = new NSUrl ("http://www.sparkleshare.org/"); +// NSWorkspace.SharedWorkspace.OpenUrl (url); +// }; - WebsiteButton.Activated += delegate { - NSUrl url = new NSUrl ("http://www.sparkleshare.org/"); - NSWorkspace.SharedWorkspace.OpenUrl (url); - }; +// CreditsButton.Activated += delegate { +// NSUrl url = new NSUrl ("http://www.sparkleshare.org/credits/"); +// NSWorkspace.SharedWorkspace.OpenUrl (url); +// }; - CreditsButton = new NSButton () { - Frame = new RectangleF (Frame.Width - 12 - 120, 12, 120, 32), - Title = "Show Credits", - BezelStyle = NSBezelStyle.Rounded, - Font = SparkleUI.Font - }; + ContentView.AddSubview (AboutImageView); - CreditsButton.Activated += delegate { - - NSUrl url = new NSUrl ("http://www.sparkleshare.org/credits/"); - NSWorkspace.SharedWorkspace.OpenUrl (url); - - }; - - ContentView.AddSubview (Box); - ContentView.AddSubview (HeaderTextField); ContentView.AddSubview (VersionTextField); ContentView.AddSubview (UpdatesTextField); ContentView.AddSubview (CreditsTextField); - ContentView.AddSubview (CreditsButton); - ContentView.AddSubview (WebsiteButton); } } diff --git a/SparkleShare/Mac/SparkleAboutController.cs b/SparkleShare/Mac/SparkleAboutController.cs new file mode 100644 index 00000000..5495dfc9 --- /dev/null +++ b/SparkleShare/Mac/SparkleAboutController.cs @@ -0,0 +1,95 @@ +// 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.Net; +using System.Threading; +using System.Timers; + +using SparkleLib; + +namespace SparkleShare { + + public class SparkleAboutController { + + public event NewVersionEventHandler NewVersionEvent; + public delegate void NewVersionEventHandler (string new_version); + + public event VersionUpToDateEventHandler VersionUpToDateEvent; + public delegate void VersionUpToDateEventHandler (); + + public event CheckingForNewVersionEventHandler CheckingForNewVersionEvent; + public delegate void CheckingForNewVersionEventHandler (); + + public string RunningVersion { + get { + return SparkleBackend.Version; + } + } + + // Check for a new version once a day + private System.Timers.Timer version_checker = new System.Timers.Timer () { + Enabled = true, + Interval = 24 * 60 * 60 * 1000 + }; + + + public SparkleAboutController () + { + CheckForNewVersion (); + + this.version_checker.Elapsed += delegate { + CheckForNewVersion (); + }; + } + + + public void CheckForNewVersion () + { + this.version_checker.Stop (); + if (CheckingForNewVersionEvent != null) + CheckingForNewVersionEvent (); + + WebClient web_client = new WebClient (); + Uri uri = new Uri ("http://www.sparkleshare.org/version"); + + web_client.DownloadStringCompleted += delegate (object o, DownloadStringCompletedEventArgs args) { + if (args.Error != null) + return; + + string new_version = args.Result.Trim (); + + // Add a little delay, making it seems we're + // actually doing hard work + Thread.Sleep (2 * 1000); + + if (RunningVersion.Equals (new_version)) { + if (VersionUpToDateEvent != null) + VersionUpToDateEvent (); + + } else { + if (NewVersionEvent != null) + NewVersionEvent (new_version); + } + + this.version_checker.Start (); + }; + + web_client.DownloadStringAsync (uri); + } + } +} diff --git a/SparkleShare/Mac/SparkleShare.csproj b/SparkleShare/Mac/SparkleShare.csproj index 848b4282..c8354a64 100644 --- a/SparkleShare/Mac/SparkleShare.csproj +++ b/SparkleShare/Mac/SparkleShare.csproj @@ -103,6 +103,7 @@ SparkleStatusIconController.cs + @@ -258,6 +259,9 @@ Translations\zh_TW.po + + Pixmaps\about.png + diff --git a/SparkleShare/Mac/SparkleStatusIcon.cs b/SparkleShare/Mac/SparkleStatusIcon.cs index 1830bb67..e68c9de9 100644 --- a/SparkleShare/Mac/SparkleStatusIcon.cs +++ b/SparkleShare/Mac/SparkleStatusIcon.cs @@ -268,10 +268,6 @@ namespace SparkleShare { if (SparkleUI.About == null) SparkleUI.About = new SparkleAbout (); - - SparkleUI.About.OrderFrontRegardless (); - SparkleUI.About.MakeKeyAndOrderFront (this); - SparkleUI.About.CheckForNewVersion (); }); }; diff --git a/SparkleShare/SparkleController.cs b/SparkleShare/SparkleController.cs index 8e32ef17..1088b3d5 100644 --- a/SparkleShare/SparkleController.cs +++ b/SparkleShare/SparkleController.cs @@ -75,12 +75,6 @@ namespace SparkleShare { public delegate void NotificationRaisedEventHandler (string user_name, string user_email, string message, string repository_path); - public event NewVersionAvailableEventHandler NewVersionAvailable; - public delegate void NewVersionAvailableEventHandler (string new_version); - - public event VersionUpToDateEventHandler VersionUpToDate; - public delegate void VersionUpToDateEventHandler (); - // Short alias for the translations public static string _ (string s) @@ -1058,11 +1052,6 @@ namespace SparkleShare { } - public string Version { - get { - return SparkleBackend.Version; - } - } public void AddNoteToFolder (string folder_name, string revision, string note) @@ -1074,29 +1063,6 @@ namespace SparkleShare { } - public void CheckForNewVersion () - { - WebClient web_client = new WebClient (); - Uri uri = new Uri ("http://www.sparkleshare.org/version"); - - web_client.DownloadStringCompleted += delegate (object o, DownloadStringCompletedEventArgs args) { - if (args.Error != null) - return; - - string new_version = args.Result.Trim (); - - if (Version.Equals (new_version)) { - if (VersionUpToDate != null) - VersionUpToDate (); - - } else { - if (NewVersionAvailable != null) - NewVersionAvailable (new_version); - } - }; - - web_client.DownloadStringAsync (uri); - } private string [] tango_palette = new string [] {"#eaab00", "#e37222", diff --git a/data/about.png b/data/about.png new file mode 100644 index 0000000000000000000000000000000000000000..89f7d71fb2aa2610a69ef7b37316cd7efb207493 GIT binary patch literal 51174 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 literal 0 HcmV?d00001 From c909eff767cd4a9bb9fe68203eb44f439ef74b0f Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Sat, 9 Jul 2011 01:53:58 +0100 Subject: [PATCH 11/16] Add about controller to build --- SparkleShare/Makefile.am | 1 + SparkleShare/SparkleShare.csproj | 1 + 2 files changed, 2 insertions(+) diff --git a/SparkleShare/Makefile.am b/SparkleShare/Makefile.am index 1b4f1754..9b1d720e 100644 --- a/SparkleShare/Makefile.am +++ b/SparkleShare/Makefile.am @@ -13,6 +13,7 @@ endif SOURCES = \ SparkleAbout.cs \ + SparkleAboutController.cs SparkleBubbles.cs \ SparkleBubblesController.cs \ SparkleController.cs \ diff --git a/SparkleShare/SparkleShare.csproj b/SparkleShare/SparkleShare.csproj index 814ce518..0e1a5616 100644 --- a/SparkleShare/SparkleShare.csproj +++ b/SparkleShare/SparkleShare.csproj @@ -47,6 +47,7 @@ + From 9d261cc74051e60e4fcf0435117ca207f2a2b2b4 Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Sat, 9 Jul 2011 03:14:45 +0100 Subject: [PATCH 12/16] prepare linux about dialog for makeover --- AUTHORS | 1 + SparkleShare/SparkleAbout.cs | 65 ++--- SparkleShare/SparkleSetup.cs~HEAD | 412 ++++++++++++++++++++++++++++++ 3 files changed, 431 insertions(+), 47 deletions(-) create mode 100644 SparkleShare/SparkleSetup.cs~HEAD diff --git a/AUTHORS b/AUTHORS index a026bdb3..9ce5dac4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -24,6 +24,7 @@ Contributors: Jakub Steiner Kristi Tsukida Lapo Calamandrei + Lars Falk-Petersen Luis Cordova Łukasz Jernaś Michael Monreal diff --git a/SparkleShare/SparkleAbout.cs b/SparkleShare/SparkleAbout.cs index 5e738371..51cf0518 100644 --- a/SparkleShare/SparkleAbout.cs +++ b/SparkleShare/SparkleAbout.cs @@ -27,6 +27,8 @@ namespace SparkleShare { public class SparkleAbout : Window { + public SparkleAboutController Controller = new SparkleAboutController (); + private Label version; @@ -39,7 +41,7 @@ namespace SparkleShare { public SparkleAbout () : base ("") { - DefaultSize = new Gdk.Size (360, 260); + DefaultSize = new Gdk.Size (640, 280); BorderWidth = 0; IconName = "folder-sparkleshare"; WindowPosition = WindowPosition.Center; @@ -48,21 +50,26 @@ namespace SparkleShare { CreateAbout (); - SparkleShare.Controller.NewVersionAvailable += delegate (string new_version) { + Controller.NewVersionEvent += delegate (string new_version) { Application.Invoke (delegate { - this.version.Markup = String.Format ("{0}: {1}", _("A newer version is available"), new_version); + this.version.Markup = String.Format ("{0}: {1}", _("A newer version is available!"), new_version); this.version.ShowAll (); }); }; - SparkleShare.Controller.VersionUpToDate += delegate { + Controller.VersionUpToDateEvent += delegate { Application.Invoke (delegate { this.version.Markup = String.Format ("{0}", _("You are running the latest version.")); this.version.ShowAll (); }); }; - SparkleShare.Controller.CheckForNewVersion (); + Controller.CheckingForNewVersionEvent += delegate { + Application.Invoke (delegate { + this.version.Markup = String.Format ("{0}", _("Checking for updates...")); + this.version.ShowAll (); + }); + }; } @@ -71,17 +78,14 @@ namespace SparkleShare { Gdk.Color color = Style.Foreground (StateType.Insensitive); string secondary_text_color = SparkleUIHelpers.GdkColorToHex (color); - EventBox box = new EventBox (); - box.ModifyBg (StateType.Normal, new TreeView ().Style.Base (StateType.Normal)); Label header = new Label () { - Markup = "SparkleShare\n" + SparkleShare.Controller.Version + "", + Markup = "version " + Controller.RunningVersion + "", Xalign = 0, Xpad = 18, Ypad = 18 }; - box.Add (header); this.version = new Label () { Markup = String.Format ("{0}", _("Checking for updates...")), @@ -98,55 +102,22 @@ namespace SparkleShare { Wrap = true, LineWrapMode = Pango.WrapMode.Word, - Markup = "Copyright © 2010–" + DateTime.Now.Year + " Hylke Bons and others\n" + + Markup = "Copyright © 2010–" + DateTime.Now.Year + " Hylke Bons and others.\n" + "\n" + - "SparkleShare is Free and Open Source Software. " + - "You are free to use, modify, and redistribute it " + - "under the terms of the GNU General Public License version 3 or later." + "SparkleShare is Free and Open Source Software. You are free to use, modify, " + + "and redistribute it under the terms of the GNU General Public License " + + "version 3 or later." }; VBox vbox = new VBox (false, 0) { BorderWidth = 0 }; - HButtonBox button_bar = new HButtonBox () { - BorderWidth = 12 - }; - Button credits_button = new Button (_("_Show Credits")) { - UseUnderline = true - }; - credits_button.Clicked += delegate { - - Process process = new Process (); - process.StartInfo.FileName = "xdg-open"; - process.StartInfo.Arguments = "http://www.sparkleshare.org/credits"; - process.Start (); - - }; - - Button website_button = new Button (_("_Visit Website")) { - UseUnderline = true - }; - - website_button.Clicked += delegate { - - Process process = new Process (); - process.StartInfo.FileName = "xdg-open"; - process.StartInfo.Arguments = "http://www.sparkleshare.org/"; - process.Start (); - - }; - - button_bar.Add (website_button); - button_bar.Add (credits_button); - - vbox.PackStart (box, true, true, 0); + vbox.PackStart (header, true, true, 0); vbox.PackStart (this.version, false, false, 0); vbox.PackStart (license, true, true, 0); - vbox.PackStart (new Label (""), true, true, 0); - vbox.PackStart (button_bar, false, false, 0); Add (vbox); } diff --git a/SparkleShare/SparkleSetup.cs~HEAD b/SparkleShare/SparkleSetup.cs~HEAD new file mode 100644 index 00000000..e9e578de --- /dev/null +++ b/SparkleShare/SparkleSetup.cs~HEAD @@ -0,0 +1,412 @@ +// 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 private 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 private License for more details. +// +// You should have received a copy of the GNU General private License +// along with this program. If not, see . + + +using System; +using System.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; +using System.Timers; +using System.Collections.Generic; + +using Gtk; +using Mono.Unix; + + +namespace SparkleShare { + + public class SparkleSetup : SparkleSetupWindow { + + public SparkleSetupController Controller = new SparkleSetupController (); + + private string SecondaryTextColor; + + private Entry NameEntry; + private Entry EmailEntry; + private SparkleEntry ServerEntry; + private SparkleEntry FolderEntry; + + private Button NextButton; + private Button SyncButton; + + private Table Table; + + private ProgressBar progress_bar = new ProgressBar () { PulseStep = 0.01 }; + private Timer progress_bar_pulse_timer = new Timer () { Interval = 25, Enabled = true }; + + + // Short alias for the translations + public static string _ (string s) + { + return Catalog.GetString (s); + } + + + public SparkleSetup () : base () + { + SecondaryTextColor = SparkleUIHelpers.GdkColorToHex (Style.Foreground (StateType.Insensitive)); + + Controller.ChangePageEvent += delegate (PageType type) { + Application.Invoke (delegate { + Reset (); + + switch (type) { + case PageType.Setup: + + Header = _("Welcome to SparkleShare!"); + Description = _("Before we can create a SparkleShare folder on this " + + "computer, we need a few bits of information from you."); + + Table = new Table (4, 2, true) { + RowSpacing = 6 + }; + + Label name_label = new Label ("" + _("Full Name:") + "") { + UseMarkup = true, + Xalign = 0 + }; + + NameEntry = new Entry (SparkleShare.Controller.UserName); + NameEntry.Changed += delegate { + CheckSetupPage (); + }; + + EmailEntry = new Entry (); + EmailEntry.Changed += delegate { + CheckSetupPage (); + }; + + Label email_label = new Label ("" + _("Email:") + "") { + UseMarkup = true, + Xalign = 0 + }; + + Table.Attach (name_label, 0, 1, 0, 1); + Table.Attach (NameEntry, 1, 2, 0, 1); + Table.Attach (email_label, 0, 1, 1, 2); + Table.Attach (EmailEntry, 1, 2, 1, 2); + + NextButton = new Button (_("Next")) { + Sensitive = false + }; + + NextButton.Clicked += delegate (object o, EventArgs args) { + string full_name = NameEntry.Text; + string email = EmailEntry.Text; + + Controller.SetupPageCompleted (full_name, email); + }; + + AddButton (NextButton); + Add (Table); + + CheckSetupPage (); + + break; + + case PageType.Add: + + Header = _("Where is your remote folder?"); + + Table = new Table (6, 2, false) { + RowSpacing = 12 + }; + + HBox layout_server = new HBox (true, 0); + + // Own server radiobutton + RadioButton radio_button = new RadioButton ("" + _("On my own server:") + ""); + (radio_button.Child as Label).UseMarkup = true; + + radio_button.Toggled += delegate { + if (radio_button.Active) { + FolderEntry.ExampleText = _("Folder"); + ServerEntry.Sensitive = true; + CheckAddPage (); + } else { + ServerEntry.Sensitive = false; + CheckAddPage (); + } + + ShowAll (); + }; + + // Own server entry + ServerEntry = new SparkleEntry () { }; + ServerEntry.Completion = new EntryCompletion(); + + ListStore server_store = new ListStore (typeof (string)); + + foreach (string host in SparkleShare.Controller.PreviousHosts) + server_store.AppendValues (host); + + ServerEntry.Completion.Model = server_store; + ServerEntry.Completion.TextColumn = 0; + + if (!string.IsNullOrEmpty (Controller.PreviousServer)) { + ServerEntry.Text = Controller.PreviousServer; + ServerEntry.ExampleTextActive = false; + } else { + ServerEntry.ExampleText = _("address-to-server.com"); + } + + ServerEntry.Changed += delegate { + CheckAddPage (); + }; + + layout_server.Add (radio_button); + layout_server.Add (ServerEntry); + + Table.Attach (layout_server, 0, 2, 1, 2); + + // Github radiobutton + string github_text = "" + "Github" + "\n" + + "" + + _("Free hosting for Free and Open Source Software projects.") + "\n" + + _("Also has paid accounts for extra private space and bandwidth.") + + ""; + + RadioButton radio_button_github = new RadioButton (radio_button, github_text); + (radio_button_github.Child as Label).UseMarkup = true; + (radio_button_github.Child as Label).Wrap = true; + + radio_button_github.Toggled += delegate { + if (radio_button_github.Active) + FolderEntry.ExampleText = _("Username/Folder"); + }; + + + // Gitorious radiobutton + string gitorious_text = "" + _("Gitorious") + "\n" + + "" + + _("Completely Free as in Freedom infrastructure.") + "\n" + + _("Free accounts for Free and Open Source projects.") + + ""; + + RadioButton radio_button_gitorious = new RadioButton (radio_button, gitorious_text); + (radio_button_gitorious.Child as Label).UseMarkup = true; + (radio_button_gitorious.Child as Label).Wrap = true; + + radio_button_gitorious.Toggled += delegate { + if (radio_button_gitorious.Active) + FolderEntry.ExampleText = _("Project/Folder"); + }; + + + // GNOME radiobutton + string gnome_text = "" + _("The GNOME Project") + "\n"+ + "" + + _("GNOME is an easy to understand interface to your computer.") + "\n" + + _("Select this option if you’re a developer or designer working on GNOME.") + + ""; + + RadioButton radio_button_gnome = new RadioButton (radio_button, gnome_text); + (radio_button_gnome.Child as Label).UseMarkup = true; + (radio_button_gnome.Child as Label).Wrap = true; + + radio_button_gnome.Toggled += delegate { + if (radio_button_gnome.Active) + FolderEntry.ExampleText = _("Project"); + }; + + Table.Attach (radio_button_github, 0, 2, 2, 3); + Table.Attach (radio_button_gitorious, 0, 2, 3, 4); + Table.Attach (radio_button_gnome, 0, 2, 4, 5); + + // Folder label and entry + HBox layout_folder = new HBox (true, 0); + + Label folder_label = new Label (_("Folder Name:")) { + UseMarkup = true, + Xalign = 1 + }; + + FolderEntry = new SparkleEntry (); + FolderEntry.ExampleText = _("Folder"); + + FolderEntry.Changed += delegate { + CheckAddPage (); + }; + + layout_folder.PackStart (folder_label, true, true, 12); + layout_folder.PackStart (FolderEntry, true, true, 0); + + Table.Attach (layout_folder, 0, 2, 5, 6); + Add (Table); + + // Cancel button + Button cancel_button = new Button (_("Cancel")); + + cancel_button.Clicked += delegate { + Close (); + }; + + + // Sync button + SyncButton = new Button (_("Sync")); + + SyncButton.Clicked += delegate { + string server = ServerEntry.Text; + string folder_name = FolderEntry.Text; + + if (radio_button_gitorious.Active) + server = "gitorious.org"; + + if (radio_button_github.Active) + server = "github.com"; + + if (radio_button_gnome.Active) + server = "gnome.org"; + + Controller.AddPageCompleted (server, folder_name); + }; + + AddButton (cancel_button); + AddButton (SyncButton); + + CheckAddPage (); + + break; + + case PageType.Syncing: + + Header = String.Format (_("Syncing folder ‘{0}’…"), Controller.SyncingFolder); + Description = _("This may take a while." + Environment.NewLine) + + _("Are you sure it’s not coffee o'clock?"); + + Button button = new Button () { + Sensitive = false, + Label = _("Finish") + }; + + button.Clicked += delegate { + Close (); + }; + + AddButton (button); + + this.progress_bar_pulse_timer.Elapsed += delegate { + Application.Invoke (delegate { + progress_bar.Pulse (); + }); + }; + + if (this.progress_bar.Parent != null) + (this.progress_bar.Parent as Container).Remove (this.progress_bar); + + VBox bar_wrapper = new VBox (false , 0); + bar_wrapper.PackStart (this.progress_bar, false, false, 0); + + Add (bar_wrapper); + + break; + + case PageType.Error: + + Header = _("Something went wrong") + "…"; + + Button try_again_button = new Button (_("Try Again")) { + Sensitive = true + }; + + try_again_button.Clicked += delegate { + Controller.ErrorPageCompleted (); + }; + + AddButton (try_again_button); + Add (null); + + break; + + case PageType.Finished: + + UrgencyHint = true; + + if (!HasToplevelFocus) { + string title = String.Format (_("‘{0}’ has been successfully added"), Controller.SyncingFolder); + string subtext = _(""); + + new SparkleBubble (title, subtext).Show (); + } + + Header = _("Folder synced successfully!"); + Description = _("Access the synced files from your SparkleShare folder."); + + // A button that opens the synced folder + Button open_folder_button = new Button (_("Open Folder")); + + open_folder_button.Clicked += delegate { + SparkleShare.Controller.OpenSparkleShareFolder (Controller.SyncingFolder); + }; + + Button finish_button = new Button (_("Finish")); + + finish_button.Clicked += delegate { + Close (); + }; + + Add (null); + + AddButton (open_folder_button); + AddButton (finish_button); + + break; + } + + ShowAll (); + }); + }; + + } + + + // Enables or disables the 'Next' button depending on the + // entries filled in by the user + private void CheckSetupPage () + { + if (NameEntry.Text.Length > 0 && + SparkleShare.Controller.IsValidEmail (EmailEntry.Text)) { + + NextButton.Sensitive = true; + } else { + NextButton.Sensitive = false; + } + } + + + // Enables or disables the 'Next' button depending on the + // entries filled in by the user + public void CheckAddPage () + { + SyncButton.Sensitive = false; + + if (FolderEntry.ExampleTextActive || + (ServerEntry.Sensitive && ServerEntry.ExampleTextActive)) + return; + + bool IsFolder = !FolderEntry.Text.Trim ().Equals (""); + bool IsServer = !ServerEntry.Text.Trim ().Equals (""); + + if (ServerEntry.Sensitive == true) { + if (IsServer && IsFolder) + SyncButton.Sensitive = true; + } else if (IsFolder) { + SyncButton.Sensitive = true; + } + } + + } +} From 27629c067e753aed9db8b523f9ec9fb147712dea Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Fri, 15 Jul 2011 00:59:27 +0100 Subject: [PATCH 13/16] Prettify linux about dialog --- SparkleShare/Makefile.am | 19 +- SparkleShare/SparkleAbout.cs | 104 +- .../{Mac => }/SparkleAboutController.cs | 0 SparkleShare/SparkleBubbles.cs | 10 +- SparkleShare/SparkleSetup.cs | 903 ++++++------------ SparkleShare/SparkleSetup.cs~HEAD | 412 -------- SparkleShare/SparkleSetupWindow.cs | 37 +- SparkleShare/SparkleShare.csproj | 41 +- SparkleShare/SparkleStatusIcon.cs | 14 +- SparkleShare/SparkleUI.cs | 31 +- data/Makefile.am | 3 +- 11 files changed, 470 insertions(+), 1104 deletions(-) rename SparkleShare/{Mac => }/SparkleAboutController.cs (100%) delete mode 100644 SparkleShare/SparkleSetup.cs~HEAD diff --git a/SparkleShare/Makefile.am b/SparkleShare/Makefile.am index 9b1d720e..69135075 100644 --- a/SparkleShare/Makefile.am +++ b/SparkleShare/Makefile.am @@ -13,7 +13,24 @@ endif SOURCES = \ SparkleAbout.cs \ - SparkleAboutController.cs + SparkleAboutController.cs \ + SparkleBubbles.cs \ + SparkleBubblesController.cs \ + SparkleController.cs \ + SparkleEntry.cs \ + SparkleEventLog.cs \ + SparkleEventLogController.cs \ + SparkleInvitation.cs \ + SparkleLinController.cs \ + SparkleSetup.cs \ + SparkleSetupController.cs \ + SparkleSetupWindow.cs \ + SparkleShare.cs \ + SparkleSpinner.cs \ + SparkleStatusIcon.cs \ + SparkleStatusIconController.cs \ + SparkleUI.cs \ + SparkleUIHelpers.cs SparkleBubbles.cs \ SparkleBubblesController.cs \ SparkleController.cs \ diff --git a/SparkleShare/SparkleAbout.cs b/SparkleShare/SparkleAbout.cs index 51cf0518..830d6310 100644 --- a/SparkleShare/SparkleAbout.cs +++ b/SparkleShare/SparkleAbout.cs @@ -29,7 +29,7 @@ namespace SparkleShare { public SparkleAboutController Controller = new SparkleAboutController (); - private Label version; + private Label updates; // Short alias for the translations @@ -41,33 +41,47 @@ namespace SparkleShare { public SparkleAbout () : base ("") { - DefaultSize = new Gdk.Size (640, 280); + DefaultSize = new Gdk.Size (600, 260); + Resizable = false; BorderWidth = 0; IconName = "folder-sparkleshare"; WindowPosition = WindowPosition.Center; Title = _("About SparkleShare"); - Resizable = false; + AppPaintable = true; + + // TODO: Should be able to do without referencing SparkleLib... + string image_path = SparkleLib.SparkleHelpers.CombineMore (SparkleLib.Defines.DATAROOTDIR, + "sparkleshare", "pixmaps", "about.png"); + + Realize (); + Gdk.Pixbuf buf = new Gdk.Pixbuf (image_path); + Gdk.Pixmap map, map2; + buf.RenderPixmapAndMask (out map, out map2, 255); + GdkWindow.SetBackPixmap (map, false); CreateAbout (); Controller.NewVersionEvent += delegate (string new_version) { Application.Invoke (delegate { - this.version.Markup = String.Format ("{0}: {1}", _("A newer version is available!"), new_version); - this.version.ShowAll (); + this.updates.Markup = String.Format ("{0}: {1}", + _("A newer version is available!"), new_version); + this.updates.ShowAll (); }); }; Controller.VersionUpToDateEvent += delegate { Application.Invoke (delegate { - this.version.Markup = String.Format ("{0}", _("You are running the latest version.")); - this.version.ShowAll (); + this.updates.Markup = String.Format ("{0}", + _("You are running the latest version.")); + this.updates.ShowAll (); }); }; Controller.CheckingForNewVersionEvent += delegate { Application.Invoke (delegate { - this.version.Markup = String.Format ("{0}", _("Checking for updates...")); - this.version.ShowAll (); + this.updates.Markup = String.Format ("{0}", + _("Checking for updates...")); + this.updates.ShowAll (); }); }; } @@ -78,48 +92,58 @@ namespace SparkleShare { Gdk.Color color = Style.Foreground (StateType.Insensitive); string secondary_text_color = SparkleUIHelpers.GdkColorToHex (color); - - Label header = new Label () { - Markup = "version " + Controller.RunningVersion + "", - Xalign = 0, - Xpad = 18, - Ypad = 18 - }; - - - this.version = new Label () { - Markup = String.Format ("{0}", _("Checking for updates...")), + Label version = new Label () { + Markup = "" + + "version " + Controller.RunningVersion + + "", Xalign = 0, - Xpad = 18, - Ypad = 22, + Xpad = 300 + }; + + this.updates = new Label () { + Markup = "" + + _("Checking for updates...") + + "", + Xalign = 0, + Xpad = 300 + }; + + Label copyright = new Label () { + Markup = "" + + "Copyright © 2010–" + DateTime.Now.Year + " " + + "Hylke Bons and others." + + "", + Xalign = 0, + Xpad = 300 }; Label license = new Label () { - Xalign = 0, - Xpad = 18, - Ypad = 0, LineWrap = true, - Wrap = true, LineWrapMode = Pango.WrapMode.Word, - - Markup = "Copyright © 2010–" + DateTime.Now.Year + " Hylke Bons and others.\n" + - "\n" + - "SparkleShare is Free and Open Source Software. You are free to use, modify, " + - "and redistribute it under the terms of the GNU General Public License " + - "version 3 or later." + Markup = "" + + "SparkleShare is Free and Open Source Software. You are free to use, modify, " + + "and redistribute it under the GNU General Public License version 3 or later." + + "", + WidthRequest = 330, + Wrap = true, + Xalign = 0, + Xpad = 300, }; - VBox vbox = new VBox (false, 0) { - BorderWidth = 0 + VBox layout_horizontal = new VBox (false, 0) { + BorderWidth = 0, + HeightRequest = 260, + WidthRequest = 640 }; + layout_horizontal.PackStart (new Label (""), false, false, 42); + layout_horizontal.PackStart (version, false, false, 0); + layout_horizontal.PackStart (this.updates, false, false, 0); + layout_horizontal.PackStart (copyright, false, false, 9); + layout_horizontal.PackStart (license, false, false, 0); + layout_horizontal.PackStart (new Label (""), false, false, 0); - - vbox.PackStart (header, true, true, 0); - vbox.PackStart (this.version, false, false, 0); - vbox.PackStart (license, true, true, 0); - - Add (vbox); + Add (layout_horizontal); } } } diff --git a/SparkleShare/Mac/SparkleAboutController.cs b/SparkleShare/SparkleAboutController.cs similarity index 100% rename from SparkleShare/Mac/SparkleAboutController.cs rename to SparkleShare/SparkleAboutController.cs diff --git a/SparkleShare/SparkleBubbles.cs b/SparkleShare/SparkleBubbles.cs index 1b2aa9fd..7f0f2cf3 100644 --- a/SparkleShare/SparkleBubbles.cs +++ b/SparkleShare/SparkleBubbles.cs @@ -31,14 +31,14 @@ namespace SparkleShare { { Controller.ShowBubbleEvent += delegate (string title, string subtext, string image_path) { Notification notification = new Notification () { - Timeout = 5 * 1000; - Urgency = Urgency.Low; - } + Timeout = 5 * 1000, + Urgency = Urgency.Low + }; if (image_path != null) - Icon = new Gdk.Pixbuf (image_path); + notification.Icon = new Gdk.Pixbuf (image_path); else - IconName = "folder-sparkleshare"; + notification.IconName = "folder-sparkleshare"; notification.Show (); }; diff --git a/SparkleShare/SparkleSetup.cs b/SparkleShare/SparkleSetup.cs index d34d7b90..0e7ecb72 100644 --- a/SparkleShare/SparkleSetup.cs +++ b/SparkleShare/SparkleSetup.cs @@ -24,22 +24,26 @@ using System.Collections.Generic; using Gtk; using Mono.Unix; -using Notifications; + namespace SparkleShare { - public class SparkleIntro : SparkleWindow { + public class SparkleSetup : SparkleSetupWindow { + + public SparkleSetupController Controller = new SparkleSetupController (); + + private string SecondaryTextColor; private Entry NameEntry; private Entry EmailEntry; private SparkleEntry ServerEntry; private SparkleEntry FolderEntry; - private string PreviousServer; - private string PreviousFolder; + private Button NextButton; private Button SyncButton; - private bool ServerFormOnly; - private string SecondaryTextColor; + + private Table Table; + private ProgressBar progress_bar = new ProgressBar () { PulseStep = 0.01 }; private Timer progress_bar_pulse_timer = new Timer () { Interval = 25, Enabled = true }; @@ -51,601 +55,350 @@ namespace SparkleShare { } - public SparkleIntro () : base () + public SparkleSetup () : base () { - ServerFormOnly = false; SecondaryTextColor = SparkleUIHelpers.GdkColorToHex (Style.Foreground (StateType.Insensitive)); - } - - public void ShowAccountForm () - { - Reset (); + Controller.ChangePageEvent += delegate (PageType type) { + Application.Invoke (delegate { + Reset (); - VBox layout_vertical = new VBox (false, 0); - - Deletable = false; + switch (type) { + case PageType.Setup: - Label header = new Label ("" + - _("Welcome to SparkleShare!") + - "") { - UseMarkup = true, - Xalign = 0 - }; + Header = _("Welcome to SparkleShare!"); + Description = _("Before we can create a SparkleShare folder on this " + + "computer, we need a few bits of information from you."); - Label information = new Label (_("Before we can create a SparkleShare folder on this " + - "computer, we need a few bits of information from you.")) { - Xalign = 0, - Wrap = true - }; - - Table table = new Table (4, 2, true) { - RowSpacing = 6 - }; - - Label name_label = new Label ("" + _("Full Name:") + "") { - UseMarkup = true, - Xalign = 0 - }; - - NameEntry = new Entry (SparkleShare.Controller.UserName); - NameEntry.Changed += delegate { - CheckAccountForm (); - }; - - EmailEntry = new Entry (); - EmailEntry.Changed += delegate { - CheckAccountForm (); - }; - - Label email_label = new Label ("" + _("Email:") + "") { - UseMarkup = true, - Xalign = 0 - }; - - - table.Attach (name_label, 0, 1, 0, 1); - table.Attach (NameEntry, 1, 2, 0, 1); - table.Attach (email_label, 0, 1, 1, 2); - table.Attach (EmailEntry, 1, 2, 1, 2); - - NextButton = new Button (_("Next")) { - Sensitive = false - }; - - NextButton.Clicked += delegate (object o, EventArgs args) { - NextButton.Remove (NextButton.Child); - NextButton.Add (new Label (_("Configuring…"))); - - NextButton.Sensitive = false; - table.Sensitive = false; - - NextButton.ShowAll (); - - SparkleShare.Controller.UserName = NameEntry.Text; - SparkleShare.Controller.UserEmail = EmailEntry.Text; - - SparkleShare.Controller.GenerateKeyPair (); - SparkleShare.Controller.AddKey (); - - SparkleUI.StatusIcon.CreateMenu (); - - Deletable = true; - ShowServerForm (); - }; - - AddButton (NextButton); - - layout_vertical.PackStart (header, false, false, 0); - layout_vertical.PackStart (information, false, false, 21); - layout_vertical.PackStart (new Label (""), false, false, 0); - layout_vertical.PackStart (table, false, false, 0); - - Add (layout_vertical); - CheckAccountForm (); - ShowAll (); - } - - - public void ShowServerForm (bool server_form_only) - { - ServerFormOnly = server_form_only; - ShowServerForm (); - } - - - public void ShowServerForm () - { - Reset (); - - VBox layout_vertical = new VBox (false, 0); - - Label header = new Label ("" + - _("Where is your remote folder?") + - "") { - UseMarkup = true, - Xalign = 0 - }; - - Table table = new Table (7, 2, false) { - RowSpacing = 12 - }; - - HBox layout_server = new HBox (true, 0); - - ServerEntry = new SparkleEntry () { }; - ServerEntry.Completion = new EntryCompletion(); - ServerEntry.Completion.Model = ServerEntryCompletion(); - ServerEntry.Completion.TextColumn = 0; - - if (PreviousServer != null) { - ServerEntry.Text = PreviousServer; - ServerEntry.ExampleTextActive = false; - } else - ServerEntry.ExampleText = _("address-to-server.com"); - - ServerEntry.Changed += CheckServerForm; - - RadioButton radio_button = new RadioButton ("" + _("On my own server:") + ""); - - layout_server.Add (radio_button); - layout_server.Add (ServerEntry); - - string github_text = "" + "Github" + "\n" + - "" + - _("Free hosting for Free and Open Source Software projects.") + "\n" + - _("Also has paid accounts for extra private space and bandwidth.") + - ""; - - RadioButton radio_button_github = new RadioButton (radio_button, github_text); - - (radio_button_github.Child as Label).UseMarkup = true; - (radio_button_github.Child as Label).Wrap = true; - - string gnome_text = "" + _("The GNOME Project") + "\n" + - "" + - _("GNOME is an easy to understand interface to your computer.") + "\n" + - _("Select this option if you’re a developer or designer working on GNOME.") + - ""; - - RadioButton radio_button_gnome = new RadioButton (radio_button, gnome_text); - - (radio_button_gnome.Child as Label).UseMarkup = true; - (radio_button_gnome.Child as Label).Wrap = true; - - string gitorious_text = "" + _("Gitorious") + "\n" + - "" + - _("Completely Free as in Freedom infrastructure.") + "\n" + - _("Free accounts for Free and Open Source projects.") + - ""; - RadioButton radio_button_gitorious = new RadioButton (radio_button, gitorious_text) { - Xalign = 0 - }; - - (radio_button_gitorious.Child as Label).UseMarkup = true; - (radio_button_gitorious.Child as Label).Wrap = true; - - radio_button_github.Toggled += delegate { - if (radio_button_github.Active) - FolderEntry.ExampleText = _("Username/Folder"); - }; - - radio_button_gitorious.Toggled += delegate { - if (radio_button_gitorious.Active) - FolderEntry.ExampleText = _("Project/Folder"); - }; - - radio_button_gnome.Toggled += delegate { - if (radio_button_gnome.Active) - FolderEntry.ExampleText = _("Project"); - }; - - radio_button.Toggled += delegate { - if (radio_button.Active) { - FolderEntry.ExampleText = _("Folder"); - ServerEntry.Sensitive = true; - CheckServerForm (); - } else { - ServerEntry.Sensitive = false; - CheckServerForm (); - } - - ShowAll (); - }; - - table.Attach (layout_server, 0, 2, 1, 2); - table.Attach (radio_button_github, 0, 2, 2, 3); - table.Attach (radio_button_gitorious, 0, 2, 3, 4); - table.Attach (radio_button_gnome, 0, 2, 4, 5); - - HBox layout_folder = new HBox (true, 0); - - FolderEntry = new SparkleEntry () { }; - FolderEntry.Completion = new EntryCompletion(); - FolderEntry.Completion.Model = FolderEntryCompletion(); - FolderEntry.Completion.TextColumn = 0; - - if (PreviousFolder != null) { - FolderEntry.Text = PreviousFolder; - FolderEntry.ExampleTextActive = false; - } else - FolderEntry.ExampleText = _("Folder"); - - FolderEntry.Changed += CheckServerForm; - - Label folder_label = new Label (_("Folder Name:")) { - UseMarkup = true, - Xalign = 1 - }; - - (radio_button.Child as Label).UseMarkup = true; - - layout_folder.PackStart (folder_label, true, true, 12); - layout_folder.PackStart (FolderEntry, true, true, 0); - - SyncButton = new Button (_("Sync")); - - SyncButton.Clicked += delegate { - string folder_name = FolderEntry.Text; - string server = ServerEntry.Text; - string canonical_name = System.IO.Path.GetFileNameWithoutExtension (folder_name); - - PreviousServer = ServerEntry.Text; - PreviousFolder = FolderEntry.Text; - - if (radio_button_gitorious.Active) - server = "gitorious.org"; - - if (radio_button_github.Active) - server = "github.com"; - - if (radio_button_gnome.Active) - server = "gnome.org"; - - Application.Invoke (delegate { - Deletable = false; - ShowSyncingPage (canonical_name); - }); - - SparkleShare.Controller.FolderFetched += delegate { - Application.Invoke (delegate { - this.progress_bar_pulse_timer.Stop (); - Deletable = true; - UrgencyHint = true; - ShowSuccessPage (canonical_name); - }); - }; - - SparkleShare.Controller.FolderFetchError += delegate { - Application.Invoke (delegate { - this.progress_bar_pulse_timer.Stop (); - Deletable = true; - ShowErrorPage (); - }); + Table = new Table (4, 2, true) { + RowSpacing = 6 }; - SparkleShare.Controller.FetchFolder (server, folder_name); - }; + Label name_label = new Label ("" + _("Full Name:") + "") { + UseMarkup = true, + Xalign = 0 + }; + NameEntry = new Entry (SparkleShare.Controller.UserName); + NameEntry.Changed += delegate { + CheckSetupPage (); + }; - if (ServerFormOnly) { - Button cancel_button = new Button (_("Cancel")); + EmailEntry = new Entry (); + EmailEntry.Changed += delegate { + CheckSetupPage (); + }; - cancel_button.Clicked += delegate { - Close (); - }; + Label email_label = new Label ("" + _("Email:") + "") { + UseMarkup = true, + Xalign = 0 + }; - AddButton (cancel_button); - } else { - Button skip_button = new Button (_("Skip")); + Table.Attach (name_label, 0, 1, 0, 1); + Table.Attach (NameEntry, 1, 2, 0, 1); + Table.Attach (email_label, 0, 1, 1, 2); + Table.Attach (EmailEntry, 1, 2, 1, 2); - skip_button.Clicked += delegate { - ShowCompletedPage (); - }; + NextButton = new Button (_("Next")) { + Sensitive = false + }; - AddButton (skip_button); - } + NextButton.Clicked += delegate (object o, EventArgs args) { + string full_name = NameEntry.Text; + string email = EmailEntry.Text; - AddButton (SyncButton); + Controller.SetupPageCompleted (full_name, email); + }; - layout_vertical.PackStart (header, false, false, 0); - layout_vertical.PackStart (new Label (""), false, false, 3); - layout_vertical.PackStart (table, false, false, 0); - layout_vertical.PackStart (layout_folder, false, false, 6); + AddButton (NextButton); + Add (Table); - Add (layout_vertical); - CheckServerForm (); - ShowAll (); - } + CheckSetupPage (); + break; - public void ShowInvitationPage (string server, string folder, string token) - { - VBox layout_vertical = new VBox (false, 0); + case PageType.Add: - Label header = new Label ("" + - _("Invitation received!") + - "") { - UseMarkup = true, - Xalign = 0 - }; + Header = _("Where is your remote folder?"); - Label information = new Label (_("You've received an invitation to join a shared folder.\n" + - "We're ready to hook you up immediately if you wish.")) { - Xalign = 0, - Wrap = true - }; - - Label question = new Label (_("Do you accept this invitation?")) { - Xalign = 0, - Wrap = true - }; - - Table table = new Table (2, 2, false) { - RowSpacing = 6 - }; - - Label server_label = new Label (_("Server Address:")) { - Xalign = 0 - }; - - Label server_text = new Label ("" + server + "") { - UseMarkup = true, - Xalign = 0 - }; - - Label folder_label = new Label (_("Folder Name:")) { - Xalign = 0 - }; - - Label folder_text = new Label ("" + folder + "") { - UseMarkup = true, - Xalign = 0 - }; - - table.Attach (folder_label, 0, 1, 0, 1); - table.Attach (folder_text, 1, 2, 0, 1); - table.Attach (server_label, 0, 1, 1, 2); - table.Attach (server_text, 1, 2, 1, 2); - - Button reject_button = new Button (_("Reject")); - Button accept_button = new Button (_("Accept and Sync")); - - reject_button.Clicked += delegate { - Close (); - }; - - accept_button.Clicked += delegate { - string url = "ssh://git@" + server + "/" + folder; - if (server.StartsWith("http")) { - url = server; - } - - SparkleShare.Controller.FolderFetched += delegate { - Application.Invoke (delegate { - this.progress_bar_pulse_timer.Stop (); - ShowSuccessPage (folder); - }); - }; - - SparkleShare.Controller.FolderFetchError += delegate { - Application.Invoke (delegate { - this.progress_bar_pulse_timer.Stop (); - ShowErrorPage (); - }); - }; - - - SparkleShare.Controller.FetchFolder (url, folder); - }; - - AddButton (reject_button); - AddButton (accept_button); - - layout_vertical.PackStart (header, false, false, 0); - layout_vertical.PackStart (information, false, false, 21); - layout_vertical.PackStart (new Label (""), false, false, 0); - layout_vertical.PackStart (table, false, false, 0); - layout_vertical.PackStart (new Label (""), false, false, 0); - layout_vertical.PackStart (question, false, false, 21); - - Add (layout_vertical); - ShowAll (); - } - - - // The page shown when syncing has failed - private void ShowErrorPage () - { - Reset (); - - VBox layout_vertical = new VBox (false, 0); - - Label header = new Label ("" + - _("Something went wrong…") + - "\n") { - UseMarkup = true, - Xalign = 0 - }; - - Button try_again_button = new Button (_("Try Again")) { - Sensitive = true - }; - - try_again_button.Clicked += delegate (object o, EventArgs args) { - ShowServerForm (); - }; - - AddButton (try_again_button); - - layout_vertical.PackStart (header, false, false, 0); - - Add (layout_vertical); - ShowAll (); - } - - - // The page shown when syncing has succeeded - private void ShowSuccessPage (string folder_name) - { - Reset (); - - UrgencyHint = true; - - PreviousServer = null; - PreviousFolder = null; - - if (!HasToplevelFocus) { - string title = String.Format (_("‘{0}’ has been successfully added"), folder_name); - string subtext = _(""); - - new SparkleBubble (title, subtext).Show (); - } - - VBox layout_vertical = new VBox (false, 0); - - Label header = new Label ("" + - _("Folder synced successfully!") + - "") { - UseMarkup = true, - Xalign = 0 - }; - - Label information = new Label ( - String.Format (_("Now you can access the synced files from ‘{0}’ in your SparkleShare folder."), - folder_name)) { - Xalign = 0, - Wrap = true, - UseMarkup = true - }; - - // A button that opens the synced folder - Button open_folder_button = new Button (_("Open Folder")); - - open_folder_button.Clicked += delegate { - SparkleShare.Controller.OpenSparkleShareFolder (folder_name); + Table = new Table (6, 2, false) { + RowSpacing = 12 }; - Button finish_button = new Button (_("Finish")); - - finish_button.Clicked += delegate (object o, EventArgs args) { - Close (); - }; - - AddButton (open_folder_button); - AddButton (finish_button); + HBox layout_server = new HBox (true, 0); - layout_vertical.PackStart (header, false, false, 0); - layout_vertical.PackStart (information, false, false, 21); + // Own server radiobutton + RadioButton radio_button = new RadioButton ("" + _("On my own server:") + ""); + (radio_button.Child as Label).UseMarkup = true; - Add (layout_vertical); - ShowAll (); - } + radio_button.Toggled += delegate { + if (radio_button.Active) { + FolderEntry.ExampleText = _("Folder"); + ServerEntry.Sensitive = true; + CheckAddPage (); + } else { + ServerEntry.Sensitive = false; + CheckAddPage (); + } + + ShowAll (); + }; + + // Own server entry + ServerEntry = new SparkleEntry () { }; + ServerEntry.Completion = new EntryCompletion(); + + ListStore server_store = new ListStore (typeof (string)); + + //TODO foreach (string host in SparkleShare.Controller.PreviousHosts) + // server_store.AppendValues (host); + + ServerEntry.Completion.Model = server_store; + ServerEntry.Completion.TextColumn = 0; + + if (!string.IsNullOrEmpty (Controller.PreviousServer)) { + ServerEntry.Text = Controller.PreviousServer; + ServerEntry.ExampleTextActive = false; + } else { + ServerEntry.ExampleText = _("address-to-server.com"); + } + + ServerEntry.Changed += delegate { + CheckAddPage (); + }; + + layout_server.Add (radio_button); + layout_server.Add (ServerEntry); + + Table.Attach (layout_server, 0, 2, 1, 2); + + // Github radiobutton + string github_text = "" + "Github" + "\n" + + "" + + _("Free hosting for Free and Open Source Software projects.") + "\n" + + _("Also has paid accounts for extra private space and bandwidth.") + + ""; + + RadioButton radio_button_github = new RadioButton (radio_button, github_text); + (radio_button_github.Child as Label).UseMarkup = true; + (radio_button_github.Child as Label).Wrap = true; + + radio_button_github.Toggled += delegate { + if (radio_button_github.Active) + FolderEntry.ExampleText = _("Username/Folder"); + }; - // The page shown whilst syncing - private void ShowSyncingPage (string name) - { - Reset (); + // Gitorious radiobutton + string gitorious_text = "" + _("Gitorious") + "\n" + + "" + + _("Completely Free as in Freedom infrastructure.") + "\n" + + _("Free accounts for Free and Open Source projects.") + + ""; - VBox layout_vertical = new VBox (false, 0); + RadioButton radio_button_gitorious = new RadioButton (radio_button, gitorious_text); + (radio_button_gitorious.Child as Label).UseMarkup = true; + (radio_button_gitorious.Child as Label).Wrap = true; - Label header = new Label ("" + - String.Format (_("Syncing folder ‘{0}’…"), name) + - "") { - UseMarkup = true, - Xalign = 0, - Wrap = true - }; + radio_button_gitorious.Toggled += delegate { + if (radio_button_gitorious.Active) + FolderEntry.ExampleText = _("Project/Folder"); + }; - Label information = new Label (_("This may take a while.\n") + - _("Are you sure it’s not coffee o'clock?")) { - UseMarkup = true, - Xalign = 0 - }; + + // GNOME radiobutton + string gnome_text = "" + _("The GNOME Project") + "\n"+ + "" + + _("GNOME is an easy to understand interface to your computer.") + "\n" + + _("Select this option if you’re a developer or designer working on GNOME.") + + ""; + + RadioButton radio_button_gnome = new RadioButton (radio_button, gnome_text); + (radio_button_gnome.Child as Label).UseMarkup = true; + (radio_button_gnome.Child as Label).Wrap = true; + + radio_button_gnome.Toggled += delegate { + if (radio_button_gnome.Active) + FolderEntry.ExampleText = _("Project"); + }; + + Table.Attach (radio_button_github, 0, 2, 2, 3); + Table.Attach (radio_button_gitorious, 0, 2, 3, 4); + Table.Attach (radio_button_gnome, 0, 2, 4, 5); + + // Folder label and entry + HBox layout_folder = new HBox (true, 0); + + Label folder_label = new Label (_("Folder Name:")) { + UseMarkup = true, + Xalign = 1 + }; + + FolderEntry = new SparkleEntry (); + FolderEntry.ExampleText = _("Folder"); + + FolderEntry.Changed += delegate { + CheckAddPage (); + }; + + layout_folder.PackStart (folder_label, true, true, 12); + layout_folder.PackStart (FolderEntry, true, true, 0); + + Table.Attach (layout_folder, 0, 2, 5, 6); + Add (Table); + + // Cancel button + Button cancel_button = new Button (_("Cancel")); + + cancel_button.Clicked += delegate { + Close (); + }; + + + // Sync button + SyncButton = new Button (_("Sync")); + + SyncButton.Clicked += delegate { + string server = ServerEntry.Text; + string folder_name = FolderEntry.Text; + + if (radio_button_gitorious.Active) + server = "gitorious.org"; + + if (radio_button_github.Active) + server = "github.com"; + + if (radio_button_gnome.Active) + server = "gnome.org"; + + Controller.AddPageCompleted (server, folder_name); + }; + + AddButton (cancel_button); + AddButton (SyncButton); + + CheckAddPage (); + + break; + + case PageType.Syncing: + + Header = String.Format (_("Syncing folder ‘{0}’…"), Controller.SyncingFolder); + Description = _("This may take a while." + Environment.NewLine) + + _("Are you sure it’s not coffee o'clock?"); Button button = new Button () { Sensitive = false, Label = _("Finish") }; - + button.Clicked += delegate { Close (); }; - AddButton (button); + AddButton (button); - layout_vertical.PackStart (header, false, false, 0); - layout_vertical.PackStart (information, false, false, 21); + this.progress_bar_pulse_timer.Elapsed += delegate { + Application.Invoke (delegate { + progress_bar.Pulse (); + }); + }; - this.progress_bar_pulse_timer.Elapsed += delegate { - Application.Invoke (delegate { - progress_bar.Pulse (); - }); - }; + if (this.progress_bar.Parent != null) + (this.progress_bar.Parent as Container).Remove (this.progress_bar); - if (this.progress_bar.Parent != null) - layout_vertical.Reparent(this.progress_bar); + VBox bar_wrapper = new VBox (false , 0); + bar_wrapper.PackStart (this.progress_bar, false, false, 0); - layout_vertical.PackStart (this.progress_bar, false, false, 54); + Add (bar_wrapper); + + break; + + case PageType.Error: + + string n = Environment.NewLine; + + Header = _("Something went wrong") + "…"; + Description = "We don't know exactly what the problem is, " + + "but we can try to help you pinpoint it."; + + + Label l = new Label ( + "First, have you tried turning it off and on again?" + n + + n + + Controller.SyncingFolder +" is the address we've compiled from the information " + + "you entered. Does this look correct?" + n + + n + + "The host needs to know who you are. Have you uploaded the key that sits in your SparkleShare folder?"); + + + + l.Xpad = 12; + l.Wrap = true; + + + + Button try_again_button = new Button (_("Try Again")) { + Sensitive = true + }; + + try_again_button.Clicked += delegate { + Controller.ErrorPageCompleted (); + }; + + AddButton (try_again_button); + Add (l); + + break; + + case PageType.Finished: + + UrgencyHint = true; + + if (!HasToplevelFocus) { + string title = String.Format (_("‘{0}’ has been successfully added"), Controller.SyncingFolder); + string subtext = _(""); + + //TODO new SparkleBubble (title, subtext).Show (); + } + + Header = _("Folder synced successfully!"); + Description = _("Access the synced files from your SparkleShare folder."); + + // A button that opens the synced folder + Button open_folder_button = new Button (_("Open Folder")); + + open_folder_button.Clicked += delegate { + SparkleShare.Controller.OpenSparkleShareFolder (Controller.SyncingFolder); + }; + + Button finish_button = new Button (_("Finish")); + + finish_button.Clicked += delegate { + Close (); + }; + + Add (null); + + AddButton (open_folder_button); + AddButton (finish_button); + + break; + } + + ShowAll (); + }); + }; - Add (layout_vertical); - ShowAll (); } - // The page shown when the setup has been completed - private void ShowCompletedPage () - { - Reset (); - - VBox layout_vertical = new VBox (false, 0); - - Label header = new Label ("" + - _("SparkleShare is ready to go!") + - "") { - UseMarkup = true, - Xalign = 0 - }; - - Label information = new Label (_("Now you can start accepting invitations from others. " + "\n" + - "Just click on invitations you get by email and " + - "we will take care of the rest.")) { - UseMarkup = true, - Wrap = true, - Xalign = 0 - }; - - - HBox link_wrapper = new HBox (false, 0); - LinkButton link = new LinkButton ("http://www.sparkleshare.org/", - _("Learn how to host your own SparkleServer")); - - link_wrapper.PackStart (link, false, false, 0); - - layout_vertical.PackStart (header, false, false, 0); - layout_vertical.PackStart (information, false, false, 21); - layout_vertical.PackStart (link_wrapper, false, false, 0); - - Button finish_button = new Button (_("Finish")); - - finish_button.Clicked += delegate (object o, EventArgs args) { - Close (); - }; - - AddButton (finish_button); - - Add (layout_vertical); - ShowAll (); - } - - - // Enables or disables the 'Next' button depending on the + // Enables or disables the 'Next' button depending on the // entries filled in by the user - private void CheckAccountForm () + private void CheckSetupPage () { if (NameEntry.Text.Length > 0 && - IsValidEmail (EmailEntry.Text)) { + SparkleShare.Controller.IsValidEmail (EmailEntry.Text)) { NextButton.Sensitive = true; } else { @@ -654,17 +407,9 @@ namespace SparkleShare { } - // Enables the Add button when the fields are - // filled in correctly - public void CheckServerForm (object o, EventArgs args) - { - CheckServerForm (); - } - - - // Enables the Add button when the fields are - // filled in correctly - public void CheckServerForm () + // Enables or disables the 'Next' button depending on the + // entries filled in by the user + public void CheckAddPage () { SyncButton.Sensitive = false; @@ -683,39 +428,5 @@ namespace SparkleShare { } } - - private TreeModel ServerEntryCompletion () - { - List hosts = SparkleShare.Controller.PreviousHosts; - - ListStore store = new ListStore (typeof (string)); - store.AppendValues ("user@localhost"); - store.AppendValues ("user@example.com"); - - foreach (string host in hosts) - store.AppendValues (host); - - return store; - } - - - private TreeModel FolderEntryCompletion () - { - ListStore store = new ListStore (typeof (string)); - store.AppendValues ("~/test.git"); - - foreach (string folder in SparkleShare.Controller.Folders) - store.AppendValues (folder); - - return store; - } - - - // Checks to see if an email address is valid - private bool IsValidEmail (string email) - { - Regex regex = new Regex (@"^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$", RegexOptions.IgnoreCase); - return regex.IsMatch (email); - } } } diff --git a/SparkleShare/SparkleSetup.cs~HEAD b/SparkleShare/SparkleSetup.cs~HEAD deleted file mode 100644 index e9e578de..00000000 --- a/SparkleShare/SparkleSetup.cs~HEAD +++ /dev/null @@ -1,412 +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 private 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 private License for more details. -// -// You should have received a copy of the GNU General private License -// along with this program. If not, see . - - -using System; -using System.Diagnostics; -using System.IO; -using System.Text.RegularExpressions; -using System.Timers; -using System.Collections.Generic; - -using Gtk; -using Mono.Unix; - - -namespace SparkleShare { - - public class SparkleSetup : SparkleSetupWindow { - - public SparkleSetupController Controller = new SparkleSetupController (); - - private string SecondaryTextColor; - - private Entry NameEntry; - private Entry EmailEntry; - private SparkleEntry ServerEntry; - private SparkleEntry FolderEntry; - - private Button NextButton; - private Button SyncButton; - - private Table Table; - - private ProgressBar progress_bar = new ProgressBar () { PulseStep = 0.01 }; - private Timer progress_bar_pulse_timer = new Timer () { Interval = 25, Enabled = true }; - - - // Short alias for the translations - public static string _ (string s) - { - return Catalog.GetString (s); - } - - - public SparkleSetup () : base () - { - SecondaryTextColor = SparkleUIHelpers.GdkColorToHex (Style.Foreground (StateType.Insensitive)); - - Controller.ChangePageEvent += delegate (PageType type) { - Application.Invoke (delegate { - Reset (); - - switch (type) { - case PageType.Setup: - - Header = _("Welcome to SparkleShare!"); - Description = _("Before we can create a SparkleShare folder on this " + - "computer, we need a few bits of information from you."); - - Table = new Table (4, 2, true) { - RowSpacing = 6 - }; - - Label name_label = new Label ("" + _("Full Name:") + "") { - UseMarkup = true, - Xalign = 0 - }; - - NameEntry = new Entry (SparkleShare.Controller.UserName); - NameEntry.Changed += delegate { - CheckSetupPage (); - }; - - EmailEntry = new Entry (); - EmailEntry.Changed += delegate { - CheckSetupPage (); - }; - - Label email_label = new Label ("" + _("Email:") + "") { - UseMarkup = true, - Xalign = 0 - }; - - Table.Attach (name_label, 0, 1, 0, 1); - Table.Attach (NameEntry, 1, 2, 0, 1); - Table.Attach (email_label, 0, 1, 1, 2); - Table.Attach (EmailEntry, 1, 2, 1, 2); - - NextButton = new Button (_("Next")) { - Sensitive = false - }; - - NextButton.Clicked += delegate (object o, EventArgs args) { - string full_name = NameEntry.Text; - string email = EmailEntry.Text; - - Controller.SetupPageCompleted (full_name, email); - }; - - AddButton (NextButton); - Add (Table); - - CheckSetupPage (); - - break; - - case PageType.Add: - - Header = _("Where is your remote folder?"); - - Table = new Table (6, 2, false) { - RowSpacing = 12 - }; - - HBox layout_server = new HBox (true, 0); - - // Own server radiobutton - RadioButton radio_button = new RadioButton ("" + _("On my own server:") + ""); - (radio_button.Child as Label).UseMarkup = true; - - radio_button.Toggled += delegate { - if (radio_button.Active) { - FolderEntry.ExampleText = _("Folder"); - ServerEntry.Sensitive = true; - CheckAddPage (); - } else { - ServerEntry.Sensitive = false; - CheckAddPage (); - } - - ShowAll (); - }; - - // Own server entry - ServerEntry = new SparkleEntry () { }; - ServerEntry.Completion = new EntryCompletion(); - - ListStore server_store = new ListStore (typeof (string)); - - foreach (string host in SparkleShare.Controller.PreviousHosts) - server_store.AppendValues (host); - - ServerEntry.Completion.Model = server_store; - ServerEntry.Completion.TextColumn = 0; - - if (!string.IsNullOrEmpty (Controller.PreviousServer)) { - ServerEntry.Text = Controller.PreviousServer; - ServerEntry.ExampleTextActive = false; - } else { - ServerEntry.ExampleText = _("address-to-server.com"); - } - - ServerEntry.Changed += delegate { - CheckAddPage (); - }; - - layout_server.Add (radio_button); - layout_server.Add (ServerEntry); - - Table.Attach (layout_server, 0, 2, 1, 2); - - // Github radiobutton - string github_text = "" + "Github" + "\n" + - "" + - _("Free hosting for Free and Open Source Software projects.") + "\n" + - _("Also has paid accounts for extra private space and bandwidth.") + - ""; - - RadioButton radio_button_github = new RadioButton (radio_button, github_text); - (radio_button_github.Child as Label).UseMarkup = true; - (radio_button_github.Child as Label).Wrap = true; - - radio_button_github.Toggled += delegate { - if (radio_button_github.Active) - FolderEntry.ExampleText = _("Username/Folder"); - }; - - - // Gitorious radiobutton - string gitorious_text = "" + _("Gitorious") + "\n" + - "" + - _("Completely Free as in Freedom infrastructure.") + "\n" + - _("Free accounts for Free and Open Source projects.") + - ""; - - RadioButton radio_button_gitorious = new RadioButton (radio_button, gitorious_text); - (radio_button_gitorious.Child as Label).UseMarkup = true; - (radio_button_gitorious.Child as Label).Wrap = true; - - radio_button_gitorious.Toggled += delegate { - if (radio_button_gitorious.Active) - FolderEntry.ExampleText = _("Project/Folder"); - }; - - - // GNOME radiobutton - string gnome_text = "" + _("The GNOME Project") + "\n"+ - "" + - _("GNOME is an easy to understand interface to your computer.") + "\n" + - _("Select this option if you’re a developer or designer working on GNOME.") + - ""; - - RadioButton radio_button_gnome = new RadioButton (radio_button, gnome_text); - (radio_button_gnome.Child as Label).UseMarkup = true; - (radio_button_gnome.Child as Label).Wrap = true; - - radio_button_gnome.Toggled += delegate { - if (radio_button_gnome.Active) - FolderEntry.ExampleText = _("Project"); - }; - - Table.Attach (radio_button_github, 0, 2, 2, 3); - Table.Attach (radio_button_gitorious, 0, 2, 3, 4); - Table.Attach (radio_button_gnome, 0, 2, 4, 5); - - // Folder label and entry - HBox layout_folder = new HBox (true, 0); - - Label folder_label = new Label (_("Folder Name:")) { - UseMarkup = true, - Xalign = 1 - }; - - FolderEntry = new SparkleEntry (); - FolderEntry.ExampleText = _("Folder"); - - FolderEntry.Changed += delegate { - CheckAddPage (); - }; - - layout_folder.PackStart (folder_label, true, true, 12); - layout_folder.PackStart (FolderEntry, true, true, 0); - - Table.Attach (layout_folder, 0, 2, 5, 6); - Add (Table); - - // Cancel button - Button cancel_button = new Button (_("Cancel")); - - cancel_button.Clicked += delegate { - Close (); - }; - - - // Sync button - SyncButton = new Button (_("Sync")); - - SyncButton.Clicked += delegate { - string server = ServerEntry.Text; - string folder_name = FolderEntry.Text; - - if (radio_button_gitorious.Active) - server = "gitorious.org"; - - if (radio_button_github.Active) - server = "github.com"; - - if (radio_button_gnome.Active) - server = "gnome.org"; - - Controller.AddPageCompleted (server, folder_name); - }; - - AddButton (cancel_button); - AddButton (SyncButton); - - CheckAddPage (); - - break; - - case PageType.Syncing: - - Header = String.Format (_("Syncing folder ‘{0}’…"), Controller.SyncingFolder); - Description = _("This may take a while." + Environment.NewLine) + - _("Are you sure it’s not coffee o'clock?"); - - Button button = new Button () { - Sensitive = false, - Label = _("Finish") - }; - - button.Clicked += delegate { - Close (); - }; - - AddButton (button); - - this.progress_bar_pulse_timer.Elapsed += delegate { - Application.Invoke (delegate { - progress_bar.Pulse (); - }); - }; - - if (this.progress_bar.Parent != null) - (this.progress_bar.Parent as Container).Remove (this.progress_bar); - - VBox bar_wrapper = new VBox (false , 0); - bar_wrapper.PackStart (this.progress_bar, false, false, 0); - - Add (bar_wrapper); - - break; - - case PageType.Error: - - Header = _("Something went wrong") + "…"; - - Button try_again_button = new Button (_("Try Again")) { - Sensitive = true - }; - - try_again_button.Clicked += delegate { - Controller.ErrorPageCompleted (); - }; - - AddButton (try_again_button); - Add (null); - - break; - - case PageType.Finished: - - UrgencyHint = true; - - if (!HasToplevelFocus) { - string title = String.Format (_("‘{0}’ has been successfully added"), Controller.SyncingFolder); - string subtext = _(""); - - new SparkleBubble (title, subtext).Show (); - } - - Header = _("Folder synced successfully!"); - Description = _("Access the synced files from your SparkleShare folder."); - - // A button that opens the synced folder - Button open_folder_button = new Button (_("Open Folder")); - - open_folder_button.Clicked += delegate { - SparkleShare.Controller.OpenSparkleShareFolder (Controller.SyncingFolder); - }; - - Button finish_button = new Button (_("Finish")); - - finish_button.Clicked += delegate { - Close (); - }; - - Add (null); - - AddButton (open_folder_button); - AddButton (finish_button); - - break; - } - - ShowAll (); - }); - }; - - } - - - // Enables or disables the 'Next' button depending on the - // entries filled in by the user - private void CheckSetupPage () - { - if (NameEntry.Text.Length > 0 && - SparkleShare.Controller.IsValidEmail (EmailEntry.Text)) { - - NextButton.Sensitive = true; - } else { - NextButton.Sensitive = false; - } - } - - - // Enables or disables the 'Next' button depending on the - // entries filled in by the user - public void CheckAddPage () - { - SyncButton.Sensitive = false; - - if (FolderEntry.ExampleTextActive || - (ServerEntry.Sensitive && ServerEntry.ExampleTextActive)) - return; - - bool IsFolder = !FolderEntry.Text.Trim ().Equals (""); - bool IsServer = !ServerEntry.Text.Trim ().Equals (""); - - if (ServerEntry.Sensitive == true) { - if (IsServer && IsFolder) - SyncButton.Sensitive = true; - } else if (IsFolder) { - SyncButton.Sensitive = true; - } - } - - } -} diff --git a/SparkleShare/SparkleSetupWindow.cs b/SparkleShare/SparkleSetupWindow.cs index 9f510a20..8ccac09b 100644 --- a/SparkleShare/SparkleSetupWindow.cs +++ b/SparkleShare/SparkleSetupWindow.cs @@ -28,21 +28,26 @@ using SparkleLib; namespace SparkleShare { - public class SparkleWindow : Window { + public class SparkleSetupWindow : Window { private HBox HBox; private VBox VBox; private VBox Wrapper; private HButtonBox Buttons; + public string Header; + public string Description; - public SparkleWindow () : base ("") + public Container Content; + + public SparkleSetupWindow () : base ("") { Title = Catalog.GetString ("SparkleShare Setup"); BorderWidth = 0; IconName = "folder-sparkleshare"; Resizable = false; WindowPosition = WindowPosition.Center; + Deletable = false; SetSizeRequest (680, 440); @@ -104,13 +109,35 @@ namespace SparkleShare { new public void Add (Widget widget) { - Wrapper.PackStart (widget, true, true, 0); + Label header = new Label ("" + Header + "") { + UseMarkup = true, + Xalign = 0 + }; + + Label description = new Label (Description) { + Xalign = 0, + Wrap = true + }; + + VBox layout_vertical = new VBox (false, 0); + layout_vertical.PackStart (header, false, false, 0); + + if (!string.IsNullOrEmpty (Description)) + layout_vertical.PackStart (description, false, false, 21); + + if (widget != null) + layout_vertical.PackStart (widget, true, true, 21); + + Wrapper.PackStart (layout_vertical, true, true, 0); ShowAll (); } public void Reset () { + Header = ""; + Description = ""; + if (Wrapper.Children.Length > 0) Wrapper.Remove (Wrapper.Children [0]); @@ -122,7 +149,9 @@ namespace SparkleShare { new public void ShowAll () { - Present (); + + Present (); + base.ShowAll (); } diff --git a/SparkleShare/SparkleShare.csproj b/SparkleShare/SparkleShare.csproj index 0e1a5616..2e60021d 100644 --- a/SparkleShare/SparkleShare.csproj +++ b/SparkleShare/SparkleShare.csproj @@ -37,26 +37,6 @@ - - - - - - - - - - - - - - - - - - - - {2C914413-B31C-4362-93C7-1AE34F09112A} @@ -78,4 +58,25 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/SparkleShare/SparkleStatusIcon.cs b/SparkleShare/SparkleStatusIcon.cs index 3c29c866..b833c60f 100644 --- a/SparkleShare/SparkleStatusIcon.cs +++ b/SparkleShare/SparkleStatusIcon.cs @@ -223,16 +223,16 @@ namespace SparkleShare { sync_item.Activated += delegate { Application.Invoke (delegate { - if (SparkleUI.Intro == null) { - SparkleUI.Intro = new SparkleIntro (); - SparkleUI.Intro.ShowServerForm (true); + if (SparkleUI.Setup == null) { + SparkleUI.Setup = new SparkleSetup (); + SparkleUI.Setup.Controller.ShowAddPage (); } - if (!SparkleUI.Intro.Visible) - SparkleUI.Intro.ShowServerForm (true); + if (!SparkleUI.Setup.Visible) + SparkleUI.Setup.Controller.ShowAddPage (); - SparkleUI.Intro.ShowAll (); - SparkleUI.Intro.Present (); + //SparkleUI.Intro.ShowAll (); + //SparkleUI.Intro.Present (); }); }; diff --git a/SparkleShare/SparkleUI.cs b/SparkleShare/SparkleUI.cs index 60494f7b..3f6320e5 100644 --- a/SparkleShare/SparkleUI.cs +++ b/SparkleShare/SparkleUI.cs @@ -34,7 +34,7 @@ namespace SparkleShare { public static SparkleStatusIcon StatusIcon; public static SparkleEventLog EventLog; - public static SparkleIntro Intro; + public static SparkleSetup Setup; // Short alias for the translations @@ -56,20 +56,15 @@ namespace SparkleShare { StatusIcon = new SparkleStatusIcon (); if (SparkleShare.Controller.FirstRun) { - Intro = new SparkleIntro (); - Intro.ShowAccountForm (); + Setup = new SparkleSetup (); + Setup.Controller.ShowSetupPage (); } SparkleShare.Controller.OnQuitWhileSyncing += delegate { // TODO: Pop up a warning when quitting whilst syncing }; - SparkleShare.Controller.OnInvitation += delegate (string server, string folder, string token) { - Application.Invoke (delegate { - SparkleIntro intro = new SparkleIntro (); - intro.ShowInvitationPage (server, folder, token); - }); - }; + // Show a bubble when there are new changes SparkleShare.Controller.NotificationRaised += delegate (string user_name, string user_email, @@ -77,19 +72,19 @@ namespace SparkleShare { Application.Invoke (delegate { if (EventLog != null) EventLog.UpdateEvents (); - + if (!SparkleShare.Controller.NotificationsEnabled) return; - SparkleBubble bubble = new SparkleBubble (user_name, message); - string avatar_file_path = SparkleShare.Controller.GetAvatar (user_email, 36); + // TODO SparkleBubble bubble = new SparkleBubble (user_name, message); + //string avatar_file_path = SparkleShare.Controller.GetAvatar (user_email, 36); - if (avatar_file_path != null) - bubble.Icon = new Gdk.Pixbuf (avatar_file_path); - else - bubble.Icon = SparkleUIHelpers.GetIcon ("avatar-default", 36); + //if (avatar_file_path != null) + // bubble.Icon = new Gdk.Pixbuf (avatar_file_path); + //else + // bubble.Icon = SparkleUIHelpers.GetIcon ("avatar-default", 36); - bubble.Show (); + //bubble.Show (); }); }; @@ -99,7 +94,7 @@ namespace SparkleShare { string title = _("Ouch! Mid-air collision!"); string subtext = _("Don't worry, SparkleShare made a copy of each conflicting file."); - new SparkleBubble (title, subtext).Show (); + // TODO new SparkleBubble (title, subtext).Show (); }); }; diff --git a/data/Makefile.am b/data/Makefile.am index 24edd829..69b419e6 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -5,7 +5,8 @@ SUBDIRS = \ dist_pixmaps_DATA = \ sparkleshare-gnome.svg \ sparkleshare-mist.svg \ - side-splash.png + side-splash.png \ + about.png pixmapsdir = $(pkgdatadir)/pixmaps/ From c2a9692247416fa1fcccd498761d2ae07b61b2f8 Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Fri, 15 Jul 2011 01:31:40 +0100 Subject: [PATCH 14/16] Only allow for one about dialog to be open at the same time --- SparkleShare/SparkleAbout.cs | 5 +++++ SparkleShare/SparkleStatusIcon.cs | 9 +++++++-- SparkleShare/SparkleUI.cs | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/SparkleShare/SparkleAbout.cs b/SparkleShare/SparkleAbout.cs index 830d6310..ffa5b000 100644 --- a/SparkleShare/SparkleAbout.cs +++ b/SparkleShare/SparkleAbout.cs @@ -41,6 +41,11 @@ namespace SparkleShare { public SparkleAbout () : base ("") { + DeleteEvent += delegate (object o, DeleteEventArgs args) { + HideAll (); + args.RetVal = true; + }; + DefaultSize = new Gdk.Size (600, 260); Resizable = false; BorderWidth = 0; diff --git a/SparkleShare/SparkleStatusIcon.cs b/SparkleShare/SparkleStatusIcon.cs index b833c60f..8090febf 100644 --- a/SparkleShare/SparkleStatusIcon.cs +++ b/SparkleShare/SparkleStatusIcon.cs @@ -275,8 +275,13 @@ namespace SparkleShare { MenuItem about_item = new MenuItem (_("About SparkleShare")); about_item.Activated += delegate { - SparkleAbout about = new SparkleAbout (); - about.ShowAll (); + Application.Invoke (delegate { + if (SparkleUI.About == null) + SparkleUI.About = new SparkleAbout (); + + SparkleUI.About.ShowAll (); + SparkleUI.About.Present (); + }); }; Menu.Add (about_item); diff --git a/SparkleShare/SparkleUI.cs b/SparkleShare/SparkleUI.cs index 3f6320e5..1a298c1e 100644 --- a/SparkleShare/SparkleUI.cs +++ b/SparkleShare/SparkleUI.cs @@ -35,6 +35,7 @@ namespace SparkleShare { public static SparkleStatusIcon StatusIcon; public static SparkleEventLog EventLog; public static SparkleSetup Setup; + public static SparkleAbout About; // Short alias for the translations From 6e7c2db4bad3e5f9d62319f2180ebac52bebd0bf Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Fri, 15 Jul 2011 02:10:36 +0100 Subject: [PATCH 15/16] cleanup unused code --- SparkleShare/Makefile.am | 1 - SparkleShare/SparkleAbout.cs | 4 ++-- SparkleShare/SparkleEntry.cs | 2 +- SparkleShare/SparkleInvitation.cs | 5 ----- SparkleShare/SparkleShare.csproj | 9 ++++----- SparkleShare/SparkleUI.cs | 26 +------------------------- 6 files changed, 8 insertions(+), 39 deletions(-) delete mode 100644 SparkleShare/SparkleInvitation.cs diff --git a/SparkleShare/Makefile.am b/SparkleShare/Makefile.am index 69135075..15dee260 100644 --- a/SparkleShare/Makefile.am +++ b/SparkleShare/Makefile.am @@ -20,7 +20,6 @@ SOURCES = \ SparkleEntry.cs \ SparkleEventLog.cs \ SparkleEventLogController.cs \ - SparkleInvitation.cs \ SparkleLinController.cs \ SparkleSetup.cs \ SparkleSetupController.cs \ diff --git a/SparkleShare/SparkleAbout.cs b/SparkleShare/SparkleAbout.cs index ffa5b000..3d2e401b 100644 --- a/SparkleShare/SparkleAbout.cs +++ b/SparkleShare/SparkleAbout.cs @@ -68,8 +68,8 @@ namespace SparkleShare { Controller.NewVersionEvent += delegate (string new_version) { Application.Invoke (delegate { - this.updates.Markup = String.Format ("{0}: {1}", - _("A newer version is available!"), new_version); + this.updates.Markup = String.Format ("{0}", + String.Format (_("A newer version ({0}) is available!"), new_version)); this.updates.ShowAll (); }); }; diff --git a/SparkleShare/SparkleEntry.cs b/SparkleShare/SparkleEntry.cs index 96ac5bef..3d0f846c 100644 --- a/SparkleShare/SparkleEntry.cs +++ b/SparkleShare/SparkleEntry.cs @@ -17,6 +17,7 @@ using Gtk; +// TODO: Remove with Gtk3 namespace SparkleShare { public class SparkleEntry : Entry { @@ -33,7 +34,6 @@ namespace SparkleShare { ClipboardPasted += delegate { OnEntered (); }; FocusOutEvent += delegate { - if (Text.Equals ("") || Text == null) ExampleTextActive = true; diff --git a/SparkleShare/SparkleInvitation.cs b/SparkleShare/SparkleInvitation.cs deleted file mode 100644 index 3f2ff2d6..00000000 --- a/SparkleShare/SparkleInvitation.cs +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/SparkleShare/SparkleShare.csproj b/SparkleShare/SparkleShare.csproj index 2e60021d..079ec8de 100644 --- a/SparkleShare/SparkleShare.csproj +++ b/SparkleShare/SparkleShare.csproj @@ -59,15 +59,10 @@ - - - - - @@ -78,5 +73,9 @@ + + + + diff --git a/SparkleShare/SparkleUI.cs b/SparkleShare/SparkleUI.cs index 1a298c1e..af53c843 100644 --- a/SparkleShare/SparkleUI.cs +++ b/SparkleShare/SparkleUI.cs @@ -66,7 +66,6 @@ namespace SparkleShare { }; - // Show a bubble when there are new changes SparkleShare.Controller.NotificationRaised += delegate (string user_name, string user_email, string message, string repository_path) { @@ -98,30 +97,7 @@ namespace SparkleShare { // TODO new SparkleBubble (title, subtext).Show (); }); }; - - SparkleShare.Controller.AvatarFetched += delegate { - Application.Invoke (delegate { - if (EventLog != null) - EventLog.UpdateEvents (); - }); - }; - - SparkleShare.Controller.OnIdle += delegate { - Application.Invoke (delegate { - if (EventLog != null) - EventLog.UpdateEvents (); - }); - }; - - SparkleShare.Controller.FolderListChanged += delegate { - Application.Invoke (delegate { - if (EventLog != null) { - EventLog.UpdateChooser (); - EventLog.UpdateEvents (); - } - }); - }; - } + } // Runs the application public void Run () From 57b81fa363620108e7ee6884a98aedd839c25287 Mon Sep 17 00:00:00 2001 From: Hylke Bons Date: Fri, 15 Jul 2011 18:03:29 +0100 Subject: [PATCH 16/16] remove duplicate events --- SparkleShare/SparkleUI.cs | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/SparkleShare/SparkleUI.cs b/SparkleShare/SparkleUI.cs index af53c843..5c7dad0a 100644 --- a/SparkleShare/SparkleUI.cs +++ b/SparkleShare/SparkleUI.cs @@ -64,39 +64,6 @@ namespace SparkleShare { SparkleShare.Controller.OnQuitWhileSyncing += delegate { // TODO: Pop up a warning when quitting whilst syncing }; - - - // Show a bubble when there are new changes - SparkleShare.Controller.NotificationRaised += delegate (string user_name, string user_email, - string message, string repository_path) { - Application.Invoke (delegate { - if (EventLog != null) - EventLog.UpdateEvents (); - - if (!SparkleShare.Controller.NotificationsEnabled) - return; - - // TODO SparkleBubble bubble = new SparkleBubble (user_name, message); - //string avatar_file_path = SparkleShare.Controller.GetAvatar (user_email, 36); - - //if (avatar_file_path != null) - // bubble.Icon = new Gdk.Pixbuf (avatar_file_path); - //else - // bubble.Icon = SparkleUIHelpers.GetIcon ("avatar-default", 36); - - //bubble.Show (); - }); - }; - - // Show a bubble when there was a conflict - SparkleShare.Controller.ConflictNotificationRaised += delegate { - Application.Invoke (delegate { - string title = _("Ouch! Mid-air collision!"); - string subtext = _("Don't worry, SparkleShare made a copy of each conflicting file."); - - // TODO new SparkleBubble (title, subtext).Show (); - }); - }; } // Runs the application