mirror of
https://github.com/QL-Win/QuickLook.git
synced 2025-09-10 09:19:06 +00:00
Compare commits
32 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ce8cfacee1 | ||
![]() |
00c98acee6 | ||
![]() |
20fbda1e44 | ||
![]() |
69cad9c841 | ||
![]() |
c320249f20 | ||
![]() |
96a46e3a23 | ||
![]() |
903c90472b | ||
![]() |
8deddb2537 | ||
![]() |
f58ef0dcb6 | ||
![]() |
134c164a7e | ||
![]() |
6ed5b9b6d9 | ||
![]() |
4dede66da9 | ||
![]() |
43e67356f2 | ||
![]() |
4dafe7eebe | ||
![]() |
3be1c221c5 | ||
![]() |
ad5fd47c26 | ||
![]() |
5ad20b8a53 | ||
![]() |
13dfa7a815 | ||
![]() |
a7bd0ceb91 | ||
![]() |
3af6448b50 | ||
![]() |
3768470a33 | ||
![]() |
7e95d5ea2b | ||
![]() |
f4e68c109e | ||
![]() |
93e1512b94 | ||
![]() |
6471125a0b | ||
![]() |
16f20dc79d | ||
![]() |
9ffb4a0e91 | ||
![]() |
7d713682ba | ||
![]() |
cdddb303bd | ||
![]() |
250e796095 | ||
![]() |
4c82f1a9f4 | ||
![]() |
9fbf33b4aa |
@@ -5,22 +5,12 @@
|
||||
|
||||
#define EXPORT extern "C" __declspec(dllexport)
|
||||
|
||||
EXPORT int GetFocusedWindowType()
|
||||
EXPORT Shell32::FocusedWindowType GetFocusedWindowType()
|
||||
{
|
||||
return Shell32::GetFocusedWindowType();
|
||||
}
|
||||
|
||||
EXPORT void SaveCurrentSelection()
|
||||
EXPORT void GetCurrentSelection(PWCHAR buffer)
|
||||
{
|
||||
Shell32::SaveCurrentSelection();
|
||||
}
|
||||
|
||||
EXPORT int GetCurrentSelectionCount()
|
||||
{
|
||||
return Shell32::GetCurrentSelectionCount();
|
||||
}
|
||||
|
||||
EXPORT void GetCurrentSelectionBuffer(PWCHAR buffer)
|
||||
{
|
||||
Shell32::GetCurrentSelectionBuffer(buffer);
|
||||
Shell32::GetCurrentSelection(buffer);
|
||||
}
|
||||
|
@@ -58,6 +58,7 @@
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;QUICKLOOKSHELL32HELPER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
@@ -72,6 +73,7 @@
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;QUICKLOOKSHELL32HELPER_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
|
@@ -1,201 +1,19 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "Shell32.h"
|
||||
#include <atlcomcli.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
vector<wstring> Shell32::vector_items;
|
||||
|
||||
void Shell32::SaveCurrentSelection()
|
||||
{
|
||||
vector_items.clear();
|
||||
|
||||
switch (GetFocusedWindowType())
|
||||
{
|
||||
case EXPLORER:
|
||||
SaveSelectedFromExplorer();
|
||||
break;
|
||||
case DESKTOP:
|
||||
SaveSelectedFromDesktop();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UINT Shell32::GetCurrentSelectionCount()
|
||||
{
|
||||
return vector_items.size();
|
||||
}
|
||||
|
||||
void Shell32::GetCurrentSelectionBuffer(PWCHAR buffer)
|
||||
{
|
||||
PWCHAR pos = buffer;
|
||||
|
||||
for (vector<wstring>::iterator it = vector_items.begin(); it < vector_items.end(); ++it)
|
||||
{
|
||||
int l = it->length();
|
||||
wcscpy_s(pos, l + 1, it->c_str());
|
||||
|
||||
pos += l;
|
||||
|
||||
// overwrite NULL
|
||||
wcscpy_s(pos++, 2, L"|");
|
||||
}
|
||||
|
||||
// remove last "|"
|
||||
wcscpy_s(pos - 1, 1, L"");
|
||||
}
|
||||
|
||||
void Shell32::SaveSelectedFromExplorer()
|
||||
{
|
||||
CoInitialize(nullptr);
|
||||
|
||||
CComPtr<IShellWindows> psw;
|
||||
HRESULT ret = psw.CoCreateInstance(CLSID_ShellWindows);
|
||||
|
||||
auto hwndFGW = GetForegroundWindow();
|
||||
|
||||
auto fFound = FALSE;
|
||||
|
||||
for (int i = 0; !fFound; i++)
|
||||
{
|
||||
VARIANT vi;
|
||||
V_VT(&vi) = VT_I4;
|
||||
V_I4(&vi) = i;
|
||||
|
||||
CComPtr<IDispatch> pdisp;
|
||||
// ReSharper disable once CppSomeObjectMembersMightNotBeInitialized
|
||||
if (SUCCEEDED(psw->Item(vi, &pdisp)))
|
||||
{
|
||||
CComPtr<IWebBrowserApp> pwba;
|
||||
if (SUCCEEDED(pdisp->QueryInterface(IID_IWebBrowserApp, reinterpret_cast<void**>(&pwba))))
|
||||
{
|
||||
HWND hwndWBA;
|
||||
if (SUCCEEDED(pwba->get_HWND(reinterpret_cast<LONG_PTR*>(&hwndWBA))) && hwndWBA == hwndFGW)
|
||||
{
|
||||
fFound = TRUE;
|
||||
|
||||
CComPtr<IDispatch> ppdisp;
|
||||
if (SUCCEEDED(pwba->get_Document(&ppdisp)))
|
||||
{
|
||||
CComPtr<IShellFolderViewDual2> pshvd;
|
||||
if (SUCCEEDED(ppdisp->QueryInterface(IID_IShellFolderViewDual2, reinterpret_cast<void**>(&pshvd))))
|
||||
{
|
||||
CComPtr<FolderItems> pfis;
|
||||
if (SUCCEEDED(pshvd->SelectedItems(&pfis)))
|
||||
{
|
||||
LONG pCount = 0L;
|
||||
pfis->get_Count(&pCount);
|
||||
|
||||
for (int ii = 0; ii < pCount; ii++)
|
||||
{
|
||||
VARIANT vii;
|
||||
V_VT(&vii) = VT_I4;
|
||||
V_I4(&vii) = ii;
|
||||
|
||||
CComPtr<FolderItem> pfi;
|
||||
// ReSharper disable once CppSomeObjectMembersMightNotBeInitialized
|
||||
if (SUCCEEDED(pfis->Item(vii, &pfi)))
|
||||
{
|
||||
CComBSTR pbs = SysAllocStringLen(L"", MAX_PATH);
|
||||
pfi->get_Path(&pbs);
|
||||
|
||||
wstring ws = wstring(pbs);
|
||||
ws.shrink_to_fit();
|
||||
|
||||
vector_items.push_back(ws);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CComQIPtr<IWebBrowser2> Shell32::AttachDesktopShellWindow()
|
||||
{
|
||||
CoInitialize(nullptr);
|
||||
|
||||
CComPtr<IShellWindows> psw;
|
||||
CComQIPtr<IWebBrowser2> pdispOut;
|
||||
|
||||
if (SUCCEEDED(psw.CoCreateInstance(CLSID_ShellWindows)))
|
||||
{
|
||||
VARIANT pvarLoc = {VT_EMPTY};
|
||||
long phwnd;
|
||||
psw->FindWindowSW(&pvarLoc, &pvarLoc, SWC_DESKTOP, &phwnd, SWFO_NEEDDISPATCH, reinterpret_cast<IDispatch**>(&pdispOut));
|
||||
}
|
||||
return pdispOut;
|
||||
}
|
||||
|
||||
void Shell32::SaveSelectedFromDesktop()
|
||||
{
|
||||
auto pWebBrowser2 = AttachDesktopShellWindow();
|
||||
|
||||
if (!pWebBrowser2)
|
||||
return;
|
||||
|
||||
CComQIPtr<IServiceProvider> psp(pWebBrowser2);
|
||||
|
||||
if (!psp) return;
|
||||
|
||||
CComPtr<IShellBrowser> psb;
|
||||
if (SUCCEEDED(psp->QueryService(SID_STopLevelBrowser, IID_IShellBrowser, reinterpret_cast<LPVOID*>(&psb))))
|
||||
{
|
||||
CComPtr<IShellView> psv;
|
||||
if (SUCCEEDED(psb->QueryActiveShellView(&psv)))
|
||||
{
|
||||
CComPtr<IFolderView> pfv;
|
||||
if (SUCCEEDED(psv->QueryInterface(IID_IFolderView, reinterpret_cast<void**>(&pfv))))
|
||||
{
|
||||
CComPtr<IDataObject> dao;
|
||||
if (SUCCEEDED(psv->GetItemObject(SVGIO_SELECTION, IID_IDataObject, reinterpret_cast<void**>(&dao))))
|
||||
vectorFromDataObject(dao);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Shell32::vectorFromDataObject(CComPtr<IDataObject> dao)
|
||||
{
|
||||
FORMATETC formatetc;
|
||||
STGMEDIUM medium;
|
||||
|
||||
formatetc.cfFormat = CF_HDROP;
|
||||
formatetc.ptd = nullptr;
|
||||
formatetc.dwAspect = DVASPECT_CONTENT;
|
||||
formatetc.lindex = -1;
|
||||
formatetc.tymed = TYMED_HGLOBAL;
|
||||
|
||||
medium.tymed = TYMED_HGLOBAL;
|
||||
|
||||
if (SUCCEEDED(dao->GetData(&formatetc, &medium)))
|
||||
{
|
||||
int n = DragQueryFile(HDROP(medium.hGlobal), 0xFFFFFFFF, nullptr, 0);
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
WCHAR buffer[MAX_PATH];
|
||||
DragQueryFile(HDROP(medium.hGlobal), i, buffer, MAX_PATH - 1);
|
||||
|
||||
wstring ws = wstring(buffer);
|
||||
ws.shrink_to_fit();
|
||||
|
||||
vector_items.push_back(ws);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Shell32::FocusedWindowType Shell32::GetFocusedWindowType()
|
||||
{
|
||||
auto type = INVALID;
|
||||
|
||||
auto hwndfg = GetForegroundWindow();
|
||||
|
||||
if (isCursorActivated(hwndfg))
|
||||
return INVALID;
|
||||
|
||||
auto classBuffer = new WCHAR[MAX_PATH];
|
||||
if (SUCCEEDED(GetClassName(hwndfg, classBuffer, MAX_PATH)))
|
||||
{
|
||||
@@ -215,3 +33,132 @@ Shell32::FocusedWindowType Shell32::GetFocusedWindowType()
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
void Shell32::GetCurrentSelection(PWCHAR buffer)
|
||||
{
|
||||
switch (GetFocusedWindowType())
|
||||
{
|
||||
case EXPLORER:
|
||||
getSelectedFromExplorer(buffer);
|
||||
break;
|
||||
case DESKTOP:
|
||||
getSelectedFromDesktop(buffer);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Shell32::getSelectedFromExplorer(PWCHAR buffer)
|
||||
{
|
||||
CoInitialize(nullptr);
|
||||
|
||||
CComPtr<IShellWindows> psw;
|
||||
if (FAILED(psw.CoCreateInstance(CLSID_ShellWindows)))
|
||||
return;
|
||||
|
||||
auto hwndFGW = GetForegroundWindow();
|
||||
|
||||
auto count = 0L;
|
||||
psw->get_Count(&count);
|
||||
|
||||
for (auto i = 0; i < count; i++)
|
||||
{
|
||||
VARIANT vi;
|
||||
V_VT(&vi) = VT_I4;
|
||||
V_I4(&vi) = i;
|
||||
|
||||
CComPtr<IDispatch> pdisp;
|
||||
// ReSharper disable once CppSomeObjectMembersMightNotBeInitialized
|
||||
if (FAILED(psw->Item(vi, &pdisp)))
|
||||
continue;
|
||||
|
||||
CComQIPtr<IWebBrowserApp> pwba;
|
||||
if (FAILED(pdisp->QueryInterface(IID_IWebBrowserApp, reinterpret_cast<void**>(&pwba))))
|
||||
continue;
|
||||
|
||||
HWND hwndWBA;
|
||||
if (FAILED(pwba->get_HWND(reinterpret_cast<LONG_PTR*>(&hwndWBA))))
|
||||
continue;
|
||||
|
||||
if (hwndWBA != hwndFGW || isCursorActivated(hwndWBA))
|
||||
continue;
|
||||
|
||||
getSelectedInternal(pwba, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void Shell32::getSelectedFromDesktop(PWCHAR buffer)
|
||||
{
|
||||
CoInitialize(nullptr);
|
||||
|
||||
CComPtr<IShellWindows> psw;
|
||||
CComQIPtr<IWebBrowserApp> pwba;
|
||||
|
||||
if (FAILED(psw.CoCreateInstance(CLSID_ShellWindows)))
|
||||
return;
|
||||
|
||||
VARIANT pvarLoc = {VT_EMPTY};
|
||||
long phwnd;
|
||||
if (FAILED(psw->FindWindowSW(&pvarLoc, &pvarLoc, SWC_DESKTOP, &phwnd, SWFO_NEEDDISPATCH, reinterpret_cast<IDispatch**>(&pwba))))
|
||||
return;
|
||||
|
||||
if (isCursorActivated(reinterpret_cast<HWND>(phwnd)))
|
||||
return;
|
||||
|
||||
getSelectedInternal(pwba, buffer);
|
||||
}
|
||||
|
||||
void Shell32::getSelectedInternal(CComQIPtr<IWebBrowserApp> pwba, PWCHAR buffer)
|
||||
{
|
||||
CComQIPtr<IServiceProvider> psp;
|
||||
if (FAILED(pwba->QueryInterface(IID_IServiceProvider, reinterpret_cast<void**>(&psp))))
|
||||
return;
|
||||
|
||||
CComPtr<IShellBrowser> psb;
|
||||
if (FAILED(psp->QueryService(SID_STopLevelBrowser, IID_IShellBrowser, reinterpret_cast<LPVOID*>(&psb))))
|
||||
return;
|
||||
|
||||
CComPtr<IShellView> psv;
|
||||
if (FAILED(psb->QueryActiveShellView(&psv)))
|
||||
return;
|
||||
|
||||
CComPtr<IDataObject> dao;
|
||||
if (FAILED(psv->GetItemObject(SVGIO_SELECTION, IID_IDataObject, reinterpret_cast<void**>(&dao))))
|
||||
return;
|
||||
|
||||
return obtainFirstItem(dao, buffer);
|
||||
}
|
||||
|
||||
void Shell32::obtainFirstItem(CComPtr<IDataObject> dao, PWCHAR buffer)
|
||||
{
|
||||
FORMATETC formatetc;
|
||||
STGMEDIUM medium;
|
||||
|
||||
formatetc.cfFormat = CF_HDROP;
|
||||
formatetc.ptd = nullptr;
|
||||
formatetc.dwAspect = DVASPECT_CONTENT;
|
||||
formatetc.lindex = -1;
|
||||
formatetc.tymed = TYMED_HGLOBAL;
|
||||
|
||||
medium.tymed = TYMED_HGLOBAL;
|
||||
|
||||
if (FAILED(dao->GetData(&formatetc, &medium)))
|
||||
return;
|
||||
|
||||
int n = DragQueryFile(HDROP(medium.hGlobal), 0xFFFFFFFF, nullptr, 0);
|
||||
|
||||
if (n < 1)
|
||||
return;
|
||||
|
||||
DragQueryFile(HDROP(medium.hGlobal), 0, buffer, MAX_PATH - 1);
|
||||
}
|
||||
|
||||
bool Shell32::isCursorActivated(HWND hwnd)
|
||||
{
|
||||
auto tId = GetWindowThreadProcessId(hwnd, nullptr);
|
||||
|
||||
GUITHREADINFO gui = {sizeof gui};
|
||||
GetGUIThreadInfo(tId, &gui);
|
||||
return gui.flags || gui.hwndCaret;
|
||||
}
|
||||
|
@@ -13,16 +13,13 @@ public:
|
||||
};
|
||||
|
||||
static FocusedWindowType GetFocusedWindowType();
|
||||
static void SaveCurrentSelection();
|
||||
static UINT GetCurrentSelectionCount();
|
||||
static void GetCurrentSelectionBuffer(PWCHAR buffer);
|
||||
static void GetCurrentSelection(PWCHAR buffer);
|
||||
|
||||
private:
|
||||
static std::vector<std::wstring> vector_items;
|
||||
static void getSelectedFromDesktop(PWCHAR buffer);
|
||||
static void getSelectedFromExplorer(PWCHAR buffer);
|
||||
|
||||
static void SaveSelectedFromDesktop();
|
||||
static void SaveSelectedFromExplorer();
|
||||
|
||||
static CComQIPtr<IWebBrowser2> AttachDesktopShellWindow();
|
||||
static void vectorFromDataObject(CComPtr<IDataObject> dao);
|
||||
static void getSelectedInternal(CComQIPtr<IWebBrowserApp> pWebBrowserApp, PWCHAR buffer);
|
||||
static void obtainFirstItem(CComPtr<IDataObject> dao, PWCHAR buffer);
|
||||
static bool isCursorActivated(HWND hwndfg);
|
||||
};
|
||||
|
@@ -42,23 +42,33 @@ namespace QuickLook.Plugin.ArchiveViewer
|
||||
{
|
||||
LoadItemsFromArchive(path);
|
||||
|
||||
var folder = 0;
|
||||
var folders = -1; // do not count root node
|
||||
var files = 0;
|
||||
ulong sizeU = 0L;
|
||||
_fileEntries.ForEach(e =>
|
||||
{
|
||||
if (e.Value.IsFolder)
|
||||
folder++;
|
||||
folders++;
|
||||
else
|
||||
files++;
|
||||
|
||||
sizeU += e.Value.Size;
|
||||
});
|
||||
|
||||
var s = _solid ? " solid," : "";
|
||||
var s = _solid ? ", solid" : "";
|
||||
|
||||
string t;
|
||||
var d = folders != 0 ? $"{folders} folders" : string.Empty;
|
||||
var f = files != 0 ? $"{files} files" : string.Empty;
|
||||
if (!string.IsNullOrEmpty(d) && !string.IsNullOrEmpty(f))
|
||||
t = $", {d} and {f}";
|
||||
else if (string.IsNullOrEmpty(d) && string.IsNullOrEmpty(f))
|
||||
t = string.Empty;
|
||||
else
|
||||
t = $", {d}{f}";
|
||||
|
||||
archiveCount.Content =
|
||||
$"{_type} archive,{s} {folder - 1} folders and {files} files"; // do not count root node
|
||||
$"{_type} archive{s}{t}";
|
||||
archiveSizeC.Content = $"Compressed size {_totalZippedSize.ToPrettySize(2)}";
|
||||
archiveSizeU.Content = $"Uncompressed size {sizeU.ToPrettySize(2)}";
|
||||
}
|
||||
|
@@ -9,8 +9,9 @@
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>QuickLook.Plugin.ArchiveViewer</RootNamespace>
|
||||
<AssemblyName>QuickLook.Plugin.ArchiveViewer</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
50
QuickLook.Plugin/QuickLook.Plugin.HtmlViewer/Helper.cs
Normal file
50
QuickLook.Plugin/QuickLook.Plugin.HtmlViewer/Helper.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace QuickLook.Plugin.HtmlViewer
|
||||
{
|
||||
internal static class Helper
|
||||
{
|
||||
public static string FilePathToFileUrl(string filePath)
|
||||
{
|
||||
var uri = new StringBuilder();
|
||||
foreach (var v in filePath)
|
||||
if (v >= 'a' && v <= 'z' || v >= 'A' && v <= 'Z' || v >= '0' && v <= '9' ||
|
||||
v == '+' || v == '/' || v == ':' || v == '.' || v == '-' || v == '_' || v == '~' ||
|
||||
v > '\xFF')
|
||||
uri.Append(v);
|
||||
else if (v == Path.DirectorySeparatorChar || v == Path.AltDirectorySeparatorChar)
|
||||
uri.Append('/');
|
||||
else
|
||||
uri.Append($"%{(int) v:X2}");
|
||||
if (uri.Length >= 2 && uri[0] == '/' && uri[1] == '/') // UNC path
|
||||
uri.Insert(0, "file:");
|
||||
else
|
||||
uri.Insert(0, "file:///");
|
||||
return uri.ToString();
|
||||
}
|
||||
|
||||
public static void SetBrowserFeatureControl()
|
||||
{
|
||||
var exeName = Path.GetFileName(App.AppFullPath);
|
||||
|
||||
// use latest engine
|
||||
SetBrowserFeatureControlKey("FEATURE_BROWSER_EMULATION", exeName, 0);
|
||||
//
|
||||
SetBrowserFeatureControlKey("FEATURE_GPU_RENDERING", exeName, 0);
|
||||
// turn on hi-dpi mode
|
||||
SetBrowserFeatureControlKey("FEATURE_96DPI_PIXEL", exeName, 1);
|
||||
}
|
||||
|
||||
private static void SetBrowserFeatureControlKey(string feature, string appName, uint value)
|
||||
{
|
||||
using (var key = Registry.CurrentUser.CreateSubKey(
|
||||
string.Concat(@"Software\Microsoft\Internet Explorer\Main\FeatureControl\", feature),
|
||||
RegistryKeyPermissionCheck.ReadWriteSubTree))
|
||||
{
|
||||
key?.SetValue(appName, value, RegistryValueKind.DWord);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,35 +0,0 @@
|
||||
// Copyright © 2010-2017 The CefSharp Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
using CefSharp;
|
||||
|
||||
namespace QuickLook.Plugin.HtmlViewer
|
||||
{
|
||||
internal class MenuHandler : IContextMenuHandler
|
||||
{
|
||||
public void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame,
|
||||
IContextMenuParams parameters,
|
||||
IMenuModel model)
|
||||
{
|
||||
}
|
||||
|
||||
public bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame,
|
||||
IContextMenuParams parameters,
|
||||
CefMenuCommand commandId, CefEventFlags eventFlags)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame)
|
||||
{
|
||||
}
|
||||
|
||||
public bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame,
|
||||
IContextMenuParams parameters,
|
||||
IMenuModel model, IRunContextMenuCallback callback)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,15 +1,16 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace QuickLook.Plugin.HtmlViewer
|
||||
{
|
||||
public class Plugin : IViewer
|
||||
{
|
||||
private WebkitPanel _panel;
|
||||
private WebpagePanel _panel;
|
||||
|
||||
public int Priority => int.MaxValue;
|
||||
public bool AllowsTransparency => true;
|
||||
public bool AllowsTransparency => false;
|
||||
|
||||
public bool CanHandle(string path)
|
||||
{
|
||||
@@ -36,12 +37,12 @@ namespace QuickLook.Plugin.HtmlViewer
|
||||
|
||||
public void View(string path, ContextObject context)
|
||||
{
|
||||
_panel = new WebkitPanel();
|
||||
_panel = new WebpagePanel();
|
||||
context.ViewerContent = _panel;
|
||||
context.Title = Path.IsPathRooted(path) ? Path.GetFileName(path) : path;
|
||||
|
||||
_panel.Navigate(path);
|
||||
context.IsBusy = false;
|
||||
_panel.Dispatcher.Invoke(() => { context.IsBusy = false; }, DispatcherPriority.Loaded);
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
|
@@ -8,12 +8,13 @@
|
||||
<OutputType>library</OutputType>
|
||||
<RootNamespace>QuickLook.Plugin.HtmlViewer</RootNamespace>
|
||||
<AssemblyName>QuickLook.Plugin.HtmlViewer</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@@ -34,20 +35,10 @@
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="CefSharp, Version=57.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>References\CefSharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="CefSharp.Core, Version=57.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>References\CefSharp.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="CefSharp.Wpf, Version=57.0.0.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=x86">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>References\CefSharp.Wpf.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xaml">
|
||||
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||
</Reference>
|
||||
@@ -56,19 +47,18 @@
|
||||
<Reference Include="PresentationFramework" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Include="WebkitPanel.xaml">
|
||||
<Compile Include="WpfBrowserWrapper.cs" />
|
||||
<Page Include="WebpagePanel.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Compile Include="..\..\GitVersion.cs">
|
||||
<Link>Properties\GitVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="MenuHandler.cs" />
|
||||
<Compile Include="Plugin.cs" />
|
||||
<Compile Include="RequestHandler.cs" />
|
||||
<Compile Include="UrlHelper.cs" />
|
||||
<Compile Include="WebkitPanel.xaml.cs">
|
||||
<DependentUpon>WebkitPanel.xaml</DependentUpon>
|
||||
<Compile Include="Helper.cs" />
|
||||
<Compile Include="WebpagePanel.xaml.cs">
|
||||
<DependentUpon>WebpagePanel.xaml</DependentUpon>
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
@@ -76,38 +66,6 @@
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<ContentWithTargetPath Include="CefSharp\cef.pak">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>cef.pak</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="CefSharp\cef_100_percent.pak">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>cef_100_percent.pak</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="CefSharp\cef_200_percent.pak">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>cef_200_percent.pak</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="CefSharp\cef_extensions.pak">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>cef_extensions.pak</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="CefSharp\icudtl.dat">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>icudtl.dat</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="CefSharp\locales\en-US.pak">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>locales\en-US.pak</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="CefSharp\natives_blob.bin">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>natives_blob.bin</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="CefSharp\snapshot_blob.bin">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>snapshot_blob.bin</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\QuickLook\QuickLook.csproj">
|
||||
@@ -117,38 +75,15 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ContentWithTargetPath Include="CefSharp\CefSharp.BrowserSubprocess.Core.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>CefSharp.BrowserSubprocess.Core.dll</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="CefSharp\CefSharp.BrowserSubprocess.exe">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>CefSharp.BrowserSubprocess.exe</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="CefSharp\chrome_elf.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>chrome_elf.dll</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="CefSharp\d3dcompiler_47.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>d3dcompiler_47.dll</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="CefSharp\libcef.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>libcef.dll</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="CefSharp\libEGL.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>libEGL.dll</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="CefSharp\libGLESv2.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>libGLESv2.dll</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<ContentWithTargetPath Include="CefSharp\widevinecdmadapter.dll">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<TargetPath>widevinecdmadapter.dll</TargetPath>
|
||||
</ContentWithTargetPath>
|
||||
<COMReference Include="SHDocVw">
|
||||
<Guid>{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>1</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
@@ -1,113 +0,0 @@
|
||||
// Copyright © 2010-2017 The CefSharp Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using CefSharp;
|
||||
|
||||
namespace QuickLook.Plugin.HtmlViewer
|
||||
{
|
||||
public class RequestHandler : IRequestHandler
|
||||
{
|
||||
bool IRequestHandler.OnBeforeBrowse(IWebBrowser browserControl, IBrowser browser, IFrame frame,
|
||||
IRequest request, bool isRedirect)
|
||||
{
|
||||
return request.TransitionType != TransitionType.Explicit;
|
||||
}
|
||||
|
||||
bool IRequestHandler.OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame,
|
||||
string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture)
|
||||
{
|
||||
return OnOpenUrlFromTab(browserControl, browser, frame, targetUrl, targetDisposition, userGesture);
|
||||
}
|
||||
|
||||
bool IRequestHandler.OnCertificateError(IWebBrowser browserControl, IBrowser browser, CefErrorCode errorCode,
|
||||
string requestUrl, ISslInfo sslInfo, IRequestCallback callback)
|
||||
{
|
||||
callback.Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
void IRequestHandler.OnPluginCrashed(IWebBrowser browserControl, IBrowser browser, string pluginPath)
|
||||
{
|
||||
}
|
||||
|
||||
CefReturnValue IRequestHandler.OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame,
|
||||
IRequest request, IRequestCallback callback)
|
||||
{
|
||||
return CefReturnValue.Continue;
|
||||
}
|
||||
|
||||
bool IRequestHandler.GetAuthCredentials(IWebBrowser browserControl, IBrowser browser, IFrame frame,
|
||||
bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback)
|
||||
{
|
||||
callback.Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IRequestHandler.OnSelectClientCertificate(IWebBrowser browserControl, IBrowser browser, bool isProxy,
|
||||
string host, int port, X509Certificate2Collection certificates, ISelectClientCertificateCallback callback)
|
||||
{
|
||||
//NOTE: If you do not wish to implement this method returning false is the default behaviour
|
||||
// We also suggest you explicitly Dispose of the callback as it wraps an unmanaged resource.
|
||||
|
||||
return OnSelectClientCertificate(browserControl, browser, isProxy, host, port, certificates, callback);
|
||||
}
|
||||
|
||||
void IRequestHandler.OnRenderProcessTerminated(IWebBrowser browserControl, IBrowser browser,
|
||||
CefTerminationStatus status)
|
||||
{
|
||||
}
|
||||
|
||||
bool IRequestHandler.OnQuotaRequest(IWebBrowser browserControl, IBrowser browser, string originUrl,
|
||||
long newSize, IRequestCallback callback)
|
||||
{
|
||||
callback.Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
void IRequestHandler.OnResourceRedirect(IWebBrowser browserControl, IBrowser browser, IFrame frame,
|
||||
IRequest request, IResponse response, ref string newUrl)
|
||||
{
|
||||
}
|
||||
|
||||
bool IRequestHandler.OnProtocolExecution(IWebBrowser browserControl, IBrowser browser, string url)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void IRequestHandler.OnRenderViewReady(IWebBrowser browserControl, IBrowser browser)
|
||||
{
|
||||
}
|
||||
|
||||
bool IRequestHandler.OnResourceResponse(IWebBrowser browserControl, IBrowser browser, IFrame frame,
|
||||
IRequest request, IResponse response)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IResponseFilter IRequestHandler.GetResourceResponseFilter(IWebBrowser browserControl, IBrowser browser,
|
||||
IFrame frame, IRequest request, IResponse response)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
void IRequestHandler.OnResourceLoadComplete(IWebBrowser browserControl, IBrowser browser, IFrame frame,
|
||||
IRequest request, IResponse response, UrlRequestStatus status, long receivedContentLength)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual bool OnOpenUrlFromTab(IWebBrowser browserControl, IBrowser browser, IFrame frame,
|
||||
string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual bool OnSelectClientCertificate(IWebBrowser browserControl, IBrowser browser, bool isProxy,
|
||||
string host, int port, X509Certificate2Collection certificates, ISelectClientCertificateCallback callback)
|
||||
{
|
||||
callback.Dispose();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,27 +0,0 @@
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace QuickLook.Plugin.HtmlViewer
|
||||
{
|
||||
internal static class UrlHelper
|
||||
{
|
||||
internal static string FilePathToFileUrl(string filePath)
|
||||
{
|
||||
var uri = new StringBuilder();
|
||||
foreach (var v in filePath)
|
||||
if (v >= 'a' && v <= 'z' || v >= 'A' && v <= 'Z' || v >= '0' && v <= '9' ||
|
||||
v == '+' || v == '/' || v == ':' || v == '.' || v == '-' || v == '_' || v == '~' ||
|
||||
v > '\xFF')
|
||||
uri.Append(v);
|
||||
else if (v == Path.DirectorySeparatorChar || v == Path.AltDirectorySeparatorChar)
|
||||
uri.Append('/');
|
||||
else
|
||||
uri.Append($"%{(int) v:X2}");
|
||||
if (uri.Length >= 2 && uri[0] == '/' && uri[1] == '/') // UNC path
|
||||
uri.Insert(0, "file:");
|
||||
else
|
||||
uri.Insert(0, "file:///");
|
||||
return uri.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,62 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using CefSharp;
|
||||
|
||||
namespace QuickLook.Plugin.HtmlViewer
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for UserControl1.xaml
|
||||
/// </summary>
|
||||
public partial class WebkitPanel : UserControl, IDisposable
|
||||
{
|
||||
private readonly string _cefPath =
|
||||
Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
|
||||
|
||||
public WebkitPanel()
|
||||
{
|
||||
var libraryLoader = new CefLibraryHandle(Path.Combine(_cefPath, "libcef.dll"));
|
||||
|
||||
if (!Cef.IsInitialized)
|
||||
Cef.Initialize(new CefSettings
|
||||
{
|
||||
BrowserSubprocessPath = Path.Combine(_cefPath, "CefSharp.BrowserSubprocess.exe"),
|
||||
LocalesDirPath = Path.Combine(_cefPath, "locales"),
|
||||
ResourcesDirPath = _cefPath,
|
||||
LogSeverity = LogSeverity.Disable,
|
||||
CefCommandLineArgs = {new KeyValuePair<string, string>("disable-gpu", "1")}
|
||||
});
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
Application.Current.Exit += (sender, e) => Cef.Shutdown();
|
||||
|
||||
browser.RequestHandler = new RequestHandler();
|
||||
browser.MenuHandler = new MenuHandler();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
browser?.Dispose();
|
||||
}
|
||||
|
||||
public void Navigate(string path)
|
||||
{
|
||||
if (Path.IsPathRooted(path))
|
||||
path = UrlHelper.FilePathToFileUrl(path);
|
||||
|
||||
browser.IsBrowserInitializedChanged += (sender, e) => browser.Load(path);
|
||||
}
|
||||
|
||||
public void LoadHtml(string html, string path)
|
||||
{
|
||||
if (Path.IsPathRooted(path))
|
||||
path = UrlHelper.FilePathToFileUrl(path);
|
||||
|
||||
browser.IsBrowserInitializedChanged += (sender, e) => browser.LoadHtml(html, path);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,13 +1,12 @@
|
||||
<UserControl x:Class="QuickLook.Plugin.HtmlViewer.WebkitPanel"
|
||||
<UserControl x:Class="QuickLook.Plugin.HtmlViewer.WebpagePanel"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:QuickLook.Plugin.HtmlViewer"
|
||||
xmlns:cef="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="300" d:DesignWidth="300">
|
||||
<Grid>
|
||||
<cef:ChromiumWebBrowser x:Name="browser" />
|
||||
<local:WpfWebBrowserWrapper x:Name="browser" Zoom="200" />
|
||||
</Grid>
|
||||
</UserControl>
|
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace QuickLook.Plugin.HtmlViewer
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for UserControl1.xaml
|
||||
/// </summary>
|
||||
public partial class WebpagePanel : UserControl, IDisposable
|
||||
{
|
||||
public WebpagePanel()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Helper.SetBrowserFeatureControl();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
browser?.Dispose();
|
||||
browser = null;
|
||||
}
|
||||
|
||||
public void Navigate(string path)
|
||||
{
|
||||
if (Path.IsPathRooted(path))
|
||||
path = Helper.FilePathToFileUrl(path);
|
||||
|
||||
browser.Dispatcher.Invoke(() => { browser.Navigate(path); }, DispatcherPriority.Loaded);
|
||||
}
|
||||
|
||||
public void LoadHtml(string html)
|
||||
{
|
||||
var s = new MemoryStream(Encoding.UTF8.GetBytes(html ?? ""));
|
||||
|
||||
browser.Dispatcher.Invoke(() => { browser.Navigate(s); }, DispatcherPriority.Loaded);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,233 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Navigation;
|
||||
using SHDocVw;
|
||||
using HorizontalAlignment = System.Windows.HorizontalAlignment;
|
||||
using WebBrowser = System.Windows.Controls.WebBrowser;
|
||||
|
||||
namespace QuickLook.Plugin.HtmlViewer
|
||||
{
|
||||
/// <summary>
|
||||
/// Class wraps a Browser (which itself is a bad designed WPF control) and presents itself as
|
||||
/// a better designed WPF control. For example provides a bindable source property or commands.
|
||||
/// </summary>
|
||||
public class WpfWebBrowserWrapper : ContentControl, IDisposable
|
||||
{
|
||||
private static readonly Guid SidSWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");
|
||||
|
||||
private WebBrowser _innerBrowser;
|
||||
private bool _loaded;
|
||||
private int _zoom;
|
||||
|
||||
public WpfWebBrowserWrapper()
|
||||
{
|
||||
_innerBrowser = new WebBrowser
|
||||
{
|
||||
HorizontalAlignment = HorizontalAlignment.Stretch,
|
||||
VerticalAlignment = VerticalAlignment.Stretch
|
||||
};
|
||||
|
||||
Content = _innerBrowser;
|
||||
_innerBrowser.Navigated += InnerBrowserNavigated;
|
||||
_innerBrowser.Navigating += InnerBrowserNavigating;
|
||||
_innerBrowser.LoadCompleted += InnerBrowserLoadCompleted;
|
||||
_innerBrowser.Loaded += InnerBrowserLoaded;
|
||||
_innerBrowser.SizeChanged += InnerBrowserSizeChanged;
|
||||
}
|
||||
|
||||
public string Url { get; private set; }
|
||||
|
||||
public int Zoom
|
||||
{
|
||||
get => _zoom;
|
||||
set
|
||||
{
|
||||
_zoom = value;
|
||||
ApplyZoom();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// gets the browser control's underlying activeXcontrol. Ready only from within Loaded-event but before loaded Document!
|
||||
// do not use prior loaded event.
|
||||
public InternetExplorer ActiveXControl
|
||||
{
|
||||
get
|
||||
{
|
||||
// this is a brilliant way to access the WebBrowserObject prior to displaying the actual document (eg. Document property)
|
||||
var fiComWebBrowser =
|
||||
typeof(WebBrowser).GetField("_axIWebBrowser2", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (fiComWebBrowser == null) return null;
|
||||
var objComWebBrowser = fiComWebBrowser.GetValue(_innerBrowser);
|
||||
return objComWebBrowser as InternetExplorer;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_innerBrowser.Source = null;
|
||||
_innerBrowser.Dispose();
|
||||
_innerBrowser = null;
|
||||
Content = null;
|
||||
}
|
||||
|
||||
private void InnerBrowserSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
ApplyZoom();
|
||||
}
|
||||
|
||||
private void InnerBrowserLoaded(object sender, EventArgs e)
|
||||
{
|
||||
var ie = ActiveXControl;
|
||||
ie.Silent = true;
|
||||
}
|
||||
|
||||
// called when the loading of a web page is done
|
||||
private void InnerBrowserLoadCompleted(object sender, NavigationEventArgs e)
|
||||
{
|
||||
ApplyZoom(); // apply later and not only at changed event, since only works if browser is rendered.
|
||||
}
|
||||
|
||||
// called when the browser started to load and retrieve data.
|
||||
private void InnerBrowserNavigating(object sender, NavigatingCancelEventArgs e)
|
||||
{
|
||||
if (_loaded)
|
||||
e.Cancel = true;
|
||||
_loaded = true;
|
||||
}
|
||||
|
||||
// re query the commands once done navigating.
|
||||
private void InnerBrowserNavigated(object sender, NavigationEventArgs e)
|
||||
{
|
||||
RegisterWindowErrorHanlder_();
|
||||
|
||||
var alertBlocker =
|
||||
"window.print = window.alert = window.open = null;document.oncontextmenu=function(){return false;}";
|
||||
_innerBrowser.InvokeScript("execScript", alertBlocker, "JavaScript");
|
||||
}
|
||||
|
||||
public void Navigate(string uri)
|
||||
{
|
||||
Url = uri;
|
||||
|
||||
if (_innerBrowser == null)
|
||||
return;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(uri) && Uri.IsWellFormedUriString(uri, UriKind.Absolute))
|
||||
try
|
||||
{
|
||||
_innerBrowser.Source = new Uri(uri);
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
// just don't crash because of a malformed url
|
||||
}
|
||||
else
|
||||
_innerBrowser.Source = null;
|
||||
}
|
||||
|
||||
public void Navigate(Stream stream)
|
||||
{
|
||||
if (_innerBrowser == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
_innerBrowser.NavigateToStream(stream);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
// register script errors handler on DOM - document.window
|
||||
private void RegisterWindowErrorHanlder_()
|
||||
{
|
||||
object parwin = ((dynamic) _innerBrowser.Document).parentWindow;
|
||||
var cookie = new AxHost.ConnectionPointCookie(parwin, new HtmlWindowEvents2Impl(this),
|
||||
typeof(IIntHTMLWindowEvents2));
|
||||
// MemoryLEAK? No: cookie has a Finalize() to Disconnect istelf. We'll rely on that. If disconnected too early,
|
||||
// though (eg. in LoadCompleted-event) scripts continue to run and can cause error messages to appear again.
|
||||
// --> forget cookie and be happy.
|
||||
}
|
||||
|
||||
private void ApplyZoom()
|
||||
{
|
||||
if (_innerBrowser == null || !_innerBrowser.IsLoaded)
|
||||
return;
|
||||
|
||||
// grab a handle to the underlying ActiveX object
|
||||
IServiceProvider serviceProvider = null;
|
||||
if (_innerBrowser.Document != null)
|
||||
serviceProvider = (IServiceProvider) _innerBrowser.Document;
|
||||
if (serviceProvider == null)
|
||||
return;
|
||||
|
||||
var serviceGuid = SidSWebBrowserApp;
|
||||
var iid = typeof(IWebBrowser2).GUID;
|
||||
var browserInst =
|
||||
(IWebBrowser2) serviceProvider.QueryService(ref serviceGuid, ref iid);
|
||||
|
||||
try
|
||||
{
|
||||
object zoomPercObj = _zoom;
|
||||
// send the zoom command to the ActiveX object
|
||||
browserInst.ExecWB(OLECMDID.OLECMDID_OPTICAL_ZOOM,
|
||||
OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER,
|
||||
ref zoomPercObj,
|
||||
IntPtr.Zero);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignore this dynamic call if it fails.
|
||||
}
|
||||
}
|
||||
|
||||
// needed to implement the Event for script errors
|
||||
[Guid("3050f625-98b5-11cf-bb82-00aa00bdce0b")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
|
||||
[TypeLibType(TypeLibTypeFlags.FHidden)]
|
||||
[ComImport]
|
||||
private interface IIntHTMLWindowEvents2
|
||||
{
|
||||
[DispId(1002)]
|
||||
bool onerror(string description, string url, int line);
|
||||
}
|
||||
|
||||
// needed to implement the Event for script errors
|
||||
private class HtmlWindowEvents2Impl : IIntHTMLWindowEvents2
|
||||
{
|
||||
private readonly WpfWebBrowserWrapper _control;
|
||||
|
||||
public HtmlWindowEvents2Impl(WpfWebBrowserWrapper control)
|
||||
{
|
||||
_control = control;
|
||||
}
|
||||
|
||||
// implementation of the onerror Javascript error. Return true to indicate a "Handled" state.
|
||||
public bool onerror(string description, string urlString, int line)
|
||||
{
|
||||
Debug.WriteLine(description + "@" + urlString + ": " + line);
|
||||
// Handled:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Needed to expose the WebBrowser's underlying ActiveX control for zoom functionality
|
||||
[ComImport]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[Guid("6d5140c1-7436-11ce-8034-00aa006009fa")]
|
||||
internal interface IServiceProvider
|
||||
{
|
||||
[return: MarshalAs(UnmanagedType.IUnknown)]
|
||||
object QueryService(ref Guid guidService, ref Guid riid);
|
||||
}
|
||||
}
|
||||
}
|
@@ -18,17 +18,25 @@ namespace QuickLook.Plugin.IPreviewHandlers
|
||||
|
||||
switch (Path.GetExtension(path).ToLower())
|
||||
{
|
||||
// Word
|
||||
case ".doc":
|
||||
case ".docx":
|
||||
case ".docm":
|
||||
// Excel
|
||||
case ".xls":
|
||||
case ".xlsx":
|
||||
case ".bbb":
|
||||
case ".xlsm":
|
||||
case ".xlsb":
|
||||
// Visio Viewer will not quit after preview, which cause serious memory issue
|
||||
//case ".vsd":
|
||||
//case ".vsdx":
|
||||
// PowerPoint
|
||||
case ".ppt":
|
||||
case ".pptx":
|
||||
// OpenDocument
|
||||
case ".odt":
|
||||
case ".ods":
|
||||
case ".odp":
|
||||
return PreviewHandlerHost.GetPreviewHandlerGUID(path) != Guid.Empty;
|
||||
}
|
||||
|
||||
|
@@ -127,6 +127,9 @@ namespace QuickLook.Plugin.IPreviewHandlers
|
||||
if (_mCurrentPreviewHandler == null)
|
||||
return false;
|
||||
|
||||
if (IsDisposed)
|
||||
return false;
|
||||
|
||||
// bind the preview handler to the control's bounds and preview the content
|
||||
var r = ClientRectangle;
|
||||
_mCurrentPreviewHandler.SetWindow(Handle, ref r);
|
||||
|
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace QuickLook.Plugin.IPreviewHandlers
|
||||
@@ -37,10 +36,10 @@ namespace QuickLook.Plugin.IPreviewHandlers
|
||||
_control = new PreviewHandlerHost();
|
||||
presenter.Child = _control;
|
||||
_control.Open(file);
|
||||
}), DispatcherPriority.Render);
|
||||
}), DispatcherPriority.Loaded);
|
||||
|
||||
SetForegroundWindow(new WindowInteropHelper(context.ViewerWindow).Handle);
|
||||
SetActiveWindow(presenter.Handle);
|
||||
//SetForegroundWindow(new WindowInteropHelper(context.ViewerWindow).Handle);
|
||||
//SetActiveWindow(presenter.Handle);
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
|
@@ -8,10 +8,11 @@
|
||||
<OutputType>library</OutputType>
|
||||
<RootNamespace>QuickLook.Plugin.IPreviewHandlers</RootNamespace>
|
||||
<AssemblyName>QuickLook.Plugin.IPreviewHandlers</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
@@ -1,53 +1,48 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Imaging;
|
||||
using ExifLib;
|
||||
using ImageMagick;
|
||||
|
||||
namespace QuickLook.Plugin.ImageViewer
|
||||
{
|
||||
internal static class ImageFileHelper
|
||||
{
|
||||
internal static Size GetImageSize(string path)
|
||||
internal static Size? GetImageSize(string path)
|
||||
{
|
||||
var ori = GetOrientationFromExif(path);
|
||||
|
||||
using (var stream = File.OpenRead(path))
|
||||
try
|
||||
{
|
||||
var decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.None);
|
||||
var frame = decoder.Frames[0];
|
||||
var info = new MagickImageInfo(path);
|
||||
|
||||
if (ori == ExifOrientation.Rotate90CW || ori == ExifOrientation.Rotate270CW)
|
||||
return new Size {Width = frame.PixelHeight, Height = frame.PixelWidth};
|
||||
|
||||
return new Size {Width = frame.PixelWidth, Height = frame.PixelHeight};
|
||||
if (ori == OrientationType.RightTop || ori == OrientationType.LeftBotom)
|
||||
return new Size {Width = info.Height, Height = info.Width};
|
||||
return new Size {Width = info.Width, Height = info.Height};
|
||||
}
|
||||
catch (MagickException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal static ExifOrientation GetOrientationFromExif(string path)
|
||||
private static OrientationType GetOrientationFromExif(string path)
|
||||
{
|
||||
using (var stream = File.OpenRead(path))
|
||||
try
|
||||
{
|
||||
var decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.None);
|
||||
var frame = decoder.Frames[0];
|
||||
using (var re = new ExifReader(path))
|
||||
{
|
||||
re.GetTagValue(ExifTags.Orientation, out ushort orientation);
|
||||
|
||||
var orientation = ((BitmapMetadata) frame.Metadata)?.GetQuery(@"/app1/{ushort=0}/{ushort=274}");
|
||||
if (orientation == 0)
|
||||
return OrientationType.Undefined;
|
||||
|
||||
if (orientation == null)
|
||||
return ExifOrientation.Horizontal;
|
||||
|
||||
return (ExifOrientation) (ushort) orientation;
|
||||
return (OrientationType) orientation;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return OrientationType.Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
internal enum ExifOrientation
|
||||
{
|
||||
Horizontal = 1,
|
||||
MirrorHorizontal = 2,
|
||||
Rotate180 = 3,
|
||||
MirrorVertical = 4,
|
||||
MirrorHorizontal270CW = 5,
|
||||
Rotate90CW = 6,
|
||||
MirrorHorizontal90CW = 7,
|
||||
Rotate270CW = 8
|
||||
}
|
||||
}
|
||||
}
|
@@ -4,7 +4,7 @@ using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using ImageMagick;
|
||||
using XamlAnimatedGif;
|
||||
|
||||
namespace QuickLook.Plugin.ImageViewer
|
||||
@@ -30,37 +30,25 @@ namespace QuickLook.Plugin.ImageViewer
|
||||
|
||||
viewPanel.PreviewMouseLeftButtonDown += ViewPanel_PreviewMouseLeftButtonDown;
|
||||
viewPanel.PreviewMouseMove += ViewPanel_PreviewMouseMove;
|
||||
|
||||
viewPanel.TouchDown += ViewPanel_TouchDown;
|
||||
}
|
||||
|
||||
private void ViewPanel_TouchDown(object sender, TouchEventArgs e)
|
||||
{
|
||||
// TODO: touch support
|
||||
}
|
||||
|
||||
private void LoadImage(string path)
|
||||
{
|
||||
var ori = ImageFileHelper.GetOrientationFromExif(path);
|
||||
|
||||
var bitmap = new BitmapImage();
|
||||
using (var fs = File.OpenRead(path))
|
||||
{
|
||||
bitmap.BeginInit();
|
||||
bitmap.StreamSource = fs;
|
||||
bitmap.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bitmap.Rotation = ori == ImageFileHelper.ExifOrientation.Rotate90CW
|
||||
? Rotation.Rotate90
|
||||
: ori == ImageFileHelper.ExifOrientation.Rotate270CW
|
||||
? Rotation.Rotate270
|
||||
: Rotation.Rotate0;
|
||||
bitmap.EndInit();
|
||||
}
|
||||
|
||||
viewPanelImage.Source = bitmap;
|
||||
|
||||
if (Path.GetExtension(path).ToLower() == ".gif")
|
||||
AnimationBehavior.SetSourceUri(viewPanelImage, new Uri(path));
|
||||
|
||||
using (var image = new MagickImage(path))
|
||||
{
|
||||
image.Rotate(image.Orientation == OrientationType.RightTop
|
||||
? 90
|
||||
: image.Orientation == OrientationType.BottomRight
|
||||
? 180
|
||||
: image.Orientation == OrientationType.LeftBotom
|
||||
? 270
|
||||
: 0);
|
||||
|
||||
viewPanelImage.Source = image.ToBitmapSource();
|
||||
}
|
||||
}
|
||||
|
||||
private void ViewPanel_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
@@ -92,12 +80,18 @@ namespace QuickLook.Plugin.ImageViewer
|
||||
|
||||
private void ViewPanel_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
|
||||
{
|
||||
if ((Keyboard.Modifiers & ModifierKeys.Control) == 0)
|
||||
return;
|
||||
|
||||
e.Handled = true;
|
||||
|
||||
var newZoom = _zoomFactor + e.Delta / 120 * 0.1;
|
||||
if ((Keyboard.Modifiers & ModifierKeys.Control) == 0)
|
||||
{
|
||||
// normal scroll
|
||||
viewPanel.ScrollToVerticalOffset(viewPanel.VerticalOffset - e.Delta);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// zoom
|
||||
var newZoom = _zoomFactor + (double) e.Delta / 120 * 0.1;
|
||||
|
||||
newZoom = Math.Max(newZoom, _minZoomFactor);
|
||||
newZoom = Math.Min(newZoom, 3);
|
||||
|
@@ -1,10 +1,22 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
|
||||
namespace QuickLook.Plugin.ImageViewer
|
||||
{
|
||||
public class Plugin : IViewer
|
||||
{
|
||||
private static readonly string[] _formats =
|
||||
{
|
||||
// camera raw
|
||||
".3fr", ".ari", ".arw", ".bay", ".crw", ".cr2", ".cap", ".data", ".dcs", ".dcr", ".dng", ".drf", ".eip",
|
||||
".erf", ".fff", ".gpr", ".iiq", ".k25", ".kdc", ".mdc", ".mef", ".mos", ".mrw", ".nef", ".nrw", ".obm",
|
||||
".orf", ".pef", ".ptx", ".pxn", ".r3d", ".raf", ".raw", ".rwl", ".rw2", ".rwz", ".sr2", ".srf", ".srw",
|
||||
".tif", ".x3f",
|
||||
// normal
|
||||
".bmp", ".gif", ".ico", ".jpg", ".jpeg", ".png", ".psd", ".svg", ".wdp", ".tiff", ".tga"
|
||||
};
|
||||
private Size _imageSize;
|
||||
private ImagePanel _ip;
|
||||
|
||||
@@ -13,47 +25,39 @@ namespace QuickLook.Plugin.ImageViewer
|
||||
|
||||
public bool CanHandle(string path)
|
||||
{
|
||||
// TODO: determine file type by content
|
||||
|
||||
if (Directory.Exists(path))
|
||||
return false;
|
||||
|
||||
switch (Path.GetExtension(path).ToLower())
|
||||
{
|
||||
case ".bmp":
|
||||
case ".gif":
|
||||
case ".ico":
|
||||
case ".jpg":
|
||||
case ".jpeg":
|
||||
case ".png":
|
||||
case ".wdp":
|
||||
case ".tiff":
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return _formats.Contains(Path.GetExtension(path).ToLower());
|
||||
}
|
||||
|
||||
public void Prepare(string path, ContextObject context)
|
||||
{
|
||||
_imageSize = ImageFileHelper.GetImageSize(path);
|
||||
// ImageMagick want to have dcraw.exe
|
||||
Directory.SetCurrentDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
|
||||
|
||||
context.SetPreferredSizeFit(_imageSize, 0.8);
|
||||
_imageSize = ImageFileHelper.GetImageSize(path) ?? Size.Empty;
|
||||
|
||||
if (!_imageSize.IsEmpty)
|
||||
context.SetPreferredSizeFit(_imageSize, 0.8);
|
||||
else
|
||||
context.PreferredSize = new Size(1024, 768);
|
||||
}
|
||||
|
||||
public void View(string path, ContextObject context)
|
||||
{
|
||||
_ip = new ImagePanel(path);
|
||||
|
||||
context.ViewerContent = _ip;
|
||||
context.Title = $"{Path.GetFileName(path)} ({_imageSize.Width}×{_imageSize.Height})";
|
||||
context.Title = _imageSize.IsEmpty
|
||||
? $"{Path.GetFileName(path)}"
|
||||
: $"{Path.GetFileName(path)} ({_imageSize.Width}×{_imageSize.Height})";
|
||||
|
||||
context.IsBusy = false;
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
{
|
||||
Directory.SetCurrentDirectory(App.AppPath);
|
||||
_ip = null;
|
||||
}
|
||||
}
|
||||
|
@@ -9,8 +9,11 @@
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>QuickLook.Plugin.ImageViewer</RootNamespace>
|
||||
<AssemblyName>QuickLook.Plugin.ImageViewer</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
<NuGetPackageImportStamp>
|
||||
</NuGetPackageImportStamp>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
@@ -31,10 +34,17 @@
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="ExifLib, Version=1.7.0.0, Culture=neutral, PublicKeyToken=30284005913968db, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\ExifLib.1.7.0.0\lib\net45\ExifLib.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Magick.NET-Q8-x86, Version=7.0.0.0, Culture=neutral, PublicKeyToken=2004825badfa91ec, processorArchitecture=x86">
|
||||
<HintPath>..\..\packages\Magick.NET-Q8-x86.7.0.5.900\lib\net40-client\Magick.NET-Q8-x86.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Xaml" />
|
||||
<Reference Include="WindowsBase" />
|
||||
<Reference Include="XamlAnimatedGif, Version=1.1.9.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
@@ -68,5 +78,17 @@
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="dcraw.exe">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\..\packages\Magick.NET-Q8-x86.7.0.5.900\build\net40-client\Magick.NET-Q8-x86.targets" Condition="Exists('..\..\packages\Magick.NET-Q8-x86.7.0.5.900\build\net40-client\Magick.NET-Q8-x86.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\packages\Magick.NET-Q8-x86.7.0.5.900\build\net40-client\Magick.NET-Q8-x86.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Magick.NET-Q8-x86.7.0.5.900\build\net40-client\Magick.NET-Q8-x86.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
BIN
QuickLook.Plugin/QuickLook.Plugin.ImageViewer/dcraw.exe
Normal file
BIN
QuickLook.Plugin/QuickLook.Plugin.ImageViewer/dcraw.exe
Normal file
Binary file not shown.
@@ -1,5 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<packages>
|
||||
<package id="XamlAnimatedGif" version="1.1.9" targetFramework="net452" />
|
||||
<package id="ExifLib" version="1.7.0.0" targetFramework="net462" />
|
||||
<package id="Magick.NET-Q8-x86" version="7.0.5.900" targetFramework="net462" />
|
||||
<package id="XamlAnimatedGif" version="1.1.9" targetFramework="net462" />
|
||||
</packages>
|
@@ -1,16 +1,17 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
using QuickLook.Plugin.HtmlViewer;
|
||||
|
||||
namespace QuickLook.Plugin.MarkdownViewer
|
||||
{
|
||||
public class Plugin : IViewer
|
||||
{
|
||||
private WebkitPanel _panel;
|
||||
private WebpagePanel _panel;
|
||||
|
||||
public int Priority => int.MaxValue;
|
||||
public bool AllowsTransparency => true;
|
||||
public bool AllowsTransparency => false;
|
||||
|
||||
public bool CanHandle(string path)
|
||||
{
|
||||
@@ -20,6 +21,7 @@ namespace QuickLook.Plugin.MarkdownViewer
|
||||
switch (Path.GetExtension(path).ToLower())
|
||||
{
|
||||
case ".md":
|
||||
case ".rmd":
|
||||
return true;
|
||||
|
||||
default:
|
||||
@@ -36,13 +38,12 @@ namespace QuickLook.Plugin.MarkdownViewer
|
||||
|
||||
public void View(string path, ContextObject context)
|
||||
{
|
||||
_panel = new WebkitPanel();
|
||||
_panel = new WebpagePanel();
|
||||
context.ViewerContent = _panel;
|
||||
context.Title = Path.GetFileName(path);
|
||||
|
||||
_panel.LoadHtml(GenerateMarkdownHtml(path), path);
|
||||
|
||||
context.IsBusy = false;
|
||||
_panel.LoadHtml(GenerateMarkdownHtml(path));
|
||||
_panel.Dispatcher.Invoke(() => { context.IsBusy = false; }, DispatcherPriority.Loaded);
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
|
@@ -9,8 +9,9 @@
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>QuickLook.Plugin.MarkdownViewer</RootNamespace>
|
||||
<AssemblyName>QuickLook.Plugin.MarkdownViewer</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
@@ -2,7 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
@@ -37,12 +36,8 @@ namespace QuickLook.Plugin.PDFViewer
|
||||
return foundElement;
|
||||
}
|
||||
|
||||
[DllImport("gdi32")]
|
||||
private static extern int DeleteObject(IntPtr o);
|
||||
|
||||
public static BitmapSource ToBitmapSource(this Bitmap source)
|
||||
{
|
||||
var ip = source.GetHbitmap();
|
||||
BitmapSource bs = null;
|
||||
try
|
||||
{
|
||||
@@ -57,9 +52,9 @@ namespace QuickLook.Plugin.PDFViewer
|
||||
|
||||
bs.Freeze();
|
||||
}
|
||||
finally
|
||||
catch
|
||||
{
|
||||
DeleteObject(ip);
|
||||
// ignored
|
||||
}
|
||||
|
||||
return bs;
|
||||
|
@@ -11,7 +11,7 @@ namespace QuickLook.Plugin.PDFViewer
|
||||
if (values.Length < 2)
|
||||
throw new Exception("PageIdToImageConverter");
|
||||
|
||||
var zoom = 0.5f;
|
||||
var zoom = 0.3f;
|
||||
if (parameter != null)
|
||||
float.TryParse((string) parameter, out zoom);
|
||||
|
||||
@@ -21,7 +21,13 @@ namespace QuickLook.Plugin.PDFViewer
|
||||
var pageId = (int) values[1];
|
||||
if (pageId < 0) return null;
|
||||
|
||||
return handle.GetPage(pageId, zoom).ToBitmapSource();
|
||||
var bitmap = handle.GetPage(pageId, zoom);
|
||||
var bs = bitmap.ToBitmapSource();
|
||||
bitmap.Dispose();
|
||||
|
||||
GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized);
|
||||
|
||||
return bs;
|
||||
}
|
||||
|
||||
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
|
||||
|
@@ -23,7 +23,7 @@
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ListBox x:Name="listThumbnails" Grid.Column="0" VirtualizingPanel.ScrollUnit="Item"
|
||||
ScrollViewer.IsDeferredScrollingEnabled="False"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
SelectedIndex="0"
|
||||
Focusable="False"
|
||||
Background="#00FFFFFF"
|
||||
|
@@ -79,19 +79,28 @@ namespace QuickLook.Plugin.PDFViewer
|
||||
if (Keyboard.Modifiers != ModifierKeys.None)
|
||||
return;
|
||||
|
||||
e.Handled = true;
|
||||
|
||||
if (e.Delta > 0) // up
|
||||
{
|
||||
if (pageViewPanel.VerticalOffset != 0) return;
|
||||
if (pageViewPanel.VerticalOffset != 0)
|
||||
{
|
||||
pageViewPanel.ScrollToVerticalOffset(pageViewPanel.VerticalOffset - e.Delta); // normal scroll
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
PrevPage();
|
||||
e.Handled = true;
|
||||
}
|
||||
else // down
|
||||
{
|
||||
if (pageViewPanel.VerticalOffset != pageViewPanel.ScrollableHeight) return;
|
||||
if (pageViewPanel.VerticalOffset != pageViewPanel.ScrollableHeight)
|
||||
{
|
||||
pageViewPanel.ScrollToVerticalOffset(pageViewPanel.VerticalOffset - e.Delta); // normal scroll
|
||||
return;
|
||||
}
|
||||
|
||||
NextPage();
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +152,9 @@ namespace QuickLook.Plugin.PDFViewer
|
||||
if (!PdfLoaded)
|
||||
return;
|
||||
|
||||
var image = PdfHandle.GetPage(CurrentPage, ZoomFactor).ToBitmapSource();
|
||||
var bitmap = PdfHandle.GetPage(CurrentPage, ZoomFactor);
|
||||
var image = bitmap.ToBitmapSource();
|
||||
bitmap.Dispose();
|
||||
|
||||
pageViewPanelImage.Source = image;
|
||||
pageViewPanelImage.Width = pageViewPanelImage.Source.Width;
|
||||
@@ -276,7 +287,7 @@ namespace QuickLook.Plugin.PDFViewer
|
||||
|
||||
e.Handled = true;
|
||||
|
||||
newZoom = newZoom + e.Delta / 120 * 0.1;
|
||||
newZoom = newZoom + (double) e.Delta / 120 * 0.1;
|
||||
|
||||
newZoom = Math.Max(newZoom, MinZoomFactor);
|
||||
newZoom = Math.Min(newZoom, 3);
|
||||
|
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace QuickLook.Plugin.PDFViewer
|
||||
{
|
||||
@@ -37,16 +39,27 @@ namespace QuickLook.Plugin.PDFViewer
|
||||
_pdfControl = new PdfViewerControl();
|
||||
context.ViewerContent = _pdfControl;
|
||||
|
||||
_pdfControl.Loaded += (sender, e) =>
|
||||
Exception exception = null;
|
||||
|
||||
_pdfControl.Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
_pdfControl.LoadPdf(path);
|
||||
try
|
||||
{
|
||||
_pdfControl.LoadPdf(path);
|
||||
|
||||
context.Title = $"{Path.GetFileName(path)} (1 / {_pdfControl.TotalPages})";
|
||||
_pdfControl.CurrentPageChanged += (sender2, e2) => context.Title =
|
||||
$"{Path.GetFileName(path)} ({_pdfControl.CurrentPage + 1} / {_pdfControl.TotalPages})";
|
||||
context.Title = $"{Path.GetFileName(path)} (1 / {_pdfControl.TotalPages})";
|
||||
_pdfControl.CurrentPageChanged += (sender2, e2) => context.Title =
|
||||
$"{Path.GetFileName(path)} ({_pdfControl.CurrentPage + 1} / {_pdfControl.TotalPages})";
|
||||
context.IsBusy = false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
exception = e;
|
||||
}
|
||||
}), DispatcherPriority.Loaded).Wait();
|
||||
|
||||
context.IsBusy = false;
|
||||
};
|
||||
if (exception != null)
|
||||
ExceptionDispatchInfo.Capture(exception).Throw();
|
||||
}
|
||||
|
||||
public void Cleanup()
|
||||
|
@@ -9,8 +9,9 @@
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>QuickLook.Plugin.PDFViewer</RootNamespace>
|
||||
<AssemblyName>QuickLook.Plugin.PDFViewer</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
@@ -9,8 +9,9 @@
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>QuickLook.Plugin.TextViewer</RootNamespace>
|
||||
<AssemblyName>QuickLook.Plugin.TextViewer</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
@@ -8,10 +8,11 @@
|
||||
<OutputType>library</OutputType>
|
||||
<RootNamespace>QuickLook.Plugin.VideoViewer</RootNamespace>
|
||||
<AssemblyName>QuickLook.Plugin.VideoViewer</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
|
||||
</startup>
|
||||
<runtime>
|
||||
<legacyCorruptedStateExceptionsPolicy enabled="true" />
|
||||
|
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using QuickLook.Helpers;
|
||||
|
||||
namespace QuickLook
|
||||
{
|
||||
|
@@ -11,12 +11,13 @@ namespace QuickLook
|
||||
|
||||
protected BackgroundListener()
|
||||
{
|
||||
InstallHook(HotkeyEventHandler);
|
||||
InstallKeyHook(HotkeyEventHandler);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_hook?.Dispose();
|
||||
_hook = null;
|
||||
}
|
||||
|
||||
private void HotkeyEventHandler(object sender, KeyEventArgs e)
|
||||
@@ -24,21 +25,22 @@ namespace QuickLook
|
||||
if (e.Modifiers != Keys.None)
|
||||
return;
|
||||
|
||||
ViewWindowManager.GetInstance().InvokeRoutine(e.KeyCode);
|
||||
ViewWindowManager.GetInstance().InvokeRoutine(e);
|
||||
}
|
||||
|
||||
private void InstallHook(KeyEventHandler handler)
|
||||
private void InstallKeyHook(KeyEventHandler handler)
|
||||
{
|
||||
_hook = GlobalKeyboardHook.GetInstance();
|
||||
|
||||
_hook.HookedKeys.Add(Keys.Space);
|
||||
//_hook.HookedKeys.Add(Keys.Enter);
|
||||
|
||||
_hook.HookedKeys.Add(Keys.Up);
|
||||
_hook.HookedKeys.Add(Keys.Down);
|
||||
_hook.HookedKeys.Add(Keys.Left);
|
||||
_hook.HookedKeys.Add(Keys.Right);
|
||||
_hook.HookedDownKeys.Add(Keys.Enter);
|
||||
_hook.KeyDown += handler;
|
||||
|
||||
_hook.HookedUpKeys.Add(Keys.Space);
|
||||
_hook.HookedUpKeys.Add(Keys.Escape);
|
||||
_hook.HookedUpKeys.Add(Keys.Up);
|
||||
_hook.HookedUpKeys.Add(Keys.Down);
|
||||
_hook.HookedUpKeys.Add(Keys.Left);
|
||||
_hook.HookedUpKeys.Add(Keys.Right);
|
||||
_hook.KeyUp += handler;
|
||||
}
|
||||
|
||||
|
59
QuickLook/FocusMonitor.cs
Normal file
59
QuickLook/FocusMonitor.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QuickLook
|
||||
{
|
||||
internal class FocusMonitor
|
||||
{
|
||||
public delegate void HeartbeatEventHandler(HeartbeatEventArgs e);
|
||||
|
||||
private static FocusMonitor _instance;
|
||||
|
||||
public bool IsRunning { get; private set; }
|
||||
|
||||
public event HeartbeatEventHandler Heartbeat;
|
||||
|
||||
public void Start()
|
||||
{
|
||||
IsRunning = true;
|
||||
|
||||
new Task(() =>
|
||||
{
|
||||
while (IsRunning)
|
||||
{
|
||||
Thread.Sleep(500);
|
||||
|
||||
if (NativeMethods.QuickLook.GetFocusedWindowType() ==
|
||||
NativeMethods.QuickLook.FocusedWindowType.Invalid)
|
||||
continue;
|
||||
|
||||
var file = NativeMethods.QuickLook.GetCurrentSelection();
|
||||
Heartbeat?.Invoke(new HeartbeatEventArgs(DateTime.Now.Ticks, file));
|
||||
}
|
||||
}).Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
IsRunning = false;
|
||||
}
|
||||
|
||||
internal static FocusMonitor GetInstance()
|
||||
{
|
||||
return _instance ?? (_instance = new FocusMonitor());
|
||||
}
|
||||
}
|
||||
|
||||
internal class HeartbeatEventArgs : EventArgs
|
||||
{
|
||||
public HeartbeatEventArgs(long tick, string files)
|
||||
{
|
||||
InvokeTick = tick;
|
||||
FocusedFile = files;
|
||||
}
|
||||
|
||||
public long InvokeTick { get; }
|
||||
public string FocusedFile { get; }
|
||||
}
|
||||
}
|
@@ -14,7 +14,8 @@ namespace QuickLook
|
||||
|
||||
private User32.KeyboardHookProc _callback;
|
||||
private IntPtr _hhook = IntPtr.Zero;
|
||||
internal List<Keys> HookedKeys = new List<Keys>();
|
||||
internal List<Keys> HookedDownKeys = new List<Keys>();
|
||||
internal List<Keys> HookedUpKeys = new List<Keys>();
|
||||
|
||||
protected GlobalKeyboardHook()
|
||||
{
|
||||
@@ -63,13 +64,21 @@ namespace QuickLook
|
||||
if (code >= 0)
|
||||
{
|
||||
var key = (Keys) lParam.vkCode;
|
||||
if (HookedKeys.Contains(key))
|
||||
if (HookedDownKeys.Contains(key))
|
||||
{
|
||||
key = AddModifiers(key);
|
||||
|
||||
var kea = new KeyEventArgs(key);
|
||||
if (wParam == User32.WM_KEYDOWN || wParam == User32.WM_SYSKEYDOWN)
|
||||
KeyDown?.Invoke(this, kea);
|
||||
if (kea.Handled)
|
||||
return 1;
|
||||
}
|
||||
else if (HookedUpKeys.Contains(key))
|
||||
{
|
||||
key = AddModifiers(key);
|
||||
|
||||
var kea = new KeyEventArgs(key);
|
||||
if (wParam == User32.WM_KEYUP || wParam == User32.WM_SYSKEYUP)
|
||||
KeyUp?.Invoke(this, kea);
|
||||
if (kea.Handled)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices.ComTypes;
|
||||
using QuickLook.NativeMethods;
|
||||
using QuickLook.NativeMethods.Shell32;
|
||||
|
||||
namespace QuickLook.Helpers
|
||||
{
|
||||
|
@@ -2,9 +2,10 @@
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using QuickLook.NativeMethods;
|
||||
using QuickLook.NativeMethods.Shell32;
|
||||
|
||||
namespace QuickLook.Helpers
|
||||
{
|
||||
@@ -20,9 +21,8 @@ namespace QuickLook.Helpers
|
||||
return ret[0];
|
||||
}
|
||||
|
||||
public static bool? GetAssocApplication(string path, out string executePath, out string appFriendlyName)
|
||||
public static bool? GetAssocApplication(string path, out string appFriendlyName)
|
||||
{
|
||||
executePath = string.Empty;
|
||||
appFriendlyName = string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
@@ -42,14 +42,13 @@ namespace QuickLook.Helpers
|
||||
}
|
||||
|
||||
var ext = Path.GetExtension(path).ToLower();
|
||||
var isExe = ext.ToLower() == ".exe";
|
||||
var isExe = new[] {".cmd", ".bat", ".pif", ".scf", ".exe", ".com", ".scr"}.Contains(ext.ToLower());
|
||||
|
||||
// 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);
|
||||
|
@@ -1,7 +1,7 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace QuickLook
|
||||
namespace QuickLook.Helpers
|
||||
{
|
||||
internal static class PidHelper
|
||||
{
|
15
QuickLook/Helpers/ProcessHelper.cs
Normal file
15
QuickLook/Helpers/ProcessHelper.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QuickLook.Helpers
|
||||
{
|
||||
internal class ProcessHelper
|
||||
{
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static void PerformAggressiveGC()
|
||||
{
|
||||
// delay some time to make sure that all windows are closed
|
||||
Task.Delay(1000).ContinueWith(t => GC.Collect(GC.MaxGeneration));
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Interop;
|
||||
@@ -61,65 +60,21 @@ namespace QuickLook.Helpers
|
||||
pixelY = (int) (matrix.M22 * unitY);
|
||||
}
|
||||
|
||||
internal static bool IsForegroundWindowBelongToSelf()
|
||||
{
|
||||
var hwnd = User32.GetForegroundWindow();
|
||||
if (hwnd == IntPtr.Zero)
|
||||
return false;
|
||||
|
||||
User32.GetWindowThreadProcessId(hwnd, out uint procId);
|
||||
return procId == Process.GetCurrentProcess().Id;
|
||||
}
|
||||
|
||||
internal static void SetNoactivate(WindowInteropHelper window)
|
||||
{
|
||||
User32.SetWindowLong(window.Handle, User32.GWL_EXSTYLE,
|
||||
User32.GetWindowLong(window.Handle, User32.GWL_EXSTYLE) |
|
||||
User32.WS_EX_NOACTIVATE);
|
||||
}
|
||||
|
||||
internal static string GetWindowClassName(IntPtr window)
|
||||
{
|
||||
var pClassName = new StringBuilder(256);
|
||||
User32.GetClassName(window, pClassName, pClassName.Capacity);
|
||||
|
||||
return pClassName.ToString();
|
||||
}
|
||||
|
||||
internal static IntPtr GetParentWindow(IntPtr child)
|
||||
{
|
||||
return User32.GetParent(child);
|
||||
}
|
||||
|
||||
internal static IntPtr GetFocusedWindow()
|
||||
{
|
||||
var activeWindowHandle = User32.GetForegroundWindow();
|
||||
|
||||
var activeWindowThread = User32.GetWindowThreadProcessId(activeWindowHandle, IntPtr.Zero);
|
||||
var currentThread = Kernel32.GetCurrentThreadId();
|
||||
|
||||
User32.AttachThreadInput(activeWindowThread, currentThread, true);
|
||||
var focusedControlHandle = User32.GetFocus();
|
||||
User32.AttachThreadInput(activeWindowThread, currentThread, false);
|
||||
|
||||
return focusedControlHandle;
|
||||
}
|
||||
|
||||
internal static bool IsFocusedWindowSelf()
|
||||
{
|
||||
var procId = Process.GetCurrentProcess().Id;
|
||||
uint activeProcId;
|
||||
User32.GetWindowThreadProcessId(GetFocusedWindow(), out activeProcId);
|
||||
|
||||
return activeProcId == procId;
|
||||
}
|
||||
|
||||
internal static bool IsFocusedControlExplorerItem()
|
||||
{
|
||||
if (NativeMethods.QuickLook.GetFocusedWindowType() == 0)
|
||||
return false;
|
||||
|
||||
var focusedWindowClass = GetWindowClassName(GetFocusedWindow());
|
||||
var focusedWindowParentClass =
|
||||
GetWindowClassName(GetParentWindow(GetFocusedWindow()));
|
||||
|
||||
if (focusedWindowClass != "SysListView32" && focusedWindowClass != "DirectUIHWND")
|
||||
return false;
|
||||
|
||||
if (focusedWindowParentClass != "SHELLDLL_DefView")
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@@ -62,11 +62,17 @@
|
||||
WindowChrome.IsHitTestVisibleInChrome="True"
|
||||
Height="15" Margin="10,0" Foreground="Gray"
|
||||
Cursor="Hand" />
|
||||
<Button x:Name="buttonOpenWith" DockPanel.Dock="Right" Content="Open with ..." Height="20"
|
||||
<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" />
|
||||
WindowChrome.IsHitTestVisibleInChrome="True" Foreground="#FF404040">
|
||||
<Button.ContentTemplate>
|
||||
<DataTemplate>
|
||||
<ContentPresenter Content="{Binding}" RecognizesAccessKey="False" />
|
||||
</DataTemplate>
|
||||
</Button.ContentTemplate>
|
||||
</Button>
|
||||
<!-- set grid.background colour makes it clickable -->
|
||||
<Grid x:Name="titleArea" Background="Transparent">
|
||||
<TextBlock Text="{Binding ContextObject.Title, ElementName=mainWindow}" FontSize="14"
|
||||
@@ -75,7 +81,8 @@
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
<Grid>
|
||||
<ContentControl x:Name="container" />
|
||||
<ContentControl x:Name="container"
|
||||
Content="{Binding ContextObject.ViewerContent, ElementName=mainWindow}" />
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
<Grid x:Name="busyIndicatorLayer">
|
||||
|
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Threading;
|
||||
using QuickLook.Helpers;
|
||||
using QuickLook.Helpers.BlurLibrary;
|
||||
@@ -15,6 +17,7 @@ namespace QuickLook
|
||||
public partial class MainWindowTransparent : Window
|
||||
{
|
||||
private string _path = string.Empty;
|
||||
private IViewer _plugin;
|
||||
|
||||
internal MainWindowTransparent()
|
||||
{
|
||||
@@ -33,26 +36,25 @@ namespace QuickLook
|
||||
BlurWindow.EnableWindowBlur(this);
|
||||
};
|
||||
|
||||
buttonCloseWindow.MouseLeftButtonUp += (sender, e) => BeginHide(true);
|
||||
buttonCloseWindow.MouseLeftButtonUp += (sender, e) =>
|
||||
ViewWindowManager.GetInstance().ClosePreview();
|
||||
|
||||
/*PreviewKeyUp += (sender, e) =>
|
||||
{
|
||||
if (e.Key == Key.Enter)
|
||||
OpenWithAssocApp();
|
||||
};*/
|
||||
|
||||
buttonOpenWith.Click += (sender, e) => OpenWithAssocApp();
|
||||
buttonOpenWith.Click += (sender, e) =>
|
||||
ViewWindowManager.GetInstance().RunAndClosePreview();
|
||||
}
|
||||
|
||||
public ContextObject ContextObject { get; private set; }
|
||||
|
||||
private void OpenWithAssocApp()
|
||||
internal void RunAndHide()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_path))
|
||||
return;
|
||||
|
||||
Process.Start(new ProcessStartInfo(_path) {WorkingDirectory = Path.GetDirectoryName(_path)});
|
||||
BeginHide(true);
|
||||
Process.Start(new ProcessStartInfo(_path)
|
||||
{
|
||||
WorkingDirectory = Path.GetDirectoryName(_path)
|
||||
});
|
||||
BeginHide();
|
||||
}
|
||||
|
||||
private void ResizeAndCenter(Size size)
|
||||
@@ -79,22 +81,31 @@ namespace QuickLook
|
||||
|
||||
internal void UnloadPlugin()
|
||||
{
|
||||
container.Content = null;
|
||||
// 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();
|
||||
|
||||
// clean up plugin and refresh ContextObject for next use
|
||||
ContextObject.ViewerPlugin?.Cleanup();
|
||||
ContextObject.Reset();
|
||||
|
||||
_plugin?.Cleanup();
|
||||
_plugin = null;
|
||||
|
||||
ProcessHelper.PerformAggressiveGC();
|
||||
}
|
||||
|
||||
internal void BeginShow(IViewer matchedPlugin, string path)
|
||||
{
|
||||
ContextObject.CurrentContentContainer = container;
|
||||
ContextObject.ViewerPlugin = matchedPlugin;
|
||||
_path = path;
|
||||
_plugin = matchedPlugin;
|
||||
|
||||
ContextObject.ViewerWindow = this;
|
||||
|
||||
// get window size before showing it
|
||||
ContextObject.ViewerPlugin.Prepare(path, ContextObject);
|
||||
_plugin.Prepare(path, ContextObject);
|
||||
|
||||
SetOpenWithButtonAndPath(path);
|
||||
SetOpenWithButtonAndPath();
|
||||
|
||||
// revert UI changes
|
||||
ContextObject.IsBusy = true;
|
||||
@@ -116,7 +127,7 @@ namespace QuickLook
|
||||
{
|
||||
try
|
||||
{
|
||||
ContextObject.ViewerPlugin.View(path, ContextObject);
|
||||
_plugin.View(path, ContextObject);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -126,43 +137,33 @@ namespace QuickLook
|
||||
DispatcherPriority.Render).Wait();
|
||||
|
||||
if (thrown != null)
|
||||
throw thrown;
|
||||
ExceptionDispatchInfo.Capture(thrown).Throw();
|
||||
}
|
||||
|
||||
private void SetOpenWithButtonAndPath(string path)
|
||||
private void SetOpenWithButtonAndPath()
|
||||
{
|
||||
var isExe = FileHelper.GetAssocApplication(path, out string executePath, out string appFriendlyName);
|
||||
var isExe = FileHelper.GetAssocApplication(_path, out string appFriendlyName);
|
||||
|
||||
_path = executePath;
|
||||
buttonOpenWith.Visibility = isExe == null ? Visibility.Collapsed : Visibility.Visible;
|
||||
buttonOpenWith.Content = isExe == true ? $"Run {appFriendlyName}" : $"Open with {appFriendlyName}";
|
||||
buttonOpenWith.Content = isExe == null
|
||||
? Directory.Exists(_path)
|
||||
? $"Browse “{Path.GetFileName(_path)}”"
|
||||
: "Select ..."
|
||||
: isExe == true
|
||||
? $"Run “{appFriendlyName}”"
|
||||
: $"Open with “{appFriendlyName}”";
|
||||
}
|
||||
|
||||
internal bool BeginHide(bool quitIfViewer = false, bool disposePluginOnly = false)
|
||||
internal void BeginHide()
|
||||
{
|
||||
if (quitIfViewer && App.RunningAsViewer)
|
||||
{
|
||||
Application.Current.Shutdown();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Visibility != Visibility.Visible)
|
||||
return false;
|
||||
|
||||
UnloadPlugin();
|
||||
ContextObject.Reset();
|
||||
GC.Collect();
|
||||
|
||||
// revert UI changes
|
||||
ContextObject.IsBusy = true;
|
||||
// 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;
|
||||
|
||||
if (!disposePluginOnly)
|
||||
{
|
||||
Hide();
|
||||
return true;
|
||||
}
|
||||
Hide();
|
||||
|
||||
return false;
|
||||
ProcessHelper.PerformAggressiveGC();
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,20 +1,36 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace QuickLook.NativeMethods
|
||||
{
|
||||
internal static class QuickLook
|
||||
{
|
||||
[DllImport("QuickLook.Native.Shell32.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern int GetFocusedWindowType();
|
||||
internal static extern FocusedWindowType GetFocusedWindowType();
|
||||
|
||||
[DllImport("QuickLook.Native.Shell32.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern void SaveCurrentSelection();
|
||||
[DllImport("QuickLook.Native.Shell32.dll", EntryPoint = "GetCurrentSelection",
|
||||
CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern void GetCurrentSelectionNative([MarshalAs(UnmanagedType.LPWStr)] StringBuilder sb);
|
||||
|
||||
[DllImport("QuickLook.Native.Shell32.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern int GetCurrentSelectionCount();
|
||||
internal static string GetCurrentSelection()
|
||||
{
|
||||
StringBuilder sb = null;
|
||||
// communicate with COM in a separate thread
|
||||
Task.Run(() =>
|
||||
{
|
||||
sb = new StringBuilder(255 + 1);
|
||||
GetCurrentSelectionNative(sb);
|
||||
}).Wait();
|
||||
|
||||
[DllImport("QuickLook.Native.Shell32.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
internal static extern void GetCurrentSelectionBuffer([MarshalAs(UnmanagedType.LPWStr)] StringBuilder buffer);
|
||||
return sb?.ToString() ?? string.Empty;
|
||||
}
|
||||
|
||||
internal enum FocusedWindowType
|
||||
{
|
||||
Invalid,
|
||||
Desktop,
|
||||
Explorer
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,14 +2,8 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
namespace QuickLook.NativeMethods
|
||||
namespace QuickLook.NativeMethods.Shell32
|
||||
{
|
||||
[ComImport]
|
||||
[Guid("00021401-0000-0000-C000-000000000046")]
|
||||
internal class ShellLink
|
||||
{
|
||||
}
|
||||
|
||||
[ComImport]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[Guid("000214F9-0000-0000-C000-000000000046")]
|
||||
@@ -42,36 +36,4 @@ namespace QuickLook.NativeMethods
|
||||
void Resolve(IntPtr hwnd, int fFlags);
|
||||
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();
|
||||
}
|
||||
}
|
12
QuickLook/NativeMethods/Shell32/IWshShell.cs
Normal file
12
QuickLook/NativeMethods/Shell32/IWshShell.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace QuickLook.NativeMethods.Shell32
|
||||
{
|
||||
[ComImport]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
|
||||
[Guid("F935DC21-1CF0-11D0-ADB9-00C04FD58A0B")]
|
||||
internal interface IWshShell
|
||||
{
|
||||
IWshShortcut CreateShortcut(string pathLink);
|
||||
}
|
||||
}
|
22
QuickLook/NativeMethods/Shell32/IWshShortcut.cs
Normal file
22
QuickLook/NativeMethods/Shell32/IWshShortcut.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace QuickLook.NativeMethods.Shell32
|
||||
{
|
||||
[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();
|
||||
}
|
||||
}
|
10
QuickLook/NativeMethods/Shell32/ShellLink.cs
Normal file
10
QuickLook/NativeMethods/Shell32/ShellLink.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace QuickLook.NativeMethods.Shell32
|
||||
{
|
||||
[ComImport]
|
||||
[Guid("00021401-0000-0000-C000-000000000046")]
|
||||
internal class ShellLink
|
||||
{
|
||||
}
|
||||
}
|
10
QuickLook/NativeMethods/Shell32/WshShell.cs
Normal file
10
QuickLook/NativeMethods/Shell32/WshShell.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace QuickLook.NativeMethods.Shell32
|
||||
{
|
||||
[ComImport]
|
||||
[Guid("72C24DD5-D70A-438B-8A42-98424B88AFB8")]
|
||||
internal class WshShell
|
||||
{
|
||||
}
|
||||
}
|
@@ -2,7 +2,6 @@
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using QuickLook.Annotations;
|
||||
using QuickLook.Helpers;
|
||||
|
||||
@@ -18,8 +17,7 @@ namespace QuickLook.Plugin
|
||||
|
||||
private bool _isBusy = true;
|
||||
private string _title = "";
|
||||
internal ContentControl CurrentContentContainer;
|
||||
internal IViewer ViewerPlugin;
|
||||
private object _viewerContent;
|
||||
|
||||
/// <summary>
|
||||
/// Get the viewer window.
|
||||
@@ -44,8 +42,12 @@ namespace QuickLook.Plugin
|
||||
/// </summary>
|
||||
public object ViewerContent
|
||||
{
|
||||
get => CurrentContentContainer.Content;
|
||||
set => CurrentContentContainer.Content = value;
|
||||
get => _viewerContent;
|
||||
set
|
||||
{
|
||||
_viewerContent = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -94,12 +96,6 @@ namespace QuickLook.Plugin
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public void DisposePlugin()
|
||||
{
|
||||
ViewerPlugin?.Cleanup();
|
||||
ViewerPlugin = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show a notification balloon.
|
||||
/// </summary>
|
||||
|
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
@@ -9,16 +7,12 @@ namespace QuickLook.Plugin.InfoPanel
|
||||
{
|
||||
public static class Extensions
|
||||
{
|
||||
[DllImport("gdi32")]
|
||||
private static extern int DeleteObject(IntPtr o);
|
||||
|
||||
public static BitmapSource ToBitmapSource(this Bitmap source)
|
||||
{
|
||||
// BitmapSource.Create throws an exception when the image is scanned backward.
|
||||
// The Clone() will make it back scanning forward.
|
||||
source = (Bitmap) source.Clone();
|
||||
|
||||
var ip = source.GetHbitmap();
|
||||
BitmapSource bs = null;
|
||||
try
|
||||
{
|
||||
@@ -33,9 +27,9 @@ namespace QuickLook.Plugin.InfoPanel
|
||||
|
||||
bs.Freeze();
|
||||
}
|
||||
finally
|
||||
catch
|
||||
{
|
||||
DeleteObject(ip);
|
||||
// ignored
|
||||
}
|
||||
|
||||
return bs;
|
||||
|
@@ -31,8 +31,9 @@
|
||||
</Image>
|
||||
<Grid Grid.Column="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="20" />
|
||||
<ColumnDefinition Width="5" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="10" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="10" />
|
||||
@@ -42,7 +43,11 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock x:Name="filename" Grid.Row="1" Grid.Column="1" FontSize="19" FontWeight="SemiBold" Padding="3">Filename.ext</TextBlock>
|
||||
<TextBlock x:Name="filename" Grid.Row="1" Grid.Column="1" FontSize="19" FontWeight="SemiBold" Padding="3"
|
||||
TextWrapping="Wrap"
|
||||
LineHeight="25" MaxHeight="60" TextTrimming="CharacterEllipsis">
|
||||
FilenameFilenameFilenameFilenameFilenameFilenameFilenameFilenameFilenameFilename.ext
|
||||
</TextBlock>
|
||||
<TextBlock x:Name="modDate" Grid.Row="3" Grid.Column="1" Padding="3">Last modified at 01/01/2017 00:00:00</TextBlock>
|
||||
<TextBlock x:Name="totalSize" Grid.Row="4" Grid.Column="1" Padding="3">Calculating size...</TextBlock>
|
||||
</Grid>
|
||||
|
@@ -71,17 +71,24 @@ namespace QuickLook.Plugin.InfoPanel
|
||||
}
|
||||
else if (Directory.Exists(path))
|
||||
{
|
||||
long totalDirsL;
|
||||
long totalFilesL;
|
||||
long totalSizeL;
|
||||
|
||||
FileHelper.CountFolder(path, ref _stop, out totalDirsL, out totalFilesL, out totalSizeL);
|
||||
FileHelper.CountFolder(path, ref _stop,
|
||||
out long totalDirsL, out long totalFilesL, out long totalSizeL);
|
||||
|
||||
if (!Stop)
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
string t;
|
||||
var d = totalDirsL != 0 ? $"{totalDirsL} folders" : string.Empty;
|
||||
var f = totalFilesL != 0 ? $"{totalFilesL} files" : string.Empty;
|
||||
if (!string.IsNullOrEmpty(d) && !string.IsNullOrEmpty(f))
|
||||
t = $"({d} and {f})";
|
||||
else if (string.IsNullOrEmpty(d) && string.IsNullOrEmpty(f))
|
||||
t = string.Empty;
|
||||
else
|
||||
t = $"({d}{f})";
|
||||
|
||||
totalSize.Text =
|
||||
$"{totalSizeL.ToPrettySize(2)} ({totalDirsL} folders and {totalFilesL} files)";
|
||||
$"{totalSizeL.ToPrettySize(2)} {t}";
|
||||
});
|
||||
}
|
||||
});
|
||||
|
22
QuickLook/Properties/Settings.Designer.cs
generated
22
QuickLook/Properties/Settings.Designer.cs
generated
@@ -8,21 +8,17 @@
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace QuickLook.Properties
|
||||
{
|
||||
|
||||
|
||||
namespace QuickLook.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.1.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
|
||||
public static Settings Default {
|
||||
get {
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>QuickLook</RootNamespace>
|
||||
<AssemblyName>QuickLook</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
@@ -28,6 +28,7 @@
|
||||
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
|
||||
<UseApplicationTrust>false</UseApplicationTrust>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>Resources\app.ico</ApplicationIcon>
|
||||
@@ -83,6 +84,7 @@
|
||||
<Compile Include="Converters\BooleanToResizeModeConverter.cs" />
|
||||
<Compile Include="Converters\BooleanToVisibilityCollapsedConverter.cs" />
|
||||
<Compile Include="Converters\BooleanToResizeBorderThicknessConverter.cs" />
|
||||
<Compile Include="FocusMonitor.cs" />
|
||||
<Compile Include="Helpers\AutoStartupHelper.cs" />
|
||||
<Compile Include="Controls\BackgroundVisualHost.cs" />
|
||||
<Compile Include="Controls\BusyDecorator.cs" />
|
||||
@@ -107,9 +109,14 @@
|
||||
<Compile Include="Helpers\BlurLibrary\PlatformsImpl\Windows8WindowBlurController.cs" />
|
||||
<Compile Include="Helpers\BlurLibrary\PlatformsImpl\WindowsVistaWindowBlurController.cs" />
|
||||
<Compile Include="Helpers\FileHelper.cs" />
|
||||
<Compile Include="Helpers\ProcessHelper.cs" />
|
||||
<Compile Include="MainWindowNoTransparent.cs" />
|
||||
<Compile Include="NativeMethods\ShellLink.cs" />
|
||||
<Compile Include="PidHelper.cs" />
|
||||
<Compile Include="NativeMethods\Shell32\IShellLink.cs" />
|
||||
<Compile Include="NativeMethods\Shell32\IWshShell.cs" />
|
||||
<Compile Include="NativeMethods\Shell32\IWshShortcut.cs" />
|
||||
<Compile Include="NativeMethods\Shell32\ShellLink.cs" />
|
||||
<Compile Include="NativeMethods\Shell32\WshShell.cs" />
|
||||
<Compile Include="Helpers\PidHelper.cs" />
|
||||
<Compile Include="PluginManager.cs" />
|
||||
<Compile Include="Plugin\InfoPanel\DpiHelpers.cs" />
|
||||
<Compile Include="Plugin\InfoPanel\Extensions.cs" />
|
||||
|
@@ -3,7 +3,6 @@ using System.Diagnostics;
|
||||
using System.Windows.Forms;
|
||||
using QuickLook.Helpers;
|
||||
using QuickLook.Properties;
|
||||
using Application = System.Windows.Application;
|
||||
|
||||
namespace QuickLook
|
||||
{
|
||||
@@ -26,14 +25,17 @@ namespace QuickLook
|
||||
{
|
||||
_icon = new NotifyIcon
|
||||
{
|
||||
Text = $"QuickLook v{Application.ProductVersion}",
|
||||
Icon = Resources.app,
|
||||
Visible = true,
|
||||
ContextMenu = new ContextMenu(new[]
|
||||
{
|
||||
new MenuItem($"v{Application.ProductVersion}") {Enabled = false},
|
||||
new MenuItem("-"),
|
||||
new MenuItem("Check for &Updates...",
|
||||
(sender, e) => Process.Start(@"http://pooi.moe/QuickLook/")),
|
||||
_itemAutorun,
|
||||
new MenuItem("&Quit", (sender, e) => Application.Current.Shutdown())
|
||||
new MenuItem("&Quit", (sender, e) => System.Windows.Application.Current.Shutdown())
|
||||
})
|
||||
};
|
||||
|
||||
|
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using QuickLook.Helpers;
|
||||
@@ -11,13 +9,16 @@ using QuickLook.Plugin;
|
||||
|
||||
namespace QuickLook
|
||||
{
|
||||
internal class ViewWindowManager
|
||||
internal class ViewWindowManager : IDisposable
|
||||
{
|
||||
private static ViewWindowManager _instance;
|
||||
|
||||
private readonly MainWindowNoTransparent _viewWindowNoTransparent;
|
||||
private readonly MainWindowTransparent _viewWindowTransparentTransparent;
|
||||
|
||||
private MainWindowTransparent _currentMainWindow;
|
||||
private long _lastSwitchTick;
|
||||
|
||||
private string _path = string.Empty;
|
||||
|
||||
internal ViewWindowManager()
|
||||
{
|
||||
@@ -27,37 +28,151 @@ namespace QuickLook
|
||||
_currentMainWindow = _viewWindowTransparentTransparent;
|
||||
}
|
||||
|
||||
internal void InvokeRoutine(Keys key)
|
||||
public void Dispose()
|
||||
{
|
||||
// do we need switch to another file?
|
||||
var replaceView = key == Keys.Up || key == Keys.Down || key == Keys.Left || key == Keys.Right;
|
||||
|
||||
if (replaceView && _currentMainWindow.Visibility != Visibility.Visible)
|
||||
return;
|
||||
|
||||
if (!WindowHelper.IsFocusedControlExplorerItem())
|
||||
if (replaceView || !WindowHelper.IsFocusedWindowSelf())
|
||||
return;
|
||||
|
||||
// should the window be closed (replaceView == false), return without showing new one
|
||||
if (_currentMainWindow.BeginHide(disposePluginOnly: replaceView))
|
||||
return;
|
||||
|
||||
var path = GetCurrentSelection();
|
||||
|
||||
InvokeViewer(path);
|
||||
StopFocusMonitor();
|
||||
}
|
||||
|
||||
internal void InvokeViewer(string path)
|
||||
internal void InvokeRoutine(KeyEventArgs kea)
|
||||
{
|
||||
switch (kea.KeyCode)
|
||||
{
|
||||
case Keys.Up:
|
||||
case Keys.Down:
|
||||
case Keys.Left:
|
||||
case Keys.Right:
|
||||
SwitchPreview();
|
||||
break;
|
||||
case Keys.Space:
|
||||
TogglePreview();
|
||||
break;
|
||||
case Keys.Escape:
|
||||
ClosePreview();
|
||||
break;
|
||||
case Keys.Enter:
|
||||
RunAndClosePreview();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal void RunAndClosePreview()
|
||||
{
|
||||
if (NativeMethods.QuickLook.GetFocusedWindowType() ==
|
||||
NativeMethods.QuickLook.FocusedWindowType.Invalid)
|
||||
if (!WindowHelper.IsForegroundWindowBelongToSelf())
|
||||
return;
|
||||
|
||||
if (_currentMainWindow.Visibility != Visibility.Visible)
|
||||
return;
|
||||
|
||||
StopFocusMonitor();
|
||||
_currentMainWindow.RunAndHide();
|
||||
}
|
||||
|
||||
internal void ClosePreview()
|
||||
{
|
||||
if (NativeMethods.QuickLook.GetFocusedWindowType() ==
|
||||
NativeMethods.QuickLook.FocusedWindowType.Invalid)
|
||||
if (!WindowHelper.IsForegroundWindowBelongToSelf())
|
||||
return;
|
||||
|
||||
if (_currentMainWindow.Visibility != Visibility.Visible)
|
||||
return;
|
||||
|
||||
StopFocusMonitor();
|
||||
_currentMainWindow.BeginHide();
|
||||
}
|
||||
|
||||
private void TogglePreview()
|
||||
{
|
||||
_lastSwitchTick = DateTime.Now.Ticks;
|
||||
|
||||
if (NativeMethods.QuickLook.GetFocusedWindowType() ==
|
||||
NativeMethods.QuickLook.FocusedWindowType.Invalid)
|
||||
if (!WindowHelper.IsForegroundWindowBelongToSelf())
|
||||
return;
|
||||
|
||||
if (_currentMainWindow.Visibility == Visibility.Visible)
|
||||
{
|
||||
ClosePreview();
|
||||
}
|
||||
else
|
||||
{
|
||||
_path = NativeMethods.QuickLook.GetCurrentSelection();
|
||||
InvokeViewer();
|
||||
}
|
||||
}
|
||||
|
||||
private void SwitchPreview()
|
||||
{
|
||||
if (_currentMainWindow.Visibility != Visibility.Visible)
|
||||
return;
|
||||
|
||||
_lastSwitchTick = DateTime.Now.Ticks;
|
||||
|
||||
if (NativeMethods.QuickLook.GetFocusedWindowType() ==
|
||||
NativeMethods.QuickLook.FocusedWindowType.Invalid)
|
||||
return;
|
||||
|
||||
_path = NativeMethods.QuickLook.GetCurrentSelection();
|
||||
|
||||
InvokeViewer();
|
||||
}
|
||||
|
||||
private void SwitchPreviewRemoteInvoke(HeartbeatEventArgs e)
|
||||
{
|
||||
// sleep for 1.5s
|
||||
if (e.InvokeTick - _lastSwitchTick < 1.5 * TimeSpan.TicksPerSecond)
|
||||
return;
|
||||
|
||||
if (e.FocusedFile == _path)
|
||||
return;
|
||||
|
||||
Debug.WriteLine("SwitchPreviewRemoteInvoke:" + (e.InvokeTick - _lastSwitchTick));
|
||||
|
||||
if (string.IsNullOrEmpty(e.FocusedFile))
|
||||
return;
|
||||
|
||||
_currentMainWindow?.Dispatcher.Invoke(() => SwitchPreview());
|
||||
}
|
||||
|
||||
private void RunFocusMonitor()
|
||||
{
|
||||
if (!FocusMonitor.GetInstance().IsRunning)
|
||||
{
|
||||
FocusMonitor.GetInstance().Start();
|
||||
FocusMonitor.GetInstance().Heartbeat += SwitchPreviewRemoteInvoke;
|
||||
}
|
||||
}
|
||||
|
||||
private void StopFocusMonitor()
|
||||
{
|
||||
if (FocusMonitor.GetInstance().IsRunning)
|
||||
{
|
||||
FocusMonitor.GetInstance().Stop();
|
||||
FocusMonitor.GetInstance().Heartbeat -= SwitchPreviewRemoteInvoke;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool InvokeViewer(string path = null)
|
||||
{
|
||||
if (path == null)
|
||||
path = _path;
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return;
|
||||
return false;
|
||||
if (!Directory.Exists(path) && !File.Exists(path))
|
||||
return;
|
||||
return false;
|
||||
|
||||
RunFocusMonitor();
|
||||
|
||||
var matchedPlugin = PluginManager.GetInstance().FindMatch(path);
|
||||
|
||||
BeginShowNewWindow(matchedPlugin, path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void BeginShowNewWindow(IViewer matchedPlugin, string path)
|
||||
@@ -93,39 +208,11 @@ namespace QuickLook
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
ExceptionDispatchInfo.Capture(e).Throw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetCurrentSelection()
|
||||
{
|
||||
var path = string.Empty;
|
||||
|
||||
// communicate with COM in a separate thread
|
||||
Task.Run(() =>
|
||||
{
|
||||
var paths = GetCurrentSelectionNative();
|
||||
|
||||
if (paths.Any())
|
||||
path = paths.First();
|
||||
})
|
||||
.Wait();
|
||||
|
||||
return string.IsNullOrEmpty(path) ? string.Empty : path;
|
||||
}
|
||||
|
||||
private string[] GetCurrentSelectionNative()
|
||||
{
|
||||
NativeMethods.QuickLook.SaveCurrentSelection();
|
||||
|
||||
var n = NativeMethods.QuickLook.GetCurrentSelectionCount();
|
||||
var sb = new StringBuilder(n * 261); // MAX_PATH + NULL = 261
|
||||
NativeMethods.QuickLook.GetCurrentSelectionBuffer(sb);
|
||||
|
||||
return sb.Length == 0 ? new string[0] : sb.ToString().Split('|');
|
||||
}
|
||||
|
||||
internal static ViewWindowManager GetInstance()
|
||||
{
|
||||
return _instance ?? (_instance = new ViewWindowManager());
|
||||
|
11
README.md
11
README.md
@@ -8,6 +8,8 @@
|
||||
|
||||
<img src="http://pooi.moe/QuickLook/sample.gif" width="400">
|
||||
|
||||
↑ The image above is caputred from an ancient version. It has changed a lot.
|
||||
|
||||
## Background
|
||||
[Quick Look](https://en.wikipedia.org/wiki/Quick_Look) is among the few features I missed from Mac OS X. It enables *very* quick preview of file by pressing <kbd>Space</kbd> key while highlighting it, without opening its associated application. Then I decide to add this feature to Windows by myself, which results this “QuickLook” project.
|
||||
|
||||
@@ -16,11 +18,12 @@ You may ask, why you write this when there several alternatives available on the
|
||||
## Features
|
||||
Till now, QuickLook supports the preview of
|
||||
|
||||
- Images: e.g. `.png`, `.jpg`, `.bmp` and `.gif`
|
||||
- Images: e.g. `.png`, `.jpg`, `.bmp`, `.gif`, `.psd` and Camera RAW formats
|
||||
- Compressed archives: `.zip`, `.rar`, `.7z` etc.
|
||||
- Pdf file
|
||||
- All kinds of text files (determined by file content)
|
||||
- Microsoft Word (`.doc`, `.docx`), Excel (`.xls`, `.xlsx`) and PowerPoint (`.ppt`, `.pptx`) files (requires MS Office installation)
|
||||
- OpenDocument (`odt`, `.ods` and `.odp`) files (requires MS Office installation)
|
||||
- Video files (`.mp4`, `.mkv`, `.m2ts` etc.)
|
||||
- HTML files (`.htm`, `.html`)
|
||||
- Markdown file (`.md`)
|
||||
@@ -33,8 +36,10 @@ In-place preview / Integrate with 3rd-party file manager
|
||||
Hotkeys in preview window:
|
||||
|
||||
- <kbd>Space</kbd> Show/Hide the preview window
|
||||
- <kbd>↑</kbd> <kbd>↓</kbd> <kbd>←</kbd> <kbd>→</kbd> Preview another file
|
||||
- <kbd>Ctrl+Wheel</kbd> Zoom in/out
|
||||
- <kbd>Esc</kbd> Hide the preview window
|
||||
- <kbd>Enter</kbd> Open/Execute current file
|
||||
- <kbd>Mouse️</kbd> <kbd>↑</kbd> <kbd>↓</kbd> <kbd>←</kbd> <kbd>→</kbd> Preview another file
|
||||
- <kbd>Ctrl-Wheel</kbd> Zoom in/out
|
||||
|
||||
## Development
|
||||
|
||||
|
Reference in New Issue
Block a user