// Jump to page DDE command. Format: // [<DDECOMMAND_PAGE>("<pdffilepath>", <page number>)] static const WCHAR *HandlePageCmd(const WCHAR *cmd, DDEACK& ack) { ScopedMem<WCHAR> pdfFile; UINT page; const WCHAR *next = str::Parse(cmd, L"[" DDECOMMAND_PAGE L"(\"%S\",%u)]", &pdfFile, &page); if (!next) return false; // check if the PDF is already opened WindowInfo *win = FindWindowInfoByFile(pdfFile); if (!win) return next; if (!win->IsDocLoaded()) { ReloadDocument(win); if (!win->IsDocLoaded()) return next; } if (!win->dm->ValidPageNo(page)) return next; win->dm->GoToPage(page, 0, true); ack.fAck = 1; win->Focus(); return next; }
// Open file DDE command, format: // [<DDECOMMAND_OPEN>("<pdffilepath>"[,<newwindow>,<setfocus>,<forcerefresh>])] static const WCHAR *HandleOpenCmd(const WCHAR *cmd, DDEACK& ack) { ScopedMem<WCHAR> pdfFile; BOOL newWindow = 0, setFocus = 0, forceRefresh = 0; const WCHAR *next = str::Parse(cmd, L"[" DDECOMMAND_OPEN L"(\"%S\")]", &pdfFile); if (!next) next = str::Parse(cmd, L"[" DDECOMMAND_OPEN L"(\"%S\",%u,%u,%u)]", &pdfFile, &newWindow, &setFocus, &forceRefresh); if (!next) return NULL; WindowInfo *win = FindWindowInfoByFile(pdfFile); if (newWindow || !win) { LoadArgs args(pdfFile, !newWindow ? win : NULL); win = LoadDocument(args); } else if (win && !win->IsDocLoaded()) { ReloadDocument(win); forceRefresh = 0; } assert(!win || !win->IsAboutWindow()); if (!win) return next; ack.fAck = 1; if (forceRefresh) ReloadDocument(win, true); if (setFocus) win->Focus(); return next; }
// Synchronization command format: // [<DDECOMMAND_SYNC>(["<pdffile>",]"<srcfile>",<line>,<col>[,<newwindow>,<setfocus>])] static const WCHAR *HandleSyncCmd(const WCHAR *cmd, DDEACK& ack) { ScopedMem<WCHAR> pdfFile, srcFile; BOOL line = 0, col = 0, newWindow = 0, setFocus = 0; const WCHAR *next = str::Parse(cmd, L"[" DDECOMMAND_SYNC L"(\"%S\",%? \"%S\",%u,%u)]", &pdfFile, &srcFile, &line, &col); if (!next) next = str::Parse(cmd, L"[" DDECOMMAND_SYNC L"(\"%S\",%? \"%S\",%u,%u,%u,%u)]", &pdfFile, &srcFile, &line, &col, &newWindow, &setFocus); // allow to omit the pdffile path, so that editors don't have to know about // multi-file projects (requires that the PDF has already been opened) if (!next) { pdfFile.Set(NULL); next = str::Parse(cmd, L"[" DDECOMMAND_SYNC L"(\"%S\",%u,%u)]", &srcFile, &line, &col); if (!next) next = str::Parse(cmd, L"[" DDECOMMAND_SYNC L"(\"%S\",%u,%u,%u,%u)]", &srcFile, &line, &col, &newWindow, &setFocus); } if (!next) return NULL; WindowInfo *win = NULL; if (pdfFile) { // check if the PDF is already opened win = FindWindowInfoByFile(pdfFile); // if not then open it if (newWindow || !win) { LoadArgs args(pdfFile, !newWindow ? win : NULL); win = LoadDocument(args); } else if (win && !win->IsDocLoaded()) { ReloadDocument(win); } } else { // check if any opened PDF has sync information for the source file win = FindWindowInfoBySyncFile(srcFile); if (newWindow) { LoadArgs args(win->loadedFilePath); win = LoadDocument(args); } } if (!win || !win->IsDocLoaded()) return next; if (!win->pdfsync) return next; ack.fAck = 1; assert(win->IsDocLoaded()); UINT page; Vec<RectI> rects; int ret = win->pdfsync->SourceToDoc(srcFile, line, col, &page, rects); ShowForwardSearchResult(win, srcFile, line, col, ret, page, rects); if (setFocus) win->Focus(); return next; }
// Going to a bookmark within current file scrolls to a given page. // Going to a bookmark in another file, loads the file and scrolls to a page // (similar to how invoking one of the recently opened files works) static void GoToFavorite(WindowInfo* win, DisplayState* f, Favorite* fn) { CrashIf(!f || !fn); if (!f || !fn) { return; } WindowInfo* existingWin = FindWindowInfoByFile(f->filePath, true); if (existingWin) { int pageNo = fn->pageNo; uitask::Post([=] { GoToFavorite(existingWin, pageNo); }); return; } if (!HasPermission(Perm_DiskAccess)) { return; } // When loading a new document, go directly to selected page instead of // first showing last seen page stored in file history // A hacky solution because I don't want to add even more parameters to // LoadDocument() and LoadDocumentInto() int pageNo = fn->pageNo; DisplayState* ds = gFileHistory.Find(f->filePath); if (ds && !ds->useDefaultState && gGlobalPrefs->rememberStatePerDocument) { ds->pageNo = fn->pageNo; ds->scrollPos = PointI(-1, -1); // don't scroll the page pageNo = -1; } LoadArgs args(f->filePath, win); win = LoadDocument(args); if (win) { uitask::Post([=] { (win, pageNo); }); } }
void LinkHandler::LaunchFile(const WCHAR *path, PageDestination *link) { // for safety, only handle relative paths and only open them in SumatraPDF // (unless they're of an allowed perceived type) and never launch any external // file in plugin mode (where documents are supposed to be self-contained) WCHAR drive; if (str::StartsWith(path, L"\\") || str::Parse(path, L"%c:\\", &drive) || gPluginMode) { return; } ScopedMem<WCHAR> fullPath(path::GetDir(owner->dm->FilePath())); fullPath.Set(path::Join(fullPath, path)); fullPath.Set(path::Normalize(fullPath)); // TODO: respect link->ld.gotor.new_window for PDF documents ? WindowInfo *newWin = FindWindowInfoByFile(fullPath); // TODO: don't show window until it's certain that there was no error if (!newWin) { LoadArgs args(fullPath, owner); newWin = LoadDocument(args); if (!newWin) return; } if (!newWin->IsDocLoaded()) { CloseWindow(newWin, true); // OpenFileExternally rejects files we'd otherwise // have to show a notification to be sure (which we // consider bad UI and thus simply don't) bool ok = OpenFileExternally(fullPath); if (!ok) { ScopedMem<WCHAR> msg(str::Format(_TR("Error loading %s"), fullPath)); ShowNotification(owner, msg, true /* autoDismiss */, true /* highlight */); } return; } newWin->Focus(); if (!link) return; ScopedMem<WCHAR> name(link->GetDestName()); if (!name) newWin->linkHandler->ScrollTo(link); else { PageDestination *dest = newWin->dm->engine->GetNamedDest(name); if (dest) { newWin->linkHandler->ScrollTo(dest); delete dest; } } }
// Set view mode and zoom level. Format: // [<DDECOMMAND_SETVIEW>("<pdffilepath>", "<view mode>", <zoom level>[, <scrollX>, <scrollY>])] static const WCHAR *HandleSetViewCmd(const WCHAR *cmd, DDEACK& ack) { ScopedMem<WCHAR> pdfFile, viewMode; float zoom = INVALID_ZOOM; PointI scroll(-1, -1); const WCHAR *next = str::Parse(cmd, L"[" DDECOMMAND_SETVIEW L"(\"%S\",%? \"%S\",%f)]", &pdfFile, &viewMode, &zoom); if (!next) next = str::Parse(cmd, L"[" DDECOMMAND_SETVIEW L"(\"%S\",%? \"%S\",%f,%d,%d)]", &pdfFile, &viewMode, &zoom, &scroll.x, &scroll.y); if (!next) return NULL; WindowInfo *win = FindWindowInfoByFile(pdfFile); if (!win) return next; if (!win->IsDocLoaded()) { ReloadDocument(win); if (!win->IsDocLoaded()) return next; } DisplayMode mode = DisplayModeConv::EnumFromName(viewMode, DM_AUTOMATIC); if (mode != DM_AUTOMATIC) SwitchToDisplayMode(win, mode); if (zoom != INVALID_ZOOM) ZoomToSelection(win, zoom); if (scroll.x != -1 || scroll.y != -1) { ScrollState ss = win->dm->GetScrollState(); ss.x = scroll.x; ss.y = scroll.y; win->dm->SetScrollState(ss); } ack.fAck = 1; return next; }
// Jump to named destination DDE command. Command format: // [<DDECOMMAND_GOTO>("<pdffilepath>", "<destination name>")] static const WCHAR *HandleGotoCmd(const WCHAR *cmd, DDEACK& ack) { ScopedMem<WCHAR> pdfFile, destName; const WCHAR *next = str::Parse(cmd, L"[" DDECOMMAND_GOTO L"(\"%S\",%? \"%S\")]", &pdfFile, &destName); if (!next) return NULL; WindowInfo *win = FindWindowInfoByFile(pdfFile); if (!win) return next; if (!win->IsDocLoaded()) { ReloadDocument(win); if (!win->IsDocLoaded()) return next; } win->linkHandler->GotoNamedDest(destName); ack.fAck = 1; win->Focus(); return next; }
void LinkHandler::GotoLink(PageDestination *link) { CrashIf(!owner || owner->linkHandler != this); if (!link || !owner->IsDocLoaded()) return; ScopedMem<WCHAR> path(link->GetDestValue()); PageDestType type = link->GetDestType(); if (Dest_ScrollTo == type) { // TODO: respect link->ld.gotor.new_window for PDF documents ? ScrollTo(link); } else if (Dest_LaunchURL == type) { if (!path) /* ignore missing URLs */; else { WCHAR *colon = str::FindChar(path, ':'); WCHAR *hash = str::FindChar(path, '#'); if (!colon || (hash && colon > hash)) { // treat relative URIs as file paths (without fragment identifier) if (hash) *hash = '\0'; // LaunchFile will reject unsupported file types LaunchFile(path, NULL); } else { // LaunchBrowser will reject unsupported URI schemes LaunchBrowser(path); } } } else if (Dest_LaunchEmbedded == type) { // open embedded PDF documents in a new window if (path && str::StartsWith(path.Get(), owner->ctrl->FilePath())) { WindowInfo *newWin = FindWindowInfoByFile(path, true); if (!newWin) { LoadArgs args(path, owner); newWin = LoadDocument(args); } if (newWin) newWin->Focus(); } // offer to save other attachments to a file else { LinkSaver linkSaverTmp(*owner, path); link->SaveEmbedded(linkSaverTmp); } } else if (Dest_LaunchFile == type) { if (path) { // LaunchFile only opens files inside SumatraPDF // (except for allowed perceived file types) LaunchFile(path, link); } } // predefined named actions else if (Dest_NextPage == type) owner->ctrl->GoToNextPage(); else if (Dest_PrevPage == type) owner->ctrl->GoToPrevPage(); else if (Dest_FirstPage == type) owner->ctrl->GoToFirstPage(); else if (Dest_LastPage == type) owner->ctrl->GoToLastPage(); // Adobe Reader extensions to the spec, cf. http://www.tug.org/applications/hyperref/manual.html else if (Dest_FindDialog == type) PostMessage(owner->hwndFrame, WM_COMMAND, IDM_FIND_FIRST, 0); else if (Dest_FullScreen == type) PostMessage(owner->hwndFrame, WM_COMMAND, IDM_VIEW_PRESENTATION_MODE, 0); else if (Dest_GoBack == type) owner->ctrl->Navigate(-1); else if (Dest_GoForward == type) owner->ctrl->Navigate(1); else if (Dest_GoToPageDialog == type) PostMessage(owner->hwndFrame, WM_COMMAND, IDM_GOTO_PAGE, 0); else if (Dest_PrintDialog == type) PostMessage(owner->hwndFrame, WM_COMMAND, IDM_PRINT, 0); else if (Dest_SaveAsDialog == type) PostMessage(owner->hwndFrame, WM_COMMAND, IDM_SAVEAS, 0); else if (Dest_ZoomToDialog == type) PostMessage(owner->hwndFrame, WM_COMMAND, IDM_ZOOM_CUSTOM, 0); else CrashIf(Dest_None != type); }