diff --git a/QuickLook/QuickLook.csproj b/QuickLook/QuickLook.csproj
index b6f14ac..3a1f5fb 100644
--- a/QuickLook/QuickLook.csproj
+++ b/QuickLook/QuickLook.csproj
@@ -150,6 +150,8 @@
+
+
Component
diff --git a/QuickLook/ViewerWindow.Actions.cs b/QuickLook/ViewerWindow.Actions.cs
new file mode 100644
index 0000000..2016897
--- /dev/null
+++ b/QuickLook/ViewerWindow.Actions.cs
@@ -0,0 +1,273 @@
+// Copyright © 2017 Paddy Xu
+//
+// This file is part of QuickLook program.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.ExceptionServices;
+using System.Windows;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Threading;
+using QuickLook.Helpers;
+using QuickLook.Plugin;
+
+namespace QuickLook
+{
+ public partial class ViewerWindow
+ {
+ internal void RunWith(string with, string arg)
+ {
+ if (string.IsNullOrEmpty(_path))
+ return;
+
+ try
+ {
+ Process.Start(new ProcessStartInfo(with)
+ {
+ Arguments = arg,
+ WorkingDirectory = Path.GetDirectoryName(_path)
+ });
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.Message);
+ }
+ }
+
+ internal void Run()
+ {
+ if (string.IsNullOrEmpty(_path))
+ return;
+
+ try
+ {
+ Process.Start(new ProcessStartInfo(_path)
+ {
+ WorkingDirectory = Path.GetDirectoryName(_path)
+ });
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e.Message);
+ }
+ }
+
+ internal void RunAndHide()
+ {
+ Run();
+ BeginHide();
+ }
+
+ internal void RunAndClose()
+ {
+ Run();
+ BeginClose();
+ }
+
+ private static void ResizeAndCenter(Window window, Size size, bool canOldPluginResize, bool canNextPluginResize)
+ {
+ // resize to MinSize first
+ size.Width = Math.Max(size.Width, window.MinWidth);
+ size.Height = Math.Max(size.Height, window.MinHeight);
+
+ if (!window.IsLoaded)
+ {
+ // if the window is not loaded yet, just leave the problem to WPF
+ window.Width = size.Width;
+ window.Height = size.Height;
+ window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
+ window.Dispatcher.BeginInvoke(new Action(window.BringToFront), DispatcherPriority.Render);
+
+ return;
+ }
+
+ // is the window is now now maximized, do not move it
+ if (window.WindowState == WindowState.Maximized)
+ return;
+
+ // if this is a new window, place it to top
+ if (window.Visibility != Visibility.Visible)
+ window.BringToFront();
+
+ var screen = WindowHelper.GetCurrentWindowRect();
+
+ // do not resize or reposition the window is it is visible - unless the next window is size-fixed
+ if (window.Visibility == Visibility.Visible && canOldPluginResize && canNextPluginResize)
+ return;
+
+ // otherwise, resize it and place it to the old window center.
+ var oldCenterX = window.Left + window.Width / 2;
+ var oldCenterY = window.Top + window.Height / 2;
+
+ var newLeft = oldCenterX - size.Width / 2;
+ var newTop = oldCenterY - size.Height / 2;
+
+ // ensure the new window is fully visible
+ newLeft = Math.Max(newLeft, screen.Left); // left
+ newTop = Math.Max(newTop, screen.Top); // top
+ newLeft = newLeft + size.Width > screen.Right ? screen.Right - size.Width : newLeft; // right
+ newTop = newTop + size.Height > screen.Bottom ? screen.Bottom - size.Height : newTop; // bottom
+
+ window.MoveWindow(newLeft, newTop, size.Width, size.Height);
+ }
+
+ internal void UnloadPlugin()
+ {
+ // the focused element will not processed by GC: https://stackoverflow.com/questions/30848939/memory-leak-due-to-window-efectivevalues-retention
+ FocusManager.SetFocusedElement(this, null);
+ Keyboard.DefaultRestoreFocusMode =
+ RestoreFocusMode.None; // WPF will put the focused item into a "_restoreFocus" list ... omg
+ Keyboard.ClearFocus();
+
+ _canOldPluginResize = ContextObject.CanResize;
+
+ ContextObject.Reset();
+
+ try
+ {
+ Plugin?.Cleanup();
+ }
+ catch (Exception e)
+ {
+ Debug.WriteLine(e);
+ }
+ Plugin = null;
+
+ _path = string.Empty;
+ }
+
+ internal void BeginShow(IViewer matchedPlugin, string path,
+ Action exceptionHandler)
+ {
+ _path = path;
+ Plugin = matchedPlugin;
+
+ ContextObject.ViewerWindow = this;
+
+ // get window size before showing it
+ Plugin.Prepare(path, ContextObject);
+
+ SetOpenWithButtonAndPath();
+
+ // revert UI changes
+ ContextObject.IsBusy = true;
+
+ var margin = windowFrameContainer.Margin.Top * 2;
+
+ var newHeight = ContextObject.PreferredSize.Height + margin +
+ (ContextObject.TitlebarOverlap ? 0 : windowCaptionContainer.Height);
+ var newWidth = ContextObject.PreferredSize.Width + margin;
+
+ ResizeAndCenter(this, new Size(newWidth, newHeight), _canOldPluginResize, ContextObject.CanResize);
+
+ if (Visibility != Visibility.Visible)
+ Show();
+
+ ShowWindowCaptionContainer(null, null);
+ //WindowHelper.SetActivate(new WindowInteropHelper(this), ContextObject.CanFocus);
+
+ // load plugin, do not block UI
+ Dispatcher.BeginInvoke(new Action(() =>
+ {
+ try
+ {
+ Plugin.View(path, ContextObject);
+ }
+ catch (Exception e)
+ {
+ exceptionHandler(path, ExceptionDispatchInfo.Capture(e));
+ }
+ }),
+ DispatcherPriority.Input);
+ }
+
+ private void SetOpenWithButtonAndPath()
+ {
+ buttonOpenWithText.Inlines.Clear();
+
+ if (Directory.Exists(_path))
+ {
+ AddToInlines("MW_BrowseFolder", Path.GetFileName(_path));
+ return;
+ }
+ var isExe = FileHelper.IsExecutable(_path, out var appFriendlyName);
+ if (isExe)
+ {
+ AddToInlines("MW_Run", appFriendlyName);
+ return;
+ }
+ // not an exe
+ var found = FileHelper.GetAssocApplication(_path, out appFriendlyName);
+ if (found)
+ {
+ AddToInlines("MW_OpenWith", appFriendlyName);
+ return;
+ }
+ // assoc not found
+ AddToInlines("MW_Open", Path.GetFileName(_path));
+
+ void AddToInlines(string str, string replaceWith)
+ {
+ // limit str length
+ if (replaceWith.Length > 16)
+ replaceWith = replaceWith.Substring(0, 14) + "…" + replaceWith.Substring(replaceWith.Length - 2);
+
+ str = TranslationHelper.GetString(str);
+ var elements = str.Split(new[] {"{0}"}, StringSplitOptions.None).ToList();
+ while (elements.Count < 2)
+ elements.Add(string.Empty);
+
+ buttonOpenWithText.Inlines.Add(
+ new Run(elements[0]) {FontWeight = FontWeights.Normal}); // text beforehand
+ buttonOpenWithText.Inlines.Add(
+ new Run(replaceWith) {FontWeight = FontWeights.SemiBold}); // appFriendlyName
+ buttonOpenWithText.Inlines.Add(
+ new Run(elements[1]) {FontWeight = FontWeights.Normal}); // text afterward
+ }
+ }
+
+ internal void BeginHide()
+ {
+ UnloadPlugin();
+
+ // if the this window is hidden in Max state, new show() will results in failure:
+ // "Cannot show Window when ShowActivated is false and WindowState is set to Maximized"
+ WindowState = WindowState.Normal;
+
+ Hide();
+ //Dispatcher.BeginInvoke(new Action(Hide), DispatcherPriority.ApplicationIdle);
+
+ ProcessHelper.PerformAggressiveGC();
+ }
+
+ internal void BeginClose()
+ {
+ UnloadPlugin();
+
+ Close();
+
+ ProcessHelper.PerformAggressiveGC();
+ }
+
+ internal void Share(object sender, RoutedEventArgs e)
+ {
+ RunWith("rundll32.exe", $"shell32.dll,OpenAs_RunDLL {_path}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/QuickLook/ViewerWindow.Properties.cs b/QuickLook/ViewerWindow.Properties.cs
new file mode 100644
index 0000000..537af3a
--- /dev/null
+++ b/QuickLook/ViewerWindow.Properties.cs
@@ -0,0 +1,68 @@
+// Copyright © 2017 Paddy Xu
+//
+// This file is part of QuickLook program.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using System;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Windows;
+using QuickLook.Annotations;
+using QuickLook.Plugin;
+
+namespace QuickLook
+{
+ public partial class ViewerWindow : INotifyPropertyChanged
+ {
+ private readonly ResourceDictionary _darkDict = new ResourceDictionary
+ {
+ Source = new Uri("pack://application:,,,/QuickLook;component/Styles/MainWindowStyles.Dark.xaml")
+ };
+ private bool _canOldPluginResize;
+ private bool _pinned;
+ private bool Pinned
+ {
+ get => _pinned;
+ set
+ {
+ _pinned = value;
+ OnPropertyChanged();
+ }
+ }
+ public IViewer Plugin { get; private set; }
+ public ContextObject ContextObject { get; private set; }
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ public void SwitchTheme(bool dark)
+ {
+ if (dark)
+ {
+ if (!Resources.MergedDictionaries.Contains(_darkDict))
+ Resources.MergedDictionaries.Add(_darkDict);
+ }
+ else
+ {
+ if (Resources.MergedDictionaries.Contains(_darkDict))
+ Resources.MergedDictionaries.Remove(_darkDict);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/QuickLook/ViewerWindow.xaml.cs b/QuickLook/ViewerWindow.xaml.cs
index 7b9a547..feda012 100644
--- a/QuickLook/ViewerWindow.xaml.cs
+++ b/QuickLook/ViewerWindow.xaml.cs
@@ -16,19 +16,10 @@
// along with this program. If not, see .
using System;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using System.Runtime.ExceptionServices;
using System.Windows;
-using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
-using System.Windows.Threading;
-using QuickLook.Annotations;
using QuickLook.Controls;
using QuickLook.Helpers;
using QuickLook.Plugin;
@@ -38,16 +29,10 @@ namespace QuickLook
///
/// Interaction logic for ViewerWindow.xaml
///
- public partial class ViewerWindow : MainWindowBase, INotifyPropertyChanged
+ public partial class ViewerWindow : MainWindowBase
{
- private readonly ResourceDictionary _darkDict = new ResourceDictionary
- {
- Source = new Uri("pack://application:,,,/QuickLook;component/Styles/MainWindowStyles.Dark.xaml")
- };
private string _path;
- private bool _pinned;
private bool _restoreForDragMove;
- private bool _canOldPluginResize;
internal ViewerWindow()
{
@@ -99,26 +84,9 @@ namespace QuickLook
buttonWindowStatus.Click += (sender, e) =>
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
- buttonShare.Click +=
- (sender, e) => RunWith("rundll32.exe", $"shell32.dll,OpenAs_RunDLL {_path}");
+ buttonShare.Click += Share;
}
- public bool Pinned
- {
- get => _pinned;
- private set
- {
- _pinned = value;
- OnPropertyChanged();
- }
- }
-
- public IViewer Plugin { get; private set; }
-
- public ContextObject ContextObject { get; private set; }
-
- public event PropertyChangedEventHandler PropertyChanged;
-
private void ShowWindowCaptionContainer(object sender, MouseEventArgs e)
{
var show = (Storyboard) windowCaptionContainer.FindResource("ShowCaptionContainerStoryboard");
@@ -191,259 +159,5 @@ namespace QuickLook
DragMove();
}
}
-
- internal void RunWith(string with, string arg)
- {
- if (string.IsNullOrEmpty(_path))
- return;
-
- try
- {
- Process.Start(new ProcessStartInfo(with)
- {
- Arguments = arg,
- WorkingDirectory = Path.GetDirectoryName(_path)
- });
- }
- catch (Exception e)
- {
- Debug.WriteLine(e.Message);
- }
- }
-
- internal void Run()
- {
- if (string.IsNullOrEmpty(_path))
- return;
-
- try
- {
- Process.Start(new ProcessStartInfo(_path)
- {
- WorkingDirectory = Path.GetDirectoryName(_path)
- });
- }
- catch (Exception e)
- {
- Debug.WriteLine(e.Message);
- }
- }
-
- internal void RunAndHide()
- {
- Run();
- BeginHide();
- }
-
- internal void RunAndClose()
- {
- Run();
- BeginClose();
- }
-
- private static void ResizeAndCenter(Window window, Size size, bool canOldPluginResize, bool canNextPluginResize)
- {
- // resize to MinSize first
- size.Width = Math.Max(size.Width, window.MinWidth);
- size.Height = Math.Max(size.Height, window.MinHeight);
-
- if (!window.IsLoaded)
- {
- // if the window is not loaded yet, just leave the problem to WPF
- window.Width = size.Width;
- window.Height = size.Height;
- window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
- window.Dispatcher.BeginInvoke(new Action(window.BringToFront), DispatcherPriority.Render);
-
- return;
- }
-
- // is the window is now now maximized, do not move it
- if (window.WindowState == WindowState.Maximized)
- return;
-
- // if this is a new window, place it to top
- if (window.Visibility != Visibility.Visible)
- window.BringToFront();
-
- var screen = WindowHelper.GetCurrentWindowRect();
-
- // do not resize or reposition the window is it is visible - unless the next window is size-fixed
- if (window.Visibility == Visibility.Visible && canOldPluginResize && canNextPluginResize)
- return;
-
- // otherwise, resize it and place it to the old window center.
- var oldCenterX = window.Left + window.Width / 2;
- var oldCenterY = window.Top + window.Height / 2;
-
- var newLeft = oldCenterX - size.Width / 2;
- var newTop = oldCenterY - size.Height / 2;
-
- // ensure the new window is fully visible
- newLeft = Math.Max(newLeft, screen.Left); // left
- newTop = Math.Max(newTop, screen.Top); // top
- newLeft = newLeft + size.Width > screen.Right ? screen.Right - size.Width : newLeft; // right
- newTop = newTop + size.Height > screen.Bottom ? screen.Bottom - size.Height : newTop; // bottom
-
- window.MoveWindow(newLeft, newTop, size.Width, size.Height);
- }
-
- internal void UnloadPlugin()
- {
- // the focused element will not processed by GC: https://stackoverflow.com/questions/30848939/memory-leak-due-to-window-efectivevalues-retention
- FocusManager.SetFocusedElement(this, null);
- Keyboard.DefaultRestoreFocusMode =
- RestoreFocusMode.None; // WPF will put the focused item into a "_restoreFocus" list ... omg
- Keyboard.ClearFocus();
-
- _canOldPluginResize = ContextObject.CanResize;
-
- ContextObject.Reset();
-
- try
- {
- Plugin?.Cleanup();
- }
- catch (Exception e)
- {
- Debug.WriteLine(e);
- }
- Plugin = null;
-
- _path = string.Empty;
- }
-
- internal void BeginShow(IViewer matchedPlugin, string path,
- Action exceptionHandler)
- {
- _path = path;
- Plugin = matchedPlugin;
-
- ContextObject.ViewerWindow = this;
-
- // get window size before showing it
- Plugin.Prepare(path, ContextObject);
-
- SetOpenWithButtonAndPath();
-
- // revert UI changes
- ContextObject.IsBusy = true;
-
- var margin = windowFrameContainer.Margin.Top * 2;
-
- var newHeight = ContextObject.PreferredSize.Height + margin +
- (ContextObject.TitlebarOverlap ? 0 : windowCaptionContainer.Height);
- var newWidth = ContextObject.PreferredSize.Width + margin;
-
- ResizeAndCenter(this, new Size(newWidth, newHeight), _canOldPluginResize, ContextObject.CanResize);
-
- if (Visibility != Visibility.Visible)
- Show();
-
- ShowWindowCaptionContainer(null, null);
- //WindowHelper.SetActivate(new WindowInteropHelper(this), ContextObject.CanFocus);
-
- // load plugin, do not block UI
- Dispatcher.BeginInvoke(new Action(() =>
- {
- try
- {
- Plugin.View(path, ContextObject);
- }
- catch (Exception e)
- {
- exceptionHandler(path, ExceptionDispatchInfo.Capture(e));
- }
- }),
- DispatcherPriority.Input);
- }
-
- private void SetOpenWithButtonAndPath()
- {
- buttonOpenWithText.Inlines.Clear();
-
- if (Directory.Exists(_path))
- {
- AddToInlines("MW_BrowseFolder", Path.GetFileName(_path));
- return;
- }
- var isExe = FileHelper.IsExecutable(_path, out var appFriendlyName);
- if (isExe)
- {
- AddToInlines("MW_Run", appFriendlyName);
- return;
- }
- // not an exe
- var found = FileHelper.GetAssocApplication(_path, out appFriendlyName);
- if (found)
- {
- AddToInlines("MW_OpenWith", appFriendlyName);
- return;
- }
- // assoc not found
- AddToInlines("MW_Open", Path.GetFileName(_path));
-
- void AddToInlines(string str, string replaceWith)
- {
- // limit str length
- if (replaceWith.Length > 16)
- replaceWith = replaceWith.Substring(0, 14) + "…" + replaceWith.Substring(replaceWith.Length - 2);
-
- str = TranslationHelper.GetString(str);
- var elements = str.Split(new[] {"{0}"}, StringSplitOptions.None).ToList();
- while (elements.Count < 2)
- elements.Add(string.Empty);
-
- buttonOpenWithText.Inlines.Add(
- new Run(elements[0]) {FontWeight = FontWeights.Normal}); // text beforehand
- buttonOpenWithText.Inlines.Add(
- new Run(replaceWith) {FontWeight = FontWeights.SemiBold}); // appFriendlyName
- buttonOpenWithText.Inlines.Add(
- new Run(elements[1]) {FontWeight = FontWeights.Normal}); // text afterward
- }
- }
-
- internal void BeginHide()
- {
- UnloadPlugin();
-
- // if the this window is hidden in Max state, new show() will results in failure:
- // "Cannot show Window when ShowActivated is false and WindowState is set to Maximized"
- WindowState = WindowState.Normal;
-
- Hide();
- //Dispatcher.BeginInvoke(new Action(Hide), DispatcherPriority.ApplicationIdle);
-
- ProcessHelper.PerformAggressiveGC();
- }
-
- internal void BeginClose()
- {
- UnloadPlugin();
-
- Close();
-
- ProcessHelper.PerformAggressiveGC();
- }
-
- [NotifyPropertyChangedInvocator]
- protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
- {
- PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
- }
-
- public void SwitchTheme(bool dark)
- {
- if (dark)
- {
- if (!Resources.MergedDictionaries.Contains(_darkDict))
- Resources.MergedDictionaries.Add(_darkDict);
- }
- else
- {
- if (Resources.MergedDictionaries.Contains(_darkDict))
- Resources.MergedDictionaries.Remove(_darkDict);
- }
- }
}
}
\ No newline at end of file