PageRenderTime 56ms CodeModel.GetById 16ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 0ms

/SparkleShare/Mac/SparkleSetup.cs

http://github.com/hbons/SparkleShare
C# | 878 lines | 632 code | 228 blank | 18 comment | 63 complexity | e8caed08f4f348107f7b9cf6f8fd7711 MD5 | raw file
  1//   SparkleShare, a collaboration and sharing tool.
  2//   Copyright (C) 2010  Hylke Bons <hylkebons@gmail.com>
  3//
  4//   This program is free software: you can redistribute it and/or modify
  5//   it under the terms of the GNU General Public License as published by
  6//   the Free Software Foundation, either version 3 of the License, or
  7//   (at your option) any later version.
  8//
  9//   This program is distributed in the hope that it will be useful,
 10//   but WITHOUT ANY WARRANTY; without even the implied warranty of
 11//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 12//   GNU General Public License for more details.
 13//
 14//   You should have received a copy of the GNU General Public License
 15//   along with this program. If not, see <http://www.gnu.org/licenses/>.
 16
 17
 18using System;
 19using System.Collections.Generic;
 20using System.Drawing;
 21using System.IO;
 22
 23using MonoMac.Foundation;
 24using MonoMac.AppKit;
 25using MonoMac.WebKit;
 26
 27using Mono.Unix;
 28
 29namespace SparkleShare {
 30
 31    public class SparkleSetup : SparkleSetupWindow {
 32
 33        public SparkleSetupController Controller = new SparkleSetupController ();
 34
 35        private NSButton ContinueButton, AddButton, CopyButton, TryAgainButton, CancelButton, 
 36            SkipTutorialButton, FinishButton, ShowFilesButton;
 37
 38        private NSTextField FullNameTextField, FullNameLabel, EmailLabel, EmailTextField,
 39        LinkCodeTextField, AddressTextField, AddressLabel, AddressHelpLabel, PathTextField, PathLabel,
 40        PathHelpLabel, ProgressLabel, PasswordTextField, VisiblePasswordTextField, PasswordLabel, WarningTextField;
 41
 42        private NSButton StartupCheckButton, HistoryCheckButton, ShowPasswordCheckButton;
 43        private NSProgressIndicator ProgressIndicator;
 44        private NSImage WarningImage, SlideImage;
 45        private NSImageView WarningImageView, SlideImageView;
 46        private NSTableColumn IconColumn, DescriptionColumn;
 47        private NSTableView TableView;
 48        private NSScrollView ScrollView;
 49        private SparkleDataSource DataSource;
 50
 51
 52        public SparkleSetup () : base ()
 53        {
 54            Controller.HideWindowEvent += delegate {
 55                Program.Controller.Invoke (() => PerformClose (this));
 56            };
 57
 58            Controller.ShowWindowEvent += delegate {
 59                Program.Controller.Invoke (() => OrderFrontRegardless ());
 60            };
 61
 62            Controller.ChangePageEvent += delegate (PageType type, string [] warnings) {
 63                Program.Controller.Invoke (() => {
 64                    Reset ();
 65                    ShowPage (type, warnings);
 66                    ShowAll ();
 67                });
 68            };
 69        }
 70
 71
 72        public void ShowPage (PageType type, string [] warnings)
 73        {
 74            if (type == PageType.Setup) {
 75                Header      = "Welcome to SparkleShare!";
 76                Description = "First off, what’s your name and email?\n(visible only to team members)";
 77
 78                FullNameLabel       = new SparkleLabel ("Full Name:", NSTextAlignment.Right);
 79                FullNameLabel.Frame = new RectangleF (165, Frame.Height - 234, 160, 17);
 80
 81                FullNameTextField = new NSTextField () {
 82                    Frame       = new RectangleF (330, Frame.Height - 238, 196, 22),
 83                    StringValue = UnixUserInfo.GetRealUser ().RealName,
 84                    Delegate    = new SparkleTextFieldDelegate ()
 85                };
 86
 87                EmailLabel       = new SparkleLabel ("Email:", NSTextAlignment.Right);
 88                EmailLabel.Frame = new RectangleF (165, Frame.Height - 264, 160, 17);
 89                    
 90                EmailTextField = new NSTextField () {
 91                    Frame       = new RectangleF (330, Frame.Height - 268, 196, 22),
 92                    Delegate    = new SparkleTextFieldDelegate ()
 93                };
 94
 95                CancelButton = new NSButton () { Title = "Cancel" };
 96
 97                ContinueButton = new NSButton () {
 98                    Title    = "Continue",
 99                    Enabled  = false
100                };
101
102
103                (FullNameTextField.Delegate as SparkleTextFieldDelegate).StringValueChanged += delegate {
104                    Controller.CheckSetupPage (FullNameTextField.StringValue, EmailTextField.StringValue);
105                };
106
107                (EmailTextField.Delegate as SparkleTextFieldDelegate).StringValueChanged += delegate {
108                    Controller.CheckSetupPage (FullNameTextField.StringValue, EmailTextField.StringValue);
109                };
110
111                ContinueButton.Activated += delegate {
112                    string full_name = FullNameTextField.StringValue.Trim ();
113                    string email     = EmailTextField.StringValue.Trim ();
114
115                    Controller.SetupPageCompleted (full_name, email);
116                };
117
118                CancelButton.Activated += delegate { Controller.SetupPageCancelled (); };
119
120                Controller.UpdateSetupContinueButtonEvent += delegate (bool button_enabled) {
121                    Program.Controller.Invoke (() => {
122                        ContinueButton.Enabled = button_enabled;
123                    });
124                };
125
126
127                ContentView.AddSubview (FullNameLabel);
128                ContentView.AddSubview (FullNameTextField);
129                ContentView.AddSubview (EmailLabel);
130                ContentView.AddSubview (EmailTextField);
131
132                Buttons.Add (ContinueButton);
133                Buttons.Add (CancelButton);
134
135                Controller.CheckSetupPage (FullNameTextField.StringValue, EmailTextField.StringValue);
136
137                if (FullNameTextField.StringValue.Equals (""))
138                    MakeFirstResponder ((NSResponder) FullNameTextField);
139                else
140                    MakeFirstResponder ((NSResponder) EmailTextField);
141            }
142
143            if (type == PageType.Invite) {
144                Header      = "You’ve received an invite!";
145                Description = "Do you want to add this project to SparkleShare?";
146
147                AddressLabel       = new SparkleLabel ("Address:", NSTextAlignment.Right);
148                AddressLabel.Frame = new RectangleF (165, Frame.Height - 238, 160, 17);
149                AddressLabel.Font  = NSFont.FromFontName (SparkleUI.FontName + " Bold", NSFont.SystemFontSize);
150     
151                AddressTextField = new SparkleLabel (Controller.PendingInvite.Address, NSTextAlignment.Left) {
152                    Frame = new RectangleF (330, Frame.Height - 240, 260, 17)
153                };
154
155                PathLabel       = new SparkleLabel ("Remote Path:", NSTextAlignment.Right);
156                PathLabel.Frame = new RectangleF (165, Frame.Height - 262, 160, 17);
157                PathLabel.Font  = NSFont.FromFontName (SparkleUI.FontName + " Bold", NSFont.SystemFontSize);
158
159
160                PathTextField = new SparkleLabel (Controller.PendingInvite.RemotePath, NSTextAlignment.Left) {
161                    Frame = new RectangleF (330, Frame.Height - 264, 260, 17)
162                };
163
164                CancelButton = new NSButton () { Title = "Cancel" };
165                AddButton = new NSButton () { Title = "Add" };
166
167
168                CancelButton.Activated += delegate { Controller.PageCancelled (); };
169                AddButton.Activated += delegate { Controller.InvitePageCompleted (); };
170
171
172                ContentView.AddSubview (AddressLabel);
173                ContentView.AddSubview (PathLabel);
174                ContentView.AddSubview (AddressTextField);
175                ContentView.AddSubview (PathTextField);
176
177                Buttons.Add (AddButton);
178                Buttons.Add (CancelButton);
179            }
180
181            if (type == PageType.Add) {
182                Header      = "Where’s your project hosted?";
183                Description = "";
184
185                AddressLabel = new SparkleLabel ("Address:", NSTextAlignment.Left) {
186                    Frame = new RectangleF (190, Frame.Height - 308, 160, 17),
187                    Font  = NSFont.FromFontName (SparkleUI.FontName + " Bold", NSFont.SystemFontSize)
188                };
189
190                AddressTextField = new NSTextField () {
191                    Frame       = new RectangleF (190, Frame.Height - 336, 196, 22),
192                    Enabled     = (Controller.SelectedPlugin.Address == null),
193                    Delegate    = new SparkleTextFieldDelegate (),
194                    StringValue = "" + Controller.PreviousAddress
195                };
196
197                AddressTextField.Cell.LineBreakMode = NSLineBreakMode.TruncatingTail;
198
199                PathLabel = new SparkleLabel ("Remote Path:", NSTextAlignment.Left) {
200                    Frame = new RectangleF (190 + 196 + 16, Frame.Height - 308, 160, 17),
201                    Font  = NSFont.FromFontName (SparkleUI.FontName + " Bold", NSFont.SystemFontSize)
202                };
203
204                PathTextField = new NSTextField () {
205                    Frame       = new RectangleF (190 + 196 + 16, Frame.Height - 336, 196, 22),
206                    Enabled     = (Controller.SelectedPlugin.Path == null),
207                    Delegate    = new SparkleTextFieldDelegate (),
208                    StringValue = "" + Controller.PreviousPath
209                };
210
211                PathTextField.Cell.LineBreakMode = NSLineBreakMode.TruncatingTail;
212
213                PathHelpLabel = new SparkleLabel (Controller.SelectedPlugin.PathExample, NSTextAlignment.Left) {
214                    TextColor       = NSColor.DisabledControlText,
215                    Frame           = new RectangleF (190 + 196 + 16, Frame.Height - 358, 204, 19)
216                };
217
218                AddressHelpLabel = new SparkleLabel (Controller.SelectedPlugin.AddressExample, NSTextAlignment.Left) {
219                    TextColor       = NSColor.DisabledControlText,
220                    Frame           = new RectangleF (190, Frame.Height - 358, 204, 19)
221                };
222
223                if (TableView == null || TableView.RowCount != Controller.Plugins.Count) {
224                    TableView = new NSTableView () {
225                        Frame            = new RectangleF (0, 0, 0, 0),
226                        RowHeight        = 38,
227                        IntercellSpacing = new SizeF (8, 12),
228                        HeaderView       = null,
229                        Delegate         = new SparkleTableViewDelegate ()
230                    };
231
232                    ScrollView = new NSScrollView () {
233                        Frame               = new RectangleF (190, Frame.Height - 280, 408, 185),
234                        DocumentView        = TableView,
235                        HasVerticalScroller = true,
236                        BorderType          = NSBorderType.BezelBorder
237                    };
238
239                    IconColumn = new NSTableColumn () {
240                        Width         = 36,
241                        HeaderToolTip = "Icon",
242                        DataCell      = new NSImageCell () { ImageAlignment = NSImageAlignment.Right }
243                    };
244
245                    DescriptionColumn = new NSTableColumn () {
246                        Width         = 350,
247                        HeaderToolTip = "Description",
248                        Editable      = false
249                    };
250
251                    DescriptionColumn.DataCell.Font = NSFontManager.SharedFontManager.FontWithFamily (
252                        SparkleUI.FontName, NSFontTraitMask.Condensed, 0, 11);
253
254                    TableView.AddColumn (IconColumn);
255                    TableView.AddColumn (DescriptionColumn);
256
257                    // Hi-res display support was added after Snow Leopard
258                    if (Environment.OSVersion.Version.Major < 11)
259                        DataSource = new SparkleDataSource (1, Controller.Plugins);
260                    else
261                        DataSource = new SparkleDataSource (BackingScaleFactor, Controller.Plugins);
262
263                    TableView.DataSource = DataSource;
264                    TableView.ReloadData ();
265                    
266                    (TableView.Delegate as SparkleTableViewDelegate).SelectionChanged += delegate {
267                        Controller.SelectedPluginChanged (TableView.SelectedRow);
268                        Controller.CheckAddPage (AddressTextField.StringValue, PathTextField.StringValue, TableView.SelectedRow);
269                    };
270                }
271                
272                TableView.SelectRow (Controller.SelectedPluginIndex, false);
273                TableView.ScrollRowToVisible (Controller.SelectedPluginIndex);
274                MakeFirstResponder ((NSResponder) TableView);
275
276                HistoryCheckButton = new NSButton () {
277                    Frame = new RectangleF (190, Frame.Height - 400, 300, 18),
278                    Title = "Fetch prior revisions"
279                };
280
281                if (Controller.FetchPriorHistory)
282                    HistoryCheckButton.State = NSCellStateValue.On;
283
284                HistoryCheckButton.SetButtonType (NSButtonType.Switch);
285
286                AddButton = new NSButton () {
287                    Title = "Add",
288                    Enabled = false
289                };
290
291                CancelButton = new NSButton () { Title = "Cancel" };
292
293
294                Controller.ChangeAddressFieldEvent += delegate (string text, string example_text, FieldState state) {
295                    Program.Controller.Invoke (() => {
296                        AddressTextField.StringValue = text;
297                        AddressTextField.Enabled     = (state == FieldState.Enabled);
298                        AddressHelpLabel.StringValue = example_text;
299                    });
300                };
301
302                Controller.ChangePathFieldEvent += delegate (string text, string example_text, FieldState state) {
303                    Program.Controller.Invoke (() => {
304                        PathTextField.StringValue = text;
305                        PathTextField.Enabled     = (state == FieldState.Enabled);
306                        PathHelpLabel.StringValue = example_text;
307                    });
308                };
309
310
311                (AddressTextField.Delegate as SparkleTextFieldDelegate).StringValueChanged += delegate {
312                    Controller.CheckAddPage (AddressTextField.StringValue, PathTextField.StringValue, TableView.SelectedRow);
313                };
314
315                 (PathTextField.Delegate as SparkleTextFieldDelegate).StringValueChanged += delegate {
316                    Controller.CheckAddPage (AddressTextField.StringValue, PathTextField.StringValue, TableView.SelectedRow);
317                };
318
319
320                HistoryCheckButton.Activated += delegate {
321                    Controller.HistoryItemChanged (HistoryCheckButton.State == NSCellStateValue.On);
322                };
323
324                AddButton.Activated += delegate {
325                    Controller.AddPageCompleted (AddressTextField.StringValue, PathTextField.StringValue);
326                };
327
328                CancelButton.Activated += delegate { Controller.PageCancelled (); };
329
330                Controller.UpdateAddProjectButtonEvent += delegate (bool button_enabled) {
331                    Program.Controller.Invoke (() => {
332                        AddButton.Enabled = button_enabled;
333                    });
334                };
335
336                ContentView.AddSubview (ScrollView);
337                ContentView.AddSubview (AddressLabel);
338                ContentView.AddSubview (AddressTextField);
339                ContentView.AddSubview (AddressHelpLabel);
340                ContentView.AddSubview (PathLabel);
341                ContentView.AddSubview (PathTextField);
342                ContentView.AddSubview (PathHelpLabel);
343                ContentView.AddSubview (HistoryCheckButton);
344
345                Buttons.Add (AddButton);
346                Buttons.Add (CancelButton);
347
348                Controller.CheckAddPage (AddressTextField.StringValue, PathTextField.StringValue, TableView.SelectedRow);
349            }
350
351            if (type == PageType.Syncing) {
352                Header      = "Adding project ‘" + Controller.SyncingFolder + "’…";
353                Description = "This may take a while for large projects.\nIsn’t it coffee-o’clock?";
354
355                ProgressIndicator = new NSProgressIndicator () {
356                    Frame         = new RectangleF (190, Frame.Height - 200, 640 - 150 - 80, 20),
357                    Style         = NSProgressIndicatorStyle.Bar,
358                    MinValue      = 0.0,
359                    MaxValue      = 100.0,
360                    Indeterminate = false,
361                    DoubleValue   = Controller.ProgressBarPercentage
362                };
363
364                ProgressIndicator.StartAnimation (this);
365
366                CancelButton = new NSButton () { Title = "Cancel" };
367
368                FinishButton = new NSButton () {
369                    Title = "Finish",
370                    Enabled = false
371                };
372
373                ProgressLabel       = new SparkleLabel ("Preparing to fetch files…", NSTextAlignment.Right);
374                ProgressLabel.Frame = new RectangleF (Frame.Width - 40 - 250, 185, 250, 25);
375
376
377                Controller.UpdateProgressBarEvent += delegate (double percentage, string speed) {
378                    Program.Controller.Invoke (() => {
379                        ProgressIndicator.DoubleValue = percentage;
380                        ProgressLabel.StringValue     = speed;
381                    });
382                };
383
384
385                CancelButton.Activated += delegate { Controller.SyncingCancelled (); };
386
387
388                ContentView.AddSubview (ProgressLabel);
389                ContentView.AddSubview (ProgressIndicator);
390
391                Buttons.Add (FinishButton);
392                Buttons.Add (CancelButton);
393            }
394
395            if (type == PageType.Error) {
396                Header      = "Oops! Something went wrong…";
397                Description = "Please check the following:";
398
399                // Displaying marked up text with Cocoa is
400                // a pain, so we just use a webview instead
401                WebView web_view = new WebView ();
402                web_view.Frame   = new RectangleF (190, Frame.Height - 525, 375, 400);
403
404                string html = "<style>" +
405                    "* {" +
406                    "  font-family: '" + SparkleUI.FontName + "';" +
407                    "  font-size: 12px; cursor: default;" +
408                    "}" +
409                    "body {" +
410                    "  -webkit-user-select: none;" +
411                    "  margin: 0;" +
412                    "  padding: 3px;" +
413                    "}" +
414                    "li {" +
415                    "  margin-bottom: 16px;" +
416                    "  margin-left: 0;" +
417                    "  padding-left: 0;" +
418                    "  line-height: 20px;" +
419                    "  word-wrap: break-word;" +
420                    "}" +
421                    "ul {" +
422                    "  padding-left: 24px;" +
423                    "}" +
424                    "</style>" +
425                    "<ul>" +
426                    "  <li><b>" + Controller.PreviousUrl + "</b> is the address we’ve compiled. Does this look alright?</li>" +
427                    "  <li>Is this computer’s Client ID known by the host?</li>" +
428                    "</ul>";
429
430                if (warnings.Length > 0) {
431                    string warnings_markup = "";
432
433                    foreach (string warning in warnings)
434                        warnings_markup += "<br><b>" + warning + "</b>";
435
436                    html = html.Replace ("</ul>", "<li>Here’s the raw error message: " + warnings_markup + "</li></ul>");
437                }
438
439                web_view.MainFrame.LoadHtmlString (html, new NSUrl (""));
440                web_view.DrawsBackground = false;
441
442                CancelButton = new NSButton () { Title = "Cancel" };
443                TryAgainButton = new NSButton () { Title = "Try Again…" };
444
445
446                CancelButton.Activated += delegate { Controller.PageCancelled (); };
447                TryAgainButton.Activated += delegate { Controller.ErrorPageCompleted (); };
448
449
450                ContentView.AddSubview (web_view);
451
452                Buttons.Add (TryAgainButton);
453                Buttons.Add (CancelButton);
454            }
455
456            if (type == PageType.CryptoSetup || type == PageType.CryptoPassword) {
457                if (type == PageType.CryptoSetup) {
458                    Header      = "Set up file encryption";
459                    Description = "Please a provide a strong password that you don’t use elsewhere.";
460                
461                } else {
462                    Header      = "This project contains encrypted files";
463                    Description = "Please enter the password to see their contents.";
464                }
465
466                int extra_pos_y = 0;
467
468                if (type == PageType.CryptoPassword)
469                    extra_pos_y = 20;
470  
471                PasswordLabel = new SparkleLabel ("Password:", NSTextAlignment.Right) {
472                    Frame           = new RectangleF (155, Frame.Height - 202 - extra_pos_y, 160, 17),
473                    Font            = NSFont.FromFontName (SparkleUI.FontName + " Bold", NSFont.SystemFontSize)
474                };
475
476                PasswordTextField = new NSSecureTextField () {
477                    Frame       = new RectangleF (320, Frame.Height - 208 - extra_pos_y, 196, 22),
478                    Delegate    = new SparkleTextFieldDelegate ()
479                };
480
481                VisiblePasswordTextField = new NSTextField () {
482                    Frame       = new RectangleF (320, Frame.Height - 208 - extra_pos_y, 196, 22),
483                    Delegate    = new SparkleTextFieldDelegate ()
484                };
485
486                ShowPasswordCheckButton = new NSButton () {
487                    Frame = new RectangleF (318, Frame.Height - 235 - extra_pos_y, 300, 18),
488                    Title = "Show password",
489                    State = NSCellStateValue.Off
490                };
491
492                ShowPasswordCheckButton.SetButtonType (NSButtonType.Switch);
493
494                WarningImage = NSImage.ImageNamed ("NSInfo");
495                WarningImage.Size = new SizeF (24, 24);
496
497                WarningImageView = new NSImageView () {
498                    Image = WarningImage,
499                    Frame = new RectangleF (200, Frame.Height - 320, 24, 24)
500                };
501
502                WarningTextField = new SparkleLabel ("This password can’t be changed later, and your files can’t be recovered if it’s forgotten.", NSTextAlignment.Left) {
503                    Frame = new RectangleF (235, Frame.Height - 390, 325, 100),
504                };
505
506                CancelButton = new NSButton () { Title = "Cancel" };
507
508                ContinueButton = new NSButton () {
509                    Title    = "Continue",
510                    Enabled  = false
511                };
512
513
514                Controller.UpdateCryptoPasswordContinueButtonEvent += delegate (bool button_enabled) {
515                    Program.Controller.Invoke (() => { ContinueButton.Enabled = button_enabled; });
516                };
517
518                Controller.UpdateCryptoSetupContinueButtonEvent += delegate (bool button_enabled) {
519                    Program.Controller.Invoke (() => { ContinueButton.Enabled = button_enabled; });
520                };
521                
522                ShowPasswordCheckButton.Activated += delegate {
523                    if (PasswordTextField.Superview == ContentView) {
524                        PasswordTextField.RemoveFromSuperview ();
525                        ContentView.AddSubview (VisiblePasswordTextField);
526
527                    } else {
528                        VisiblePasswordTextField.RemoveFromSuperview ();
529                        ContentView.AddSubview (PasswordTextField);
530                    }
531                };
532
533                (PasswordTextField.Delegate as SparkleTextFieldDelegate).StringValueChanged += delegate {
534                    VisiblePasswordTextField.StringValue = PasswordTextField.StringValue;
535
536                    if (type == PageType.CryptoSetup)
537                        Controller.CheckCryptoSetupPage (PasswordTextField.StringValue);
538                    else
539                        Controller.CheckCryptoPasswordPage (PasswordTextField.StringValue);
540                };
541
542                (VisiblePasswordTextField.Delegate as SparkleTextFieldDelegate).StringValueChanged += delegate {
543                    PasswordTextField.StringValue = VisiblePasswordTextField.StringValue;
544                    
545                    if (type == PageType.CryptoSetup)
546                        Controller.CheckCryptoSetupPage (PasswordTextField.StringValue);
547                    else
548                        Controller.CheckCryptoPasswordPage (PasswordTextField.StringValue);
549                };
550
551                ContinueButton.Activated += delegate {
552                    if (type == PageType.CryptoSetup)
553                        Controller.CryptoSetupPageCompleted (PasswordTextField.StringValue);
554                    else
555                        Controller.CryptoPasswordPageCompleted (PasswordTextField.StringValue);
556                };
557
558                CancelButton.Activated += delegate { Controller.CryptoPageCancelled (); };
559
560
561                ContentView.AddSubview (PasswordLabel);
562                ContentView.AddSubview (PasswordTextField);
563                ContentView.AddSubview (ShowPasswordCheckButton);
564
565                if (type == PageType.CryptoSetup) {
566                    ContentView.AddSubview (WarningImageView);
567                    ContentView.AddSubview (WarningTextField);
568                }
569
570                Buttons.Add (ContinueButton);
571                Buttons.Add (CancelButton);
572
573                MakeFirstResponder ((NSResponder) PasswordTextField);
574                NSApplication.SharedApplication.RequestUserAttention (NSRequestUserAttentionType.CriticalRequest);
575            }
576
577
578            if (type == PageType.Finished) {
579                Header      = "Your shared project is ready!";
580                Description = "You can find the files in your SparkleShare folder.";
581
582                if (warnings.Length > 0) {
583                    WarningImage = NSImage.ImageNamed ("NSInfo");
584                    WarningImage.Size = new SizeF (24, 24);
585
586                    WarningImageView = new NSImageView () {
587                        Image = WarningImage,
588                        Frame = new RectangleF (200, Frame.Height - 175, 24, 24)
589                    };
590
591                    WarningTextField = new SparkleLabel (warnings [0], NSTextAlignment.Left);
592                    WarningTextField.Frame       = new RectangleF (235, Frame.Height - 245, 325, 100);
593
594                    ContentView.AddSubview (WarningImageView);
595                    ContentView.AddSubview (WarningTextField);
596                }
597
598                ShowFilesButton = new NSButton () { Title = "Show Files…" };
599                FinishButton    = new NSButton () { Title = "Finish" };
600
601
602                ShowFilesButton.Activated += delegate { Controller.ShowFilesClicked (); };
603                FinishButton.Activated += delegate { Controller.FinishPageCompleted (); };
604
605
606                Buttons.Add (FinishButton);
607                Buttons.Add (ShowFilesButton);
608
609                NSApplication.SharedApplication.RequestUserAttention (NSRequestUserAttentionType.CriticalRequest);
610            }
611
612            if (type == PageType.Tutorial) {
613                SlideImage = NSImage.ImageNamed ("tutorial-slide-" + Controller.TutorialPageNumber);
614                if (SlideImage != null) {
615                    SlideImage.Size = new SizeF (324, 200);
616
617                    SlideImageView = new NSImageView () {
618                        Image = SlideImage,
619                        Frame = new RectangleF (228, Frame.Height - 350, 324, 200)
620                    };
621
622                    ContentView.AddSubview (SlideImageView);
623                }
624
625                switch (Controller.TutorialPageNumber) {
626                    case 1: {
627                        Header      = "What’s happening next?";
628                        Description = "SparkleShare creates a special folder on your computer " +
629                            "that will keep track of your projects.";
630
631                        SkipTutorialButton = new NSButton () { Title = "Skip Tutorial" };
632                        ContinueButton     = new NSButton () { Title = "Continue" };
633
634
635                        SkipTutorialButton.Activated += delegate { Controller.TutorialSkipped (); };
636                        ContinueButton.Activated     += delegate { Controller.TutorialPageCompleted (); };
637
638
639                        ContentView.AddSubview (SlideImageView);
640
641                        Buttons.Add (ContinueButton);
642                        Buttons.Add (SkipTutorialButton);
643
644                        break;
645                    }
646
647                    case 2: {
648                        Header      = "Sharing files with others";
649                        Description = "All files added to your project folders are synced automatically with " +
650                            "the host and your team members.";
651
652                        ContinueButton = new NSButton () { Title = "Continue" };
653                        ContinueButton.Activated += delegate { Controller.TutorialPageCompleted (); };
654                        Buttons.Add (ContinueButton);
655
656                        break;
657                    }
658
659                    case 3: {
660                        Header      = "The status icon helps you";
661                        Description = "It shows the syncing progress, provides easy access to " +
662                            "your projects, and lets you view recent changes.";
663
664                        ContinueButton = new NSButton () { Title = "Continue" };
665                        ContinueButton.Activated += delegate { Controller.TutorialPageCompleted (); };
666                        Buttons.Add (ContinueButton);
667
668                        break;
669                    }
670
671                    case 4: {
672                        Header      = "Here’s your unique Client ID";
673                        Description = "You’ll need it whenever you want to link this computer to a host. " +
674                            "You can also find it in the status icon menu.";
675
676                        LinkCodeTextField = new NSTextField () {
677                            StringValue = Program.Controller.CurrentUser.PublicKey,
678                            Enabled     = false,
679                            Selectable  = false,
680                            Frame       = new RectangleF (230, Frame.Height - 238, 246, 22)
681                        };
682
683                        LinkCodeTextField.Cell.UsesSingleLineMode = true;
684                        LinkCodeTextField.Cell.LineBreakMode      = NSLineBreakMode.TruncatingTail;
685                        
686                        CopyButton = new NSButton () {
687                            Title      = "Copy",
688                            BezelStyle = NSBezelStyle.RoundRect,
689                            Frame      = new RectangleF (480, Frame.Height - 238, 60, 22)
690                        };
691                        
692                        StartupCheckButton = new NSButton () {
693                            Frame = new RectangleF (190, Frame.Height - 400, 300, 18),
694                            Title = "Add SparkleShare to startup items",
695                            State = NSCellStateValue.On
696                        };
697
698                        StartupCheckButton.SetButtonType (NSButtonType.Switch);
699
700                        FinishButton = new NSButton () { Title = "Finish" };
701
702
703                        StartupCheckButton.Activated += delegate {
704                            Controller.StartupItemChanged (StartupCheckButton.State == NSCellStateValue.On);
705                        };
706
707                        CopyButton.Activated += delegate { Controller.CopyToClipboardClicked (); };
708                        FinishButton.Activated += delegate { Controller.TutorialPageCompleted (); };
709
710
711                        ContentView.AddSubview (LinkCodeTextField);
712                        ContentView.AddSubview (CopyButton);
713                        ContentView.AddSubview (StartupCheckButton);
714
715                        Buttons.Add (FinishButton);
716
717                        break;
718                    }
719                }
720            }
721        }
722    }
723
724
725    [Register("SparkleDataSource")]
726    public class SparkleDataSource : NSTableViewDataSource {
727
728        public List<object> Items;
729        public NSAttributedString [] Cells, SelectedCells;
730
731        int backing_scale_factor;
732
733        public SparkleDataSource (float backing_scale_factor, List<SparklePlugin> plugins)
734        {
735            Items         = new List <object> ();
736            Cells         = new NSAttributedString [plugins.Count];
737            SelectedCells = new NSAttributedString [plugins.Count];
738
739            this.backing_scale_factor = (int) backing_scale_factor;
740
741            int i = 0;
742            foreach (SparklePlugin plugin in plugins) {
743                Items.Add (plugin);
744
745                NSTextFieldCell cell = new NSTextFieldCell ();
746
747                NSData name_data = NSData.FromString ("<font face='" + SparkleUI.FontName + "'><b>" + plugin.Name + "</b></font>");
748
749                NSDictionary name_dictionary       = new NSDictionary();
750                NSAttributedString name_attributes = new NSAttributedString (
751                    name_data, new NSUrl ("file://"), out name_dictionary);
752
753                NSData description_data = NSData.FromString (
754                    "<small><font style='line-height: 150%' color='#aaa' face='" + SparkleUI.FontName + "'>" + plugin.Description + "</font></small>");
755
756                NSDictionary description_dictionary       = new NSDictionary();
757                NSAttributedString description_attributes = new NSAttributedString (
758                    description_data, new NSUrl ("file://"), out description_dictionary);
759
760                NSMutableAttributedString mutable_attributes = new NSMutableAttributedString (name_attributes);
761                mutable_attributes.Append (new NSAttributedString ("\n"));
762                mutable_attributes.Append (description_attributes);
763
764                cell.AttributedStringValue = mutable_attributes;
765                Cells [i] = (NSAttributedString) cell.ObjectValue;
766
767                NSTextFieldCell selected_cell = new NSTextFieldCell ();
768
769                NSData selected_name_data = NSData.FromString (
770                    "<font color='white' face='" + SparkleUI.FontName +"'><b>" + plugin.Name + "</b></font>");
771
772                NSDictionary selected_name_dictionary = new NSDictionary ();
773                NSAttributedString selected_name_attributes = new NSAttributedString (
774                    selected_name_data, new NSUrl ("file://"), out selected_name_dictionary);
775
776                NSData selected_description_data = NSData.FromString (
777                    "<small><font style='line-height: 150%' color='#9bbaeb' face='" + SparkleUI.FontName + "'>" +
778                    plugin.Description + "</font></small>");
779
780                NSDictionary selected_description_dictionary       = new NSDictionary ();
781                NSAttributedString selected_description_attributes = new NSAttributedString (
782                    selected_description_data, new NSUrl ("file://"), out selected_description_dictionary);
783
784                NSMutableAttributedString selected_mutable_attributes =
785                    new NSMutableAttributedString (selected_name_attributes);
786
787                selected_mutable_attributes.Append (new NSAttributedString ("\n"));
788                selected_mutable_attributes.Append (selected_description_attributes);
789
790                selected_cell.AttributedStringValue = selected_mutable_attributes;
791                SelectedCells [i] = (NSAttributedString) selected_cell.ObjectValue;
792
793                i++;
794            }
795        }
796
797
798        [Export("numberOfRowsInTableView:")]
799        public int numberOfRowsInTableView (NSTableView table_view)
800        {
801            if (Items == null)
802                return 0;
803            else
804                return Items.Count;
805        }
806
807
808        [Export("tableView:objectValueForTableColumn:row:")]
809        public NSObject objectValueForTableColumn (NSTableView table_view,
810            NSTableColumn table_column, int row_index)
811        {
812            if (table_column.HeaderToolTip.Equals ("Description")) {
813                if (table_view.SelectedRow == row_index &&
814                    Program.UI.Setup.IsKeyWindow &&
815                    Program.UI.Setup.FirstResponder == table_view) {
816
817                    return SelectedCells [row_index];
818
819                } else {
820                    return Cells [row_index];
821                }
822
823            } else {
824                SparklePlugin plugin = (SparklePlugin) Items [row_index];
825                string path = plugin.ImagePath;
826
827                if (backing_scale_factor >= 2) {
828                    string hi_path = String.Format ("{0}@{1}x{2}",
829                        Path.Combine (Path.GetDirectoryName (path), Path.GetFileNameWithoutExtension (path)),
830                        backing_scale_factor, Path.GetExtension (path)
831                    );
832
833                    if (File.Exists (hi_path))
834                        path = hi_path;
835                }
836
837                return new NSImage (path) { Size = new SizeF (24, 24) };
838            }
839        }
840    }
841
842
843    public class SparkleTextFieldDelegate : NSTextFieldDelegate {
844        
845        public event Action StringValueChanged = delegate { };
846
847        public override void Changed (NSNotification notification)
848        {
849                StringValueChanged ();
850        }
851    }
852
853
854    public class SparkleTableViewDelegate : NSTableViewDelegate {
855
856        public event Action SelectionChanged = delegate { };
857
858        public override void SelectionDidChange (NSNotification notification)
859        {
860            SelectionChanged ();
861        }
862    }
863
864
865    public class SparkleLabel : NSTextField {
866
867        public SparkleLabel (string label, NSTextAlignment alignment)
868        {
869            if (!string.IsNullOrEmpty (label))
870                StringValue = label;
871
872            Alignment       = alignment;
873            BackgroundColor = NSColor.WindowBackground;
874            Bordered        = false;
875            Editable        = false;
876        }
877    }
878}