add "Open with" button

This commit is contained in:
Paddy Xu
2017-05-23 23:34:49 +03:00
parent 9489af472b
commit 62fed98ba1
11 changed files with 217 additions and 11 deletions

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="QuickLook" Language="1033" <Product Id="*" Name="QuickLook" Language="1033"
Version="!(bind.FileVersion.fil22F7B7F07E63622AD2451459086A3CF2)" Manufacturer="Paddy Xu" Version="!(bind.FileVersion.fil22F7B7F07E63622AD2451459086A3CF2)" Manufacturer="Paddy Xu"
UpgradeCode="c83b9c02-87d6-494e-9f5f-cf4c3900a54d"> UpgradeCode="c83b9c02-87d6-494e-9f5f-cf4c3900a54d">
<Package InstallerVersion="500" Compressed="yes" Platform="x86" InstallScope="perUser" /> <Package InstallerVersion="500" Compressed="yes" Platform="x86" InstallScope="perUser" />
@@ -13,7 +13,7 @@
<Property Id='MSIINSTALLPERUSER' Value='1' /> <Property Id='MSIINSTALLPERUSER' Value='1' />
<WixVariable Id="WixUILicenseRtf" Value="lgpl.rtf" /> <WixVariable Id="WixUILicenseRtf" Value="lgpl.rtf" />
<Feature Id="ProductFeature" Title="QuickLook.Installer" Level="1"> <Feature Id="ProductFeature" Title="QuickLook.Installer" Level="1">
<ComponentGroupRef Id="QuickLookComponents" /> <ComponentGroupRef Id="QuickLookComponents" />
<ComponentRef Id="ApplicationShortcutDesktop" /> <ComponentRef Id="ApplicationShortcutDesktop" />
@@ -64,4 +64,4 @@
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" /> <Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
</Product> </Product>
</Wix> </Wix>

View File

