mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-08-31 04:13:37 +00:00
3 linking issues left: * _wstr_dup * _WinMain@16 * _FT_Get_X11_Font_Format svn path=/trunk/; revision=29313
872 lines
26 KiB
C++
872 lines
26 KiB
C++
/* Copyright Krzysztof Kowalczyk 2006-2007
|
|
License: GPLv2 */
|
|
#include "DisplayModelSplash.h"
|
|
#include "str_util.h"
|
|
|
|
#include "GlobalParams.h"
|
|
#include "GooMutex.h"
|
|
#include "GooString.h"
|
|
#include "Link.h"
|
|
#include "Object.h" /* must be included before SplashOutputDev.h because of sloppiness in SplashOutputDev.h */
|
|
#include "PDFDoc.h"
|
|
#include "SplashBitmap.h"
|
|
#include "SplashOutputDev.h"
|
|
#include "TextOutputDev.h"
|
|
|
|
#ifdef __GNUC__
|
|
#include <ctype.h>
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h> /* malloc etc. */
|
|
|
|
#define ACTION_NEXT_PAGE "NextPage"
|
|
#define ACTION_PREV_PAGE "PrevPage"
|
|
#define ACTION_FIRST_PAGE "FirstPage"
|
|
#define ACTION_LAST_PAGE "LastPage"
|
|
|
|
void CDECL error(int pos, char *msg, ...) {
|
|
va_list args;
|
|
char buf[4096], *p = buf;
|
|
|
|
// NB: this can be called before the globalParams object is created
|
|
if (globalParams && globalParams->getErrQuiet()) {
|
|
return;
|
|
}
|
|
|
|
if (pos >= 0) {
|
|
p += _snprintf(p, sizeof(buf)-1, "Error (%d): ", pos);
|
|
*p = '\0';
|
|
OutputDebugString((TCHAR*)p); /* @note: TCHAR* cast */
|
|
} else {
|
|
OutputDebugString(TEXT("Error: ")); /* @note: TEXT() cast */
|
|
}
|
|
|
|
p = buf;
|
|
va_start(args, msg);
|
|
p += _vsnprintf(p, sizeof(buf) - 1, msg, args);
|
|
while ( p > buf && isspace(p[-1]) )
|
|
*--p = '\0';
|
|
*p++ = '\r';
|
|
*p++ = '\n';
|
|
*p = '\0';
|
|
OutputDebugString((TCHAR*)buf); /* @note: TCHAR* cast */
|
|
va_end(args);
|
|
}
|
|
|
|
#if 0
|
|
static Links *GetLinksForPage(PDFDoc *doc, int pageNo)
|
|
{
|
|
Object obj;
|
|
Catalog *catalog = doc->getCatalog();
|
|
Page *page = catalog->getPage(pageNo);
|
|
Links *links = new Links(page->getAnnots(&obj), catalog->getBaseURI());
|
|
obj.free();
|
|
return links;
|
|
}
|
|
|
|
static int GetPageRotation(PDFDoc *doc, int pageNo)
|
|
{
|
|
Catalog *catalog = doc->getCatalog();
|
|
Page *page = catalog->getPage(pageNo);
|
|
int rotation = page->getRotate();
|
|
return rotation;
|
|
}
|
|
|
|
static const char * GetLinkActionKindName(LinkActionKind kind) {
|
|
switch (kind) {
|
|
case (actionGoTo):
|
|
return "actionGoTo";
|
|
case actionGoToR:
|
|
return "actionGoToR";
|
|
case actionLaunch:
|
|
return "actionLaunch";
|
|
case actionURI:
|
|
return "actionURI";
|
|
case actionNamed:
|
|
return "actionNamed";
|
|
case actionMovie:
|
|
return "actionMovie";
|
|
case actionUnknown:
|
|
return "actionUnknown";
|
|
default:
|
|
assert(0);
|
|
return "unknown action";
|
|
}
|
|
}
|
|
|
|
static void DumpLinks(DisplayModelSplash *dm, PDFDoc *doc)
|
|
{
|
|
Links * links = NULL;
|
|
int pagesCount, linkCount;
|
|
int pageRotation;
|
|
Link * link;
|
|
LinkURI * linkUri;
|
|
GBool upsideDown;
|
|
|
|
DBG_OUT("DumpLinks() started\n");
|
|
if (!doc)
|
|
return;
|
|
|
|
upsideDown = dm->outputDevice->upsideDown();
|
|
pagesCount = doc->getNumPages();
|
|
for (int pageNo = 1; pageNo < pagesCount; ++pageNo) {
|
|
delete links;
|
|
links = GetLinksForPage(doc, pageNo);
|
|
if (!links)
|
|
goto Exit;
|
|
linkCount = links->getNumLinks();
|
|
if (linkCount > 0)
|
|
DBG_OUT(" :page %d linkCount = %d\n", pageNo, linkCount);
|
|
pageRotation = GetPageRotation(doc, pageNo);
|
|
for (int i=0; i<linkCount; i++) {
|
|
link = links->getLink(i);
|
|
LinkAction *action = link->getAction();
|
|
LinkActionKind actionKind = action->getKind();
|
|
double xs, ys, xe, ye;
|
|
link->getRect(&xs, &ys, &xe, &ye);
|
|
DBG_OUT( " link %d: pageRotation=%d upsideDown=%d, action=%d (%s), (xs=%d,ys=%d - xe=%d,ye=%d)\n", i,
|
|
pageRotation, (int)upsideDown,
|
|
(int)actionKind,
|
|
GetLinkActionKindName(actionKind),
|
|
(int)xs, (int)ys, (int)xe, (int)ye);
|
|
if (actionURI == actionKind) {
|
|
linkUri = (LinkURI*)action;
|
|
DBG_OUT(" uri=%s\n", linkUri->getURI()->getCString());
|
|
}
|
|
}
|
|
}
|
|
Exit:
|
|
delete links;
|
|
DBG_OUT("DumpLinks() finished\n");
|
|
}
|
|
#endif
|
|
|
|
static void TransfromUpsideDown(DisplayModelSplash *dm, int pageNo, double *y1, double *y2)
|
|
{
|
|
assert(dm);
|
|
if (!dm) return;
|
|
|
|
PdfPageInfo *pageInfo = dm->getPageInfo(pageNo);
|
|
double dy = pageInfo->pageDy;
|
|
assert(*y1 <= dy);
|
|
*y1 = dy - *y1;
|
|
assert(*y2 <= dy);
|
|
*y2 = dy - *y2;
|
|
}
|
|
|
|
DisplayModelSplash::DisplayModelSplash(DisplayMode displayMode) :
|
|
DisplayModel(displayMode)
|
|
{
|
|
_pdfEngine = new PdfEnginePoppler();
|
|
}
|
|
|
|
TextPage *DisplayModelSplash::GetTextPage(int pageNo)
|
|
{
|
|
assert(pdfDoc);
|
|
if (!pdfDoc) return NULL;
|
|
assert(validPageNo(pageNo));
|
|
assert(pagesInfo);
|
|
if (!pagesInfo) return NULL;
|
|
|
|
PdfPageInfo * pdfPageInfo = &(pagesInfo[pageNo-1]);
|
|
if (!pdfPageInfo->textPage) {
|
|
TextOutputDev *textOut = new TextOutputDev(NULL, gTrue, gFalse, gFalse);
|
|
if (!textOut)
|
|
return NULL;
|
|
if (!textOut->isOk()) {
|
|
delete textOut;
|
|
return NULL;
|
|
}
|
|
pdfDoc->displayPage(textOut, pageNo, 72, 72, 0, gFalse, gTrue, gFalse);
|
|
pdfPageInfo->textPage = textOut->takeText();
|
|
delete textOut;
|
|
}
|
|
return pdfPageInfo->textPage;
|
|
}
|
|
|
|
void DisplayModelSplash::FreeLinks(void)
|
|
{
|
|
for (int pageNo = 1; pageNo <= pageCount(); ++pageNo) {
|
|
delete pagesInfo[pageNo-1].links;
|
|
pagesInfo[pageNo-1].links = NULL;
|
|
}
|
|
}
|
|
|
|
void DisplayModelSplash::FreeTextPages()
|
|
{
|
|
for (int pageNo = 1; pageNo <= pageCount(); ++pageNo) {
|
|
delete pagesInfo[pageNo-1].textPage;
|
|
pagesInfo[pageNo-1].textPage = NULL;
|
|
}
|
|
}
|
|
|
|
DisplayModelSplash::~DisplayModelSplash()
|
|
{
|
|
RenderQueue_RemoveForDisplayModel(this);
|
|
BitmapCache_FreeForDisplayModel(this);
|
|
cancelRenderingForDisplayModel(this);
|
|
FreeLinks();
|
|
FreeTextPages();
|
|
|
|
delete searchState.str;
|
|
delete searchState.strU;
|
|
free((void*)_links);
|
|
free((void*)pagesInfo);
|
|
}
|
|
|
|
/* Map point <x>/<y> on the page <pageNo> to point on the screen. */
|
|
void DisplayModelSplash::cvtUserToScreen(int pageNo, double *x, double *y)
|
|
{
|
|
double xTmp = *x;
|
|
double yTmp = *y;
|
|
double ctm[6];
|
|
double dpi;
|
|
int rotationTmp;
|
|
|
|
assert(pdfDoc);
|
|
if (!pdfDoc) return;
|
|
|
|
dpi = (double)PDF_FILE_DPI * _zoomReal * 0.01;
|
|
rotationTmp = rotation();
|
|
normalizeRotation(&rotationTmp);
|
|
SplashOutputDev *outputDev = pdfEnginePoppler()->outputDevice();
|
|
pdfDoc->getCatalog()->getPage(pageNo)->getDefaultCTM(ctm, dpi, dpi, rotationTmp, outputDev->upsideDown());
|
|
|
|
PdfPageInfo *pageInfo = getPageInfo(pageNo);
|
|
*x = ctm[0] * xTmp + ctm[2] * yTmp + ctm[4] + 0.5 + pageInfo->screenX - pageInfo->bitmapX;
|
|
*y = ctm[1] * xTmp + ctm[3] * yTmp + ctm[5] + 0.5 + pageInfo->screenY - pageInfo->bitmapY;
|
|
}
|
|
|
|
/* Map point <x>/<y> on screen to user coordinates <pageNo>, <x>, <y>.
|
|
* If it's not possible (i.e. given point is out of any page),
|
|
* returns CONVERSION_IMPOSSIBLE as a <pageNo> */
|
|
void DisplayModelSplash::cvtScreenToUser(int *pageNo, double *x, double *y)
|
|
{
|
|
double xTmp = *x;
|
|
double yTmp = *y;
|
|
double ctm[6], invCtm[6];
|
|
double dpi, invDpi;
|
|
int rotationTmp, invRotationTmp;
|
|
|
|
assert(pdfDoc);
|
|
if (!pdfDoc) return;
|
|
|
|
/* find page number of point <x>/<y> */
|
|
*pageNo = getPageNoByPoint (*x, *y);
|
|
|
|
if (*pageNo == POINT_OUT_OF_PAGE) return;
|
|
|
|
dpi = (double)PDF_FILE_DPI * _zoomReal * 0.01;
|
|
invDpi = (double)PDF_FILE_DPI * 1/(_zoomReal * 0.01);
|
|
rotationTmp = rotation();
|
|
normalizeRotation(&rotationTmp);
|
|
invRotationTmp = rotationTmp == 0 ? 0 : 360 - rotationTmp;
|
|
SplashOutputDev *outputDev = pdfEnginePoppler()->outputDevice();
|
|
pdfDoc->getCatalog()->getPage(*pageNo)->getDefaultCTM(ctm, dpi, dpi, rotationTmp, outputDev->upsideDown());
|
|
pdfDoc->getCatalog()->getPage(*pageNo)->getDefaultCTM(invCtm, invDpi, invDpi, invRotationTmp, outputDev->upsideDown());
|
|
|
|
//DBG_OUT("Rotation = %d\n", rotationTmp);
|
|
|
|
int sign;
|
|
if (rotationTmp == 0 || rotationTmp == 180)
|
|
sign = 1;
|
|
else //rotationTmp == 90 || rotationTmp == 270
|
|
sign = -1;
|
|
|
|
PdfPageInfo *pageInfo = getPageInfo(*pageNo);
|
|
*x = invCtm[0] * sign*(xTmp - ctm[4] - 0.5 - pageInfo->screenX + pageInfo->bitmapX)
|
|
+ invCtm[2] * sign*(yTmp - ctm[5] - 0.5 - pageInfo->screenY + pageInfo->bitmapY);
|
|
*y = invCtm[1] * sign*(xTmp - ctm[4] - 0.5 - pageInfo->screenX + pageInfo->bitmapX)
|
|
+ invCtm[3] * sign*(yTmp - ctm[5] - 0.5 - pageInfo->screenY + pageInfo->bitmapY);
|
|
}
|
|
|
|
/* Given <region> (in user coordinates ) on page <pageNo>, return a text in that
|
|
region or NULL if no text */
|
|
int DisplayModelSplash::getTextInRegion(int pageNo, RectD *region, unsigned short *buf, int buflen)
|
|
{
|
|
GooString * txt = NULL;
|
|
double xMin, yMin, xMax, yMax;
|
|
GBool useMediaBox = gFalse;
|
|
GBool crop = gTrue;
|
|
GBool doLinks = gFalse;
|
|
PDFRectangle selection;
|
|
PdfPageInfo * pageInfo;
|
|
|
|
assert(pdfDoc);
|
|
if (!pdfDoc) return NULL;
|
|
|
|
double dpi = (double)PDF_FILE_DPI;
|
|
|
|
/* TODO: cache textOut? */
|
|
TextOutputDev *textOut = new TextOutputDev(NULL, gTrue, gFalse, gFalse);
|
|
if (!textOut->isOk()) {
|
|
delete textOut;
|
|
return 0;
|
|
}
|
|
/* TODO: make sure we're not doing background threading */
|
|
pageInfo = getPageInfo(pageNo);
|
|
xMin = region->x;
|
|
yMin = pageInfo->pageDy - (region->y + region->dy);//since coordinates are upside-dwon
|
|
xMax = xMin + region->dx;
|
|
yMax = yMin + region->dy;
|
|
pdfDoc->displayPage(textOut, pageNo, dpi, dpi, 0, useMediaBox, crop, doLinks);
|
|
|
|
txt = textOut->getText(xMin, yMin, xMax, yMax);
|
|
|
|
int length = 0;
|
|
if (txt && (txt->getLength() > 0)) {
|
|
//DBG_OUT("DisplayModelSplash::getTextInRegion() found text '%s' on pageNo=%d, (x=%d, y=%d), (dx=%d, dy=%d)\n",
|
|
//txt->getCString(), pageNo, (int)region->x, (int)region->y, (int)region->dx, (int)region->dy);
|
|
length = min (txt->getLength(), buflen);
|
|
for (int i = 0; i < length; i++) {
|
|
buf[i] = (unsigned short)(unsigned char)txt->getChar(i);
|
|
//DBG_OUT("Char: %c : %d; ushort: %hu\n", txt->getChar(i), (int)(unsigned char)txt->getChar(i), (unsigned short)(unsigned char)txt->getChar(i));
|
|
}
|
|
} else {
|
|
DBG_OUT("DisplayModelSplash::getTextInRegion() didn't find text on pageNo=%d, (x=%d, y=%d), (dx=%d, dy=%d)\n",
|
|
pageNo, (int)region->x, (int)region->y, (int)region->dx, (int)region->dy);
|
|
if (txt) {
|
|
delete txt;
|
|
txt = NULL;
|
|
}
|
|
}
|
|
delete textOut;
|
|
return length;
|
|
}
|
|
|
|
/* Create display info from a 'fileName' etc. 'pdfDoc' will be owned by DisplayInfo
|
|
from now on, so the caller should not delete it itself. Not very good but
|
|
alternative is worse.
|
|
*/
|
|
DisplayModelSplash *DisplayModelSplash_CreateFromFileName(
|
|
const char *fileName,
|
|
SizeD totalDrawAreaSize,
|
|
int scrollbarXDy, int scrollbarYDx,
|
|
DisplayMode displayMode, int startPage,
|
|
WindowInfo *win)
|
|
{
|
|
DisplayModelSplash * dm = new DisplayModelSplash(displayMode);
|
|
if (!dm)
|
|
goto Error;
|
|
|
|
if (!dm->load(fileName, startPage, win))
|
|
goto Error;
|
|
|
|
dm->setScrollbarsSize(scrollbarXDy, scrollbarYDx);
|
|
dm->setTotalDrawAreaSize(totalDrawAreaSize);
|
|
|
|
dm->pdfDoc = dm->pdfEnginePoppler()->pdfDoc();
|
|
|
|
DBG_OUT("DisplayModelSplash::CreateFromPdfDoc() pageCount = %d, startPage=%d, displayMode=%d\n",
|
|
dm->pageCount(), (int)dm->startPage(), (int)displayMode);
|
|
return dm;
|
|
Error:
|
|
delete dm;
|
|
return NULL;
|
|
}
|
|
|
|
/* Make sure that search hit is visible on the screen */
|
|
void DisplayModelSplash::EnsureSearchHitVisible()
|
|
{
|
|
int pageNo;
|
|
int yStart, yEnd;
|
|
int xStart, xEnd;
|
|
int xNewPos, yNewPos;
|
|
BOOL needScroll = FALSE;
|
|
|
|
pageNo = searchHitPageNo;
|
|
yStart = searchHitRectCanvas.y;
|
|
yEnd = yStart + searchHitRectCanvas.dy + 24; /* TODO: 24 to account for the find ui bar */
|
|
|
|
xStart = searchHitRectCanvas.x;
|
|
xEnd = xStart + searchHitRectCanvas.dx;
|
|
|
|
//DBG_OUT("DisplayModelSplash::EnsureSearchHitVisible(), (yStart=%d, yEnd=%d)\n", yStart, yEnd);
|
|
|
|
// TODO: this logic is flawed
|
|
yNewPos = (int)areaOffset.y;
|
|
|
|
if (yStart < (int)areaOffset.y) {
|
|
yNewPos -= ((int)areaOffset.y - yStart);
|
|
needScroll = TRUE;
|
|
}
|
|
if (yEnd > (int)(areaOffset.y + drawAreaSize.dy())) {
|
|
yNewPos -= ((int)(areaOffset.y + drawAreaSize.dy()) - yEnd);
|
|
needScroll = TRUE;
|
|
}
|
|
|
|
xNewPos = (int)areaOffset.x;
|
|
if (xStart < (int)areaOffset.x) {
|
|
xNewPos -= ((int)areaOffset.x - xStart);
|
|
needScroll = TRUE;
|
|
}
|
|
if (xEnd > (int)(areaOffset.x + drawAreaSize.dx())) {
|
|
xNewPos -= ((int)(areaOffset.x + drawAreaSize.dx()) - xEnd);
|
|
needScroll = TRUE;
|
|
}
|
|
|
|
PdfPageInfo *pageInfo = getPageInfo(pageNo);
|
|
bool pageRendered = BitmapCache_Exists(this, pageNo, _zoomReal, _rotation);
|
|
if (!pageInfo->visible || needScroll || !pageRendered)
|
|
goToPage(pageNo, yNewPos, xNewPos);
|
|
}
|
|
|
|
/* Recalcualte 'linkCount' and 'links' out of 'pagesInfo' data.
|
|
Should only be called if link data has chagned in 'pagesInfo'. */
|
|
void DisplayModelSplash::RecalcLinks(void)
|
|
{
|
|
int pageNo;
|
|
int i;
|
|
PdfPageInfo * pageInfo;
|
|
Link * popplerLink;
|
|
PdfLink * currPdfLink;
|
|
int currPdfLinkNo;
|
|
double xs, ys, xe, ye;
|
|
|
|
DBG_OUT("DisplayModelSplash::RecalcLinks()\n");
|
|
|
|
free((void*)_links);
|
|
_links = NULL;
|
|
_linkCount = 0;
|
|
|
|
/* calculate number of links */
|
|
for (pageNo = 1; pageNo <= pageCount(); ++pageNo) {
|
|
pageInfo = getPageInfo(pageNo);
|
|
if (!pageInfo->links)
|
|
continue;
|
|
_linkCount += pageInfo->links->getNumLinks();
|
|
}
|
|
|
|
assert(_linkCount > 0);
|
|
_links = (PdfLink*)malloc(_linkCount * sizeof(PdfLink));
|
|
if (!_links)
|
|
return;
|
|
|
|
/* build links info */
|
|
currPdfLinkNo = 0;
|
|
for (pageNo = 1; pageNo <= pageCount(); ++pageNo) {
|
|
pageInfo = getPageInfo(pageNo);
|
|
if (!pageInfo->links)
|
|
continue;
|
|
for (i = 0; i < pageInfo->links->getNumLinks(); i++) {
|
|
currPdfLink = link(currPdfLinkNo);
|
|
popplerLink = pageInfo->links->getLink(i);
|
|
popplerLink->getRect(&xs, &ys, &xe, &ye);
|
|
/* note: different param order than getRect() is intentional */
|
|
RectD_FromXY(&currPdfLink->rectPage, xs, xe, ys, ye);
|
|
assert(currPdfLink->rectPage.dx >= 0);
|
|
assert(currPdfLink->rectPage.dy >= 0);
|
|
currPdfLink->pageNo = pageNo;
|
|
currPdfLink->link = popplerLink;
|
|
++currPdfLinkNo;
|
|
}
|
|
}
|
|
assert(_linkCount == currPdfLinkNo);
|
|
recalcLinksCanvasPos();
|
|
DBG_OUT("DisplayModelSplash::RecalcLinks() new link count: %d\n", _linkCount);
|
|
}
|
|
|
|
void DisplayModelSplash::GoToDest(LinkDest *linkDest)
|
|
{
|
|
Ref pageRef;
|
|
int newPage = INVALID_PAGE_NO;
|
|
int left, top;
|
|
int scrollY = 0;
|
|
|
|
assert(linkDest);
|
|
if (!linkDest) return;
|
|
|
|
if (linkDest->isPageRef()) {
|
|
pageRef = linkDest->getPageRef();
|
|
newPage = pdfDoc->findPage(pageRef.num, pageRef.gen);
|
|
} else {
|
|
newPage = linkDest->getPageNum();
|
|
}
|
|
|
|
if (newPage <= 0 || newPage > pdfDoc->getNumPages()) {
|
|
newPage = 1;
|
|
}
|
|
|
|
left = (int)linkDest->getLeft();
|
|
top = (int)linkDest->getTop();
|
|
/* TODO: convert left/top coordinates to window space */
|
|
|
|
/* TODO: this logic needs to be implemented */
|
|
switch (linkDest->getKind()) {
|
|
case destXYZ:
|
|
break;
|
|
case destFitR:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
goToPage( newPage, scrollY);
|
|
}
|
|
|
|
void DisplayModelSplash::GoToNamedDest(UGooString *dest)
|
|
{
|
|
LinkDest *d;
|
|
|
|
assert(dest);
|
|
if (!dest) return;
|
|
|
|
d = pdfDoc->findDest(dest);
|
|
assert(d);
|
|
if (!d) return;
|
|
GoToDest(d);
|
|
delete d;
|
|
}
|
|
|
|
void DisplayModelSplash::handleLinkGoTo(LinkGoTo *linkGoTo)
|
|
{
|
|
LinkDest * linkDest;
|
|
UGooString * linkNamedDest;
|
|
|
|
assert(linkGoTo);
|
|
if (!linkGoTo) return;
|
|
|
|
linkDest = linkGoTo->getDest();
|
|
linkNamedDest = linkGoTo->getNamedDest();
|
|
if (linkDest) {
|
|
assert(!linkNamedDest);
|
|
GoToDest(linkDest);
|
|
} else {
|
|
assert(linkNamedDest);
|
|
GoToNamedDest(linkNamedDest);
|
|
}
|
|
}
|
|
|
|
void DisplayModelSplash::handleLinkGoToR(LinkGoToR *linkGoToR)
|
|
{
|
|
LinkDest * linkDest;
|
|
UGooString * linkNamedDest;
|
|
GooString * fileName;
|
|
|
|
assert(linkGoToR);
|
|
if (!linkGoToR) return;
|
|
|
|
linkDest = linkGoToR->getDest();
|
|
linkNamedDest = linkGoToR->getNamedDest();
|
|
|
|
fileName = linkGoToR->getFileName();
|
|
/* TODO: see if a file exists, if not show a dialog box. If exists,
|
|
load it and go to a destination in this file.
|
|
Should also search currently opened files. */
|
|
/* Test file: C:\kjk\test_pdfs\pda\palm\dev tools\sdk\user_interface.pdf, page 633 */
|
|
}
|
|
|
|
void DisplayModelSplash::handleLinkURI(LinkURI *linkURI)
|
|
{
|
|
const char *uri;
|
|
|
|
uri = linkURI->getURI()->getCString();
|
|
if (str_empty(uri))
|
|
return;
|
|
//LaunchBrowser(uri);
|
|
MessageBox(NULL,(TCHAR*)uri, TEXT("Extern link blocked"), 0); /* @note: work-around to solve linking issue */
|
|
|
|
}
|
|
|
|
void DisplayModelSplash::handleLinkLaunch(LinkLaunch* linkLaunch)
|
|
{
|
|
assert(linkLaunch);
|
|
if (!linkLaunch) return;
|
|
|
|
/* Launching means executing another application. It's not supported
|
|
due to security and portability reasons */
|
|
}
|
|
|
|
void DisplayModelSplash::handleLinkNamed(LinkNamed *linkNamed)
|
|
{
|
|
GooString * name;
|
|
char * nameTxt;
|
|
|
|
assert(linkNamed);
|
|
if (!linkNamed) return;
|
|
|
|
name = linkNamed->getName();
|
|
if (!name)
|
|
return;
|
|
nameTxt = name->getCString();
|
|
if (str_eq(ACTION_NEXT_PAGE, nameTxt)) {
|
|
goToNextPage(0);
|
|
} else if (str_eq(ACTION_PREV_PAGE, nameTxt)) {
|
|
goToPrevPage(0);
|
|
} else if (str_eq(ACTION_LAST_PAGE, nameTxt)) {
|
|
goToLastPage();
|
|
} else if (str_eq(ACTION_FIRST_PAGE, nameTxt)) {
|
|
goToFirstPage();
|
|
} else {
|
|
/* not supporting: "GoBack", "GoForward", "Quit" */
|
|
}
|
|
}
|
|
|
|
/* Return TRUE if can go to previous page (i.e. is not on the first page) */
|
|
BOOL DisplayModelSplash::CanGoToPrevPage(void)
|
|
{
|
|
if (1 == currentPageNo())
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
/* Return TRUE if can go to next page (i.e. doesn't already show last page) */
|
|
BOOL DisplayModelSplash::CanGoToNextPage(void)
|
|
{
|
|
if (displayModeFacing(displayMode())) {
|
|
if (currentPageNo()+1 >= pageCount())
|
|
return FALSE;
|
|
} else {
|
|
if (pageCount() == currentPageNo())
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
void DisplayModelSplash::FindInit(int startPageNo)
|
|
{
|
|
assert(validPageNo(startPageNo));
|
|
searchState.searchState = eSsNone;
|
|
searchState.wrapped = FALSE;
|
|
searchState.startPage = startPageNo;
|
|
}
|
|
|
|
BOOL DisplayModelSplash::FindNextBackward(void)
|
|
{
|
|
GBool startAtTop, stopAtBottom;
|
|
GBool startAtLast, stopAtLast;
|
|
GBool caseSensitive, backward;
|
|
GBool found;
|
|
double xs, ys, xe, ye;
|
|
UGooString * strU;
|
|
TextPage * textPage;
|
|
int pageNo;
|
|
RectD hitRect;
|
|
|
|
DBG_OUT("DisplayModelSplash::FindNextBackward()\n");
|
|
|
|
// previous search didn't find it, so there's no point looking again
|
|
if (eSsNotFound == searchState.searchState)
|
|
return FALSE;
|
|
|
|
// search string was not entered
|
|
if (0 == searchState.str->getLength())
|
|
return FALSE;
|
|
|
|
// searching uses the same code as rendering and that code is not
|
|
// thread safe, so we have to cancel all background rendering
|
|
cancelRenderingForDisplayModel(this);
|
|
|
|
showBusyCursor();
|
|
|
|
backward = gTrue;
|
|
caseSensitive = searchState.caseSensitive;
|
|
strU = searchState.strU;
|
|
|
|
if (eSsNone == searchState.searchState) {
|
|
// starting a new search backward
|
|
searchState.currPage = searchState.startPage;
|
|
assert(FALSE == searchState.wrapped);
|
|
startAtLast = gFalse;
|
|
startAtTop = gFalse;
|
|
} else {
|
|
// continuing previous search backward or forward
|
|
startAtLast = gTrue;
|
|
startAtTop = gFalse;
|
|
}
|
|
stopAtBottom = gTrue;
|
|
stopAtLast = gFalse;
|
|
|
|
if ((eSsFoundNext == searchState.searchState)
|
|
|| (eSsFoundNextWrapped == searchState.searchState)) {
|
|
searchState.wrapped = FALSE;
|
|
}
|
|
|
|
for (;;) {
|
|
pageNo = searchState.currPage;
|
|
textPage = GetTextPage(pageNo);
|
|
|
|
DBG_OUT(" search backward for %s, case sensitive=%d, page=%d\n", searchState.str->getCString(), caseSensitive, pageNo);
|
|
found = textPage->findText(strU->unicode(), strU->getLength(), startAtTop, stopAtBottom,
|
|
startAtLast, stopAtLast, caseSensitive, backward,
|
|
&xs, &ys, &xe, &ye);
|
|
|
|
if (found) {
|
|
DBG_OUT(" found '%s' on page %d at pos (%.2f, %.2f)-(%.2f,%.2f)\n",
|
|
searchState.str->getCString(), pageNo, xs, ys, xe, ye);
|
|
|
|
TransfromUpsideDown(this, pageNo, &ys, &ye);
|
|
RectD_FromXY(&hitRect, xs, xe, ys, ye);
|
|
if (searchState.wrapped) {
|
|
searchState.wrapped = FALSE;
|
|
searchState.searchState = eSsFoundPrevWrapped;
|
|
} else {
|
|
searchState.searchState = eSsFoundPrev;
|
|
}
|
|
setSearchHit(pageNo, &hitRect);
|
|
EnsureSearchHitVisible();
|
|
goto Exit;
|
|
}
|
|
|
|
if (1 == pageNo) {
|
|
DBG_OUT(" wrapped\n");
|
|
searchState.wrapped = TRUE;
|
|
searchState.currPage = pageCount();
|
|
} else
|
|
searchState.currPage = pageNo - 1;
|
|
|
|
// moved to another page, so starting from the top
|
|
startAtTop = gTrue;
|
|
startAtLast = gFalse;
|
|
|
|
if (searchState.wrapped &&
|
|
(searchState.currPage == searchState.startPage) &&
|
|
(eSsNone == searchState.searchState) ) {
|
|
searchState.searchState = eSsNotFound;
|
|
clearSearchHit();
|
|
goto Exit;
|
|
}
|
|
}
|
|
Exit:
|
|
showNormalCursor();
|
|
return found;
|
|
}
|
|
|
|
BOOL DisplayModelSplash::FindNextForward(void)
|
|
{
|
|
GBool startAtTop, stopAtBottom;
|
|
GBool startAtLast, stopAtLast;
|
|
GBool caseSensitive, backward;
|
|
GBool found;
|
|
double xs, ys, xe, ye;
|
|
UGooString * strU;
|
|
TextPage * textPage;
|
|
int pageNo;
|
|
RectD hitRect;
|
|
|
|
DBG_OUT("DisplayModelSplash::FindNextForward()\n");
|
|
|
|
// previous search didn't find it, so there's no point looking again
|
|
if (eSsNotFound == searchState.searchState)
|
|
return FALSE;
|
|
|
|
// search string was not entered
|
|
if (0 == searchState.str->getLength())
|
|
return FALSE;
|
|
|
|
// searching uses the same code as rendering and that code is not
|
|
// thread safe, so we have to cancel all background rendering
|
|
cancelRenderingForDisplayModel(this);
|
|
|
|
backward = gFalse;
|
|
caseSensitive =searchState.caseSensitive;
|
|
strU = searchState.strU;
|
|
if (eSsNone == searchState.searchState) {
|
|
// starting a new search forward
|
|
DBG_OUT(" new search\n");
|
|
searchState.currPage = searchState.startPage;
|
|
assert(FALSE == searchState.wrapped);
|
|
startAtLast = gFalse;
|
|
startAtTop = gTrue;
|
|
} else {
|
|
// continuing previous search forward or backward
|
|
DBG_OUT(" continue search\n");
|
|
startAtLast = gTrue;
|
|
startAtTop = gFalse;
|
|
}
|
|
stopAtBottom = gTrue;
|
|
stopAtLast = gFalse;
|
|
|
|
if ((eSsFoundPrev == searchState.searchState)
|
|
|| (eSsFoundPrevWrapped == searchState.searchState)) {
|
|
searchState.wrapped = FALSE;
|
|
}
|
|
|
|
for (;;) {
|
|
pageNo = searchState.currPage;
|
|
textPage = GetTextPage(pageNo);
|
|
DBG_OUT(" search forward for '%s', case sensitive=%d, page=%d\n", searchState.str->getCString(), caseSensitive, pageNo);
|
|
found = textPage->findText(strU->unicode(), strU->getLength(), startAtTop, stopAtBottom,
|
|
startAtLast, stopAtLast, caseSensitive, backward,
|
|
&xs, &ys, &xe, &ye);
|
|
|
|
if (found) {
|
|
DBG_OUT(" found '%s' on page %d at pos (%.2f, %.2f)-(%.2f,%.2f)\n",
|
|
searchState.str->getCString(), pageNo, xs, ys, xe, ye);
|
|
|
|
TransfromUpsideDown(this, pageNo, &ys, &ye);
|
|
RectD_FromXY(&hitRect, xs, xe, ys, ye);
|
|
if (searchState.wrapped) {
|
|
searchState.wrapped = FALSE;
|
|
searchState.searchState = eSsFoundNextWrapped;
|
|
} else {
|
|
searchState.searchState = eSsFoundNext;
|
|
}
|
|
setSearchHit(pageNo, &hitRect);
|
|
EnsureSearchHitVisible();
|
|
goto Exit;
|
|
}
|
|
|
|
if (pageCount() == pageNo) {
|
|
DBG_OUT(" wrapped\n");
|
|
searchState.wrapped = TRUE;
|
|
searchState.currPage = 1;
|
|
} else
|
|
searchState.currPage = pageNo + 1;
|
|
|
|
startAtTop = gTrue;
|
|
startAtLast = gFalse;
|
|
|
|
if (searchState.wrapped &&
|
|
(searchState.currPage == searchState.startPage) &&
|
|
(eSsNone == searchState.searchState) ) {
|
|
searchState.searchState = eSsNotFound;
|
|
clearSearchHit();
|
|
goto Exit;
|
|
}
|
|
}
|
|
Exit:
|
|
showNormalCursor();
|
|
return found;
|
|
}
|
|
|
|
void DisplayModelSplash::handleLink(PdfLink *pdfLink)
|
|
{
|
|
Link * link;
|
|
LinkAction * action;
|
|
LinkActionKind actionKind;
|
|
|
|
assert(pdfLink);
|
|
if (!pdfLink) return;
|
|
|
|
link = pdfLink->link;
|
|
assert(link);
|
|
if (!link) return;
|
|
|
|
action = link->getAction();
|
|
actionKind = action->getKind();
|
|
|
|
switch (actionKind) {
|
|
case actionGoTo:
|
|
handleLinkGoTo((LinkGoTo*)action);
|
|
break;
|
|
case actionGoToR:
|
|
handleLinkGoToR((LinkGoToR*)action);
|
|
break;
|
|
case actionLaunch:
|
|
handleLinkLaunch((LinkLaunch*)action);
|
|
break;
|
|
case actionURI:
|
|
handleLinkURI((LinkURI*)action);
|
|
break;
|
|
case actionNamed:
|
|
handleLinkNamed((LinkNamed *)action);
|
|
break;
|
|
default:
|
|
/* other kinds are not supported */
|
|
break;
|
|
}
|
|
}
|
|
|
|
|