Files
PDFPatcher/App/Functions/Editor/Parts/BookmarkInViewSynchronizer.cs

147 lines
3.3 KiB
C#

using System;
using System.Drawing;
using BrightIdeasSoftware;
using PDFPatcher.Model;
namespace PDFPatcher.Functions.Editor.Parts
{
internal sealed class BookmarkInViewSynchronizer
{
readonly BookmarkEditorView _BookmarkBox;
readonly ViewerControl _ViewerBox;
readonly RowBorderDecoration _BookmarkInViewDecoration = new RowBorderDecoration() {
FillBrush = new SolidBrush(Color.FromArgb(40, Color.Yellow)),
BorderPen = new Pen(Color.FromArgb(216, Color.Yellow))
};
BookmarkElement _BookmarkInView = null;
public BookmarkInViewSynchronizer(BookmarkEditorView bookmark, ViewerControl viewer) {
_BookmarkBox = bookmark;
_BookmarkBox.FormatRow += HighlightInViewRow;
_BookmarkBox.ItemsChanged += HandleBookmarkChange;
_BookmarkBox.BookmarkChanged += HandleBookmarkChange;
_ViewerBox = viewer;
_ViewerBox.PageChanged += SyncBookmarkPosition;
}
void HandleBookmarkChange(object sender, EventArgs e) {
SyncView();
}
public BookmarkElement BookmarkInView {
get => _BookmarkInView;
internal set {
if (value != _BookmarkInView) {
var old = _BookmarkInView;
_BookmarkInView = value;
var i = _BookmarkBox.IndexOf(old);
if (i >= 0) {
_BookmarkBox.RedrawItem(i);
}
i = _BookmarkBox.IndexOf(value);
if (i >= 0) {
_BookmarkBox.RedrawItem(i);
}
}
}
}
void SyncBookmarkPosition(object sender, ViewerControl.PageChangedEventArgs e) {
SyncView();
}
void SyncView() {
int l = _BookmarkBox.GetItemCount();
if (l == 0) {
return;
}
var p = _ViewerBox.GetCurrentScrollPosition();
if (p.Page == 0) {
return;
}
BookmarkInView = FindNearestBookmark(l, p);
}
BookmarkElement FindNearestBookmark(int itemCount, PagePosition p) {
BookmarkElement e = null;
float t = float.NaN;
var h = _ViewerBox.HorizontalFlow;
int start = 0, end = itemCount, i = itemCount >> 1, bp;
BookmarkElement b, c;
// 使用二分法查找最近的书签,这里假设书签指向的页面从上到下是顺序的
while (i != start && i != end) {
c = _BookmarkBox.GetBookmark(i);
bp = c.Page;
if (bp == 0) {
++i;
continue;
}
b = c;
if (bp > p.Page) {
end = i;
i = start + ((i - start) >> 1);
}
else if (bp < p.Page) {
start = i;
i = end - ((end - i) >> 1);
if (Single.IsNaN(t)) {
e = b;
}
}
else {
if (h) {
e = b;
break;
}
if (Single.IsNaN(t)) {
t = b.Top;
e = b;
if (t > p.PageY) {
start = i;
++i;
}
else if (t < p.PageY) {
end = i;
--i;
}
else {
break;
}
}
else {
if (t > p.PageY) {
if (t > b.Top && t - p.PageY >= Math.Abs(b.Top - p.PageY)) {
t = b.Top;
e = b;
start = i;
++i;
continue;
}
}
else if (t < p.PageY) {
if (t < b.Top && p.PageY - t >= Math.Abs(b.Top - p.PageY)) {
t = b.Top;
e = b;
end = i;
--i;
continue;
}
}
else {
e = b;
}
break;
}
}
}
return e ?? _BookmarkBox.GetBookmark(start);
}
void HighlightInViewRow(object sender, FormatRowEventArgs e) {
if (e.Model == _BookmarkInView) {
e.Item.Decorations.Add(_BookmarkInViewDecoration);
}
}
}
}