diff --git a/SparkleShare/Windows/SparkleEventLogWindow.xaml b/SparkleShare/Windows/SparkleEventLogWindow.xaml new file mode 100644 index 00000000..57ba9c7e --- /dev/null +++ b/SparkleShare/Windows/SparkleEventLogWindow.xaml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SparkleShare/Windows/SparkleEventLogWindow.xaml.cs b/SparkleShare/Windows/SparkleEventLogWindow.xaml.cs new file mode 100644 index 00000000..f690aedc --- /dev/null +++ b/SparkleShare/Windows/SparkleEventLogWindow.xaml.cs @@ -0,0 +1,248 @@ +using System.Windows; + +namespace SparkleShare +{ + using System; + using System.ComponentModel; + using System.IO; + using System.Runtime.InteropServices; + using System.Windows.Controls; + using System.Windows.Media; + using System.Windows.Media.Imaging; + + using Microsoft.Win32; + + /// + /// Logics for the recent-changes-window. + /// + public partial class SparkleEventLogWindow : Window + { + public SparkleEventLogController Controller = new SparkleEventLogController (); + + [DllImport("urlmon.dll")] + [PreserveSig] + [return: MarshalAs(UnmanagedType.Error)] + static extern int CoInternetSetFeatureEnabled(int feature, [MarshalAs(UnmanagedType.U4)] int flags, bool enable); + + /// + /// Initializes a new instance of the class. + /// + public SparkleEventLogWindow () + { + this.InitializeComponent (); + + // Hide the minimize and maximize buttons. + this.SourceInitialized += (sender, args) => this.HideMinimizeAndMaximizeButtons(); + // Set some window-properties from code. + this.Background = new SolidColorBrush (Color.FromRgb(240, 240, 240)); + this.AllowsTransparency = false; + this.Icon = SparkleUIHelpers.GetImageSource ("sparkleshare-app", "ico"); + this.WindowStartupLocation = WindowStartupLocation.CenterScreen; + + // Write images to temp-folder. + this.WriteOutImages (); + + // Set values from controller to ui. + this.label_Size.Content = "Size: " + Controller.Size; + this.label_History.Content = "History: " + Controller.HistorySize; + + this.webbrowser.ObjectForScripting = new SparkleScriptingObject (); + // Disable annoying IE clicking sound + CoInternetSetFeatureEnabled (21, 0x00000002, true); + + // Tell controller on closing event. + this.Closing += this.OnClosing; + + // Show the window on controllers request. + this.Controller.ShowWindowEvent += () => this.Dispatcher.BeginInvoke ( + (Action)(() => { + this.Show (); + this.Activate (); + this.BringIntoView (); + })); + + // Hide the window on controllers request. + // Also hide the webbrowser-element and show the spinner. + this.Controller.HideWindowEvent += () => this.Dispatcher.BeginInvoke ( + (Action)(() => { + this.Hide (); + this.spinner.Visibility = Visibility.Visible; + this.webbrowser.Visibility = Visibility.Collapsed; + })); + + // Update labels on controllers request. + this.Controller.UpdateSizeInfoEvent += (size, history_size) => this.Dispatcher.BeginInvoke ( + (Action)(() => { + this.label_Size.Content = "Size: " + size; + this.label_History.Content = "History: " + history_size; + })); + + // Update the combobox-elements. + this.Controller.UpdateChooserEvent += folders => this.Dispatcher.BeginInvoke ( + (Action)(() => this.UpdateChooser (folders))); + + // Update the enabled-state of the combobox. + this.Controller.UpdateChooserEnablementEvent += enabled => this.Dispatcher.BeginInvoke ( + (Action)(() => this.combobox.IsEnabled = enabled)); + + // Update the content of the webbrowser. + this.Controller.UpdateContentEvent += html => this.Dispatcher.BeginInvoke ((Action)(() => { + this.UpdateContent (html); + + this.spinner.Visibility = Visibility.Collapsed; + this.webbrowser.Visibility = Visibility.Visible; + })); + + // Show the spinner if the content is loading. + this.Controller.ContentLoadingEvent += () => this.Dispatcher.BeginInvoke ( + (Action)(() => { + this.spinner.Visibility = Visibility.Visible; + this.spinner.Start (); + this.webbrowser.Visibility = Visibility.Collapsed; + })); + + // Show the save-file-dialog on controllers request. + this.Controller.ShowSaveDialogEvent += + (file_name, target_folder_path) => this.Dispatcher.BeginInvoke ( + (Action)(() => { + SaveFileDialog dialog = new SaveFileDialog () + { + FileName = file_name, + InitialDirectory = target_folder_path, + Title = "Restore from History", + DefaultExt = "." + Path.GetExtension (file_name), + Filter = "All Files|*.*" + }; + + bool? result = dialog.ShowDialog (this); + + if (result == true) this.Controller.SaveDialogCompleted (dialog.FileName); + else this.Controller.SaveDialogCancelled (); + })); + } + + /// + /// Called when [closing]. + /// Suppress the closing and asks controller to hide this window. + /// + /// The sender. + /// The instance containing the event data. + private void OnClosing (object sender, CancelEventArgs cancel_event_args) + { + this.Controller.WindowClosed (); + cancel_event_args.Cancel = true; + } + + /// + /// Updates the content of the webbrowser. + /// + /// The HTML. + private void UpdateContent (string html) + { + string pixmaps_path = Path.Combine (SparkleLib.SparkleConfig.DefaultConfig.TmpPath, "Pixmaps"); + pixmaps_path = pixmaps_path.Replace ("\\", "/"); + + html = html.Replace("", "'Segoe UI', sans-serif"); + html = html.Replace("", "13px"); + html = html.Replace("", "12px"); + html = html.Replace("", "#bbb"); + html = html.Replace("", "#ddd"); + html = html.Replace("", "90%"); + html = html.Replace("", "#f5f5f5"); + html = html.Replace("", "#0085cf"); + html = html.Replace("", "#009ff8"); + html = html.Replace("", pixmaps_path); + html = html.Replace("", pixmaps_path + "/document-added-12.png"); + html = html.Replace("", pixmaps_path + "/document-edited-12.png"); + html = html.Replace("", pixmaps_path + "/document-deleted-12.png"); + html = html.Replace("", pixmaps_path + "/document-moved-12.png"); + + this.spinner.Stop(); + + this.webbrowser.ObjectForScripting = new SparkleScriptingObject (); + this.webbrowser.NavigateToString (html); + } + + /// + /// Updates the combobox-items. + /// + /// The folders. + public void UpdateChooser (string [] folders) + { + if (folders == null) { + folders = Controller.Folders; + } + + this.combobox.Items.Clear (); + this.combobox.Items.Add (new ComboBoxItem () { Content = "Summary" }); + this.combobox.Items.Add(new Separator()); + this.combobox.SelectedItem = combobox.Items[0]; + + int row = 2; + foreach (string folder in folders) + { + this.combobox.Items.Add(new ComboBoxItem() { Content = folder } ); + + if (folder.Equals (Controller.SelectedFolder)) { + this.combobox.SelectedItem = this.combobox.Items [row]; + } + + row++; + } + + this.combobox.SelectionChanged += delegate { + Dispatcher.BeginInvoke((Action)delegate { + int index = this.combobox.SelectedIndex; + + if (index == 0) { + Controller.SelectedFolder = null; + } else { + Controller.SelectedFolder = (string)((ComboBoxItem)this.combobox.Items[index]).Content; + } + }); + }; + } + + /// + /// Writes the images from the pixel-map to the temp-folder. + /// + private void WriteOutImages () + { + string tmp_path = SparkleLib.SparkleConfig.DefaultConfig.TmpPath; + string pixmaps_path = Path.Combine(tmp_path, "Pixmaps"); + + if (!Directory.Exists(pixmaps_path)) + { + Directory.CreateDirectory(pixmaps_path); + + File.SetAttributes(tmp_path, File.GetAttributes(tmp_path) | FileAttributes.Hidden); + } + + BitmapSource image = SparkleUIHelpers.GetImageSource("user-icon-default"); + string file_path = Path.Combine(pixmaps_path, "user-icon-default.png"); + + using (FileStream stream = new FileStream(file_path, FileMode.Create)) + { + BitmapEncoder encoder = new PngBitmapEncoder(); + encoder.Frames.Add(BitmapFrame.Create(image)); + encoder.Save(stream); + } + + string[] actions = new string[] { "added", "deleted", "edited", "moved" }; + + foreach (string action in actions) + { + image = SparkleUIHelpers.GetImageSource("document-" + action + "-12"); + file_path = Path.Combine(pixmaps_path, "document-" + action + "-12.png"); + + using (FileStream stream = new FileStream(file_path, FileMode.Create)) + { + BitmapEncoder encoder = new PngBitmapEncoder(); + encoder.Frames.Add(BitmapFrame.Create(image)); + encoder.Save(stream); + } + } + } + } +} diff --git a/SparkleShare/Windows/SparkleScriptingObject.cs b/SparkleShare/Windows/SparkleScriptingObject.cs new file mode 100644 index 00000000..171ee087 --- /dev/null +++ b/SparkleShare/Windows/SparkleScriptingObject.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using System.Runtime.InteropServices; +using System.Security.Permissions; + +namespace SparkleShare +{ + [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] + [ComVisible(true)] + public class SparkleScriptingObject + { + public void LinkClicked(string url) + { + Program.UI.EventLog.Controller.LinkClicked(url); + } + } +} diff --git a/SparkleShare/Windows/SparkleShare.csproj b/SparkleShare/Windows/SparkleShare.csproj index 15b97abf..5b437b41 100644 --- a/SparkleShare/Windows/SparkleShare.csproj +++ b/SparkleShare/Windows/SparkleShare.csproj @@ -76,6 +76,10 @@ SparkleStatusIconController.cs + + SparkleEventLogWindow.xaml + + @@ -83,7 +87,6 @@ - @@ -99,6 +102,7 @@ + @@ -152,6 +156,7 @@ HTML\event-log.html + Designer HTML\jquery.js @@ -259,4 +264,10 @@ - + + + Designer + MSBuild:Compile + + + \ No newline at end of file diff --git a/SparkleShare/Windows/SparkleSpinner.cs b/SparkleShare/Windows/SparkleSpinner.cs index 450af909..e8a38856 100644 --- a/SparkleShare/Windows/SparkleSpinner.cs +++ b/SparkleShare/Windows/SparkleSpinner.cs @@ -20,16 +20,22 @@ using System.Timers; using System.Windows; using System.Windows.Controls; using System.Windows.Media.Imaging; +using System.ComponentModel; namespace SparkleShare { - public class SparkleSpinner : Image { private Timer timer; + public SparkleSpinner () + : this (22) { + } - public SparkleSpinner (int size) : base () - { + public SparkleSpinner (int size) : base () { + if (DesignerProperties.GetIsInDesignMode(this)) { + return; + } + Width = size; Height = size; diff --git a/SparkleShare/Windows/SparkleUI.cs b/SparkleShare/Windows/SparkleUI.cs index 1255a483..be3a5d74 100644 --- a/SparkleShare/Windows/SparkleUI.cs +++ b/SparkleShare/Windows/SparkleUI.cs @@ -23,7 +23,7 @@ namespace SparkleShare { public class SparkleUI { public SparkleSetup Setup; - public SparkleEventLog EventLog; + public SparkleEventLogWindow EventLog; public SparkleBubbles Bubbles; public SparkleStatusIcon StatusIcon; public SparkleAbout About; @@ -35,7 +35,7 @@ namespace SparkleShare { // don't have the smooth ease in animation, but appear abruptly. // The ease out animation always seems to work Setup = new SparkleSetup (); - EventLog = new SparkleEventLog (); + EventLog = new SparkleEventLogWindow(); About = new SparkleAbout (); Bubbles = new SparkleBubbles (); StatusIcon = new SparkleStatusIcon (); diff --git a/SparkleShare/Windows/WPFChromeExtensions.cs b/SparkleShare/Windows/WPFChromeExtensions.cs new file mode 100644 index 00000000..175e7a7c --- /dev/null +++ b/SparkleShare/Windows/WPFChromeExtensions.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using System.Runtime.InteropServices; +using System.Windows; + +namespace SparkleShare +{ + /// + /// Thanks to Matt Hamilton for this code! + /// See http://stackoverflow.com/questions/339620/how-do-i-remove-minimize-and-maximize-from-a-resizable-window-in-wpf + /// + internal static class WindowExtensions { + // from winuser.h + private const int GWL_STYLE = -16, + WS_MAXIMIZEBOX = 0x10000, + WS_MINIMIZEBOX = 0x20000; + + [DllImport("user32.dll")] + extern private static int GetWindowLong (IntPtr hwnd, int index); + + [DllImport("user32.dll")] + extern private static int SetWindowLong (IntPtr hwnd, int index, int value); + + internal static void HideMinimizeAndMaximizeButtons (this Window window) + { + IntPtr hwnd = new System.Windows.Interop.WindowInteropHelper(window).Handle; + var currentStyle = GetWindowLong (hwnd, GWL_STYLE); + + SetWindowLong (hwnd, GWL_STYLE, (currentStyle & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX)); + } + } +}