📄 displaymodel.cc.svn-base
字号:
newPageNo = currentPageNo();
if (newPageNo != currPageNo)
pageChanged();
repaintDisplay(true);
}
void DisplayModel::goToPage(int pageNo, int scrollY, int scrollX)
{
assert(validPageNo(pageNo));
if (!validPageNo(pageNo))
return;
/* in facing mode only start at odd pages (odd because page
numbering starts with 1, so odd is really an even page) */
if (displayModeFacing(displayMode()))
pageNo = ((pageNo-1) & ~1) + 1;
if (!displayModeContinuous(displayMode())) {
/* in single page mode going to another page involves recalculating
the size of canvas */
changeStartPage(pageNo);
}
//DBG_OUT("DisplayModel::goToPage(pageNo=%d, scrollY=%d)\n", pageNo, scrollY);
if (-1 != scrollX)
areaOffset.x = (double)scrollX;
PdfPageInfo * pageInfo = getPageInfo(pageNo);
/* Hack: if an image is smaller in Y axis than the draw area, then we center
the image by setting pageInfo->currPosY in RecalcPagesInfo. So we shouldn't
scroll (adjust areaOffset.y) there because it defeats the purpose.
TODO: is there a better way of y-centering?
TODO: it probably doesn't work in continuous mode (but that's a corner
case, I hope) */
if (!displayModeContinuous(displayMode()))
areaOffset.y = (double)scrollY;
else
areaOffset.y = pageInfo->currPosY - PADDING_PAGE_BORDER_TOP + (double)scrollY;
/* TODO: prevent scrolling too far */
recalcVisibleParts();
recalcLinksCanvasPos();
renderVisibleParts();
setScrollbarsState();
pageChanged();
repaintDisplay(true);
}
void DisplayModel::changeDisplayMode(DisplayMode displayMode)
{
if (_displayMode == displayMode)
return;
_displayMode = displayMode;
int currPageNo = currentPageNo();
if (displayModeContinuous(displayMode)) {
/* mark all pages as shown but not yet visible. The equivalent code
for non-continuous mode is in DisplayModel::changeStartPage() called
from DisplayModel::goToPage() */
for (int pageNo = 1; pageNo <= pageCount(); pageNo++) {
PdfPageInfo *pageInfo = &(_pagesInfo[pageNo-1]);
pageInfo->shown = true;
pageInfo->visible = false;
}
relayout(zoomVirtual(), rotation());
}
goToPage(currPageNo, 0);
}
/* given 'columns' and an absolute 'pageNo', return the number of the first
page in a row to which a 'pageNo' belongs e.g. if 'columns' is 2 and we
have 5 pages in 3 rows:
(1,2)
(3,4)
(5)
then, we return 1 for pages (1,2), 3 for (3,4) and 5 for (5).
This is 1-based index, not 0-based. */
static int FirstPageInARowNo(int pageNo, int columns)
{
int row = ((pageNo - 1) / columns); /* 0-based row number */
int firstPageNo = row * columns + 1; /* 1-based page in a row */
return firstPageNo;
}
/* In continuous mode just scrolls to the next page. In single page mode
rebuilds the display model for the next page.
Returns true if advanced to the next page or false if couldn't advance
(e.g. because already was at the last page) */
bool DisplayModel::goToNextPage(int scrollY)
{
int columns = columnsFromDisplayMode(displayMode());
int currPageNo = currentPageNo();
int firstPageInCurrRow = FirstPageInARowNo(currPageNo, columns);
int newPageNo = currPageNo + columns;
int firstPageInNewRow = FirstPageInARowNo(newPageNo, columns);
// DBG_OUT("DisplayModel::goToNextPage(scrollY=%d), currPageNo=%d, firstPageInNewRow=%d\n", scrollY, currPageNo, firstPageInNewRow);
if ((firstPageInNewRow > pageCount()) || (firstPageInCurrRow == firstPageInNewRow)) {
/* we're on a last row or after it, can't go any further */
return FALSE;
}
goToPage(firstPageInNewRow, scrollY);
return TRUE;
}
bool DisplayModel::goToPrevPage(int scrollY)
{
int columns = columnsFromDisplayMode(displayMode());
int currPageNo = currentPageNo();
DBG_OUT("DisplayModel::goToPrevPage(scrollY=%d), currPageNo=%d\n", scrollY, currPageNo);
if (currPageNo <= columns) {
/* we're on a first page, can't go back */
return FALSE;
}
goToPage(currPageNo - columns, scrollY);
return TRUE;
}
bool DisplayModel::goToLastPage(void)
{
DBG_OUT("DisplayModel::goToLastPage()\n");
int columns = columnsFromDisplayMode(displayMode());
int currPageNo = currentPageNo();
int firstPageInLastRow = FirstPageInARowNo(pageCount(), columns);
if (currPageNo != firstPageInLastRow) { /* are we on the last page already ? */
goToPage(firstPageInLastRow, 0);
return TRUE;
}
return FALSE;
}
bool DisplayModel::goToFirstPage(void)
{
DBG_OUT("DisplayModel::goToFirstPage()\n");
if (displayModeContinuous(displayMode())) {
if (0 == areaOffset.y) {
return FALSE;
}
} else {
assert(pageShown(_startPage));
if (1 == _startPage) {
/* we're on a first page already */
return FALSE;
}
}
goToPage(1, 0);
return TRUE;
}
void DisplayModel::scrollXTo(int xOff)
{
DBG_OUT("DisplayModel::scrollXTo(xOff=%d)\n", xOff);
areaOffset.x = (double)xOff;
recalcVisibleParts();
recalcLinksCanvasPos();
setScrollbarsState();
repaintDisplay(false);
}
void DisplayModel::scrollXBy(int dx)
{
DBG_OUT("DisplayModel::scrollXBy(dx=%d)\n", dx);
double maxX = _canvasSize.dx() - drawAreaSize.dx();
assert(maxX >= 0.0);
double prevX = areaOffset.x;
double newX = prevX + (double)dx;
if (newX < 0.0)
newX = 0.0;
else
if (newX > maxX)
newX = maxX;
if (newX == prevX)
return;
scrollXTo((int)newX);
}
void DisplayModel::scrollYTo(int yOff)
{
DBG_OUT("DisplayModel::scrollYTo(yOff=%d)\n", yOff);
int currPageNo = currentPageNo();
areaOffset.y = (double)yOff;
recalcVisibleParts();
recalcLinksCanvasPos();
renderVisibleParts();
int newPageNo = currentPageNo();
if (newPageNo != currPageNo)
pageChanged();
repaintDisplay(false);
}
/* Scroll the doc in y-axis by 'dy'. If 'changePage' is TRUE, automatically
switch to prev/next page in non-continuous mode if we scroll past the edges
of current page */
void DisplayModel::scrollYBy(int dy, bool changePage)
{
PdfPageInfo * pageInfo;
int currYOff = (int)areaOffset.y;
int newPageNo;
int currPageNo;
DBG_OUT("DisplayModel::scrollYBy(dy=%d, changePage=%d)\n", dy, (int)changePage);
assert(0 != dy);
if (0 == dy) return;
int newYOff = currYOff;
if (!displayModeContinuous(displayMode()) && changePage) {
if ((dy < 0) && (0 == currYOff)) {
if (_startPage > 1) {
newPageNo = _startPage-1;
assert(validPageNo(newPageNo));
pageInfo = getPageInfo(newPageNo);
newYOff = (int)pageInfo->currDy - drawAreaSize.dyI();
if (newYOff < 0)
newYOff = 0; /* TODO: center instead? */
goToPrevPage(newYOff);
return;
}
}
/* see if we have to change page when scrolling forward */
if ((dy > 0) && (_startPage < pageCount())) {
if ((int)areaOffset.y + drawAreaSize.dyI() >= _canvasSize.dyI()) {
goToNextPage(0);
return;
}
}
}
newYOff += dy;
if (newYOff < 0) {
newYOff = 0;
} else if (newYOff + drawAreaSize.dyI() > _canvasSize.dyI()) {
newYOff = _canvasSize.dyI() - drawAreaSize.dyI();
}
if (newYOff == currYOff)
return;
currPageNo = currentPageNo();
areaOffset.y = (double)newYOff;
recalcVisibleParts();
recalcLinksCanvasPos();
renderVisibleParts();
setScrollbarsState();
newPageNo = currentPageNo();
if (newPageNo != currPageNo)
pageChanged();
repaintDisplay(false);
}
void DisplayModel::scrollYByAreaDy(bool forward, bool changePage)
{
int toScroll = drawAreaSize.dyI();
if (forward)
scrollYBy(toScroll, changePage);
else
scrollYBy(-toScroll, changePage);
}
void DisplayModel::zoomTo(double zoomVirtual)
{
//DBG_OUT("DisplayModel::zoomTo() zoomVirtual=%.6f\n", _zoomVirtual);
int currPageNo = currentPageNo();
relayout(zoomVirtual, rotation());
goToPage(currPageNo, 0);
}
void DisplayModel::zoomBy(double zoomFactor)
{
double newZoom = _zoomReal * zoomFactor;
//DBG_OUT("DisplayModel::zoomBy() zoomReal=%.6f, zoomFactor=%.2f, newZoom=%.2f\n", dm->zoomReal, zoomFactor, newZoom);
if (newZoom > ZOOM_MAX)
return;
if (newZoom < ZOOM_MIN)
return;
zoomTo(newZoom);
}
void DisplayModel::rotateBy(int newRotation)
{
normalizeRotation(&newRotation);
assert(0 != newRotation);
if (0 == newRotation)
return;
assert(validRotation(newRotation));
if (!validRotation(newRotation))
return;
newRotation += rotation();
normalizeRotation(&newRotation);
assert(validRotation(newRotation));
if (!validRotation(newRotation))
return;
int currPageNo = currentPageNo();
relayout(zoomVirtual(), newRotation);
goToPage(currPageNo, 0);
}
void DisplayModel::showNormalCursor(void)
{
SetCursor(LoadCursor(NULL, IDC_ARROW));
}
void DisplayModel::showBusyCursor(void)
{
// TODO: can I set it per-window only?
SetCursor(LoadCursor(NULL, IDC_WAIT));
}
static inline void InitCacheMutext() {
if (!cacheMutexInitialized) {
InitializeCriticalSection(&cacheMutex);
cacheMutexInitialized = 1;
}
}
void LockCache(void) {
InitCacheMutext();
EnterCriticalSection(&cacheMutex);
}
void UnlockCache(void) {
LeaveCriticalSection(&cacheMutex);
}
static void BitmapCacheEntry_Free(BitmapCacheEntry *entry) {
assert(entry);
if (!entry) return;
delete entry->bitmap;
free((void*)entry);
}
void BitmapCache_FreeAll(void) {
LockCache();
for (int i=0; i < gBitmapCacheCount; i++) {
BitmapCacheEntry_Free(gBitmapCache[i]);
gBitmapCache[i] = NULL;
}
gBitmapCacheCount = 0;
UnlockCache();
}
/* Free all bitmaps in the cache that are not visible. Returns true if freed
at least one item. */
bool BitmapCache_FreeNotVisible(void) {
LockCache();
bool freedSomething = false;
int cacheCount = gBitmapCacheCount;
int curPos = 0;
for (int i = 0; i < cacheCount; i++) {
BitmapCacheEntry* entry = gBitmapCache[i];
bool shouldFree = !entry->dm->pageVisibleNearby(entry->pageNo);
if (shouldFree) {
if (!freedSomething)
DBG_OUT("BitmapCache_FreeNotVisible() ");
DBG_OUT("freed %d ", entry->pageNo);
freedSomething = true;
BitmapCacheEntry_Free(gBitmapCache[i]);
gBitmapCache[i] = NULL;
--gBitmapCacheCount;
}
if (curPos != i)
gBitmapCache[curPos] = gBitmapCache[i];
if (!shouldFree)
++curPos;
}
UnlockCache();
if (freedSomething)
DBG_OUT("\n");
return freedSomething;
}
static bool BitmapCache_FreePage(DisplayModel *dm, int pageNo) {
LockCache();
int cacheCount = gBitmapCacheCount;
bool freedSomething = false;
int curPos = 0;
for (int i = 0; i < cacheCount; i++) {
bool shouldFree = (gBitmapCache[i]->dm == dm) && (gBitmapCache[i]->pageNo == pageNo);
if (shouldFree) {
if (!freedSomething)
DBG_OUT("BitmapCache_FreePage() ");
DBG_OUT("freed %d ", gBitmapCache[i]->pageNo);
freedSomething = true;
BitmapCacheEntry_Free(gBitmapCache[i]);
gBitmapCache[i] = NULL;
--gBitmapCacheCount;
}
if (curPos != i)
gBitmapCache[curPos] = gBitmapCache[i];
if (!shouldFree)
++curPos;
}
UnlockCache();
if (freedSomething)
DBG_OUT("\n");
return freedSomething;
}
/* Free all bitmaps cached for a given <dm>. Returns TRUE if freed
at least one item. */
bool BitmapCache_FreeForDisplayModel(DisplayModel *dm) {
LockCache();
int cacheCount = gBitmapCacheCount;
bool freedSomething = false;
int curPos = 0;
for (int i = 0; i < cacheCount; i++) {
bool shouldFree = (gBitmapCache[i]->dm == dm);
if (shouldFree) {
if (!freedSomething)
DBG_OUT("BitmapCache_FreeForDisplayModel() ");
DBG_OUT("freed %d ", gBitmapCache[i]->pageNo);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -