Use own Pdf viewer implementation. wip on universal InfoPanel

This commit is contained in:
Paddy Xu
2017-04-16 01:18:54 +03:00
parent 7388c3874a
commit 431cf1f014
35 changed files with 2894 additions and 213 deletions

View File

@@ -0,0 +1,40 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace QuickLook.Plugin.LastResort
{
public static class Extensions
{
[DllImport("gdi32")]
private static extern int DeleteObject(IntPtr o);
public static BitmapSource ToBitmapSource(this Bitmap source)
{
var ip = source.GetHbitmap();
BitmapSource bs = null;
try
{
var data = source.LockBits(new Rectangle(0, 0, source.Width, source.Height),
ImageLockMode.ReadOnly, source.PixelFormat);
bs = BitmapSource.Create(source.Width, source.Height, source.HorizontalResolution,
source.VerticalResolution, PixelFormats.Bgra32, null,
data.Scan0, data.Stride * source.Height, data.Stride);
source.UnlockBits(data);
bs.Freeze();
}
finally
{
DeleteObject(ip);
}
return bs;
}
}
}

View File

@@ -0,0 +1,255 @@
using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
// ReSharper disable InconsistentNaming
namespace QuickLook.Plugin.LastResort
{
public class IconHelper
{
public enum IconSizeEnum
{
SmallIcon16 = SHGFI_SMALLICON,
MediumIcon32 = SHGFI_LARGEICON,
LargeIcon48 = SHIL_EXTRALARGE,
ExtraLargeIcon = SHIL_JUMBO
}
private const int SHGFI_SMALLICON = 0x1;
private const int SHGFI_LARGEICON = 0x0;
private const int SHIL_JUMBO = 0x4;
private const int SHIL_EXTRALARGE = 0x2;
private const int WM_CLOSE = 0x0010;
[DllImport("user32")]
private static extern
IntPtr SendMessage(
IntPtr handle,
int Msg,
IntPtr wParam,
IntPtr lParam);
[DllImport("shell32.dll")]
private static extern int SHGetImageList(
int iImageList,
ref Guid riid,
out IImageList ppv);
[DllImport("Shell32.dll")]
private static extern int SHGetFileInfo(
string pszPath,
int dwFileAttributes,
ref SHFILEINFO psfi,
int cbFileInfo,
uint uFlags);
[DllImport("user32")]
private static extern int DestroyIcon(
IntPtr hIcon);
public static Bitmap GetBitmapFromFolderPath(
string filepath, IconSizeEnum iconsize)
{
var hIcon = GetIconHandleFromFolderPath(filepath, iconsize);
return getBitmapFromIconHandle(hIcon);
}
public static Bitmap GetBitmapFromFilePath(
string filepath, IconSizeEnum iconsize)
{
var hIcon = GetIconHandleFromFilePath(filepath, iconsize);
return getBitmapFromIconHandle(hIcon);
}
public static Bitmap GetBitmapFromPath(
string filepath, IconSizeEnum iconsize)
{
var hIcon = IntPtr.Zero;
if (Directory.Exists(filepath))
{
hIcon = GetIconHandleFromFolderPath(filepath, iconsize);
}
else
{
if (File.Exists(filepath))
hIcon = GetIconHandleFromFilePath(filepath, iconsize);
}
return getBitmapFromIconHandle(hIcon);
}
private static Bitmap getBitmapFromIconHandle(IntPtr hIcon)
{
if (hIcon == IntPtr.Zero) throw new FileNotFoundException();
var myIcon = Icon.FromHandle(hIcon);
var bitmap = myIcon.ToBitmap();
myIcon.Dispose();
DestroyIcon(hIcon);
SendMessage(hIcon, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
return bitmap;
}
private static IntPtr GetIconHandleFromFilePath(string filepath, IconSizeEnum iconsize)
{
var shinfo = new SHFILEINFO();
const uint SHGFI_SYSICONINDEX = 0x4000;
const int FILE_ATTRIBUTE_NORMAL = 0x80;
var flags = SHGFI_SYSICONINDEX;
return getIconHandleFromFilePathWithFlags(filepath, iconsize, ref shinfo, FILE_ATTRIBUTE_NORMAL, flags);
}
private static IntPtr GetIconHandleFromFolderPath(string folderpath, IconSizeEnum iconsize)
{
var shinfo = new SHFILEINFO();
const uint SHGFI_ICON = 0x000000100;
const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;
const int FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
var flags = SHGFI_ICON | SHGFI_USEFILEATTRIBUTES;
return getIconHandleFromFilePathWithFlags(folderpath, iconsize, ref shinfo, FILE_ATTRIBUTE_DIRECTORY,
flags);
}
private static IntPtr getIconHandleFromFilePathWithFlags(
string filepath, IconSizeEnum iconsize,
ref SHFILEINFO shinfo, int fileAttributeFlag, uint flags)
{
const int ILD_TRANSPARENT = 1;
var retval = SHGetFileInfo(filepath, fileAttributeFlag, ref shinfo, Marshal.SizeOf(shinfo), flags);
if (retval == 0) throw new FileNotFoundException();
var iconIndex = shinfo.iIcon;
var iImageListGuid = new Guid("46EB5926-582E-4017-9FDF-E8998DAA0950");
IImageList iml;
var hres = SHGetImageList((int) iconsize, ref iImageListGuid, out iml);
var hIcon = IntPtr.Zero;
hres = iml.GetIcon(iconIndex, ILD_TRANSPARENT, ref hIcon);
return hIcon;
}
}
[ComImport]
[Guid("46EB5926-582E-4017-9FDF-E8998DAA0950")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IImageList
{
[PreserveSig]
int Add(
IntPtr hbmImage,
IntPtr hbmMask,
ref int pi);
[PreserveSig]
int ReplaceIcon(
int i,
IntPtr hicon,
ref int pi);
[PreserveSig]
int SetOverlayImage(
int iImage,
int iOverlay);
[PreserveSig]
int Replace(
int i,
IntPtr hbmImage,
IntPtr hbmMask);
[PreserveSig]
int AddMasked(
IntPtr hbmImage,
int crMask,
ref int pi);
[PreserveSig]
int Draw(
ref IMAGELISTDRAWPARAMS pimldp);
[PreserveSig]
int Remove(
int i);
[PreserveSig]
int GetIcon(
int i,
int flags,
ref IntPtr picon);
}
[StructLayout(LayoutKind.Sequential)]
public struct IMAGEINFO
{
public IntPtr hbmImage;
public IntPtr hbmMask;
public int Unused1;
public int Unused2;
public RECT rcImage;
}
public struct IMAGELISTDRAWPARAMS
{
public int cbSize;
public IntPtr himl;
public int i;
public IntPtr hdcDst;
public int x;
public int y;
public int cx;
public int cy;
public int xBitmap;
public int yBitmap;
public int rgbBk;
public int rgbFg;
public int fStyle;
public int dwRop;
public int fState;
public int Frame;
public int crEffect;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
private int X;
private int Y;
public POINT(int x, int y)
{
X = x;
Y = y;
}
public POINT(Point pt) : this(pt.X, pt.Y)
{
}
public static implicit operator Point(POINT p)
{
return new Point(p.X, p.Y);
}
public static implicit operator POINT(Point p)
{
return new POINT(p.X, p.Y);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
public struct SHFILEINFO
{
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 254)] public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szTypeName;
}
}

View File

@@ -0,0 +1,12 @@
<UserControl x:Class="QuickLook.Plugin.LastResort.InfoPanel"
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.LastResort"
mc:Ignorable="d" Width="450" Height="250" UseLayoutRounding="True">
<Grid>
<Image x:Name="image" Width="128" Height="128"/>
</Grid>
</UserControl>

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace QuickLook.Plugin.LastResort
{
/// <summary>
/// Interaction logic for InfoPanel.xaml
/// </summary>
public partial class InfoPanel : UserControl
{
public InfoPanel()
{
InitializeComponent();
var i = 0;
}
}
}

View File

@@ -0,0 +1,33 @@
using System.Drawing;
using System.Windows;
using Size = System.Windows.Size;
namespace QuickLook.Plugin.LastResort
{
public class Plugin : IViewer
{
private InfoPanel ip;
public int Priority => -9999;
public bool CanHandle(string sample)
{
return true;
}
public void View(string path, ViewContentContainer container)
{
var s = IconHelper.GetBitmapFromPath(path, IconHelper.IconSizeEnum.ExtraLargeIcon).ToBitmapSource();
ip = new InfoPanel();
ip.image.Source = s;
container.SetContent(ip);
container.PreferedSize = new Size {Width = ip.Width, Height = ip.Height};
}
public void Close()
{
//ip.Dispose();
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("QuickLook.Plugin.LastResort")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("QuickLook.Plugin.LastResort")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("b9a5a4f6-813e-40ce-ad32-dc5c1356215d")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B9A5A4F6-813E-40CE-AD32-DC5C1356215D}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>QuickLook.Plugin.LastResort</RootNamespace>
<AssemblyName>QuickLook.Plugin.LastResort</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Build\Debug\Plugins\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\Build\Release\Plugins\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="Extensions.cs" />
<Compile Include="InfoPanel.xaml.cs">
<DependentUpon>InfoPanel.xaml</DependentUpon>
</Compile>
<Compile Include="Plugin.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="IconHelper.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\QuickLook\QuickLook.csproj">
<Project>{8B4A9CE5-67B5-4A94-81CB-3771F688FDEB}</Project>
<Name>QuickLook</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Page Include="InfoPanel.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -1,40 +0,0 @@
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using MoonPdfLib;
using QuickLook.ExtensionMethods;
namespace QuickLook.Plugin.PDFViewer
{
public class Class1 : IViewer
{
public PluginType Type => PluginType.ByExtension | PluginType.ByContent;
public string[] SupportExtensions => new[] {".pdf"};
public bool CheckSupportByContent(byte[] sample)
{
return Encoding.ASCII.GetString(sample.Take(4).ToArray()) == "%PDF";
}
public void View(string path, ViewContentContainer container)
{
var pdfPanel = new MoonPdfPanel
{
ViewType = ViewType.SinglePage,
PageRowDisplay = PageRowDisplayType.ContinuousPageRows,
PageMargin = new Thickness(0, 2, 4, 2),
Background = new SolidColorBrush(Colors.LightGray)
};
container.SetContent(pdfPanel);
container.Dispatcher.Delay(100, o => pdfPanel.OpenFile(path));
//container.Dispatcher.Delay(200, o => pdfPanel.ZoomToWidth());
}
public void Close()
{
}
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace QuickLook.Plugin.PDFViewer
{
internal static class DpiHelper
{
public const float DEFAULT_DPI = 96;
public static Dpi GetCurrentDpi()
{
Graphics g = Graphics.FromHwnd(IntPtr.Zero);
IntPtr desktop = g.GetHdc();
var dpi = new Dpi
{
HorizontalDpi = GetDeviceCaps(desktop, (int) DeviceCap.LOGPIXELSX),
VerticalDpi = GetDeviceCaps(desktop, (int) DeviceCap.LOGPIXELSY)
};
return dpi;
}
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int GetDeviceCaps(IntPtr hDC, int nIndex);
public enum DeviceCap
{
/// <summary>
/// Logical pixels inch in X
/// </summary>
LOGPIXELSX = 88,
/// <summary>
/// Logical pixels inch in Y
/// </summary>
LOGPIXELSY = 90
}
}
internal class Dpi
{
public float HorizontalDpi { get; set; }
public float VerticalDpi { get; set; }
}
}

View File

@@ -0,0 +1,68 @@
using System;
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;
namespace QuickLook.Plugin.PDFViewer
{
public static class Extensions
{
public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action)
{
foreach (var item in enumeration)
action(item);
}
public static T GetDescendantByType<T>(this Visual element) where T : class
{
if (element == null)
return default(T);
if (element.GetType() == typeof(T))
return element as T;
T foundElement = null;
(element as FrameworkElement)?.ApplyTemplate();
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
{
var visual = VisualTreeHelper.GetChild(element, i) as Visual;
foundElement = visual.GetDescendantByType<T>();
if (foundElement != null)
break;
}
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
{
var data = source.LockBits(new Rectangle(0, 0, source.Width, source.Height),
ImageLockMode.ReadOnly, source.PixelFormat);
bs = BitmapSource.Create(source.Width, source.Height, source.HorizontalResolution,
source.VerticalResolution, PixelFormats.Bgr24, null,
data.Scan0, data.Stride * source.Height, data.Stride);
source.UnlockBits(data);
bs.Freeze();
}
finally
{
DeleteObject(ip);
}
return bs;
}
}
}

View File

@@ -0,0 +1,181 @@
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace QuickLook.Plugin.PDFViewer
{
internal class LibMuPdf
{
public static Bitmap RenderPage(IntPtr context, IntPtr document, IntPtr page, double zoomFactor)
{
var pageBound = new Rectangle();
var ctm = new Matrix();
var pix = IntPtr.Zero;
var dev = IntPtr.Zero;
NativeMethods.BoundPage(document, page, ref pageBound);
var currentDpi = DpiHelper.GetCurrentDpi();
var zoomX = zoomFactor * (currentDpi.HorizontalDpi / DpiHelper.DEFAULT_DPI);
var zoomY = zoomFactor * (currentDpi.VerticalDpi / DpiHelper.DEFAULT_DPI);
// gets the size of the page and multiplies it with zoom factors
var width = (int) (pageBound.Width * zoomX);
var height = (int) (pageBound.Height * zoomY);
// sets the matrix as a scaling matrix (zoomX,0,0,zoomY,0,0)
ctm.A = (float)zoomX;
ctm.D = (float)zoomY;
// creates a pixmap the same size as the width and height of the page
pix = NativeMethods.NewPixmap(context, NativeMethods.LookupDeviceColorSpace(context, "DeviceRGB"), width,
height);
// sets white color as the background color of the pixmap
NativeMethods.ClearPixmap(context, pix, 0xFF);
// creates a drawing device
dev = NativeMethods.NewDrawDevice(context, pix);
// draws the page on the device created from the pixmap
NativeMethods.RunPage(document, page, dev, ref ctm, IntPtr.Zero);
NativeMethods.FreeDevice(dev); // frees the resources consumed by the device
dev = IntPtr.Zero;
// creates a colorful bitmap of the same size of the pixmap
var bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
var imageData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.ReadWrite,
bmp.PixelFormat);
unsafe
{
// converts the pixmap data to Bitmap data
var ptrSrc = (byte*) NativeMethods.GetSamples(context, pix); // gets the rendered data from the pixmap
var ptrDest = (byte*) imageData.Scan0;
for (var y = 0; y < height; y++)
{
var pl = ptrDest;
var sl = ptrSrc;
for (var x = 0; x < width; x++)
{
//Swap these here instead of in MuPDF because most pdf images will be rgb or cmyk.
//Since we are going through the pixels one by one anyway swap here to save a conversion from rgb to bgr.
pl[2] = sl[0]; //b-r
pl[1] = sl[1]; //g-g
pl[0] = sl[2]; //r-b
//sl[3] is the alpha channel, we will skip it here
pl += 3;
sl += 4;
}
ptrDest += imageData.Stride;
ptrSrc += width * 4;
}
}
bmp.UnlockBits(imageData);
NativeMethods.DropPixmap(context, pix);
bmp.SetResolution(currentDpi.HorizontalDpi, currentDpi.VerticalDpi);
return bmp;
}
public struct Rectangle
{
public float Left, Top, Right, Bottom;
public float Width => Right - Left;
public float Height => Bottom - Top;
}
public struct Matrix
{
public float A, B, C, D, E, F;
public Matrix(float a, float b, float c, float d, float e, float f)
{
A = a;
B = b;
C = c;
D = d;
E = e;
F = f;
}
}
internal struct NativePage
{
public Matrix Ctm;
public Rectangle MediaBox;
public int Rotate;
}
internal class NativeMethods
{
private const uint FZ_STORE_DEFAULT = 256 << 20;
private const string DLL = "libmupdf.dll";
// please modify the version number to match the FZ_VERSION definition in "fitz\version.h" file
private const string MuPDFVersion = "1.6";
[DllImport(DLL, EntryPoint = "fz_new_context_imp", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr NewContext(IntPtr alloc, IntPtr locks, uint max_store, string version);
public static IntPtr NewContext()
{
return NewContext(IntPtr.Zero, IntPtr.Zero, FZ_STORE_DEFAULT, MuPDFVersion);
}
[DllImport(DLL, EntryPoint = "fz_free_context", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr FreeContext(IntPtr ctx);
[DllImport(DLL, EntryPoint = "fz_open_file_w", CharSet = CharSet.Unicode,
CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr OpenFile(IntPtr ctx, string fileName);
[DllImport(DLL, EntryPoint = "pdf_open_document_with_stream", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr OpenDocumentStream(IntPtr ctx, IntPtr stm);
[DllImport(DLL, EntryPoint = "fz_close", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr CloseStream(IntPtr stm);
[DllImport(DLL, EntryPoint = "pdf_close_document", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr CloseDocument(IntPtr doc);
[DllImport(DLL, EntryPoint = "pdf_count_pages", CallingConvention = CallingConvention.Cdecl)]
public static extern int CountPages(IntPtr doc);
[DllImport(DLL, EntryPoint = "pdf_bound_page", CallingConvention = CallingConvention.Cdecl)]
public static extern void BoundPage(IntPtr doc, IntPtr page, ref Rectangle bound);
[DllImport(DLL, EntryPoint = "fz_clear_pixmap_with_value", CallingConvention = CallingConvention.Cdecl)]
public static extern void ClearPixmap(IntPtr ctx, IntPtr pix, int byteValue);
[DllImport(DLL, EntryPoint = "fz_lookup_device_colorspace", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr LookupDeviceColorSpace(IntPtr ctx, string colorspace);
[DllImport(DLL, EntryPoint = "fz_free_device", CallingConvention = CallingConvention.Cdecl)]
public static extern void FreeDevice(IntPtr dev);
[DllImport(DLL, EntryPoint = "pdf_free_page", CallingConvention = CallingConvention.Cdecl)]
public static extern void FreePage(IntPtr doc, IntPtr page);
[DllImport(DLL, EntryPoint = "pdf_load_page", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr LoadPage(IntPtr doc, int pageNumber);
[DllImport(DLL, EntryPoint = "fz_new_draw_device", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr NewDrawDevice(IntPtr ctx, IntPtr pix);
[DllImport(DLL, EntryPoint = "fz_new_pixmap", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr NewPixmap(IntPtr ctx, IntPtr colorspace, int width, int height);
[DllImport(DLL, EntryPoint = "pdf_run_page", CallingConvention = CallingConvention.Cdecl)]
public static extern void RunPage(IntPtr doc, IntPtr page, IntPtr dev, ref Matrix transform, IntPtr cookie);
[DllImport(DLL, EntryPoint = "fz_drop_pixmap", CallingConvention = CallingConvention.Cdecl)]
public static extern void DropPixmap(IntPtr ctx, IntPtr pix);
[DllImport(DLL, EntryPoint = "fz_pixmap_samples", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetSamples(IntPtr ctx, IntPtr pix);
}
}
}

View File

@@ -0,0 +1,113 @@
using System;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
namespace QuickLook.Plugin.PDFViewer
{
public sealed class PreviewMouseWheelMonitor : IDisposable
{
private readonly UIElement _canvas;
private readonly Dispatcher _dispatcher;
private readonly int _sensitivity;
private bool _disposed;
private volatile bool _inactive;
private AutoResetEvent _resetMonitorEvent;
private volatile bool _stopped;
public PreviewMouseWheelMonitor(UIElement canvas, int sensitivity)
{
_canvas = canvas;
_canvas.PreviewMouseWheel += (s, e) => RaisePreviewMouseWheel(e);
_sensitivity = sensitivity;
_dispatcher = Dispatcher.CurrentDispatcher;
_resetMonitorEvent = new AutoResetEvent(false);
_disposed = false;
_inactive = true;
_stopped = true;
var monitor = new Thread(Monitor) {IsBackground = true};
monitor.Start();
}
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
DetachEventHandlers();
if (_resetMonitorEvent != null)
{
_resetMonitorEvent.Close();
_resetMonitorEvent = null;
}
}
}
public event EventHandler<MouseWheelEventArgs> PreviewMouseWheel;
public event EventHandler<EventArgs> PreviewMouseWheelStarted;
public event EventHandler<EventArgs> PreviewMouseWheelStopped;
private void Monitor()
{
while (!_disposed)
{
if (_inactive) // if wheel is still inactive...
{
_resetMonitorEvent.WaitOne(_sensitivity / 10); // ...wait negligibly small quantity of time...
continue; // ...and check again
}
// otherwise, if wheel is active...
_inactive = true; // ...purposely change the state to inactive
_resetMonitorEvent.WaitOne(_sensitivity); // wait...
if (_inactive
) // ...and after specified time check if the state is still not re-activated inside mouse wheel event
RaiseMouseWheelStopped();
}
}
private void RaisePreviewMouseWheel(MouseWheelEventArgs args)
{
if (_stopped)
RaiseMouseWheelStarted();
_inactive = false;
if (PreviewMouseWheel != null)
PreviewMouseWheel(_canvas, args);
}
private void RaiseMouseWheelStarted()
{
_stopped = false;
if (PreviewMouseWheelStarted != null)
PreviewMouseWheelStarted(_canvas, new EventArgs());
}
private void RaiseMouseWheelStopped()
{
_stopped = true;
if (PreviewMouseWheelStopped != null)
_dispatcher.Invoke(() => PreviewMouseWheelStopped(_canvas,
new
EventArgs())); // invoked on cached dispatcher for convenience (because fired from non-UI thread)
}
private void DetachEventHandlers()
{
if (PreviewMouseWheel != null)
foreach (var handler in PreviewMouseWheel.GetInvocationList().Cast<EventHandler<MouseWheelEventArgs>>())
PreviewMouseWheel -= handler;
if (PreviewMouseWheelStarted != null)
foreach (var handler in PreviewMouseWheelStarted.GetInvocationList().Cast<EventHandler<EventArgs>>())
PreviewMouseWheelStarted -= handler;
if (PreviewMouseWheelStopped != null)
foreach (var handler in PreviewMouseWheelStopped.GetInvocationList().Cast<EventHandler<EventArgs>>())
PreviewMouseWheelStopped -= handler;
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace QuickLook.Plugin.PDFViewer
{
public sealed class PageIdToImageConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length < 2)
throw new Exception("PageIdToImageConverter");
var zoom = 0.5f;
if (parameter != null)
float.TryParse((string) parameter, out zoom);
var handle = values[0] as PdfFile;
if (handle == null) return null;
var pageId = (int) values[1];
if (pageId < 0) return null;
return handle.GetPage(pageId, zoom).ToBitmapSource();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Drawing;
using Size = System.Windows.Size;
namespace QuickLook.Plugin.PDFViewer
{
public class PdfFile : IDisposable
{
private readonly IntPtr _ctx;
private readonly IntPtr _doc;
private readonly IntPtr _stm;
public PdfFile(string path)
{
_ctx = LibMuPdf.NativeMethods.NewContext();
_stm = LibMuPdf.NativeMethods.OpenFile(_ctx, path);
_doc = LibMuPdf.NativeMethods.OpenDocumentStream(_ctx, _stm);
TotalPages = LibMuPdf.NativeMethods.CountPages(_doc);
}
public int TotalPages { get; }
public void Dispose()
{
LibMuPdf.NativeMethods.CloseDocument(_doc);
LibMuPdf.NativeMethods.CloseStream(_stm);
LibMuPdf.NativeMethods.FreeContext(_ctx);
}
public bool IsLastPage(int pageId)
{
return pageId >= TotalPages;
}
public Size GetPageSize(int pageId, double zoomFactor)
{
if (pageId < 0 || pageId >= TotalPages)
throw new OverflowException(
$"Page id {pageId} should greater or equal than 0 and less than total page count {TotalPages}.");
var p = LibMuPdf.NativeMethods.LoadPage(_doc, pageId);
var realSize = new LibMuPdf.Rectangle();
LibMuPdf.NativeMethods.BoundPage(_doc, p, ref realSize);
var size = new Size
{
Width = realSize.Right * zoomFactor,
Height = realSize.Bottom * zoomFactor
};
LibMuPdf.NativeMethods.FreePage(_doc, p);
return size;
}
public Bitmap GetPage(int pageId, double zoomFactor)
{
if (pageId < 0 || pageId >= TotalPages)
throw new OverflowException(
$"Page id {pageId} should greater or equal than 0 and less than total page count {TotalPages}.");
var p = LibMuPdf.NativeMethods.LoadPage(_doc, pageId);
var bmp = LibMuPdf.RenderPage(_ctx, _doc, p, zoomFactor);
LibMuPdf.NativeMethods.FreePage(_doc, p);
return bmp;
}
}
}

View File

@@ -0,0 +1,71 @@
<UserControl x:Class="QuickLook.Plugin.PDFViewer.PdfViewerControl"
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.PDFViewer"
mc:Ignorable="d"
x:Name="thisPdfViewer"
UseLayoutRounding="True"
d:DesignHeight="476.974"
d:DesignWidth="720.29">
<UserControl.Resources>
<ResourceDictionary>
<local:PageIdToImageConverter x:Key="PageIdToImageConverter" />
<Style x:Key="ListBoxItemStyleNoFocusedBorder" TargetType="{x:Type ListBoxItem}">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox x:Name="listThumbnails" Grid.Column="0" VirtualizingPanel.ScrollUnit="Item"
ScrollViewer.IsDeferredScrollingEnabled="False"
SelectedIndex="0"
Focusable="False"
Background="#9FFFFFFF"
ItemsSource="{Binding PageIds, ElementName=thisPdfViewer}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" BorderThickness="0,0,1,0"
ItemContainerStyle="{Binding Mode=OneWay, Source={StaticResource ListBoxItemStyleNoFocusedBorder}}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid MaxHeight="150"
MaxWidth="{Binding ViewportWidth, Mode=Default, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="10" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="10" />
<RowDefinition Height="*" />
<RowDefinition Height="10" />
</Grid.RowDefinitions>
<Border Grid.Row="1" Grid.Column="1" BorderThickness="1" BorderBrush="#FFE1E1E1">
<Image>
<Image.Source>
<MultiBinding Converter="{StaticResource PageIdToImageConverter}">
<Binding Path="PdfHandleForThumbnails" ElementName="thisPdfViewer" />
<Binding />
</MultiBinding>
</Image.Source>
</Image>
</Border>
<!--
<Label Grid.Row="1" Grid.Column="1" Content="{Binding Mode=OneWay, Converter={StaticResource MathConverter}, ConverterParameter=@VALUE+1}" FontSize="14" />
-->
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid Grid.Column="1" Background="#DFEFEFEF">
<ScrollViewer x:Name="pageViewPanel" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Focusable="False">
<Image x:Name="pageViewPanelImage" Stretch="None" RenderOptions.BitmapScalingMode="NearestNeighbor">
</Image>
</ScrollViewer>
</Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,253 @@
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace QuickLook.Plugin.PDFViewer
{
/// <summary>
/// Interaction logic for PdfViewer.xaml
/// </summary>
public partial class PdfViewerControl : UserControl, INotifyPropertyChanged, IDisposable
{
private PreviewMouseWheelMonitor _whellMonitor;
public PdfViewerControl()
{
InitializeComponent();
DataContext = this;
PageIds = new ObservableCollection<int>();
}
public ObservableCollection<int> PageIds { get; set; }
public PdfFile PdfHandleForThumbnails { get; private set; }
public PdfFile PdfHandle { get; private set; }
public bool PdfLoaded { get; private set; }
public double ZoomFactor { get; set; }
public int TotalPages => PdfHandle.TotalPages;
public int CurrectPage
{
get => listThumbnails.SelectedIndex;
set
{
listThumbnails.SelectedIndex = value;
listThumbnails.ScrollIntoView(listThumbnails.SelectedItem);
CurrentPageChanged?.Invoke(this, new EventArgs());
}
}
public void Dispose()
{
_whellMonitor.Dispose();
PdfHandleForThumbnails?.Dispose();
PdfHandle?.Dispose();
}
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler CurrentPageChanged;
private void NavigatePage(object sender, MouseWheelEventArgs e)
{
if (!PdfLoaded)
return;
if (Keyboard.Modifiers != ModifierKeys.None)
return;
if (e.Delta > 0) // up
{
if (pageViewPanel.VerticalOffset != 0) return;
PrevPage();
e.Handled = true;
}
else // down
{
if (pageViewPanel.VerticalOffset != pageViewPanel.ScrollableHeight) return;
NextPage();
e.Handled = true;
}
}
private void NextPage()
{
if (CurrectPage < PdfHandle.TotalPages - 1)
{
CurrectPage++;
pageViewPanel.ScrollToTop();
}
}
private void PrevPage()
{
if (CurrectPage > 0)
{
CurrectPage--;
pageViewPanel.ScrollToBottom();
}
}
private void ReRenderCurrentPageLowQuality(double viewZoom, bool fromCenter)
{
if (pageViewPanelImage.Source == null)
return;
var position = fromCenter
? new Point(pageViewPanelImage.Source.Width / 2, pageViewPanelImage.Source.Height / 2)
: Mouse.GetPosition(pageViewPanelImage);
pageViewPanelImage.LayoutTransform = new ScaleTransform(viewZoom, viewZoom);
// critical for calcuating offset
pageViewPanel.ScrollToHorizontalOffset(0);
pageViewPanel.ScrollToVerticalOffset(0);
UpdateLayout();
var offset = pageViewPanelImage.TranslatePoint(position, pageViewPanel) - Mouse.GetPosition(pageViewPanel);
pageViewPanel.ScrollToHorizontalOffset(offset.X);
pageViewPanel.ScrollToVerticalOffset(offset.Y);
UpdateLayout();
}
private void ReRenderCurrentPage()
{
if (!PdfLoaded)
return;
var image = PdfHandle.GetPage(CurrectPage, ZoomFactor).ToBitmapSource();
pageViewPanelImage.Source = image;
pageViewPanelImage.Width = pageViewPanelImage.Source.Width;
pageViewPanelImage.Height = pageViewPanelImage.Source.Height;
// reset view zoom factor
pageViewPanelImage.LayoutTransform = new ScaleTransform();
GC.Collect();
}
private void UpdatePageViewWhenSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!PdfLoaded)
return;
if (CurrectPage == -1)
return;
ReRenderCurrentPage();
}
private void ZoomToFit()
{
if (!PdfLoaded)
return;
var size = PdfHandle.GetPageSize(CurrectPage, 1d);
var factor = Math.Min(pageViewPanel.ActualWidth / size.Width, pageViewPanel.ActualHeight / size.Height);
ZoomFactor = factor;
ReRenderCurrentPage();
}
public Size GetDesiredControlSizeByFirstPage(string path)
{
var tempHandle = new PdfFile(path);
var size = tempHandle.GetPageSize(0, 1d);
tempHandle.Dispose();
size.Width += /*listThumbnails.ActualWidth*/ 150 + 1;
return size;
}
public void LoadPdf(string path)
{
PageIds.Clear();
_whellMonitor?.Dispose();
PdfHandleForThumbnails = new PdfFile(path);
PdfHandle = new PdfFile(path);
PdfLoaded = true;
// fill thumbnails list
Enumerable.Range(0, PdfHandle.TotalPages).ForEach(PageIds.Add);
OnPropertyChanged(nameof(PageIds));
CurrectPage = 0;
// calculate zoom factor for first page
ZoomToFit();
// register events
listThumbnails.SelectionChanged += UpdatePageViewWhenSelectionChanged;
//pageViewPanel.SizeChanged += ReRenderCurrentPageWhenSizeChanged;
pageViewPanel.PreviewMouseWheel += NavigatePage;
StartMouseWhellDelayedZoomMonitor(pageViewPanel);
}
private void StartMouseWhellDelayedZoomMonitor(UIElement ui)
{
if (_whellMonitor == null)
_whellMonitor = new PreviewMouseWheelMonitor(ui, 100);
var newZoom = 1d;
var scrolling = false;
_whellMonitor.PreviewMouseWheelStarted += (sender, e) =>
{
if ((Keyboard.Modifiers & ModifierKeys.Control) == 0)
return;
newZoom = ZoomFactor;
scrolling = true;
};
_whellMonitor.PreviewMouseWheel += (sender, e) =>
{
if ((Keyboard.Modifiers & ModifierKeys.Control) == 0)
return;
e.Handled = true;
newZoom = newZoom + e.Delta / 120 * 0.1;
newZoom = Math.Max(newZoom, 0.2);
newZoom = Math.Min(newZoom, 3);
ReRenderCurrentPageLowQuality(newZoom / ZoomFactor, false);
};
_whellMonitor.PreviewMouseWheelStopped += (sender, e) =>
{
if (!scrolling)
return;
ZoomFactor = newZoom;
ReRenderCurrentPage();
};
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -0,0 +1,53 @@
using System.IO;
using System.Text;
namespace QuickLook.Plugin.PDFViewer
{
public class Plugin : IViewer
{
private PdfViewerControl _pdfControl;
public int Priority => 9999;
public bool CanHandle(string path)
{
if (Directory.Exists(path))
return false;
if (File.Exists(path) && Path.GetExtension(path).ToLower() == ".pdf")
return true;
using (var br = new BinaryReader(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
return Encoding.ASCII.GetString(br.ReadBytes(4)) == "%PDF";
}
}
public void View(string path, ViewContentContainer container)
{
_pdfControl = new PdfViewerControl();
var desiredSize = _pdfControl.GetDesiredControlSizeByFirstPage(path);
desiredSize.Width += 150; // add thumbnails column
container.SetPreferedSizeFit(desiredSize, 0.7);
container.SetContent(_pdfControl);
_pdfControl.Loaded += (sender, e) =>
{
_pdfControl.LoadPdf(path);
container.Title = $"{Path.GetFileName(path)} (1 / {_pdfControl.TotalPages})";
_pdfControl.CurrentPageChanged += (sender2, e2) => container.Title =
$"{Path.GetFileName(path)} ({_pdfControl.CurrectPage + 1} / {_pdfControl.TotalPages})";
};
}
public void Close()
{
_pdfControl.Dispose();
_pdfControl = null;
}
}
}

View File

@@ -16,31 +16,29 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\Build\Debug\Plugins\PDFViewer\</OutputPath>
<OutputPath>..\Build\Debug\Plugins\QuickLook.Plugin.PDFViewer\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\Build\Release\Plugins\PDFViewer\</OutputPath>
<OutputPath>..\Build\Release\Plugins\QuickLook.Plugin.PDFViewer\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<PlatformTarget>x86</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="MoonPdfLib, Version=0.3.0.0, Culture=neutral, processorArchitecture=x86">
<HintPath>..\packages\MoonPdfLib-x86.0.3.0\lib\MoonPdfLib.dll</HintPath>
</Reference>
<Reference Include="MouseKeyboardActivityMonitor, Version=3.0.1.29653, Culture=neutral, processorArchitecture=x86">
<HintPath>..\packages\MoonPdfLib-x86.0.3.0\lib\MouseKeyboardActivityMonitor.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="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
@@ -51,7 +49,16 @@
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="Class1.cs" />
<Compile Include="PdfViewerControl.xaml.cs">
<DependentUpon>PdfViewerControl.xaml</DependentUpon>
</Compile>
<Compile Include="Plugin.cs" />
<Compile Include="DpiHelpers.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="LibMuPdf.cs" />
<Compile Include="MouseWheelMonitor.cs" />
<Compile Include="PageIdToImageConverter.cs" />
<Compile Include="PdfFile.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
@@ -62,13 +69,15 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="MoonPdfLib\README" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="libmupdf.dll">
<Content Include="LibMuPdf.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Page Include="PdfViewerControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MoonPdfLib-x86" version="0.3.0" targetFramework="net452" />
</packages>

View File

@@ -10,26 +10,48 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickLook", "QuickLook\Quic
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QuickLook.Native.Shell32", "QuickLook.Native.Shell32\QuickLook.Native.Shell32.vcxproj", "{D31EE321-C2B0-4984-B749-736F7DE509F1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickLook.Plugin.PDFViewer", "QuickLook.Plugin.PDFViewer\QuickLook.Plugin.PDFViewer.csproj", "{A82AC69C-EDF5-4F0D-8CBD-8E5E3C06E64D}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickLook.Plugin.PdfViewer", "QuickLook.Plugin.PDFViewer\QuickLook.Plugin.PdfViewer.csproj", "{A82AC69C-EDF5-4F0D-8CBD-8E5E3C06E64D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickLook.Plugin.LastResort", "QuickLook.Plugin.LastResort\QuickLook.Plugin.LastResort.csproj", "{B9A5A4F6-813E-40CE-AD32-DC5C1356215D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8B4A9CE5-67B5-4A94-81CB-3771F688FDEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8B4A9CE5-67B5-4A94-81CB-3771F688FDEB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8B4A9CE5-67B5-4A94-81CB-3771F688FDEB}.Debug|x86.ActiveCfg = Debug|Any CPU
{8B4A9CE5-67B5-4A94-81CB-3771F688FDEB}.Debug|x86.Build.0 = Debug|Any CPU
{8B4A9CE5-67B5-4A94-81CB-3771F688FDEB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8B4A9CE5-67B5-4A94-81CB-3771F688FDEB}.Release|Any CPU.Build.0 = Release|Any CPU
{8B4A9CE5-67B5-4A94-81CB-3771F688FDEB}.Release|x86.ActiveCfg = Release|Any CPU
{8B4A9CE5-67B5-4A94-81CB-3771F688FDEB}.Release|x86.Build.0 = Release|Any CPU
{D31EE321-C2B0-4984-B749-736F7DE509F1}.Debug|Any CPU.ActiveCfg = Debug|Win32
{D31EE321-C2B0-4984-B749-736F7DE509F1}.Debug|x86.ActiveCfg = Debug|Win32
{D31EE321-C2B0-4984-B749-736F7DE509F1}.Debug|x86.Build.0 = Debug|Win32
{D31EE321-C2B0-4984-B749-736F7DE509F1}.Release|Any CPU.ActiveCfg = Release|Win32
{D31EE321-C2B0-4984-B749-736F7DE509F1}.Release|x86.ActiveCfg = Release|Win32
{D31EE321-C2B0-4984-B749-736F7DE509F1}.Release|x86.Build.0 = Release|Win32
{A82AC69C-EDF5-4F0D-8CBD-8E5E3C06E64D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A82AC69C-EDF5-4F0D-8CBD-8E5E3C06E64D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A82AC69C-EDF5-4F0D-8CBD-8E5E3C06E64D}.Debug|x86.ActiveCfg = Debug|Any CPU
{A82AC69C-EDF5-4F0D-8CBD-8E5E3C06E64D}.Debug|x86.Build.0 = Debug|Any CPU
{A82AC69C-EDF5-4F0D-8CBD-8E5E3C06E64D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A82AC69C-EDF5-4F0D-8CBD-8E5E3C06E64D}.Release|Any CPU.Build.0 = Release|Any CPU
{A82AC69C-EDF5-4F0D-8CBD-8E5E3C06E64D}.Release|x86.ActiveCfg = Release|Any CPU
{A82AC69C-EDF5-4F0D-8CBD-8E5E3C06E64D}.Release|x86.Build.0 = Release|Any CPU
{B9A5A4F6-813E-40CE-AD32-DC5C1356215D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B9A5A4F6-813E-40CE-AD32-DC5C1356215D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B9A5A4F6-813E-40CE-AD32-DC5C1356215D}.Debug|x86.ActiveCfg = Debug|Any CPU
{B9A5A4F6-813E-40CE-AD32-DC5C1356215D}.Debug|x86.Build.0 = Debug|Any CPU
{B9A5A4F6-813E-40CE-AD32-DC5C1356215D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B9A5A4F6-813E-40CE-AD32-DC5C1356215D}.Release|Any CPU.Build.0 = Release|Any CPU
{B9A5A4F6-813E-40CE-AD32-DC5C1356215D}.Release|x86.ActiveCfg = Release|Any CPU
{B9A5A4F6-813E-40CE-AD32-DC5C1356215D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -1,5 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeEditing/Intellisense/CodeCompletion/IntelliSenseCompletingCharacters/CSharpCompletingCharacters/UpgradedFromVSSettings/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=QuickLook_002EAnnotations/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/RecentlyUsedProfile/@EntryValue">Default: Reformat Code</s:String>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_AUTO_PROPERTY/@EntryValue">0</s:Int64>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_FIELD/@EntryValue">0</s:Int64>

View File

@@ -5,6 +5,10 @@
Startup="Application_Startup"
ShutdownMode="OnExplicitShutdown">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles/ScrollBarStyleDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -12,6 +13,8 @@ namespace QuickLook
private GlobalKeyboardHook _hook;
private MainWindow _showingWindow;
protected BackgroundListener()
{
InstallHook(HotkeyEventHandler);
@@ -19,20 +22,46 @@ namespace QuickLook
private void HotkeyEventHandler(object sender, KeyEventArgs e)
{
var paths = new string[0];
if (_showingWindow != null)
{
_showingWindow.Close();
_showingWindow = null;
GC.Collect();
return;
}
var path = String.Empty;
// communicate with COM in a separate thread
Task.Run(() => paths = GetCurrentSelection()).Wait();
Task.Run(() =>
{
var paths = GetCurrentSelection();
var ddd = PathToPluginMatcher.FindMatch(paths);
if (paths.Any())
path = paths.First();
var mw = new MainWindow();
}).Wait();
ddd.View(paths[0], mw.ViewContentContainer);
if (String.IsNullOrEmpty(path))
return;
mw.Show();
var matched = PluginManager.FindMatch(path);
mw.ShowFinishLoadingAnimation(TimeSpan.FromMilliseconds(200));
if (matched == null)
return;
_showingWindow = new MainWindow();
_showingWindow.Closed += (sender2, e2) => { _showingWindow = null; };
_showingWindow.viewContentContainer.ViewerPlugin = matched;
matched.View(path, _showingWindow.viewContentContainer);
_showingWindow.Show();
_showingWindow.ShowFinishLoadingAnimation();
}
private void InstallHook(KeyEventHandler handler)

View File

@@ -11,23 +11,19 @@
UseLayoutRounding="True"
Topmost="True" d:DesignWidth="624" d:DesignHeight="700"
WindowStartupLocation="CenterScreen"
ResizeMode="CanResizeWithGrip"
x:ClassModifier="internal">
<Window.Background>
<SolidColorBrush Color="#00FFFFFF" />
<SolidColorBrush Color="#7FFFFFFF" />
</Window.Background>
<Grid Name="WindowContainer">
<Grid.Background>
<SolidColorBrush Color="#FFFFFFFF" />
</Grid.Background>
<Border BorderThickness="1" BorderBrush="#FF7B7B7B">
<Grid>
<Grid>
<plugin:ViewContentContainer x:Name="ViewContentContainer" Width="624" Height="700" />
</Grid>
<DockPanel Name="Titlebar" Height="40" VerticalAlignment="Top" Opacity="0">
<DockPanel.Background>
<SolidColorBrush Color="#FFFFFFFF" />
</DockPanel.Background>
<DockPanel.Style>
<DockPanel Opacity="1">
<DockPanel x:Name="titlebar" Height="28" Dock="Top">
<DockPanel.Background>
<SolidColorBrush Color="#FFB8B8B8" />
</DockPanel.Background>
<!--<DockPanel.Style>
<Style TargetType="{x:Type DockPanel}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
@@ -50,20 +46,24 @@
</Trigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
<fa:ImageAwesome DockPanel.Dock="Right" Name="Close" Icon="WindowClose" Height="20" Margin="10,0"
Cursor="Hand" MouseLeftButtonUp="Close_MouseLeftButtonUp" />
<Label Content="Filename placeholder" FontSize="16" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" />
</DockPanel.Style>-->
<fa:ImageAwesome DockPanel.Dock="Right" x:Name="buttonCloseWindow" Icon="WindowClose" Height="15" Margin="10,0"
Cursor="Hand" />
<Label x:Name="titlebarTitleArea" Content="{Binding Title, ElementName=viewContentContainer}" FontSize="14" HorizontalContentAlignment="Center"
VerticalContentAlignment="Center" />
</DockPanel>
<Grid>
<plugin:ViewContentContainer x:Name="viewContentContainer"/>
</Grid>
</DockPanel>
</Grid>
<Grid Name="LoadingIconLayer">
<Grid.Background>
<SolidColorBrush Color="#FFFFFFFF" />
</Grid.Background>
<Grid Name="LoadingIcon" Height="50" Width="50" HorizontalAlignment="Center" VerticalAlignment="Center">
<fa:ImageAwesome Icon="CircleOutlineNotch" Spin="True" SpinDuration="2" />
<Grid x:Name="loadingIconLayer" Opacity="0.3">
<Grid.Background>
<SolidColorBrush Color="#FFFFFFFF" />
</Grid.Background>
<Grid x:Name="loadingIcon" Height="50" Width="50" HorizontalAlignment="Center" VerticalAlignment="Center">
<fa:ImageAwesome Icon="CircleOutlineNotch" Spin="True" SpinDuration="1" />
</Grid>
</Grid>
</Grid>
</Grid>
</Border>
</Window>

View File

@@ -1,100 +1,88 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Runtime.Remoting.Channels;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using QuickLook.Annotations;
using QuickLook.ExtensionMethods;
using QuickLook.Utilities;
namespace QuickLook
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
internal partial class MainWindow : Window
internal partial class MainWindow : Window, INotifyPropertyChanged
{
internal MainWindow()
{
InitializeComponent();
WindowContainer.Width = LoadingIcon.Width;
WindowContainer.Height = LoadingIcon.Height;
ViewContentContainer.Opacity = 0;
DataContext = this;
ContentRendered += (sender, e) => AeroGlass.EnableBlur(this);
Closed += MainWindow_Closed;
buttonCloseWindow.MouseLeftButtonUp += CloseCurrentWindow;
titlebarTitleArea.MouseDown += (sender, e) => DragMove();
}
private void MainWindow_Closed(object sender, EventArgs e)
{
viewContentContainer.ViewerPlugin.Close();
}
internal new void Show()
{
Height = ViewContentContainer.Height;
Width = ViewContentContainer.Width;
loadingIconLayer.Opacity = 1;
Height = viewContentContainer.PreferedSize.Height + titlebar.Height;
Width = viewContentContainer.PreferedSize.Width;
base.Show();
}
internal void ShowFinishLoadingAnimation(TimeSpan delay = new TimeSpan())
internal void ShowFinishLoadingAnimation()
{
var speed = 200;
var speed = 100;
var sb = new Storyboard();
var ptl = new ParallelTimeline {BeginTime = delay};
var aWidth = new DoubleAnimation
{
From = WindowContainer.Width,
To = ViewContentContainer.Width,
Duration = TimeSpan.FromMilliseconds(speed),
DecelerationRatio = 0.3
};
var aHeight = new DoubleAnimation
{
From = WindowContainer.Height,
To = ViewContentContainer.Height,
Duration = TimeSpan.FromMilliseconds(speed),
DecelerationRatio = 0.3
};
var aOpacity = new DoubleAnimation
{
From = 0,
To = 1,
BeginTime = TimeSpan.FromMilliseconds(speed * 0.25),
Duration = TimeSpan.FromMilliseconds(speed * 0.75)
};
var ptl = new ParallelTimeline();
var aOpacityR = new DoubleAnimation
{
From = 1,
To = 0,
Duration = TimeSpan.FromMilliseconds(speed * 2)
Duration = TimeSpan.FromMilliseconds(speed)
};
Storyboard.SetTarget(aWidth, WindowContainer);
Storyboard.SetTarget(aHeight, WindowContainer);
Storyboard.SetTarget(aOpacity, ViewContentContainer);
Storyboard.SetTarget(aOpacityR, LoadingIconLayer);
Storyboard.SetTargetProperty(aWidth, new PropertyPath(WidthProperty));
Storyboard.SetTargetProperty(aHeight, new PropertyPath(HeightProperty));
Storyboard.SetTargetProperty(aOpacity, new PropertyPath(OpacityProperty));
Storyboard.SetTarget(aOpacityR, loadingIconLayer);
Storyboard.SetTargetProperty(aOpacityR, new PropertyPath(OpacityProperty));
ptl.Children.Add(aWidth);
ptl.Children.Add(aHeight);
ptl.Children.Add(aOpacity);
ptl.Children.Add(aOpacityR);
sb.Children.Add(ptl);
sb.Begin();
Dispatcher.DelayWithPriority(speed * 2, o => LoadingIconLayer.Visibility = Visibility.Hidden, null,
Dispatcher.DelayWithPriority(speed, o => loadingIconLayer.Visibility = Visibility.Hidden, null,
DispatcherPriority.Render);
}
private void Close_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
private void CloseCurrentWindow(object sender, MouseButtonEventArgs e)
{
Close();
}
// useless code to make everyone happy
GC.Collect();
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -1,58 +0,0 @@
using System.IO;
using System.Linq;
using QuickLook.Plugin;
namespace QuickLook
{
internal static class PathToPluginMatcher
{
internal static IViewer FindMatch(string[] paths)
{
if (paths.Length == 0)
return null;
//TODO: Handle multiple files?
var path = paths.First();
return FindByExtension(path) ?? FindByContent(path);
}
private static IViewer FindByExtension(string path)
{
if (string.IsNullOrEmpty(path))
return null;
var ext = Path.GetExtension(path).ToLower();
return PluginManager.GetInstance()
.LoadedPlugins.FirstOrDefault(plugin =>
{
if ((plugin.Type & PluginType.ByExtension) == 0)
return false;
return plugin.SupportExtensions.Any(e => e == ext);
});
}
private static IViewer FindByContent(string path)
{
if (string.IsNullOrEmpty(path))
return null;
byte[] sample;
using (var br = new BinaryReader(new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)))
{
sample = br.ReadBytes(256);
}
return PluginManager.GetInstance()
.LoadedPlugins.FirstOrDefault(plugin =>
{
if ((plugin.Type & PluginType.ByContent) == 0)
return false;
return plugin.CheckSupportByContent(sample);
});
}
}
}

View File

@@ -2,9 +2,8 @@
{
public interface IViewer
{
PluginType Type { get; }
string[] SupportExtensions { get; }
bool CheckSupportByContent(byte[] sample);
int Priority { get; }
bool CanHandle(string sample);
void View(string path, ViewContentContainer container);
void Close();
}

View File

@@ -7,7 +7,7 @@
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<ContentControl Name="Container">
<ContentControl x:Name="container">
<Label Content="ContentControl Placeholder" VerticalContentAlignment="Center"
HorizontalContentAlignment="Center" Background="LightGray" />
</ContentControl>

View File

@@ -1,20 +1,71 @@
using System.Windows.Controls;
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
using QuickLook.Annotations;
namespace QuickLook.Plugin
{
/// <summary>
/// Interaction logic for ViewContentContainer.xaml
/// </summary>
public partial class ViewContentContainer : UserControl
public partial class ViewContentContainer : UserControl,INotifyPropertyChanged
{
private string _title = String.Empty;
public ViewContentContainer()
{
InitializeComponent();
}
public string Title
{
set
{
_title = value;
OnPropertyChanged(nameof(Title));
}
get => _title;
}
public void SetContent(object content)
{
Container.Content = content;
container.Content = content;
}
public double SetPreferedSizeFit(Size size, double maxRatio)
{
if (maxRatio > 1)
maxRatio = 1;
var max = GetMaximumDisplayBound();
var widthRatio = (max.Width * maxRatio) / size.Width;
var heightRatio = (max.Height * maxRatio) / size.Height;
var ratio = Math.Min(widthRatio, heightRatio);
PreferedSize = new Size {Width = size.Width * ratio, Height = size.Height * ratio};
return ratio;
}
public IViewer ViewerPlugin { get; set; }
public Size PreferedSize { get; set; }
public Size GetMaximumDisplayBound()
{
return new Size(SystemParameters.VirtualScreenWidth, SystemParameters.VirtualScreenHeight);
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -11,18 +11,27 @@ namespace QuickLook
{
private static PluginManager _instance;
internal PluginManager()
private PluginManager()
{
LoadPlugins();
}
internal List<IViewer> LoadedPlugins { get; } = new List<IViewer>();
internal List<IViewer> LoadedPlugins { get; private set; } = new List<IViewer>();
internal static PluginManager GetInstance()
{
return _instance ?? (_instance = new PluginManager());
}
internal static IViewer FindMatch(string path)
{
if (string.IsNullOrEmpty(path))
return null;
return GetInstance()
.LoadedPlugins.FirstOrDefault(plugin => plugin.CanHandle(path));
}
private void LoadPlugins()
{
Directory.GetFiles(Path.Combine(App.AppPath, "Plugins\\"), "QuickLook.Plugin.*.dll",
@@ -37,6 +46,8 @@ namespace QuickLook
select t).ToList()
.ForEach(type => LoadedPlugins.Add((IViewer) Activator.CreateInstance(type)));
});
LoadedPlugins = LoadedPlugins.OrderByDescending(i => i.Priority).ToList();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -73,13 +73,16 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="PathToPluginMatcher.cs" />
<Compile Include="PluginManager.cs" />
<Compile Include="Plugin\IViewer.cs" />
<Compile Include="Plugin\PluginPriority.cs" />
<Compile Include="Plugin\ViewContentContainer.xaml.cs">
<DependentUpon>ViewContentContainer.xaml</DependentUpon>
</Compile>
<Page Include="Styles\ScrollBarStyleDictionary.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@@ -92,6 +95,8 @@
<Compile Include="BackgroundListener.cs" />
<Compile Include="NativeMethods\Kernel32.cs" />
<Compile Include="NativeMethods\User32.cs" />
<Compile Include="Properties\Annotations.cs" />
<Compile Include="Utilities\AeroGlass.cs" />
<Compile Include="Utilities\GlobalKeyboardHook.cs" />
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>

View File

@@ -0,0 +1,181 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="StandardBorderBrush" Color="#888" />
<SolidColorBrush x:Key="StandardBackgroundBrush" Color="#FFF" />
<SolidColorBrush x:Key="HoverBorderBrush" Color="#DDD" />
<SolidColorBrush x:Key="SelectedBackgroundBrush" Color="Gray" />
<SolidColorBrush x:Key="SelectedForegroundBrush" Color="White" />
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
<SolidColorBrush x:Key="NormalBrush" Color="#888" />
<SolidColorBrush x:Key="NormalBorderBrush" Color="#888" />
<SolidColorBrush x:Key="HorizontalNormalBrush" Color="#888" />
<SolidColorBrush x:Key="HorizontalNormalBorderBrush" Color="#888" />
<LinearGradientBrush x:Key="ListBoxBackgroundBrush"
StartPoint="0,0" EndPoint="1,0.001">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="White" Offset="0.0" />
<GradientStop Color="White" Offset="0.6" />
<GradientStop Color="#DDDDDD" Offset="1.2" />
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<LinearGradientBrush x:Key="StandardBrush"
StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#FFF" Offset="0.0" />
<GradientStop Color="#CCC" Offset="1.0" />
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="GlyphBrush" Color="#444" />
<LinearGradientBrush x:Key="PressedBrush"
StartPoint="0,0" EndPoint="0,1">
<GradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Color="#BBB" Offset="0.0" />
<GradientStop Color="#EEE" Offset="0.1" />
<GradientStop Color="#EEE" Offset="0.9" />
<GradientStop Color="#FFF" Offset="1.0" />
</GradientStopCollection>
</GradientBrush.GradientStops>
</LinearGradientBrush>
<!-- SrollViewer ScrollBar Repeat Buttons (at each end) -->
<Style x:Key="ScrollBarLineButton" TargetType="{x:Type RepeatButton}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Focusable" Value="false" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Border Name="Border" Margin="1" CornerRadius="2" Background="{StaticResource NormalBrush}"
BorderBrush="{StaticResource NormalBorderBrush}" BorderThickness="0.25">
<Path HorizontalAlignment="Center" VerticalAlignment="Center"
Fill="{StaticResource GlyphBrush}"
Data="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="Border" Property="Background"
Value="{StaticResource PressedBrush}" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground"
Value="{StaticResource DisabledForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- SrollViewer ScrollBar Repeat Buttons (The part in the middle,
not the thumb the long area between the buttons ) -->
<Style x:Key="ScrollBarPageButton" TargetType="{x:Type RepeatButton}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Focusable" Value="false" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Border Background="Transparent" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ScrollViewer ScrollBar Thumb, that part that can be dragged
up/down or left/right Buttons -->
<Style x:Key="ScrollBarThumb" TargetType="{x:Type Thumb}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Focusable" Value="false" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Border CornerRadius="2" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0.25" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="VerticalScrollBar"
TargetType="{x:Type ScrollBar}">
<Grid Width="6">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.RowSpan="3" CornerRadius="2" Background="Transparent" />
<Track Name="PART_Track" Grid.Row="0" IsDirectionReversed="true">
<Track.DecreaseRepeatButton>
<RepeatButton Style="{StaticResource ScrollBarPageButton}" Command="ScrollBar.PageUpCommand" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource ScrollBarThumb}" Margin="0,0,0,0"
Background="{StaticResource HorizontalNormalBrush}"
BorderBrush="{StaticResource HorizontalNormalBorderBrush}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Style="{StaticResource ScrollBarPageButton}" Command="ScrollBar.PageDownCommand" />
</Track.IncreaseRepeatButton>
</Track>
</Grid>
</ControlTemplate>
<!-- HorizontalScrollBar Template using the previously created Templates -->
<ControlTemplate x:Key="HorizontalScrollBar"
TargetType="{x:Type ScrollBar}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border
Grid.ColumnSpan="3"
CornerRadius="2"
Background="#F0F0F0" />
<Track
Name="PART_Track"
Grid.Column="0"
IsDirectionReversed="False">
<Track.DecreaseRepeatButton>
<RepeatButton
Style="{StaticResource ScrollBarPageButton}"
Command="ScrollBar.PageLeftCommand" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb
Style="{StaticResource ScrollBarThumb}"
Margin="0,2"
Background="{StaticResource NormalBrush}"
BorderBrush="{StaticResource NormalBorderBrush}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton
Style="{StaticResource ScrollBarPageButton}"
Command="ScrollBar.PageRightCommand" />
</Track.IncreaseRepeatButton>
</Track>
</Grid>
</ControlTemplate>
<!-- Style for overall ScrollBar -->
<Style x:Key="{x:Type ScrollBar}" TargetType="{x:Type ScrollBar}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Style.Triggers>
<Trigger Property="Orientation" Value="Horizontal">
<Setter Property="Width" Value="Auto" />
<Setter Property="Height" Value="10" />
<Setter Property="Template"
Value="{StaticResource HorizontalScrollBar}" />
</Trigger>
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="Width" Value="10" />
<Setter Property="Height" Value="Auto" />
<Setter Property="Template"
Value="{StaticResource VerticalScrollBar}" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,67 @@
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace QuickLook.Utilities
{
internal class AeroGlass
{
internal static void EnableBlur(Window window)
{
var windowHelper = new WindowInteropHelper(window);
var accent = new AccentPolicy();
var accentStructSize = Marshal.SizeOf(accent);
accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND;
var accentPtr = Marshal.AllocHGlobal(accentStructSize);
Marshal.StructureToPtr(accent, accentPtr, false);
var data = new WindowCompositionAttributeData();
data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY;
data.SizeOfData = accentStructSize;
data.Data = accentPtr;
SetWindowCompositionAttribute(windowHelper.Handle, ref data);
Marshal.FreeHGlobal(accentPtr);
}
[DllImport("user32.dll")]
private static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data);
[StructLayout(LayoutKind.Sequential)]
private struct WindowCompositionAttributeData
{
public WindowCompositionAttribute Attribute;
public IntPtr Data;
public int SizeOfData;
}
private enum WindowCompositionAttribute
{
// ...
WCA_ACCENT_POLICY = 19
// ...
}
private enum AccentState
{
ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
ACCENT_ENABLE_BLURBEHIND = 3,
ACCENT_INVALID_STATE = 4
}
[StructLayout(LayoutKind.Sequential)]
private struct AccentPolicy
{
public AccentState AccentState;
public int AccentFlags;
public int GradientColor;
public int AnimationId;
}
}
}