mirror of
https://github.com/QL-Win/QuickLook.git
synced 2025-09-13 19:19:10 +00:00
Use own Pdf viewer implementation. wip on universal InfoPanel
This commit is contained in:
40
QuickLook.Plugin.LastResort/Extensions.cs
Normal file
40
QuickLook.Plugin.LastResort/Extensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
255
QuickLook.Plugin.LastResort/IconHelper.cs
Normal file
255
QuickLook.Plugin.LastResort/IconHelper.cs
Normal 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;
|
||||
}
|
||||
}
|
12
QuickLook.Plugin.LastResort/InfoPanel.xaml
Normal file
12
QuickLook.Plugin.LastResort/InfoPanel.xaml
Normal 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>
|
31
QuickLook.Plugin.LastResort/InfoPanel.xaml.cs
Normal file
31
QuickLook.Plugin.LastResort/InfoPanel.xaml.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
33
QuickLook.Plugin.LastResort/Plugin.cs
Normal file
33
QuickLook.Plugin.LastResort/Plugin.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
36
QuickLook.Plugin.LastResort/Properties/AssemblyInfo.cs
Normal file
36
QuickLook.Plugin.LastResort/Properties/AssemblyInfo.cs
Normal 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")]
|
@@ -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>
|
@@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
46
QuickLook.Plugin.PDFViewer/DpiHelpers.cs
Normal file
46
QuickLook.Plugin.PDFViewer/DpiHelpers.cs
Normal 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; }
|
||||
}
|
||||
}
|
68
QuickLook.Plugin.PDFViewer/Extensions.cs
Normal file
68
QuickLook.Plugin.PDFViewer/Extensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
181
QuickLook.Plugin.PDFViewer/LibMuPdf.cs
Normal file
181
QuickLook.Plugin.PDFViewer/LibMuPdf.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
113
QuickLook.Plugin.PDFViewer/MouseWheelMonitor.cs
Normal file
113
QuickLook.Plugin.PDFViewer/MouseWheelMonitor.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
32
QuickLook.Plugin.PDFViewer/PageIdToImageConverter.cs
Normal file
32
QuickLook.Plugin.PDFViewer/PageIdToImageConverter.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
73
QuickLook.Plugin.PDFViewer/PdfFile.cs
Normal file
73
QuickLook.Plugin.PDFViewer/PdfFile.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
71
QuickLook.Plugin.PDFViewer/PdfViewerControl.xaml
Normal file
71
QuickLook.Plugin.PDFViewer/PdfViewerControl.xaml
Normal 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>
|
253
QuickLook.Plugin.PDFViewer/PdfViewerControl.xaml.cs
Normal file
253
QuickLook.Plugin.PDFViewer/PdfViewerControl.xaml.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
53
QuickLook.Plugin.PDFViewer/Plugin.cs
Normal file
53
QuickLook.Plugin.PDFViewer/Plugin.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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>
|
Binary file not shown.
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<packages>
|
||||
<package id="MoonPdfLib-x86" version="0.3.0" targetFramework="net452" />
|
||||
</packages>
|
@@ -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
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
@@ -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)
|
||||
|
@@ -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>
|
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
|
@@ -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>
|
||||
|
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
1048
QuickLook/Properties/Annotations.cs
Normal file
1048
QuickLook/Properties/Annotations.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
|
181
QuickLook/Styles/ScrollBarStyleDictionary.xaml
Normal file
181
QuickLook/Styles/ScrollBarStyleDictionary.xaml
Normal 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>
|
67
QuickLook/Utilities/AeroGlass.cs
Normal file
67
QuickLook/Utilities/AeroGlass.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user