/SparkleShare/SparkleSetup.cs
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 17 18using System; 19using System.Diagnostics; 20using System.IO; 21using System.Text.RegularExpressions; 22using System.Timers; 23using System.Collections.Generic; 24 25using Gtk; 26using Mono.Unix; 27 28namespace SparkleShare { 29 30 public class SparkleSetup : SparkleSetupWindow { 31 32 public SparkleSetupController Controller = new SparkleSetupController (); 33 34 private string SecondaryTextColor; 35 private string SecondaryTextColorSelected; 36 37 private ProgressBar progress_bar = new ProgressBar (); 38 39 40 // Short alias for the translations 41 public static string _ (string s) 42 { 43 return Catalog.GetString (s); 44 } 45 46 47 public SparkleSetup () : base () 48 { 49 SecondaryTextColor = SparkleUIHelpers.GdkColorToHex (Style.Foreground (StateType.Insensitive)); 50 SecondaryTextColorSelected = 51 SparkleUIHelpers.GdkColorToHex ( 52 MixColors ( 53 new TreeView ().Style.Foreground (StateType.Selected), 54 new TreeView ().Style.Background (StateType.Selected), 55 0.15 56 ) 57 ); 58 59 Controller.ChangePageEvent += delegate (PageType type, string [] warnings) { 60 Application.Invoke (delegate { 61 Reset (); 62 63 switch (type) { 64 case PageType.Setup: { 65 66 Header = _("Welcome to SparkleShare!"); 67 Description = "We'll need some info to mark your changes in the event log. " + 68 "Don't worry, this stays between you and your peers."; 69 70 71 Table table = new Table (2, 3, true) { 72 RowSpacing = 6, 73 ColumnSpacing = 6 74 }; 75 76 Label name_label = new Label ("<b>" + _("Full Name:") + "</b>") { 77 UseMarkup = true, 78 Xalign = 1 79 }; 80 81 Entry name_entry = new Entry (Controller.GuessedUserName) { 82 Xalign = 0 83 }; 84 85 Entry email_entry = new Entry (Controller.GuessedUserEmail) { 86 Xalign = 0 87 }; 88 89 name_entry.Changed += delegate { 90 Controller.CheckSetupPage (name_entry.Text, email_entry.Text); 91 }; 92 93 email_entry.Changed += delegate { 94 Controller.CheckSetupPage (name_entry.Text, email_entry.Text); 95 }; 96 97 Label email_label = new Label ("<b>" + _("Email:") + "</b>") { 98 UseMarkup = true, 99 Xalign = 1 100 }; 101 102 table.Attach (name_label, 0, 1, 0, 1); 103 table.Attach (name_entry, 1, 2, 0, 1); 104 table.Attach (email_label, 0, 1, 1, 2); 105 table.Attach (email_entry, 1, 2, 1, 2); 106 107 VBox wrapper = new VBox (false, 9); 108 wrapper.PackStart (table, true, false, 0); 109 110 Button continue_button = new Button (_("Continue")) { 111 Sensitive = false 112 }; 113 114 continue_button.Clicked += delegate (object o, EventArgs args) { 115 string full_name = name_entry.Text; 116 string email = email_entry.Text; 117 118 Controller.SetupPageCompleted (full_name, email); 119 }; 120 121 AddButton (continue_button); 122 Add (wrapper); 123 124 125 Controller.UpdateSetupContinueButtonEvent += delegate (bool button_enabled) { 126 Application.Invoke (delegate { 127 continue_button.Sensitive = button_enabled; 128 }); 129 }; 130 131 Controller.CheckSetupPage (name_entry.Text, email_entry.Text); 132 133 break; 134 } 135 136 case PageType.Add: { 137 138 Header = _("Where's your project hosted?"); 139 140 VBox layout_vertical = new VBox (false, 12); 141 HBox layout_fields = new HBox (true, 12); 142 VBox layout_address = new VBox (true, 0); 143 VBox layout_path = new VBox (true, 0); 144 145 146 ListStore store = new ListStore (typeof (Gdk.Pixbuf), 147 typeof (string), typeof (SparklePlugin)); 148 149 SparkleTreeView tree = new SparkleTreeView (store) { HeadersVisible = false }; 150 ScrolledWindow scrolled_window = new ScrolledWindow (); 151 scrolled_window.AddWithViewport (tree); 152 153 // Icon column 154 tree.AppendColumn ("Icon", new Gtk.CellRendererPixbuf (), "pixbuf", 0); 155 tree.Columns [0].Cells [0].Xpad = 6; 156 157 // Service column 158 TreeViewColumn service_column = new TreeViewColumn () { Title = "Service" }; 159 CellRendererText service_cell = new CellRendererText () { Ypad = 4 }; 160 service_column.PackStart (service_cell, true); 161 service_column.SetCellDataFunc (service_cell, new TreeCellDataFunc (RenderServiceColumn)); 162 163 foreach (SparklePlugin plugin in Controller.Plugins) { 164 store.AppendValues ( 165 new Gdk.Pixbuf (plugin.ImagePath), 166 "<span size=\"small\"><b>" + plugin.Name + "</b>\n" + 167 "<span fgcolor=\"" + SecondaryTextColorSelected + "\">" + 168 plugin.Description + "</span>" + 169 "</span>", 170 plugin); 171 } 172 173 tree.AppendColumn (service_column); 174 175 SparkleEntry path_entry = new SparkleEntry (); 176 SparkleEntry address_entry = new SparkleEntry (); 177 178 179 // Select the first plugin by default 180 TreeSelection default_selection = tree.Selection; 181 TreePath default_path = new TreePath ("0"); 182 default_selection.SelectPath (default_path); 183 Controller.SelectedPluginChanged (0); 184 185 Controller.ChangeAddressFieldEvent += delegate (string text, 186 string example_text, FieldState state) { 187 188 Application.Invoke (delegate { 189 address_entry.Text = text; 190 address_entry.Sensitive = (state == FieldState.Enabled); 191 192 if (string.IsNullOrEmpty (example_text)) 193 address_entry.ExampleText = null; 194 else 195 address_entry.ExampleText = example_text; 196 197 if (string.IsNullOrEmpty (text)) 198 address_entry.ExampleTextActive = true; 199 else 200 address_entry.ExampleTextActive = false; 201 }); 202 }; 203 204 Controller.ChangePathFieldEvent += delegate (string text, 205 string example_text, FieldState state) { 206 207 Application.Invoke (delegate { 208 path_entry.Text = text; 209 path_entry.Sensitive = (state == FieldState.Enabled); 210 211 if (string.IsNullOrEmpty (example_text)) 212 path_entry.ExampleText = null; 213 else 214 path_entry.ExampleText = example_text; 215 216 if (string.IsNullOrEmpty (text)) 217 path_entry.ExampleTextActive = true; 218 else 219 path_entry.ExampleTextActive = false; 220 }); 221 }; 222 223 // Update the address field text when the selection changes 224 tree.CursorChanged += delegate (object sender, EventArgs e) { 225 Controller.SelectedPluginChanged (tree.SelectedRow); 226 // TODO: Scroll to selected row when using arrow keys 227 }; 228 229 tree.Model.Foreach (new TreeModelForeachFunc (delegate (TreeModel model, 230 TreePath path, TreeIter iter) { 231 232 string address; 233 234 try { 235 address = (model.GetValue (iter, 2) as SparklePlugin).Address; 236 237 } catch (NullReferenceException) { 238 address = ""; 239 } 240 241 if (!string.IsNullOrEmpty (address) && 242 address.Equals (Controller.PreviousAddress)) { 243 244 tree.SetCursor (path, service_column, false); 245 SparklePlugin plugin = (SparklePlugin) model.GetValue (iter, 2); 246 247 if (plugin.Address != null) { 248 address_entry.Sensitive = false;} 249 250 if (plugin.Path != null) 251 path_entry.Sensitive = false; 252 253 // TODO: Scroll to the selection 254 255 return true; 256 } else { 257 return false; 258 } 259 })); 260 261 address_entry.Completion = new EntryCompletion(); 262 ListStore server_store = new ListStore (typeof (string)); 263 264 foreach (string host in Program.Controller.PreviousHosts) 265 server_store.AppendValues (host); 266 267 address_entry.Completion.Model = server_store; 268 address_entry.Completion.TextColumn = 0; 269 270 address_entry.Changed += delegate { 271 Controller.CheckAddPage (address_entry.Text, path_entry.Text, tree.SelectedRow); 272 }; 273 274 layout_address.PackStart (new Label () { 275 Markup = "<b>" + _("Address") + "</b>", 276 Xalign = 0 277 }, true, true, 0); 278 279 layout_address.PackStart (address_entry, true, true, 0); 280 281 path_entry.Completion = new EntryCompletion(); 282 283 ListStore folder_store = new ListStore (typeof (string)); 284 285 //foreach (string host in Program.Controller.FolderPaths) 286 // folder_store.AppendValues (host); 287 288 path_entry.Completion.Model = folder_store; 289 path_entry.Completion.TextColumn = 0; 290 291 path_entry.Changed += delegate { 292 Controller.CheckAddPage (address_entry.Text, path_entry.Text, tree.SelectedRow); 293 }; 294 295 layout_path.PackStart (new Label () { 296 Markup = "<b>" + _("Remote Path") + "</b>", 297 Xalign = 0 298 }, true, true, 0); 299 300 layout_path.PackStart (path_entry, true, true, 0); 301 302 layout_fields.PackStart (layout_address); 303 layout_fields.PackStart (layout_path); 304 305 layout_vertical.PackStart (new Label (""), false, false, 0); 306 layout_vertical.PackStart (scrolled_window, true, true, 0); 307 layout_vertical.PackStart (layout_fields, false, false, 0); 308 309 Add (layout_vertical); 310 311 // Cancel button 312 Button cancel_button = new Button (_("Cancel")); 313 314 cancel_button.Clicked += delegate { 315 Close (); 316 }; 317 318 // Sync button 319 Button add_button = new Button (_("Add")); 320 321 add_button.Clicked += delegate { 322 string server = address_entry.Text; 323 string folder_name = path_entry.Text; 324 325 Controller.AddPageCompleted (server, folder_name); 326 }; 327 328 AddButton (cancel_button); 329 AddButton (add_button); 330 331 Controller.CheckAddPage (address_entry.Text, path_entry.Text, tree.SelectedRow); 332 333 break; 334 } 335 336 case PageType.Syncing: { 337 338 Header = String.Format (_("Adding project {0} "), Controller.SyncingFolder); 339 Description = _("This may take a while.") + Environment.NewLine + 340 _("Are you sure its not coffee o'clock?"); 341 342 Button finish_button = new Button () { 343 Sensitive = false, 344 Label = _("Finish") 345 }; 346 347 Button cancel_button = new Button () { 348 Label = _("Cancel") 349 }; 350 351 cancel_button.Clicked += delegate { 352 Controller.SyncingCancelled (); 353 }; 354 355 AddButton (cancel_button); 356 AddButton (finish_button); 357 358 Controller.UpdateProgressBarEvent += delegate (double percentage) { 359 Application.Invoke (delegate { 360 this.progress_bar.Fraction = percentage / 100; 361 }); 362 }; 363 364 if (this.progress_bar.Parent != null) 365 (this.progress_bar.Parent as Container).Remove (this.progress_bar); 366 367 VBox bar_wrapper = new VBox (false , 0); 368 bar_wrapper.PackStart (this.progress_bar, false, false, 0); 369 370 Add (bar_wrapper); 371 372 break; 373 } 374 375 case PageType.Error: { 376 377 Header = _("Something went wrong") + " "; 378 379 VBox points = new VBox (false, 0); 380 Image list_point_one = new Image (SparkleUIHelpers.GetIcon ("list-point", 16)) { }; 381 Image list_point_two = new Image (SparkleUIHelpers.GetIcon ("list-point", 16)) { }; 382 Image list_point_three = new Image (SparkleUIHelpers.GetIcon ("list-point", 16)) { }; 383 384 Label label_one = new Label () { 385 Text = "First, have you tried turning it off and on again?", 386 Wrap = true, 387 Xalign = 0 388 }; 389 390 Label label_two = new Label () { 391 Markup = "<b>" + Controller.PreviousUrl + "</b> is the address we've compiled. " + 392 "Does this look alright?", 393 Wrap = true, 394 Xalign = 0 395 }; 396 397 Label label_three = new Label () { 398 Text = "The host needs to know who you are. Did you upload the key that's in " + 399 "your SparkleShare folder?", 400 Wrap = true, 401 Xalign = 0 402 }; 403 404 405 points.PackStart (new Label ("Please check the following:") { Xalign = 0 }, false, false, 6); 406 407 HBox point_one = new HBox (false, 0); 408 point_one.PackStart (list_point_one, false, false, 0); 409 point_one.PackStart (label_one, true, true, 12); 410 points.PackStart (point_one, false, false, 12); 411 412 HBox point_two = new HBox (false, 0); 413 point_two.PackStart (list_point_two, false, false, 0); 414 point_two.PackStart (label_two, true, true, 12); 415 points.PackStart (point_two, false, false, 12); 416 417 HBox point_three = new HBox (false, 0); 418 point_three.PackStart (list_point_three, false, false, 0); 419 point_three.PackStart (label_three, true, true, 12); 420 points.PackStart (point_three, false, false, 12); 421 422 points.PackStart (new Label (""), true, true, 0); 423 424 425 Button try_again_button = new Button (_("Try Again ")) { 426 Sensitive = true 427 }; 428 429 try_again_button.Clicked += delegate { 430 Controller.ErrorPageCompleted (); 431 }; 432 433 AddButton (try_again_button); 434 Add (points); 435 436 break; 437 } 438 439 case PageType.Finished: { 440 441 UrgencyHint = true; 442 443 if (!HasToplevelFocus) { 444 string title = String.Format (_("{0} has been successfully added"), Controller.SyncingFolder); 445 string subtext = ""; 446 447 SparkleUI.Bubbles.Controller.ShowBubble (title, subtext, null); 448 } 449 450 Header = _("Project successfully added!"); 451 Description = _("Access the files from your SparkleShare folder."); 452 453 // A button that opens the synced folder 454 Button open_folder_button = new Button (_("Open Folder")); 455 456 open_folder_button.Clicked += delegate { 457 Program.Controller.OpenSparkleShareFolder (Controller.SyncingFolder); 458 }; 459 460 Button finish_button = new Button (_("Finish")); 461 462 finish_button.Clicked += delegate { 463 Controller.FinishedPageCompleted (); 464 Close (); 465 }; 466 467 468 if (warnings != null) { 469 Image warning_image = new Image ( 470 SparkleUIHelpers.GetIcon ("dialog-warning", 24) 471 ); 472 473 Label warning_label = new Label (warnings [0]) { 474 Xalign = 0, 475 Wrap = true 476 }; 477 478 HBox warning_layout = new HBox (false, 0); 479 warning_layout.PackStart (warning_image, false, false, 0); 480 warning_layout.PackStart (warning_label, true, true, 15); 481 482 VBox warning_wrapper = new VBox (false, 0); 483 warning_wrapper.PackStart (warning_layout, false, false, 0); 484 485 Add (warning_wrapper); 486 487 } else { 488 Add (null); 489 } 490 491 492 AddButton (open_folder_button); 493 AddButton (finish_button); 494 495 break; 496 } 497 498 499 case PageType.Tutorial: { 500 501 switch (Controller.TutorialPageNumber) { 502 case 1: { 503 Header = _("What's happening next?"); 504 Description = _("SparkleShare creates a special folder in your personal folder " + 505 "that will keep track of your projects."); 506 507 Button skip_tutorial_button = new Button (_("Skip Tutorial")); 508 skip_tutorial_button.Clicked += delegate { 509 Controller.TutorialSkipped (); 510 }; 511 512 Button continue_button = new Button (_("Continue")); 513 continue_button.Clicked += delegate { 514 Controller.TutorialPageCompleted (); 515 }; 516 517 Image slide = SparkleUIHelpers.GetImage ("tutorial-slide-1.png"); 518 519 Add (slide); 520 521 AddButton (skip_tutorial_button); 522 AddButton (continue_button); 523 524 break; 525 } 526 527 case 2: { 528 Header = _("Sharing files with others"); 529 Description = _("All files added to your project folders are synced with the host " + 530 "automatically, as well as with your collaborators."); 531 532 Button continue_button = new Button (_("Continue")); 533 continue_button.Clicked += delegate { 534 Controller.TutorialPageCompleted (); 535 }; 536 537 Image slide = SparkleUIHelpers.GetImage ("tutorial-slide-2.png"); 538 539 Add (slide); 540 AddButton (continue_button); 541 542 break; 543 } 544 545 case 3: { 546 Header = _("The status icon is here to help"); 547 Description = _("It shows the syncing process status, " + 548 "and contains links to your projects and the event log."); 549 550 Button continue_button = new Button (_("Continue")); 551 continue_button.Clicked += delegate { 552 Controller.TutorialPageCompleted (); 553 }; 554 555 Image slide = SparkleUIHelpers.GetImage ("tutorial-slide-3.png"); 556 557 Add (slide); 558 AddButton (continue_button); 559 560 break; 561 } 562 563 case 4: { 564 Header = _("Adding projects to SparkleShare"); 565 Description = _("Just click this button when you see it on the web, and " + 566 "the project will be automatically added:"); 567 568 Label label = new Label (_(" or select <b>Add Hosted Project </b> from the status icon menu " + 569 "to add one by hand.")) { 570 Wrap = true, 571 Xalign = 0, 572 UseMarkup = true 573 }; 574 575 Image slide = SparkleUIHelpers.GetImage ("tutorial-slide-4.png"); 576 577 Button finish_button = new Button (_("Finish")); 578 finish_button.Clicked += delegate { 579 Close (); 580 }; 581 582 583 VBox box = new VBox (false, 0); 584 box.Add (slide); 585 box.Add (label); 586 587 Add (box); 588 AddButton (finish_button); 589 590 break; 591 } 592 } 593 594 break; 595 } 596 } 597 598 ShowAll (); 599 }); 600 }; 601 } 602 603 604 private void RenderServiceColumn (TreeViewColumn column, CellRenderer cell, 605 TreeModel model, TreeIter iter) 606 { 607 string markup = (string) model.GetValue (iter, 1); 608 TreeSelection selection = (column.TreeView as TreeView).Selection; 609 610 if (selection.IterIsSelected (iter)) 611 markup = markup.Replace (SecondaryTextColor, SecondaryTextColorSelected); 612 else 613 markup = markup.Replace (SecondaryTextColorSelected, SecondaryTextColor); 614 615 (cell as CellRendererText).Markup = markup; 616 } 617 618 619 private Gdk.Color MixColors (Gdk.Color first_color, Gdk.Color second_color, double ratio) 620 { 621 return new Gdk.Color ( 622 Convert.ToByte ((255 * (Math.Min (65535, first_color.Red * (1.0 - ratio) + second_color.Red * ratio))) / 65535), 623 Convert.ToByte ((255 * (Math.Min (65535, first_color.Green * (1.0 - ratio) + second_color.Green * ratio))) / 65535), 624 Convert.ToByte ((255 * (Math.Min (65535, first_color.Blue * (1.0 - ratio) + second_color.Blue * ratio))) / 65535) 625 ); 626 } 627 } 628 629 630 public class SparkleTreeView : TreeView { 631 632 public int SelectedRow 633 { 634 get { 635 TreeIter iter; 636 TreeModel model; 637 638 Selection.GetSelected (out model, out iter); 639 640 return int.Parse (model.GetPath (iter).ToString ()); 641 } 642 } 643 644 645 public SparkleTreeView (ListStore store) : base (store) 646 { 647 } 648 } 649}