/SparkleShare/SparkleSetup.cs

http://github.com/hbons/SparkleShare · C# · 649 lines · 444 code · 178 blank · 27 comment · 21 complexity · 99bcbef9c10dbdcea299f37c99b46541 MD5 · raw file

  1. // SparkleShare, a collaboration and sharing tool.
  2. // Copyright (C) 2010 Hylke Bons <hylkebons@gmail.com>
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General private License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General private License for more details.
  13. //
  14. // You should have received a copy of the GNU General private License
  15. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. using System;
  17. using System.Diagnostics;
  18. using System.IO;
  19. using System.Text.RegularExpressions;
  20. using System.Timers;
  21. using System.Collections.Generic;
  22. using Gtk;
  23. using Mono.Unix;
  24. namespace SparkleShare {
  25. public class SparkleSetup : SparkleSetupWindow {
  26. public SparkleSetupController Controller = new SparkleSetupController ();
  27. private string SecondaryTextColor;
  28. private string SecondaryTextColorSelected;
  29. private ProgressBar progress_bar = new ProgressBar ();
  30. // Short alias for the translations
  31. public static string _ (string s)
  32. {
  33. return Catalog.GetString (s);
  34. }
  35. public SparkleSetup () : base ()
  36. {
  37. SecondaryTextColor = SparkleUIHelpers.GdkColorToHex (Style.Foreground (StateType.Insensitive));
  38. SecondaryTextColorSelected =
  39. SparkleUIHelpers.GdkColorToHex (
  40. MixColors (
  41. new TreeView ().Style.Foreground (StateType.Selected),
  42. new TreeView ().Style.Background (StateType.Selected),
  43. 0.15
  44. )
  45. );
  46. Controller.ChangePageEvent += delegate (PageType type, string [] warnings) {
  47. Application.Invoke (delegate {
  48. Reset ();
  49. switch (type) {
  50. case PageType.Setup: {
  51. Header = _("Welcome to SparkleShare!");
  52. Description = "We'll need some info to mark your changes in the event log. " +
  53. "Don't worry, this stays between you and your peers.";
  54. Table table = new Table (2, 3, true) {
  55. RowSpacing = 6,
  56. ColumnSpacing = 6
  57. };
  58. Label name_label = new Label ("<b>" + _("Full Name:") + "</b>") {
  59. UseMarkup = true,
  60. Xalign = 1
  61. };
  62. Entry name_entry = new Entry (Controller.GuessedUserName) {
  63. Xalign = 0
  64. };
  65. Entry email_entry = new Entry (Controller.GuessedUserEmail) {
  66. Xalign = 0
  67. };
  68. name_entry.Changed += delegate {
  69. Controller.CheckSetupPage (name_entry.Text, email_entry.Text);
  70. };
  71. email_entry.Changed += delegate {
  72. Controller.CheckSetupPage (name_entry.Text, email_entry.Text);
  73. };
  74. Label email_label = new Label ("<b>" + _("Email:") + "</b>") {
  75. UseMarkup = true,
  76. Xalign = 1
  77. };
  78. table.Attach (name_label, 0, 1, 0, 1);
  79. table.Attach (name_entry, 1, 2, 0, 1);
  80. table.Attach (email_label, 0, 1, 1, 2);
  81. table.Attach (email_entry, 1, 2, 1, 2);
  82. VBox wrapper = new VBox (false, 9);
  83. wrapper.PackStart (table, true, false, 0);
  84. Button continue_button = new Button (_("Continue")) {
  85. Sensitive = false
  86. };
  87. continue_button.Clicked += delegate (object o, EventArgs args) {
  88. string full_name = name_entry.Text;
  89. string email = email_entry.Text;
  90. Controller.SetupPageCompleted (full_name, email);
  91. };
  92. AddButton (continue_button);
  93. Add (wrapper);
  94. Controller.UpdateSetupContinueButtonEvent += delegate (bool button_enabled) {
  95. Application.Invoke (delegate {
  96. continue_button.Sensitive = button_enabled;
  97. });
  98. };
  99. Controller.CheckSetupPage (name_entry.Text, email_entry.Text);
  100. break;
  101. }
  102. case PageType.Add: {
  103. Header = _("Where's your project hosted?");
  104. VBox layout_vertical = new VBox (false, 12);
  105. HBox layout_fields = new HBox (true, 12);
  106. VBox layout_address = new VBox (true, 0);
  107. VBox layout_path = new VBox (true, 0);
  108. ListStore store = new ListStore (typeof (Gdk.Pixbuf),
  109. typeof (string), typeof (SparklePlugin));
  110. SparkleTreeView tree = new SparkleTreeView (store) { HeadersVisible = false };
  111. ScrolledWindow scrolled_window = new ScrolledWindow ();
  112. scrolled_window.AddWithViewport (tree);
  113. // Icon column
  114. tree.AppendColumn ("Icon", new Gtk.CellRendererPixbuf (), "pixbuf", 0);
  115. tree.Columns [0].Cells [0].Xpad = 6;
  116. // Service column
  117. TreeViewColumn service_column = new TreeViewColumn () { Title = "Service" };
  118. CellRendererText service_cell = new CellRendererText () { Ypad = 4 };
  119. service_column.PackStart (service_cell, true);
  120. service_column.SetCellDataFunc (service_cell, new TreeCellDataFunc (RenderServiceColumn));
  121. foreach (SparklePlugin plugin in Controller.Plugins) {
  122. store.AppendValues (
  123. new Gdk.Pixbuf (plugin.ImagePath),
  124. "<span size=\"small\"><b>" + plugin.Name + "</b>\n" +
  125. "<span fgcolor=\"" + SecondaryTextColorSelected + "\">" +
  126. plugin.Description + "</span>" +
  127. "</span>",
  128. plugin);
  129. }
  130. tree.AppendColumn (service_column);
  131. SparkleEntry path_entry = new SparkleEntry ();
  132. SparkleEntry address_entry = new SparkleEntry ();
  133. // Select the first plugin by default
  134. TreeSelection default_selection = tree.Selection;
  135. TreePath default_path = new TreePath ("0");
  136. default_selection.SelectPath (default_path);
  137. Controller.SelectedPluginChanged (0);
  138. Controller.ChangeAddressFieldEvent += delegate (string text,
  139. string example_text, FieldState state) {
  140. Application.Invoke (delegate {
  141. address_entry.Text = text;
  142. address_entry.Sensitive = (state == FieldState.Enabled);
  143. if (string.IsNullOrEmpty (example_text))
  144. address_entry.ExampleText = null;
  145. else
  146. address_entry.ExampleText = example_text;
  147. if (string.IsNullOrEmpty (text))
  148. address_entry.ExampleTextActive = true;
  149. else
  150. address_entry.ExampleTextActive = false;
  151. });
  152. };
  153. Controller.ChangePathFieldEvent += delegate (string text,
  154. string example_text, FieldState state) {
  155. Application.Invoke (delegate {
  156. path_entry.Text = text;
  157. path_entry.Sensitive = (state == FieldState.Enabled);
  158. if (string.IsNullOrEmpty (example_text))
  159. path_entry.ExampleText = null;
  160. else
  161. path_entry.ExampleText = example_text;
  162. if (string.IsNullOrEmpty (text))
  163. path_entry.ExampleTextActive = true;
  164. else
  165. path_entry.ExampleTextActive = false;
  166. });
  167. };
  168. // Update the address field text when the selection changes
  169. tree.CursorChanged += delegate (object sender, EventArgs e) {
  170. Controller.SelectedPluginChanged (tree.SelectedRow);
  171. // TODO: Scroll to selected row when using arrow keys
  172. };
  173. tree.Model.Foreach (new TreeModelForeachFunc (delegate (TreeModel model,
  174. TreePath path, TreeIter iter) {
  175. string address;
  176. try {
  177. address = (model.GetValue (iter, 2) as SparklePlugin).Address;
  178. } catch (NullReferenceException) {
  179. address = "";
  180. }
  181. if (!string.IsNullOrEmpty (address) &&
  182. address.Equals (Controller.PreviousAddress)) {
  183. tree.SetCursor (path, service_column, false);
  184. SparklePlugin plugin = (SparklePlugin) model.GetValue (iter, 2);
  185. if (plugin.Address != null) {
  186. address_entry.Sensitive = false;}
  187. if (plugin.Path != null)
  188. path_entry.Sensitive = false;
  189. // TODO: Scroll to the selection
  190. return true;
  191. } else {
  192. return false;
  193. }
  194. }));
  195. address_entry.Completion = new EntryCompletion();
  196. ListStore server_store = new ListStore (typeof (string));
  197. foreach (string host in Program.Controller.PreviousHosts)
  198. server_store.AppendValues (host);
  199. address_entry.Completion.Model = server_store;
  200. address_entry.Completion.TextColumn = 0;
  201. address_entry.Changed += delegate {
  202. Controller.CheckAddPage (address_entry.Text, path_entry.Text, tree.SelectedRow);
  203. };
  204. layout_address.PackStart (new Label () {
  205. Markup = "<b>" + _("Address") + "</b>",
  206. Xalign = 0
  207. }, true, true, 0);
  208. layout_address.PackStart (address_entry, true, true, 0);
  209. path_entry.Completion = new EntryCompletion();
  210. ListStore folder_store = new ListStore (typeof (string));
  211. //foreach (string host in Program.Controller.FolderPaths)
  212. // folder_store.AppendValues (host);
  213. path_entry.Completion.Model = folder_store;
  214. path_entry.Completion.TextColumn = 0;
  215. path_entry.Changed += delegate {
  216. Controller.CheckAddPage (address_entry.Text, path_entry.Text, tree.SelectedRow);
  217. };
  218. layout_path.PackStart (new Label () {
  219. Markup = "<b>" + _("Remote Path") + "</b>",
  220. Xalign = 0
  221. }, true, true, 0);
  222. layout_path.PackStart (path_entry, true, true, 0);
  223. layout_fields.PackStart (layout_address);
  224. layout_fields.PackStart (layout_path);
  225. layout_vertical.PackStart (new Label (""), false, false, 0);
  226. layout_vertical.PackStart (scrolled_window, true, true, 0);
  227. layout_vertical.PackStart (layout_fields, false, false, 0);
  228. Add (layout_vertical);
  229. // Cancel button
  230. Button cancel_button = new Button (_("Cancel"));
  231. cancel_button.Clicked += delegate {
  232. Close ();
  233. };
  234. // Sync button
  235. Button add_button = new Button (_("Add"));
  236. add_button.Clicked += delegate {
  237. string server = address_entry.Text;
  238. string folder_name = path_entry.Text;
  239. Controller.AddPageCompleted (server, folder_name);
  240. };
  241. AddButton (cancel_button);
  242. AddButton (add_button);
  243. Controller.CheckAddPage (address_entry.Text, path_entry.Text, tree.SelectedRow);
  244. break;
  245. }
  246. case PageType.Syncing: {
  247. Header = String.Format (_("Adding project ‘{0}’…"), Controller.SyncingFolder);
  248. Description = _("This may take a while.") + Environment.NewLine +
  249. _("Are you sure it’s not coffee o'clock?");
  250. Button finish_button = new Button () {
  251. Sensitive = false,
  252. Label = _("Finish")
  253. };
  254. Button cancel_button = new Button () {
  255. Label = _("Cancel")
  256. };
  257. cancel_button.Clicked += delegate {
  258. Controller.SyncingCancelled ();
  259. };
  260. AddButton (cancel_button);
  261. AddButton (finish_button);
  262. Controller.UpdateProgressBarEvent += delegate (double percentage) {
  263. Application.Invoke (delegate {
  264. this.progress_bar.Fraction = percentage / 100;
  265. });
  266. };
  267. if (this.progress_bar.Parent != null)
  268. (this.progress_bar.Parent as Container).Remove (this.progress_bar);
  269. VBox bar_wrapper = new VBox (false , 0);
  270. bar_wrapper.PackStart (this.progress_bar, false, false, 0);
  271. Add (bar_wrapper);
  272. break;
  273. }
  274. case PageType.Error: {
  275. Header = _("Something went wrong") + "…";
  276. VBox points = new VBox (false, 0);
  277. Image list_point_one = new Image (SparkleUIHelpers.GetIcon ("list-point", 16)) { };
  278. Image list_point_two = new Image (SparkleUIHelpers.GetIcon ("list-point", 16)) { };
  279. Image list_point_three = new Image (SparkleUIHelpers.GetIcon ("list-point", 16)) { };
  280. Label label_one = new Label () {
  281. Text = "First, have you tried turning it off and on again?",
  282. Wrap = true,
  283. Xalign = 0
  284. };
  285. Label label_two = new Label () {
  286. Markup = "<b>" + Controller.PreviousUrl + "</b> is the address we've compiled. " +
  287. "Does this look alright?",
  288. Wrap = true,
  289. Xalign = 0
  290. };
  291. Label label_three = new Label () {
  292. Text = "The host needs to know who you are. Did you upload the key that's in " +
  293. "your SparkleShare folder?",
  294. Wrap = true,
  295. Xalign = 0
  296. };
  297. points.PackStart (new Label ("Please check the following:") { Xalign = 0 }, false, false, 6);
  298. HBox point_one = new HBox (false, 0);
  299. point_one.PackStart (list_point_one, false, false, 0);
  300. point_one.PackStart (label_one, true, true, 12);
  301. points.PackStart (point_one, false, false, 12);
  302. HBox point_two = new HBox (false, 0);
  303. point_two.PackStart (list_point_two, false, false, 0);
  304. point_two.PackStart (label_two, true, true, 12);
  305. points.PackStart (point_two, false, false, 12);
  306. HBox point_three = new HBox (false, 0);
  307. point_three.PackStart (list_point_three, false, false, 0);
  308. point_three.PackStart (label_three, true, true, 12);
  309. points.PackStart (point_three, false, false, 12);
  310. points.PackStart (new Label (""), true, true, 0);
  311. Button try_again_button = new Button (_("Try Again…")) {
  312. Sensitive = true
  313. };
  314. try_again_button.Clicked += delegate {
  315. Controller.ErrorPageCompleted ();
  316. };
  317. AddButton (try_again_button);
  318. Add (points);
  319. break;
  320. }
  321. case PageType.Finished: {
  322. UrgencyHint = true;
  323. if (!HasToplevelFocus) {
  324. string title = String.Format (_("‘{0}’ has been successfully added"), Controller.SyncingFolder);
  325. string subtext = "";
  326. SparkleUI.Bubbles.Controller.ShowBubble (title, subtext, null);
  327. }
  328. Header = _("Project successfully added!");
  329. Description = _("Access the files from your SparkleShare folder.");
  330. // A button that opens the synced folder
  331. Button open_folder_button = new Button (_("Open Folder"));
  332. open_folder_button.Clicked += delegate {
  333. Program.Controller.OpenSparkleShareFolder (Controller.SyncingFolder);
  334. };
  335. Button finish_button = new Button (_("Finish"));
  336. finish_button.Clicked += delegate {
  337. Controller.FinishedPageCompleted ();
  338. Close ();
  339. };
  340. if (warnings != null) {
  341. Image warning_image = new Image (
  342. SparkleUIHelpers.GetIcon ("dialog-warning", 24)
  343. );
  344. Label warning_label = new Label (warnings [0]) {
  345. Xalign = 0,
  346. Wrap = true
  347. };
  348. HBox warning_layout = new HBox (false, 0);
  349. warning_layout.PackStart (warning_image, false, false, 0);
  350. warning_layout.PackStart (warning_label, true, true, 15);
  351. VBox warning_wrapper = new VBox (false, 0);
  352. warning_wrapper.PackStart (warning_layout, false, false, 0);
  353. Add (warning_wrapper);
  354. } else {
  355. Add (null);
  356. }
  357. AddButton (open_folder_button);
  358. AddButton (finish_button);
  359. break;
  360. }
  361. case PageType.Tutorial: {
  362. switch (Controller.TutorialPageNumber) {
  363. case 1: {
  364. Header = _("What's happening next?");
  365. Description = _("SparkleShare creates a special folder in your personal folder " +
  366. "that will keep track of your projects.");
  367. Button skip_tutorial_button = new Button (_("Skip Tutorial"));
  368. skip_tutorial_button.Clicked += delegate {
  369. Controller.TutorialSkipped ();
  370. };
  371. Button continue_button = new Button (_("Continue"));
  372. continue_button.Clicked += delegate {
  373. Controller.TutorialPageCompleted ();
  374. };
  375. Image slide = SparkleUIHelpers.GetImage ("tutorial-slide-1.png");
  376. Add (slide);
  377. AddButton (skip_tutorial_button);
  378. AddButton (continue_button);
  379. break;
  380. }
  381. case 2: {
  382. Header = _("Sharing files with others");
  383. Description = _("All files added to your project folders are synced with the host " +
  384. "automatically, as well as with your collaborators.");
  385. Button continue_button = new Button (_("Continue"));
  386. continue_button.Clicked += delegate {
  387. Controller.TutorialPageCompleted ();
  388. };
  389. Image slide = SparkleUIHelpers.GetImage ("tutorial-slide-2.png");
  390. Add (slide);
  391. AddButton (continue_button);
  392. break;
  393. }
  394. case 3: {
  395. Header = _("The status icon is here to help");
  396. Description = _("It shows the syncing process status, " +
  397. "and contains links to your projects and the event log.");
  398. Button continue_button = new Button (_("Continue"));
  399. continue_button.Clicked += delegate {
  400. Controller.TutorialPageCompleted ();
  401. };
  402. Image slide = SparkleUIHelpers.GetImage ("tutorial-slide-3.png");
  403. Add (slide);
  404. AddButton (continue_button);
  405. break;
  406. }
  407. case 4: {
  408. Header = _("Adding projects to SparkleShare");
  409. Description = _("Just click this button when you see it on the web, and " +
  410. "the project will be automatically added:");
  411. Label label = new Label (_("…or select <b>‘Add Hosted Project…’</b> from the status icon menu " +
  412. "to add one by hand.")) {
  413. Wrap = true,
  414. Xalign = 0,
  415. UseMarkup = true
  416. };
  417. Image slide = SparkleUIHelpers.GetImage ("tutorial-slide-4.png");
  418. Button finish_button = new Button (_("Finish"));
  419. finish_button.Clicked += delegate {
  420. Close ();
  421. };
  422. VBox box = new VBox (false, 0);
  423. box.Add (slide);
  424. box.Add (label);
  425. Add (box);
  426. AddButton (finish_button);
  427. break;
  428. }
  429. }
  430. break;
  431. }
  432. }
  433. ShowAll ();
  434. });
  435. };
  436. }
  437. private void RenderServiceColumn (TreeViewColumn column, CellRenderer cell,
  438. TreeModel model, TreeIter iter)
  439. {
  440. string markup = (string) model.GetValue (iter, 1);
  441. TreeSelection selection = (column.TreeView as TreeView).Selection;
  442. if (selection.IterIsSelected (iter))
  443. markup = markup.Replace (SecondaryTextColor, SecondaryTextColorSelected);
  444. else
  445. markup = markup.Replace (SecondaryTextColorSelected, SecondaryTextColor);
  446. (cell as CellRendererText).Markup = markup;
  447. }
  448. private Gdk.Color MixColors (Gdk.Color first_color, Gdk.Color second_color, double ratio)
  449. {
  450. return new Gdk.Color (
  451. Convert.ToByte ((255 * (Math.Min (65535, first_color.Red * (1.0 - ratio) + second_color.Red * ratio))) / 65535),
  452. Convert.ToByte ((255 * (Math.Min (65535, first_color.Green * (1.0 - ratio) + second_color.Green * ratio))) / 65535),
  453. Convert.ToByte ((255 * (Math.Min (65535, first_color.Blue * (1.0 - ratio) + second_color.Blue * ratio))) / 65535)
  454. );
  455. }
  456. }
  457. public class SparkleTreeView : TreeView {
  458. public int SelectedRow
  459. {
  460. get {
  461. TreeIter iter;
  462. TreeModel model;
  463. Selection.GetSelected (out model, out iter);
  464. return int.Parse (model.GetPath (iter).ToString ());
  465. }
  466. }
  467. public SparkleTreeView (ListStore store) : base (store)
  468. {
  469. }
  470. }
  471. }