@@ -1,8 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using System.Windows; using System.Windows;
using System.Windows.Interop;
namespace QuickLook.Plugin.IPreviewHandlers namespace QuickLook.Plugin.IPreviewHandlers
{ {
@@ -60,7 +58,7 @@ namespace QuickLook.Plugin.IPreviewHandlers
_panel?.Dispose(); _panel?.Dispose();
_panel = null; _panel = null;
} }
~PluginInterface() ~PluginInterface()
{ {
Cleanup(); Cleanup();

View File

@@ -1,6 +1,5 @@
using System; using System;
using System.IO; using System.IO;
using System.Text;
namespace QuickLook.Plugin.PDFViewer namespace QuickLook.Plugin.PDFViewer
{ {

View File

@@ -24,7 +24,7 @@ namespace QuickLook
if (e.Modifiers != Keys.None) if (e.Modifiers != Keys.None)
return; return;
ViewWindowManager.GetInstance().InvokeRoutine(e.KeyCode != Keys.Space); ViewWindowManager.GetInstance().InvokeRoutine(e.KeyCode);
} }
private void InstallHook(KeyEventHandler handler) private void InstallHook(KeyEventHandler handler)
@@ -32,6 +32,8 @@ namespace QuickLook
_hook = GlobalKeyboardHook.GetInstance(); _hook = GlobalKeyboardHook.GetInstance();
_hook.HookedKeys.Add(Keys.Space); _hook.HookedKeys.Add(Keys.Space);
//_hook.HookedKeys.Add(Keys.Enter);
_hook.HookedKeys.Add(Keys.Up); _hook.HookedKeys.Add(Keys.Up);
_hook.HookedKeys.Add(Keys.Down); _hook.HookedKeys.Add(Keys.Down);
_hook.HookedKeys.Add(Keys.Left); _hook.HookedKeys.Add(Keys.Left);

View File

@@ -0,0 +1,134 @@
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using QuickLook.NativeMethods;
namespace QuickLook.Helpers
{
internal class FileHelper
{
private static string GetExecutable(string line)
{
var ret = line.StartsWith("\"")
? line.Split(new[] {"\" "}, 2, StringSplitOptions.None)
: line.Split(new[] {' '}, 2, StringSplitOptions.None);
ret[0] = ret[0].StartsWith("\"") ? ret[0].Substring(1) : ret[0];
return ret[0];
}
public static bool? GetAssocApplication(string path, out string executePath, out string appFriendlyName)
{
executePath = string.Empty;
appFriendlyName = string.Empty;
if (string.IsNullOrEmpty(path))
return null;
if (Directory.Exists(path))
return null;
if (!File.Exists(path))
return null;
if (Path.GetExtension(path) == ".lnk")
{
var shell = (IWshShell) new WshShell();
var link = shell.CreateShortcut(path);
path = GetExecutable(link.TargetPath);
}
var ext = Path.GetExtension(path).ToLower();
var isExe = ext.ToLower() == ".exe";
// no assoc. app. found
if (string.IsNullOrEmpty(GetAssocApplicationNative(ext, AssocStr.Command)))
if (string.IsNullOrEmpty(GetAssocApplicationNative(ext, AssocStr.AppId))) // UWP
return null;
executePath = path;
appFriendlyName = isExe
? FileVersionInfo.GetVersionInfo(path).FileDescription
: GetAssocApplicationNative(ext, AssocStr.FriendlyAppName);
if (string.IsNullOrEmpty(appFriendlyName))
appFriendlyName = Path.GetFileName(path);
return isExe;
}
[DllImport("shlwapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern uint AssocQueryString(AssocF flags, AssocStr str, string pszAssoc, string pszExtra,
[Out] StringBuilder sOut, [In] [Out] ref uint nOut);
private static string GetAssocApplicationNative(string fileExtensionIncludingDot, AssocStr str)
{
uint cOut = 0;
if (AssocQueryString(AssocF.Verify | AssocF.RemapRunDll | AssocF.InitIgnoreUnknown, str,
fileExtensionIncludingDot, null, null,
ref cOut) != 1)
return null;
var pOut = new StringBuilder((int) cOut);
if (AssocQueryString(AssocF.Verify | AssocF.RemapRunDll | AssocF.InitIgnoreUnknown, str,
fileExtensionIncludingDot, null, pOut,
ref cOut) != 0)
return null;
return pOut.ToString();
}
[SuppressMessage("ReSharper", "InconsistentNaming")]
[Flags]
private enum AssocF
{
InitNoRemapCLSID = 0x1,
InitByExeName = 0x2,
OpenByExeName = 0x2,
InitDefaultToStar = 0x4,
InitDefaultToFolder = 0x8,
NoUserSettings = 0x10,
NoTruncate = 0x20,
Verify = 0x40,
RemapRunDll = 0x80,
NoFixUps = 0x100,
IgnoreBaseClass = 0x200,
InitIgnoreUnknown = 0x400,
InitFixedProgid = 0x800,
IsProtocol = 0x1000,
InitForFile = 0x2000
}
//[SuppressMessage("ReSharper", "InconsistentNaming")]
private enum AssocStr
{
Command = 1,
Executable,
FriendlyDocName,
FriendlyAppName,
NoOpen,
ShellNewValue,
DdeCommand,
DdeIfExec,
DdeApplication,
DdeTopic,
InfoTip,
QuickTip,
TileInfo,
ContentType,
DefaultIcon,
ShellExtension,
DropTarget,
DelegateExecute,
SupportedUriProtocols,
ProgId,
AppId,
AppPublisher,
AppIconReference,
Max
}
}
}

View File

@@ -57,6 +57,11 @@
WindowChrome.IsHitTestVisibleInChrome="True" WindowChrome.IsHitTestVisibleInChrome="True"
Height="15" Margin="10,0" Foreground="Gray" Height="15" Margin="10,0" Foreground="Gray"
Cursor="Hand" /> Cursor="Hand" />
<Button x:Name="buttonOpenWith" DockPanel.Dock="Right" Content="Open with ..." Height="20"
Margin="10,0,0,0" Padding="5,0"
Focusable="False" Cursor="Hand"
Background="#E5EEEEEE" BorderBrush="#E59A9A9A"
WindowChrome.IsHitTestVisibleInChrome="True" Foreground="#FF404040" />
<!-- set grid.background colour makes it clickable --> <!-- set grid.background colour makes it clickable -->
<Grid x:Name="titleArea" Background="Transparent"> <Grid x:Name="titleArea" Background="Transparent">
<TextBlock Text="{Binding ContextObject.Title, ElementName=mainWindow}" FontSize="14" <TextBlock Text="{Binding ContextObject.Title, ElementName=mainWindow}" FontSize="14"

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Windows; using System.Windows;
using System.Windows.Threading; using System.Windows.Threading;
using QuickLook.Helpers; using QuickLook.Helpers;
@@ -13,6 +14,8 @@ namespace QuickLook
/// </summary> /// </summary>
public partial class MainWindowTransparent : Window public partial class MainWindowTransparent : Window
{ {
private string _path = string.Empty;
internal MainWindowTransparent() internal MainWindowTransparent()
{ {
// this object should be initialized before loading UI components, because many of which are binding to it. // this object should be initialized before loading UI components, because many of which are binding to it.
@@ -31,10 +34,27 @@ namespace QuickLook
}; };
buttonCloseWindow.MouseLeftButtonUp += (sender, e) => BeginHide(true); buttonCloseWindow.MouseLeftButtonUp += (sender, e) => BeginHide(true);
/*PreviewKeyUp += (sender, e) =>
{
if (e.Key == Key.Enter)
OpenWithAssocApp();
};*/
buttonOpenWith.Click += (sender, e) => OpenWithAssocApp();
} }
public ContextObject ContextObject { get; private set; } public ContextObject ContextObject { get; private set; }
private void OpenWithAssocApp()
{
if (string.IsNullOrEmpty(_path))
return;
Process.Start(new ProcessStartInfo(_path) {WorkingDirectory = Path.GetDirectoryName(_path)});
BeginHide(true);
}
private new void Show() private new void Show()
{ {
// revert UI changes // revert UI changes
@@ -107,6 +127,8 @@ namespace QuickLook
// get window size before showing it // get window size before showing it
ContextObject.ViewerPlugin.Prepare(path, ContextObject); ContextObject.ViewerPlugin.Prepare(path, ContextObject);
SetOpenWithButtonAndPath(path);
Show(); Show();
// load plugin, do not block UI // load plugin, do not block UI
@@ -128,6 +150,15 @@ namespace QuickLook
throw thrown; throw thrown;
} }
private void SetOpenWithButtonAndPath(string path)
{
var isExe = FileHelper.GetAssocApplication(path, out string executePath, out string appFriendlyName);
_path = executePath;
buttonOpenWith.Visibility = isExe == null ? Visibility.Collapsed : Visibility.Visible;
buttonOpenWith.Content = isExe == true ? $"Run {appFriendlyName}" : $"Open with {appFriendlyName}";
}
internal bool BeginHide(bool quit = false) internal bool BeginHide(bool quit = false)
{ {
if (quit && App.RunningAsViewer) if (quit && App.RunningAsViewer)

View File

@@ -42,4 +42,36 @@ namespace QuickLook.NativeMethods
void Resolve(IntPtr hwnd, int fFlags); void Resolve(IntPtr hwnd, int fFlags);
void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
} }
[ComImport]
[Guid("72C24DD5-D70A-438B-8A42-98424B88AFB8")]
internal class WshShell
{
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("F935DC21-1CF0-11D0-ADB9-00C04FD58A0B")]
internal interface IWshShell
{
IWshShortcut CreateShortcut(string pathLink);
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
[Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B")]
internal interface IWshShortcut
{
string FullName { get; }
string Arguments { get; set; }
string Description { get; set; }
string Hotkey { get; set; }
string IconLocation { get; set; }
string RelativePath { set; }
string TargetPath { get; set; }
int WindowStyle { get; set; }
string WorkingDirectory { get; set; }
void Load([In] string pathLink);
void Save();
}
} }

View File

@@ -105,6 +105,7 @@
<Compile Include="Helpers\BlurLibrary\PlatformsImpl\Windows81WindowBlurController.cs" /> <Compile Include="Helpers\BlurLibrary\PlatformsImpl\Windows81WindowBlurController.cs" />
<Compile Include="Helpers\BlurLibrary\PlatformsImpl\Windows8WindowBlurController.cs" /> <Compile Include="Helpers\BlurLibrary\PlatformsImpl\Windows8WindowBlurController.cs" />
<Compile Include="Helpers\BlurLibrary\PlatformsImpl\WindowsVistaWindowBlurController.cs" /> <Compile Include="Helpers\BlurLibrary\PlatformsImpl\WindowsVistaWindowBlurController.cs" />
<Compile Include="Helpers\FileHelper.cs" />
<Compile Include="MainWindowNoTransparent.cs" /> <Compile Include="MainWindowNoTransparent.cs" />
<Compile Include="NativeMethods\ShellLink.cs" /> <Compile Include="NativeMethods\ShellLink.cs" />
<Compile Include="PidHelper.cs" /> <Compile Include="PidHelper.cs" />
@@ -118,7 +119,7 @@
<Compile Include="Plugin\InfoPanel\PluginInterface.cs" /> <Compile Include="Plugin\InfoPanel\PluginInterface.cs" />
<Compile Include="Plugin\InfoPanel\WindowsThumbnailProvider.cs" /> <Compile Include="Plugin\InfoPanel\WindowsThumbnailProvider.cs" />
<Compile Include="Plugin\IViewer.cs" /> <Compile Include="Plugin\IViewer.cs" />
<Compile Include="Helpers\DpiHelpers.cs" /> <Compile Include="Helpers\DpiHelper.cs" />
<Compile Include="TrayIconManager.cs" /> <Compile Include="TrayIconManager.cs" />
<Compile Include="Plugin\ContextObject.cs" /> <Compile Include="Plugin\ContextObject.cs" />
<Compile Include="Helpers\WindowHelper.cs" /> <Compile Include="Helpers\WindowHelper.cs" />

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Forms;
using QuickLook.Helpers; using QuickLook.Helpers;
using QuickLook.Plugin; using QuickLook.Plugin;
@@ -26,8 +27,11 @@ namespace QuickLook
_currentMainWindow = _viewWindowTransparentTransparent; _currentMainWindow = _viewWindowTransparentTransparent;
} }
internal void InvokeRoutine(bool replaceView = false) internal void InvokeRoutine(Keys key)
{ {
// do we need switch to another file?
var replaceView = key == Keys.Up || key == Keys.Down || key == Keys.Left || key == Keys.Right;
if (replaceView && _currentMainWindow.IsLoaded && _currentMainWindow.Visibility != Visibility.Visible) if (replaceView && _currentMainWindow.IsLoaded && _currentMainWindow.Visibility != Visibility.Visible)
return; return;