/SparkleShare/Mac/SparkleEventLog.cs

http://github.com/hbons/SparkleShare · C# · 419 lines · 312 code · 90 blank · 17 comment · 13 complexity · 4facd19676576e4c42c1efaae6fe1380 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. using System;
  17. using System.Drawing;
  18. using System.IO;
  19. using System.Threading;
  20. using MonoMac.Foundation;
  21. using MonoMac.AppKit;
  22. using MonoMac.ObjCRuntime;
  23. using MonoMac.WebKit;
  24. namespace SparkleShare {
  25. public class SparkleEventLog : NSWindow {
  26. public SparkleEventLogController Controller = new SparkleEventLogController ();
  27. public float TitlebarHeight;
  28. private WebView web_view;
  29. private NSBox background;
  30. private NSBox cover;
  31. private NSPopUpButton popup_button;
  32. private NSProgressIndicator progress_indicator;
  33. private NSTextField size_label, size_label_value, history_label, history_label_value;
  34. private NSButton hidden_close_button;
  35. public SparkleEventLog (IntPtr handle) : base (handle) { }
  36. public SparkleEventLog () : base ()
  37. {
  38. Title = "Recent Changes";
  39. Delegate = new SparkleEventsDelegate ();
  40. int min_width = 480;
  41. int min_height = 640;
  42. int height = (int) (NSScreen.MainScreen.Frame.Height * 0.85);
  43. float x = (float) (NSScreen.MainScreen.Frame.Width * 0.61);
  44. float y = (float) (NSScreen.MainScreen.Frame.Height * 0.5 - (height * 0.5));
  45. SetFrame (
  46. new RectangleF (
  47. new PointF (x, y),
  48. new SizeF (min_width, height)),
  49. true);
  50. StyleMask = (NSWindowStyle.Closable | NSWindowStyle.Miniaturizable |
  51. NSWindowStyle.Titled | NSWindowStyle.Resizable);
  52. MinSize = new SizeF (min_width, min_height);
  53. HasShadow = true;
  54. BackingType = NSBackingStore.Buffered;
  55. TitlebarHeight = Frame.Height - ContentView.Frame.Height;
  56. Level = NSWindowLevel.Floating;
  57. this.web_view = new WebView (new RectangleF (0, 0, 481, 579), "", "") {
  58. Frame = new RectangleF (new PointF (0, 0),
  59. new SizeF (ContentView.Frame.Width, ContentView.Frame.Height - 39))
  60. };
  61. this.cover = new NSBox () {
  62. Frame = new RectangleF (
  63. new PointF (-1, -1),
  64. new SizeF (Frame.Width + 2, this.web_view.Frame.Height + 1)),
  65. FillColor = NSColor.White,
  66. BorderType = NSBorderType.NoBorder,
  67. BoxType = NSBoxType.NSBoxCustom
  68. };
  69. this.hidden_close_button = new NSButton () {
  70. KeyEquivalentModifierMask = NSEventModifierMask.CommandKeyMask,
  71. KeyEquivalent = "w"
  72. };
  73. this.hidden_close_button.Activated += delegate {
  74. Controller.WindowClosed ();
  75. };
  76. this.size_label = new NSTextField () {
  77. Alignment = NSTextAlignment.Right,
  78. BackgroundColor = NSColor.WindowBackground,
  79. Bordered = false,
  80. Editable = false,
  81. Frame = new RectangleF (
  82. new PointF (0, ContentView.Frame.Height - 31),
  83. new SizeF (60, 20)),
  84. StringValue = "Size:"
  85. };
  86. this.size_label_value = new NSTextField () {
  87. Alignment = NSTextAlignment.Left,
  88. BackgroundColor = NSColor.WindowBackground,
  89. Bordered = false,
  90. Editable = false,
  91. Frame = new RectangleF (
  92. new PointF (60, ContentView.Frame.Height - 27),
  93. new SizeF (60, 20)),
  94. StringValue = "…",
  95. Font = NSFont.FromFontName (SparkleUI.FontName + " Bold", NSFont.SystemFontSize)
  96. };
  97. this.history_label = new NSTextField () {
  98. Alignment = NSTextAlignment.Right,
  99. BackgroundColor = NSColor.WindowBackground,
  100. Bordered = false,
  101. Editable = false,
  102. Frame = new RectangleF (
  103. new PointF (130, ContentView.Frame.Height - 31),
  104. new SizeF (60, 20)),
  105. StringValue = "History:"
  106. };
  107. this.history_label_value = new NSTextField () {
  108. Alignment = NSTextAlignment.Left,
  109. BackgroundColor = NSColor.WindowBackground,
  110. Bordered = false,
  111. Editable = false,
  112. Frame = new RectangleF (
  113. new PointF (190, ContentView.Frame.Height - 27),
  114. new SizeF (60, 20)
  115. ),
  116. StringValue = "…",
  117. Font = NSFont.FromFontName (SparkleUI.FontName + " Bold", NSFont.SystemFontSize)
  118. };
  119. this.popup_button = new NSPopUpButton () {
  120. Frame = new RectangleF (
  121. new PointF (ContentView.Frame.Width - 156 - 12, ContentView.Frame.Height - 33),
  122. new SizeF (156, 26)),
  123. PullsDown = false
  124. };
  125. this.background = new NSBox () {
  126. Frame = new RectangleF (
  127. new PointF (-1, -1),
  128. new SizeF (Frame.Width + 2, this.web_view.Frame.Height + 2)),
  129. FillColor = NSColor.White,
  130. BorderColor = NSColor.LightGray,
  131. BoxType = NSBoxType.NSBoxCustom
  132. };
  133. this.progress_indicator = new NSProgressIndicator () {
  134. Frame = new RectangleF (
  135. new PointF (Frame.Width / 2 - 10, this.web_view.Frame.Height / 2 + 10),
  136. new SizeF (20, 20)),
  137. Style = NSProgressIndicatorStyle.Spinning
  138. };
  139. this.progress_indicator.StartAnimation (this);
  140. ContentView.AddSubview (this.size_label);
  141. ContentView.AddSubview (this.size_label_value);
  142. ContentView.AddSubview (this.history_label);
  143. ContentView.AddSubview (this.history_label_value);
  144. ContentView.AddSubview (this.popup_button);
  145. ContentView.AddSubview (this.progress_indicator);
  146. ContentView.AddSubview (this.background);
  147. ContentView.AddSubview (this.hidden_close_button);
  148. (Delegate as SparkleEventsDelegate).WindowResized += delegate (SizeF new_window_size) {
  149. Program.Controller.Invoke (() => Relayout (new_window_size));
  150. };
  151. // Hook up the controller events
  152. Controller.HideWindowEvent += delegate {
  153. Program.Controller.Invoke (() => {
  154. this.progress_indicator.Hidden = true;
  155. PerformClose (this);
  156. });
  157. };
  158. Controller.ShowWindowEvent += delegate {
  159. Program.Controller.Invoke (() => OrderFrontRegardless ());
  160. };
  161. Controller.UpdateChooserEvent += delegate (string [] folders) {
  162. Program.Controller.Invoke (() => UpdateChooser (folders));
  163. };
  164. Controller.UpdateChooserEnablementEvent += delegate (bool enabled) {
  165. Program.Controller.Invoke (() => { this.popup_button.Enabled = enabled; });
  166. };
  167. Controller.UpdateContentEvent += delegate (string html) {
  168. Program.Controller.Invoke (() => {
  169. this.cover.RemoveFromSuperview ();
  170. this.progress_indicator.Hidden = true;
  171. UpdateContent (html);
  172. });
  173. };
  174. Controller.ContentLoadingEvent += delegate {
  175. Program.Controller.Invoke (() => {
  176. this.web_view.RemoveFromSuperview ();
  177. // FIXME: Hack to hide that the WebView sometimes doesn't disappear
  178. ContentView.AddSubview (this.cover);
  179. this.progress_indicator.Hidden = false;
  180. this.progress_indicator.StartAnimation (this);
  181. });
  182. };
  183. Controller.UpdateSizeInfoEvent += delegate (string size, string history_size) {
  184. Program.Controller.Invoke (() => {
  185. this.size_label_value.StringValue = size;
  186. this.history_label_value.StringValue = history_size;
  187. });
  188. };
  189. Controller.ShowSaveDialogEvent += delegate (string file_name, string target_folder_path) {
  190. Program.Controller.Invoke (() => {
  191. NSSavePanel panel = new NSSavePanel () {
  192. DirectoryUrl = new NSUrl (target_folder_path, true),
  193. NameFieldStringValue = file_name,
  194. ParentWindow = this,
  195. Title = "Restore from History",
  196. PreventsApplicationTerminationWhenModal = false
  197. };
  198. if ((NSPanelButtonType) panel.RunModal () == NSPanelButtonType.Ok) {
  199. string target_file_path = Path.Combine (panel.DirectoryUrl.RelativePath, panel.NameFieldStringValue);
  200. Controller.SaveDialogCompleted (target_file_path);
  201. } else {
  202. Controller.SaveDialogCancelled ();
  203. }
  204. });
  205. };
  206. }
  207. public void Relayout (SizeF new_window_size)
  208. {
  209. this.web_view.Frame = new RectangleF (this.web_view.Frame.Location,
  210. new SizeF (new_window_size.Width, new_window_size.Height - TitlebarHeight - 39));
  211. this.cover.Frame = new RectangleF (this.cover.Frame.Location,
  212. new SizeF (new_window_size.Width, new_window_size.Height - TitlebarHeight - 39));
  213. this.background.Frame = new RectangleF (this.background.Frame.Location,
  214. new SizeF (new_window_size.Width, new_window_size.Height - TitlebarHeight - 37));
  215. this.size_label.Frame = new RectangleF (
  216. new PointF (this.size_label.Frame.X, new_window_size.Height - TitlebarHeight - 30),
  217. this.size_label.Frame.Size);
  218. this.size_label_value.Frame = new RectangleF (
  219. new PointF (this.size_label_value.Frame.X, new_window_size.Height - TitlebarHeight - 27),
  220. this.size_label_value.Frame.Size);
  221. this.history_label.Frame = new RectangleF (
  222. new PointF (this.history_label.Frame.X, new_window_size.Height - TitlebarHeight - 30),
  223. this.history_label.Frame.Size);
  224. this.history_label_value.Frame = new RectangleF (
  225. new PointF (this.history_label_value.Frame.X, new_window_size.Height - TitlebarHeight - 27),
  226. this.history_label_value.Frame.Size);
  227. this.progress_indicator.Frame = new RectangleF (
  228. new PointF (new_window_size.Width / 2 - 10, this.web_view.Frame.Height / 2 + 10),
  229. this.progress_indicator.Frame.Size);
  230. this.popup_button.RemoveFromSuperview (); // Needed to prevent redraw glitches
  231. this.popup_button.Frame = new RectangleF (
  232. new PointF (new_window_size.Width - this.popup_button.Frame.Width - 12, new_window_size.Height - TitlebarHeight - 33),
  233. this.popup_button.Frame.Size);
  234. ContentView.AddSubview (this.popup_button);
  235. }
  236. public void UpdateChooser (string [] folders)
  237. {
  238. if (folders == null)
  239. folders = Controller.Folders;
  240. this.popup_button.RemoveAllItems ();
  241. this.popup_button.AddItem ("Summary");
  242. this.popup_button.Menu.AddItem (NSMenuItem.SeparatorItem);
  243. int row = 2;
  244. foreach (string folder in folders) {
  245. this.popup_button.AddItem (folder);
  246. if (folder.Equals (Controller.SelectedFolder))
  247. this.popup_button.SelectItem (row);
  248. row++;
  249. }
  250. this.popup_button.AddItems (folders);
  251. this.popup_button.Activated += delegate {
  252. Program.Controller.Invoke (() => {
  253. if (this.popup_button.IndexOfSelectedItem == 0)
  254. Controller.SelectedFolder = null;
  255. else
  256. Controller.SelectedFolder = this.popup_button.SelectedItem.Title;
  257. });
  258. };
  259. }
  260. public void UpdateContent (string html)
  261. {
  262. string pixmaps_path = "file://" + NSBundle.MainBundle.ResourcePath;
  263. html = html.Replace ("<!-- $body-font-family -->", "Helvetica Neue");
  264. html = html.Replace ("<!-- $day-entry-header-font-size -->", "13.6px");
  265. html = html.Replace ("<!-- $body-font-size -->", "13.4px");
  266. html = html.Replace ("<!-- $secondary-font-color -->", "#bbb");
  267. html = html.Replace ("<!-- $small-color -->", "#ddd");
  268. html = html.Replace ("<!-- $small-font-size -->", "10px");
  269. html = html.Replace ("<!-- $day-entry-header-background-color -->", "#f5f5f5");
  270. html = html.Replace ("<!-- $a-color -->", "#009ff8");
  271. html = html.Replace ("<!-- $a-hover-color -->", "#009ff8");
  272. html = html.Replace ("<!-- $pixmaps-path -->", pixmaps_path);
  273. html = html.Replace ("<!-- $document-added-background-image -->", pixmaps_path + "/document-added-12.png");
  274. html = html.Replace ("<!-- $document-deleted-background-image -->", pixmaps_path + "/document-deleted-12.png");
  275. html = html.Replace ("<!-- $document-edited-background-image -->", pixmaps_path + "/document-edited-12.png");
  276. html = html.Replace ("<!-- $document-moved-background-image -->", pixmaps_path + "/document-moved-12.png");
  277. this.web_view = new WebView (new RectangleF (0, 0, 481, 579), "", "") {
  278. Frame = new RectangleF (new PointF (0, 0), new SizeF (ContentView.Frame.Width, ContentView.Frame.Height - 39))
  279. };
  280. this.web_view.MainFrame.LoadHtmlString (html, new NSUrl (""));
  281. this.web_view.PolicyDelegate = new SparkleWebPolicyDelegate ();
  282. ContentView.AddSubview (this.web_view);
  283. (this.web_view.PolicyDelegate as SparkleWebPolicyDelegate).LinkClicked += delegate (string href) {
  284. if (href.StartsWith ("file:///"))
  285. href = href.Substring (7);
  286. Controller.LinkClicked (href);
  287. };
  288. this.progress_indicator.Hidden = true;
  289. }
  290. public override void OrderFrontRegardless ()
  291. {
  292. NSApplication.SharedApplication.ActivateIgnoringOtherApps (true);
  293. MakeKeyAndOrderFront (this);
  294. if (Program.UI != null)
  295. Program.UI.UpdateDockIconVisibility ();
  296. base.OrderFrontRegardless ();
  297. }
  298. public override void PerformClose (NSObject sender)
  299. {
  300. base.OrderOut (this);
  301. if (Program.UI != null)
  302. Program.UI.UpdateDockIconVisibility ();
  303. return;
  304. }
  305. }
  306. public class SparkleEventsDelegate : NSWindowDelegate {
  307. public event WindowResizedHandler WindowResized = delegate { };
  308. public delegate void WindowResizedHandler (SizeF new_window_size);
  309. public override SizeF WillResize (NSWindow sender, SizeF to_frame_size)
  310. {
  311. WindowResized (to_frame_size);
  312. return to_frame_size;
  313. }
  314. public override bool WindowShouldClose (NSObject sender)
  315. {
  316. (sender as SparkleEventLog).Controller.WindowClosed ();
  317. return false;
  318. }
  319. }
  320. public class SparkleWebPolicyDelegate : WebPolicyDelegate {
  321. public event LinkClickedHandler LinkClicked = delegate { };
  322. public delegate void LinkClickedHandler (string href);
  323. public override void DecidePolicyForNavigation (WebView web_view, NSDictionary action_info,
  324. NSUrlRequest request, WebFrame frame, NSObject decision_token)
  325. {
  326. LinkClicked (request.Url.ToString ());
  327. }
  328. }
  329. }