context: using System;
  using System.Collections.Generic;
  using Mono.Unix;
  
  using Gtk;
  
  namespace Tomboy.Sync
  {
  	public class SyncDialog : Gtk.Dialog, ISyncUI
  	{
  		private Gtk.Image image;
  		private Gtk.Label headerLabel;
  		private Gtk.Label messageLabel;
  		private Gtk.ProgressBar progressBar;
  		private Gtk.Label progressLabel;
  
  		private Gtk.Expander expander;
  		private Gtk.Button closeButton;
  		private uint progressBarTimeoutId;
  
  		private Gtk.TreeStore model;
  
  		// TODO: Possible to make Tomboy not crash if quit while dialog is up?
  		public SyncDialog ()
  : base (string.Empty,
  		        null,
  		        Gtk.DialogFlags.DestroyWithParent)
  		{
  			progressBarTimeoutId = 0;
  
  			SetSizeRequest (400, -1);
  
  			// Outer box. Surrounds all of our content.
  			VBox outerVBox = new VBox (false, 12);
  			outerVBox.BorderWidth = 6;
  			outerVBox.Show ();
  			ContentArea.PackStart (outerVBox, true, true, 0);
  
  			// Top image and label
  			HBox hbox = new HBox (false, 12);
  			hbox.Show ();
  			outerVBox.PackStart (hbox, false, false, 0);
  
  			image = new Image (GuiUtils.GetIcon ("tomboy", 48));
  			image.Xalign = 0;
  			image.Yalign = 0;
  			image.Show ();
  			hbox.PackStart (image, false, false, 0);
  
  			// Label header and message
  			VBox vbox = new VBox (false, 6);
  			vbox.Show ();
  			hbox.PackStart (vbox, true, true, 0);
  
  			headerLabel = new Label ();
  			headerLabel.UseMarkup = true;
  			headerLabel.Xalign = 0;
  			headerLabel.UseUnderline = false;
  			headerLabel.LineWrap = true;
  			headerLabel.Show ();
  			vbox.PackStart (headerLabel, false, false, 0);
  
  			messageLabel = new Label ();
  			messageLabel.Xalign = 0;
  			messageLabel.UseUnderline = false;
  			messageLabel.LineWrap = true;
  			messageLabel.SetSizeRequest (250, -1);
  			messageLabel.Show ();
  			vbox.PackStart (messageLabel, false, false, 0);
  
  			progressBar = new Gtk.ProgressBar ();
  			progressBar.Orientation = Gtk.Orientation.Horizontal;
  			//TODO: replace with a good value for progressBar.PulseStep
  			//progressBar.ActivityBlocks = 30;
  			progressBar.Show ();
  			outerVBox.PackStart (progressBar, false, false, 0);
  
  			progressLabel = new Label ();
  			progressLabel.UseMarkup = true;
  			progressLabel.Xalign = 0;
  			progressLabel.UseUnderline = false;
  			progressLabel.LineWrap = true;
  			progressLabel.Wrap = true;
  			progressLabel.Show ();
  			outerVBox.PackStart (progressLabel, false, false, 0);
  
  			// Expander containing TreeView
  			expander = new Gtk.Expander (Catalog.GetString ("Details"));
  			expander.Spacing = 6;
  			expander.Activated += OnExpanderActivated;
  			expander.Show ();
  			outerVBox.PackStart (expander, true, true, 0);
  
  			// Contents of expander
  			Gtk.VBox expandVBox = new Gtk.VBox ();
  			expandVBox.Show ();
  			expander.Add (expandVBox);
  
  			// Scrolled window around TreeView
  			Gtk.ScrolledWindow scrolledWindow = new Gtk.ScrolledWindow ();
  			scrolledWindow.ShadowType = Gtk.ShadowType.In;
  			scrolledWindow.SetSizeRequest (-1, 200);
  			scrolledWindow.Show ();
  			expandVBox.PackStart (scrolledWindow, true, true, 0);
  
  			// Create model for TreeView
  			model = new Gtk.TreeStore (typeof (string), typeof (string));
  
  			// Create TreeView, attach model
  			Gtk.TreeView treeView = new Gtk.TreeView ();
  			treeView.Model = model;
  			treeView.RowActivated += OnRowActivated;
  			treeView.Show ();
  			scrolledWindow.Add (treeView);
  
  			// Set up TreeViewColumns
  			Gtk.TreeViewColumn column = new Gtk.TreeViewColumn (
  			        Catalog.GetString ("Note Title"),
  			        new Gtk.CellRendererText (), "text", 0);
  			column.SortColumnId = 0;
  			column.Resizable = true;
  			treeView.AppendColumn (column);
  
  			column = new Gtk.TreeViewColumn (
  			        Catalog.GetString ("Status"),
  			        new Gtk.CellRendererText (), "text", 1);
  			column.SortColumnId = 1;
  			column.Resizable = true;
  			treeView.AppendColumn (column);
  
  			// Button to close dialog.
  			closeButton = (Gtk.Button) AddButton (Gtk.Stock.Close, Gtk.ResponseType.Close);
  			closeButton.Sensitive = false;
  		}
  
  		public override void Destroy ()
  		{
  			base.Destroy ();
  		}
  
  		protected override void OnRealized ()
  		{
  			base.OnRealized ();
  
  			SyncState state = SyncManager.State;
  			if (state == SyncState.Idle) {
  				// Kick off a timer to keep the progress bar going
  				progressBarTimeoutId = GLib.Timeout.Add (500, OnPulseProgressBar);
  
  				// Kick off a new synchronization
  				SyncManager.PerformSynchronization (this);
  			} else {
  				// Adjust the GUI accordingly
  				SyncStateChanged (state);
  			}
  		}
  
  		private void OnExpanderActivated (object sender, EventArgs e)
  		{
  			if (expander.Expanded)
  				this.Resizable = true;
  			else
  				this.Resizable = false;
  		}
  
  		void OnRowActivated (object sender, Gtk.RowActivatedArgs args)
  		{
  			// TODO: Store GUID hidden in model; use instead of title
  			Gtk.TreeIter iter;
  			if (!model.GetIter (out iter, args.Path))
  				return;
  
  			string noteTitle = (string) model.GetValue (iter, 0 /* note title */);
  
  			Note note = Tomboy.DefaultNoteManager.Find (noteTitle);
  			if (note != null)
  				note.Window.Present ();
  		}
  
  		public string HeaderText
  		{
  			set {
  				headerLabel.Markup = string.Format (
  				        "<span size=\"large\" weight=\"bold\">{0}</span>",
  				        value);
  			}
  		}
  
  		public string MessageText
  		{
  			set {
  				messageLabel.Text = value;
  			}
  		}
  
  		public string ProgressText
  		{
  			get {
  				return progressLabel.Text;
  			}
  			set {
  				progressLabel.Markup =
  				        string.Format ("<span style=\"italic\">{0}</span>",
  				                       value);
  			}
  		}
  
  		public void AddUpdateItem (string title, string status)
  		{
  			model.AppendValues (title, status);
  		}
  
  		#region Private Event Handlers
  		bool OnPulseProgressBar ()
  		{
  			if (SyncManager.State == SyncState.Idle)
  				return false;
  
  			progressBar.Pulse ();
  
  			// Return true to keep things going well
  			return true;
  		}
  		#endregion // Private Event Handlers
  
  		#region ISyncUI Members
  		public void SyncStateChanged (SyncState state)
  		{
  			// This event handler will be called by the synchronization thread
  			// so we have to use the delegate here to manipulate the GUI.
  			Gtk.Application.Invoke (delegate {
  				// FIXME: Change these strings to be user-friendly
  				switch (state) {
  				case SyncState.AcquiringLock:
  					ProgressText = Catalog.GetString ("Acquiring sync lock...");
  					break;
  				case SyncState.CommittingChanges:
  					ProgressText = Catalog.GetString ("Committing changes...");
  					break;
  				case SyncState.Connecting:
  					Title = Catalog.GetString ("Synchronizing Notes");
  					HeaderText = Catalog.GetString ("Synchronizing your notes...");
  					MessageText = Catalog.GetString ("This may take a while, kick back and enjoy!");
  					model.Clear ();
  					ProgressText = Catalog.GetString ("Connecting to the server...");
  					progressBar.Fraction = 0;
  					progressBar.Show ();
  					progressLabel.Show ();
  					break;
  				case SyncState.DeleteServerNotes:
  					ProgressText = Catalog.GetString ("Deleting notes off of the server...");
  					progressBar.Pulse ();
  					break;
  				case SyncState.Downloading:
  					ProgressText = Catalog.GetString ("Downloading new/updated notes...");
  					progressBar.Pulse ();
  					break;
  				case SyncState.Idle:
  					GLib.Source.Remove (progressBarTimeoutId);
  					progressBarTimeoutId = 0;
  					progressBar.Fraction = 0;
  					progressBar.Hide ();
  					progressLabel.Hide ();
  					closeButton.Sensitive = true;
  					break;
  				case SyncState.Locked:
  					Title = Catalog.GetString ("Server Locked");
  					HeaderText = Catalog.GetString ("Server is locked");
  					MessageText = Catalog.GetString ("One of your other computers is currently synchronizing.  Please wait 2 minutes and try again.");
  					ProgressText = string.Empty;
  					break;
  				case SyncState.PrepareDownload:
  					ProgressText = Catalog.GetString ("Preparing to download updates from server...");
  					break;
  				case SyncState.PrepareUpload:
  					ProgressText = Catalog.GetString ("Preparing to upload updates to server...");
  					break;
  				case SyncState.Uploading:
  					ProgressText = Catalog.GetString ("Uploading notes to server...");
  					break;
  				case SyncState.Failed:
  					Title = Catalog.GetString ("Synchronization Failed");
  					HeaderText = Catalog.GetString ("Failed to synchronize");
  					MessageText = Catalog.GetString ("Could not synchronize notes.  Check the details below and try again.");
  					ProgressText = string.Empty;
  					break;
  				case SyncState.Succeeded:
  					int count = 0;
  					count += model.IterNChildren ();
  					Title = Catalog.GetString ("Synchronization Complete");
  					HeaderText = Catalog.GetString ("Synchronization is complete");
  					string numNotesUpdated =
  					        string.Format (Catalog.GetPluralString ("{0} note updated.",
  					                                                "{0} notes updated.",
  					                                                count),
  					                       count);
  
gt: 					MessageText = numNotesUpdated + "  " +