// 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; }
// 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; }
static HTREEITEM AddTocItemToView(HWND hwnd, DocTocItem *entry, HTREEITEM parent, bool toggleItem) { TV_INSERTSTRUCT tvinsert; tvinsert.hParent = parent; tvinsert.hInsertAfter = TVI_LAST; tvinsert.itemex.mask = TVIF_TEXT | TVIF_PARAM | TVIF_STATE; tvinsert.itemex.state = entry->child && entry->open != toggleItem ? TVIS_EXPANDED : 0; tvinsert.itemex.stateMask = TVIS_EXPANDED; tvinsert.itemex.lParam = (LPARAM)entry; // Replace unprintable whitespace with regular spaces str::NormalizeWS(entry->title); tvinsert.itemex.pszText = entry->title; #ifdef DISPLAY_TOC_PAGE_NUMBERS WindowInfo *win = FindWindowInfoByHwnd(hwnd); if (entry->pageNo && win && win->IsDocLoaded() && !win->AsEbook()) { ScopedMem<WCHAR> label(win->ctrl->GetPageLabel(entry->pageNo)); ScopedMem<WCHAR> text(str::Format(L"%s %s", entry->title, label)); tvinsert.itemex.pszText = text; return TreeView_InsertItem(hwnd, &tvinsert); } #endif return TreeView_InsertItem(hwnd, &tvinsert); }
// 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; }
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; } } }
Doc GetDocForWindow(SumatraWindow& win) { if (win.AsWindowInfo()) { WindowInfo *iwin = win.AsWindowInfo(); if (!iwin->IsDocLoaded()) return Doc(); return Doc(iwin->dm->engine, iwin->dm->engineType); } if (win.AsEbookWindow()) { EbookWindow *ewin = win.AsEbookWindow(); return ewin->ebookController->GetDoc(); } CrashIf(true); return Doc(); }
void StressTest::OnTimer(int timerIdGot) { CrashIf(timerId != timerIdGot); KillTimer(win->hwndFrame, timerId); if (!win->IsDocLoaded()) { if (!GoToNextFile()) { Finished(true); return; } TickTimer(); return; } // chm documents aren't rendered and we block until we show them // so we can assume previous page has been shown and go to next page if (!win->AsFixed()) { if (!GoToNextPage()) return; goto Next; } // For non-image files, we detect if a page was rendered by checking the cache // (but we don't wait more than 3 seconds). // Image files are always fully rendered in WM_PAINT, so we know the page // has already been rendered. DisplayModel *dm = win->AsFixed(); bool didRender = gRenderCache.Exists(dm, currPage, dm->GetRotation()); if (!didRender && dm->ShouldCacheRendering(currPage)) { double timeInMs = currPageRenderTime.GetTimeInMs(); if (timeInMs > 3.0 * 1000) { if (!GoToNextPage()) return; } } else if (!GoToNextPage()) { return; } MakeRandomSelection(win, currPage); Next: TickTimer(); }
// note: used from CrashHandler.cpp, should not allocate memory void GetStressTestInfo(str::Str<char>* s) { // only add paths to files encountered during an explicit stress test // (for privacy reasons, users should be able to decide themselves // whether they want to share what files they had opened during a crash) if (!IsStressTesting()) return; for (size_t i = 0; i < gWindows.Count(); i++) { WindowInfo *w = gWindows.At(i); if (!w || !w->IsDocLoaded() || !w->loadedFilePath) continue; s->Append("File: "); char buf[256]; str::conv::ToCodePageBuf(buf, dimof(buf), w->loadedFilePath, CP_UTF8); s->Append(buf); w->stressTest->GetLogInfo(s); s->Append("\r\n"); } }
static WindowInfo *LoadOnStartup(const TCHAR *filePath, CommandLineInfo& i, bool isFirstWin) { bool showWin = !(i.printDialog && i.exitOnPrint) && !gPluginMode; LoadArgs args(filePath, NULL, showWin); WindowInfo *win = LoadDocument(args); if (!win) return win; if (win->IsDocLoaded() && i.destName && isFirstWin) { win->linkHandler->GotoNamedDest(i.destName); } else if (win->IsDocLoaded() && i.pageNumber > 0 && isFirstWin) { if (win->dm->ValidPageNo(i.pageNumber)) win->dm->GoToPage(i.pageNumber, 0); } if (i.hwndPluginParent) MakePluginWindow(*win, i.hwndPluginParent); if (!win->IsDocLoaded() || !isFirstWin) return win; if (i.enterPresentation || i.enterFullscreen) EnterFullscreen(*win, i.enterPresentation); if (i.startView != DM_AUTOMATIC) SwitchToDisplayMode(win, i.startView); if (i.startZoom != INVALID_ZOOM) ZoomToSelection(win, i.startZoom); if (i.startScroll.x != -1 || i.startScroll.y != -1) { ScrollState ss = win->dm->GetScrollState(); ss.x = i.startScroll.x; ss.y = i.startScroll.y; win->dm->SetScrollState(ss); } if (i.forwardSearchOrigin && i.forwardSearchLine && win->pdfsync) { UINT page; Vec<RectI> rects; ScopedMem<TCHAR> sourcePath(path::Normalize(i.forwardSearchOrigin)); int ret = win->pdfsync->SourceToDoc(sourcePath, i.forwardSearchLine, 0, &page, rects); ShowForwardSearchResult(win, sourcePath, i.forwardSearchLine, 0, ret, page, rects); } return win; }
virtual void Execute() { if (!WindowInfoStillValid(win)) return; SetForegroundWindow(win->hwndFrame); if (win->IsDocLoaded()) { if (-1 != pageNo) win->dm->GoToPage(pageNo, 0, true); // we might have been invoked by clicking on a tree view // switch focus so that keyboard navigation works, which enables // a fluid experience SetFocus(win->hwndFrame); } }
// 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; }
// 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; }
void StressTest::Finished(bool success) { win->stressTest = nullptr; // make sure we're not double-deleted if (success) { int secs = SecsSinceSystemTime(stressStartTime); AutoFreeW tm(FormatTime(secs)); AutoFreeW s(str::Format(L"Stress test complete, rendered %d files in %s", filesCount, tm)); win->ShowNotification(s, NOS_PERSIST, NG_STRESS_TEST_SUMMARY); } CloseWindow(win, exitWhenDone && MayCloseWindow(win)); delete this; }
void StressTest::Finished(bool success) { win->stressTest = NULL; // make sure we're not double-deleted if (success) { int secs = SecsSinceSystemTime(stressStartTime); ScopedMem<WCHAR> tm(FormatTime(secs)); ScopedMem<WCHAR> s(str::Format(L"Stress test complete, rendered %d files in %s", filesCount, tm)); win->ShowNotification(s, false, false, NG_STRESS_TEST_SUMMARY); } CloseWindow(win, exitWhenDone); delete this; }
void StressTest::Start(const WCHAR* path, const WCHAR* filter, const WCHAR* ranges, int cycles) { if (file::Exists(path)) { FilesProvider* filesProvider = new FilesProvider(path); ParsePageRanges(ranges, pageRanges); Start(filesProvider, cycles); } else if (dir::Exists(path)) { DirFileProvider* dirFileProvider = new DirFileProvider(path, filter); ParsePageRanges(ranges, fileRanges); Start(dirFileProvider, cycles); } else { // Note: string dev only, don't translate AutoFreeW s(str::Format(L"Path '%s' doesn't exist", path)); win->ShowNotification(s, NOS_WARNING, NG_STRESS_TEST_SUMMARY); Finished(false); } }
bool StressTest::GoToNextPage() { double pageRenderTime = currPageRenderTime.GetTimeInMs(); ScopedMem<WCHAR> s(str::Format(L"Page %d rendered in %d milliseconds", currPage, (int)pageRenderTime)); win->ShowNotification(s, NOS_DEFAULT, NG_STRESS_TEST_BENCHMARK); ++currPage; while (!IsInRange(pageRanges, currPage) && currPage <= win->ctrl->PageCount()) { currPage++; } if (currPage > win->ctrl->PageCount()) { if (GoToNextFile()) return true; Finished(true); return false; } win->ctrl->GoToPage(currPage, false); currPageRenderTime.Start(); // start text search when we're in the middle of the document, so that // search thread touches both pages that were already rendered and not yet // rendered // TODO: it would be nice to also randomize search starting page but the // current API doesn't make it easy if (currPage == pageForSearchStart) { // use text that is unlikely to be found, so that we search all pages win::SetText(win->hwndFindBox, L"!z_yt"); FindTextOnThread(win, FIND_FORWARD, true); } if (1 == rand() % 3) { ClientRect rect(win->hwndFrame); int deltaX = (rand() % 40) - 23; rect.dx += deltaX; if (rect.dx < 300) rect.dx += (abs(deltaX) * 3); int deltaY = (rand() % 40) - 23; rect.dy += deltaY; if (rect.dy < 300) rect.dy += (abs(deltaY) * 3); SendMessage(win->hwndFrame, WM_SIZE, 0, MAKELONG(rect.dx, rect.dy)); } return true; }
static void ShowSearchResult(WindowInfo& win, TextSel *result, bool addNavPt) { CrashIf(0 == result->len || !result->pages || !result->rects); if (0 == result->len || !result->pages || !result->rects) return; if (addNavPt || !win.dm->PageShown(result->pages[0]) || (win.dm->ZoomVirtual() == ZOOM_FIT_PAGE || win.dm->ZoomVirtual() == ZOOM_FIT_CONTENT)) { win.dm->GoToPage(result->pages[0], 0, addNavPt); } win.dm->textSelection->CopySelection(win.dm->textSearch); UpdateTextSelection(&win, false); win.dm->ShowResultRectToScreen(result); win.RepaintAsync(); }
virtual void Execute() { if (!WindowInfoStillValid(win)) return; if (win->findThread != thread) { // Race condition: FindTextOnThread/AbortFinding was // called after the previous find thread ended but // before this FindEndTask could be executed return; } if (!win->IsDocLoaded()) { // the UI has already been disabled and hidden } else if (textSel) { ShowSearchResult(*win, textSel, wasModifiedCanceled); ftd->HideUI(true, loopedAround); } else { // nothing found or search canceled ClearSearchResult(win); ftd->HideUI(false, !wasModifiedCanceled); } win->findThread = NULL; }
WindowElement::WindowElement(WindowInfo info, QObject *parent) : QObject(parent), m_title(info.title()), m_windowId(info.window()) { SystemIconProvider provider; if (info.icon()) m_pixmap = QPixmap::fromX11Pixmap(info.icon()); else if (!info.iconName().isEmpty()) { m_pixmap = provider.requestPixmap(info.iconName(), NULL, QSize()); } else { // App icon wasn't set, and iconName wasn't set... fallback to default m_pixmap = provider.requestPixmap("default", NULL, QSize()); } if (!info.notificationIconName().isEmpty()) { m_notify = provider.requestPixmap(info.notificationIconName(), NULL, QSize()); } }
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); }
bool StressTest::OpenFile(const TCHAR *fileName) { bool reuse = rand() % 3 != 1; _tprintf(_T("%s\n"), fileName); fflush(stdout); LoadArgs args(fileName, NULL, true /* show */, reuse); WindowInfo *w = LoadDocument(args); if (!w) return false; if (w == win) { // WindowInfo reused if (!win->dm) return false; } else if (!w->dm) { // new WindowInfo CloseWindow(w, false, true); return false; } // transfer ownership of stressTest object to a new window and close the // current one assert(this == win->stressTest); if (w != win) { if (win->IsDocLoaded()) { // try to provoke a crash in RenderCache cleanup code ClientRect rect(win->hwndFrame); rect.Inflate(rand() % 10, rand() % 10); SendMessage(win->hwndFrame, WM_SIZE, 0, MAKELONG(rect.dx, rect.dy)); win->RenderPage(1); win->RepaintAsync(); } WindowInfo *toClose = win; w->stressTest = win->stressTest; win->stressTest = NULL; win = w; CloseWindow(toClose, false, false); } if (!win->dm) return false; win->dm->ChangeDisplayMode(DM_CONTINUOUS); win->dm->ZoomTo(ZOOM_FIT_PAGE); win->dm->GoToFirstPage(); if (win->tocVisible || gGlobalPrefs.favVisible) SetSidebarVisibility(win, win->tocVisible, gGlobalPrefs.favVisible); currPage = pageRanges.At(0).start; win->dm->GoToPage(currPage, 0); currPageRenderTime.Start(); ++filesCount; pageForSearchStart = (rand() % win->dm->PageCount()) + 1; // search immediately in single page documents if (1 == pageForSearchStart) { // use text that is unlikely to be found, so that we search all pages win::SetText(win->hwndFindBox, _T("!z_yt")); FindTextOnThread(win); } int secs = SecsSinceSystemTime(stressStartTime); ScopedMem<TCHAR> tm(FormatTime(secs)); ScopedMem<TCHAR> s(str::Format(_T("File %d: %s, time: %s"), filesCount, fileName, tm)); ShowNotification(win, s, false, false, NG_STRESS_TEST_SUMMARY); return true; }
bool StressTest::OpenFile(const WCHAR* fileName) { wprintf(L"%s\n", fileName); fflush(stdout); LoadArgs args(fileName); args.forceReuse = rand() % 3 != 1; WindowInfo* w = LoadDocument(args); if (!w) return false; if (w == win) { // WindowInfo reused if (!win->IsDocLoaded()) return false; } else if (!w->IsDocLoaded()) { // new WindowInfo CloseWindow(w, false); return false; } // transfer ownership of stressTest object to a new window and close the // current one AssertCrash(this == win->stressTest); if (w != win) { if (win->IsDocLoaded()) { // try to provoke a crash in RenderCache cleanup code ClientRect rect(win->hwndFrame); rect.Inflate(rand() % 10, rand() % 10); SendMessage(win->hwndFrame, WM_SIZE, 0, MAKELONG(rect.dx, rect.dy)); if (win->AsFixed()) win->cbHandler->RequestRendering(1); win->RepaintAsync(); } WindowInfo* toClose = win; w->stressTest = win->stressTest; win->stressTest = nullptr; win = w; CloseWindow(toClose, false); } if (!win->IsDocLoaded()) return false; win->ctrl->SetDisplayMode(DM_CONTINUOUS); win->ctrl->SetZoomVirtual(ZOOM_FIT_PAGE, nullptr); win->ctrl->GoToFirstPage(); if (win->tocVisible || gGlobalPrefs->showFavorites) SetSidebarVisibility(win, win->tocVisible, gGlobalPrefs->showFavorites); currPage = pageRanges.at(0).start; win->ctrl->GoToPage(currPage, false); currPageRenderTime.Start(); ++filesCount; pageForSearchStart = (rand() % win->ctrl->PageCount()) + 1; // search immediately in single page documents if (1 == pageForSearchStart) { // use text that is unlikely to be found, so that we search all pages win::SetText(win->hwndFindBox, L"!z_yt"); FindTextOnThread(win, TextSearchDirection::Forward, true); } int secs = SecsSinceSystemTime(stressStartTime); AutoFreeW tm(FormatTime(secs)); AutoFreeW s(str::Format(L"File %d: %s, time: %s", filesCount, fileName, tm)); win->ShowNotification(s, NOS_PERSIST, NG_STRESS_TEST_SUMMARY); return true; }
static int process_event(XEvent* event) { KeySym sym; WindowInfo* info = find_handle(event->xany.window); if (!info) return 1; if (event->type == ClientMessage) { if ((Atom)event->xclient.data.l[0] == s_wm_delete_window) { info->update = 0; mfb_close(info); return 0; } } switch (event->type) { case KeyPress: { sym = XLookupKeysym(&event->xkey, 0); if (handle_special_keys(info, event, 1)) break; if (info->key_callback) info->key_callback(info->rust_data, sym, 1); if (info->char_callback) { unsigned int t = keySym2Unicode(sym); if (t != -1) info->char_callback(info->rust_data, t); } break; } case KeyRelease: { if (handle_special_keys(info, event, 0)) break; sym = XLookupKeysym(&event->xkey, 0); if (info->key_callback) info->key_callback(info->rust_data, sym, 0); break; } case ButtonPress: { if (!info->shared_data) break; if (event->xbutton.button == Button1) info->shared_data->state[0] = 1; else if (event->xbutton.button == Button2) info->shared_data->state[1] = 1; else if (event->xbutton.button == Button3) info->shared_data->state[2] = 1; else if (event->xbutton.button == Button4) info->shared_data->scroll_y = 10.0f; else if (event->xbutton.button == Button5) info->shared_data->scroll_y = -10.0f; else if (event->xbutton.button == Button6) info->shared_data->scroll_x = 10.0f; else if (event->xbutton.button == Button7) info->shared_data->scroll_y = -10.0f; break; } case ButtonRelease: { if (!info->shared_data) break; if (event->xbutton.button == Button1) info->shared_data->state[0] = 0; else if (event->xbutton.button == Button2) info->shared_data->state[1] = 0; else if (event->xbutton.button == Button3) info->shared_data->state[2] = 0; break; } case ConfigureNotify: { info->width = event->xconfigure.width; info->height = event->xconfigure.height; break; } } return 1; }
static void RelayoutTocItem(LPNMTVCUSTOMDRAW ntvcd) { // code inspired by http://www.codeguru.com/cpp/controls/treeview/multiview/article.php/c3985/ LPNMCUSTOMDRAW ncd = &ntvcd->nmcd; HWND hTV = ncd->hdr.hwndFrom; HTREEITEM hItem = (HTREEITEM)ncd->dwItemSpec; RECT rcItem; if (0 == ncd->rc.right - ncd->rc.left || 0 == ncd->rc.bottom - ncd->rc.top) return; if (!TreeView_GetItemRect(hTV, hItem, &rcItem, TRUE)) return; if (rcItem.right > ncd->rc.right) rcItem.right = ncd->rc.right; // Clear the label RECT rcFullWidth = rcItem; rcFullWidth.right = ncd->rc.right; FillRect(ncd->hdc, &rcFullWidth, GetSysColorBrush(COLOR_WINDOW)); // Get the label's text WCHAR szText[MAX_PATH]; TVITEM item; item.hItem = hItem; item.mask = TVIF_TEXT | TVIF_PARAM; item.pszText = szText; item.cchTextMax = MAX_PATH; TreeView_GetItem(hTV, &item); // Draw the page number right-aligned (if there is one) WindowInfo *win = FindWindowInfoByHwnd(hTV); DocTocItem *tocItem = (DocTocItem *)item.lParam; ScopedMem<WCHAR> label; if (tocItem->pageNo && win && win->IsDocLoaded()) { label.Set(win->ctrl->GetPageLabel(tocItem->pageNo)); label.Set(str::Join(L" ", label)); } if (label && str::EndsWith(item.pszText, label)) { RECT rcPageNo = rcFullWidth; InflateRect(&rcPageNo, -2, -1); SIZE txtSize; GetTextExtentPoint32(ncd->hdc, label, str::Len(label), &txtSize); rcPageNo.left = rcPageNo.right - txtSize.cx; SetTextColor(ncd->hdc, GetSysColor(COLOR_WINDOWTEXT)); SetBkColor(ncd->hdc, GetSysColor(COLOR_WINDOW)); DrawText(ncd->hdc, label, -1, &rcPageNo, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX); // Reduce the size of the label and cut off the page number rcItem.right = std::max(rcItem.right - txtSize.cx, 0); szText[str::Len(szText) - str::Len(label)] = '\0'; } SetTextColor(ncd->hdc, ntvcd->clrText); SetBkColor(ncd->hdc, ntvcd->clrTextBk); // Draw the focus rectangle (including proper background color) HBRUSH brushBg = CreateSolidBrush(ntvcd->clrTextBk); FillRect(ncd->hdc, &rcItem, brushBg); DeleteObject(brushBg); if ((ncd->uItemState & CDIS_FOCUS)) DrawFocusRect(ncd->hdc, &rcItem); InflateRect(&rcItem, -2, -1); DrawText(ncd->hdc, szText, -1, &rcItem, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX | DT_WORD_ELLIPSIS); }
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int retCode = 1; // by default it's error #ifdef DEBUG // Memory leak detection (only enable _CRTDBG_LEAK_CHECK_DF for // regular termination so that leaks aren't checked on exceptions, // aborts, etc. where some clean-up might not take place) _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF); //_CrtSetBreakAlloc(421); TryLoadMemTrace(); #endif DisableDataExecution(); // ensure that C functions behave consistently under all OS locales // (use Win32 functions where localized input or output is desired) setlocale(LC_ALL, "C"); // don't show system-provided dialog boxes when accessing files on drives // that are not mounted (e.g. a: drive without floppy or cd rom drive // without a cd). SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); #if defined(DEBUG) || defined(SVN_PRE_RELEASE_VER) if (str::StartsWith(lpCmdLine, "/tester")) { extern int TesterMain(); // in Tester.cpp return TesterMain(); } if (str::StartsWith(lpCmdLine, "/regress")) { extern int RegressMain(); // in Regress.cpp return RegressMain(); } #endif #ifdef SUPPORTS_AUTO_UPDATE if (str::StartsWith(lpCmdLine, "-autoupdate")) { bool quit = AutoUpdateMain(); if (quit) return 0; } #endif srand((unsigned int)time(NULL)); // load uiautomationcore.dll before installing crash handler (i.e. initializing // dbghelp.dll), so that we get function names/offsets in GetCallstack() uia::Initialize(); #ifdef DEBUG dbghelp::RememberCallstackLogs(); #endif SetupCrashHandler(); ScopedOle ole; InitAllCommonControls(); ScopedGdiPlus gdiPlus(true); mui::Initialize(); uitask::Initialize(); prefs::Load(); CommandLineInfo i(GetCommandLine()); SetCurrentLang(i.lang ? i.lang : gGlobalPrefs->uiLanguage); // This allows ad-hoc comparison of gdi, gdi+ and gdi+ quick when used // in layout #if 0 RedirectIOToConsole(); BenchEbookLayout(L"C:\\kjk\\downloads\\pg12.mobi"); system("pause"); goto Exit; #endif if (i.showConsole) { RedirectIOToConsole(); RedirectDllIOToConsole(); } if (i.makeDefault) AssociateExeWithPdfExtension(); if (i.pathsToBenchmark.Count() > 0) { BenchFileOrDir(i.pathsToBenchmark); if (i.showConsole) system("pause"); } if (i.exitImmediately) goto Exit; gCrashOnOpen = i.crashOnOpen; gPolicyRestrictions = GetPolicies(i.restrictedUse); GetFixedPageUiColors(gRenderCache.textColor, gRenderCache.backgroundColor); DebugGdiPlusDevice(gUseGdiRenderer); if (!RegisterWinClass()) goto Exit; CrashIf(hInstance != GetModuleHandle(NULL)); if (!InstanceInit(nCmdShow)) goto Exit; if (i.hwndPluginParent) { if (!SetupPluginMode(i)) goto Exit; } if (i.printerName) { // note: this prints all PDF files. Another option would be to // print only the first one for (size_t n = 0; n < i.fileNames.Count(); n++) { bool ok = PrintFile(i.fileNames.At(n), i.printerName, !i.silent, i.printSettings); if (!ok) retCode++; } --retCode; // was 1 if no print failures, turn 1 into 0 goto Exit; } bool showStartPage = i.fileNames.Count() == 0 && gGlobalPrefs->rememberOpenedFiles && gGlobalPrefs->showStartPage; if (showStartPage) { // make the shell prepare the image list, so that it's ready when the first window's loaded SHFILEINFO sfi; SHGetFileInfo(L".pdf", 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES); } if (gGlobalPrefs->reopenOnce) { WStrVec moreFileNames; ParseCmdLine(gGlobalPrefs->reopenOnce, moreFileNames); moreFileNames.Reverse(); for (WCHAR **fileName = moreFileNames.IterStart(); fileName; fileName = moreFileNames.IterNext()) { i.fileNames.Append(*fileName); } moreFileNames.RemoveAt(0, moreFileNames.Count()); str::ReplacePtr(&gGlobalPrefs->reopenOnce, NULL); } HANDLE hMutex = NULL; HWND hPrevWnd = NULL; if (i.printDialog || i.stressTestPath || gPluginMode) { // TODO: pass print request through to previous instance? } else if (i.reuseDdeInstance) { hPrevWnd = FindWindow(FRAME_CLASS_NAME, NULL); } else if (gGlobalPrefs->reuseInstance || gGlobalPrefs->useTabs) { hPrevWnd = FindPrevInstWindow(&hMutex); } if (hPrevWnd) { for (size_t n = 0; n < i.fileNames.Count(); n++) { OpenUsingDde(hPrevWnd, i.fileNames.At(n), i, 0 == n); } goto Exit; } WindowInfo *win = NULL; for (size_t n = 0; n < i.fileNames.Count(); n++) { win = LoadOnStartup(i.fileNames.At(n), i, !win); if (!win) { retCode++; continue; } if (i.printDialog) OnMenuPrint(win, i.exitWhenDone); } if (i.fileNames.Count() > 0 && !win) { // failed to create any window, even though there // were files to load (or show a failure message for) goto Exit; } if (i.printDialog && i.exitWhenDone) goto Exit; if (!win) { win = CreateAndShowWindowInfo(); if (!win) goto Exit; } UpdateUITextForLanguage(); // needed for RTL languages if (win->IsAboutWindow()) { // TODO: shouldn't CreateAndShowWindowInfo take care of this? UpdateToolbarAndScrollbarState(*win); } // Make sure that we're still registered as default, // if the user has explicitly told us to be if (gGlobalPrefs->associatedExtensions) RegisterForPdfExtentions(win->hwndFrame); if (i.stressTestPath) { // don't save file history and preference changes gPolicyRestrictions = (gPolicyRestrictions | Perm_RestrictedUse) & ~Perm_SavePreferences; RebuildMenuBarForWindow(win); StartStressTest(&i, win, &gRenderCache); } if (gGlobalPrefs->checkForUpdates) UpdateCheckAsync(win, true); // only hide newly missing files when showing the start page on startup if (showStartPage && gFileHistory.Get(0)) { gFileExistenceChecker = new FileExistenceChecker(); gFileExistenceChecker->Start(); } // call this once it's clear whether Perm_SavePreferences has been granted prefs::RegisterForFileChanges(); retCode = RunMessageLoop(); SafeCloseHandle(&hMutex); CleanUpThumbnailCache(gFileHistory); Exit: prefs::UnregisterForFileChanges(); while (gWindows.Count() > 0) { DeleteWindowInfo(gWindows.At(0)); } #ifndef DEBUG // leave all the remaining clean-up to the OS // (as recommended for a quick exit) ExitProcess(retCode); #else DeleteObject(GetDefaultGuiFont()); DeleteBitmap(gBitmapReloadingCue); DeleteSplitterBrush(); // wait for FileExistenceChecker to terminate // (which should be necessary only very rarely) while (gFileExistenceChecker) { Sleep(10); uitask::DrainQueue(); } mui::Destroy(); uitask::Destroy(); trans::Destroy(); SaveCallstackLogs(); dbghelp::FreeCallstackLogs(); // must be after uitask::Destroy() because we might have queued prefs::Reload() // which crashes if gGlobalPrefs is freed gFileHistory.UpdateStatesSource(NULL); DeleteGlobalPrefs(gGlobalPrefs); // it's still possible to crash after this (destructors of static classes, // atexit() code etc.) point, but it's very unlikely UninstallCrashHandler(); // output leaks after all destructors of static objects have run _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); return retCode; #endif }
static int process_event(XEvent* event) { KeySym sym; WindowInfo* info = find_handle(event->xany.window); if (!info) return 1; if (event->type == ClientMessage) { if ((Atom)event->xclient.data.l[0] == s_wm_delete_window) { info->update = 0; mfb_close(info); return 0; } } switch (event->type) { case KeyPress: { sym = XLookupKeysym(&event->xkey, 0); if (info->key_callback) info->key_callback(info->rust_data, sym, 1); break; } case KeyRelease: { sym = XLookupKeysym(&event->xkey, 0); if (info->key_callback) info->key_callback(info->rust_data, sym, 0); break; } case ButtonPress: { if (!info->shared_data) break; if (event->xbutton.button == Button1) info->shared_data->state[0] = 1; else if (event->xbutton.button == Button2) info->shared_data->state[1] = 1; else if (event->xbutton.button == Button3) info->shared_data->state[2] = 1; else if (event->xbutton.button == Button4) info->shared_data->scroll_y = 10.0f; else if (event->xbutton.button == Button5) info->shared_data->scroll_y = -10.0f; else if (event->xbutton.button == Button6) info->shared_data->scroll_x = 10.0f; else if (event->xbutton.button == Button7) info->shared_data->scroll_y = -10.0f; break; } case ButtonRelease: { if (!info->shared_data) break; if (event->xbutton.button == Button1) info->shared_data->state[0] = 0; else if (event->xbutton.button == Button2) info->shared_data->state[1] = 0; else if (event->xbutton.button == Button3) info->shared_data->state[2] = 0; break; } } return 1; }