void AddFavorite(WindowInfo* win) { TabInfo* tab = win->currentTab; CrashIf(!tab); int pageNo = win->currPageNo; AutoFreeW name; if (tab->ctrl->HasTocTree()) { // use the current ToC heading as default name DocTocItem* root = tab->ctrl->GetTocTree(); DocTocItem* item = TocItemForPageNo(root, pageNo); if (item) { name.SetCopy(item->title); } delete root; } AutoFreeW pageLabel(tab->ctrl->GetPageLabel(pageNo)); bool shouldAdd = Dialog_AddFavorite(win->hwndFrame, pageLabel, name); if (!shouldAdd) { return; } AutoFreeW plainLabel(str::Format(L"%d", pageNo)); bool needsLabel = !str::Eq(plainLabel, pageLabel); RememberFavTreeExpansionStateForAllWindows(); gFavorites.AddOrReplace(tab->filePath, pageNo, name, needsLabel ? pageLabel.Get() : nullptr); // expand newly added favorites by default DisplayState* fav = gFavorites.GetFavByFilePath(tab->filePath); if (fav && fav->favorites->size() == 2) { win->expandedFavorites.Append(fav); } UpdateFavoritesTreeForAllWindows(); prefs::Save(); }
int Pdfsync::DocToSource(UINT pageNo, PointI pt, AutoFreeW& filename, UINT* line, UINT* col) { if (IsIndexDiscarded()) if (RebuildIndex() != PDFSYNCERR_SUCCESS) return PDFSYNCERR_SYNCFILE_CANNOT_BE_OPENED; // find the entry in the index corresponding to this page if (pageNo <= 0 || pageNo >= sheetIndex.size() || pageNo > (UINT)engine->PageCount()) return PDFSYNCERR_INVALID_PAGE_NUMBER; // PdfSync coordinates are y-inversed RectI mbox = engine->PageMediabox(pageNo).Round(); pt.y = mbox.dy - pt.y; // distance to the closest pdf location (in the range <PDFSYNC_EPSILON_SQUARE) UINT closest_xydist = UINT_MAX; UINT selected_record = UINT_MAX; // If no record is found within a distance^2 of PDFSYNC_EPSILON_SQUARE // (selected_record == -1) then we pick up the record that is closest // vertically to the hit-point. UINT closest_ydist = UINT_MAX; // vertical distance between the hit point and the vertically-closest record UINT closest_xdist = UINT_MAX; // horizontal distance between the hit point and the vertically-closest record UINT closest_ydist_record = UINT_MAX; // vertically-closest record // read all the sections of 'p' declarations for this pdf sheet for (size_t i = sheetIndex.at(pageNo); i < points.size() && points.at(i).page == pageNo; i++) { // check whether it is closer than the closest point found so far UINT dx = abs(pt.x - (int)SYNC_TO_PDF_COORDINATE(points.at(i).x)); UINT dy = abs(pt.y - (int)SYNC_TO_PDF_COORDINATE(points.at(i).y)); UINT dist = dx * dx + dy * dy; if (dist < PDFSYNC_EPSILON_SQUARE && dist < closest_xydist) { selected_record = points.at(i).record; closest_xydist = dist; } else if ((closest_xydist == UINT_MAX) && dy < PDFSYNC_EPSILON_Y && (dy < closest_ydist || (dy == closest_ydist && dx < closest_xdist))) { closest_ydist_record = points.at(i).record; closest_ydist = dy; closest_xdist = dx; } } if (selected_record == UINT_MAX) selected_record = closest_ydist_record; if (selected_record == UINT_MAX) return PDFSYNCERR_NO_SYNC_AT_LOCATION; // no record was found close enough to the hit point // We have a record number, we need to find its declaration ('l ...') in the syncfile PdfsyncLine cmp; cmp.record = selected_record; PdfsyncLine* found = (PdfsyncLine*)bsearch(&cmp, lines.LendData(), lines.size(), sizeof(PdfsyncLine), cmpLineRecords); AssertCrash(found); if (!found) return PDFSYNCERR_NO_SYNC_AT_LOCATION; filename.SetCopy(srcfiles.at(found->file)); *line = found->line; *col = found->column; return PDFSYNCERR_SUCCESS; }
// For easy access, we try to show favorites in the menu, similar to a list of // recently opened files. // The first menu items are for currently opened file (up to MAX_FAV_MENUS), based // on the assumption that user is usually interested in navigating current file. // Then we have a submenu for each file for which there are bookmarks (up to // MAX_FAV_SUBMENUS), each having up to MAX_FAV_MENUS menu items. // If not all favorites can be shown, we also enable "Show all favorites" menu which // will provide a way to see all favorites. // Note: not sure if that's the best layout. Maybe we should always use submenu and // put the submenu for current file as the first one (potentially named as "Current file" // or some such, to make it stand out from other submenus) static void AppendFavMenus(HMENU m, const WCHAR* currFilePath) { // To minimize mouse movement when navigating current file via favorites // menu, put favorites for current file first DisplayState* currFileFav = nullptr; if (currFilePath) { currFileFav = gFavorites.GetFavByFilePath(currFilePath); } // sort the files with favorites by base file name of file path Vec<const WCHAR*> filePathsSorted; if (HasPermission(Perm_DiskAccess)) { // only show favorites for other files, if we're allowed to open them GetSortedFilePaths(filePathsSorted, currFileFav); } if (currFileFav && currFileFav->favorites->size() > 0) { filePathsSorted.InsertAt(0, currFileFav->filePath); } if (filePathsSorted.size() == 0) { return; } AppendMenu(m, MF_SEPARATOR, 0, nullptr); gFavorites.ResetMenuIds(); UINT menuId = IDM_FAV_FIRST; size_t menusCount = filePathsSorted.size(); if (menusCount > MAX_FAV_MENUS) { menusCount = MAX_FAV_MENUS; } for (size_t i = 0; i < menusCount; i++) { const WCHAR* filePath = filePathsSorted.at(i); DisplayState* f = gFavorites.GetFavByFilePath(filePath); CrashIf(!f); HMENU sub = m; bool combined = (f->favorites->size() == 1); if (!combined) { sub = CreateMenu(); } AppendFavMenuItems(sub, f, menuId, combined, f == currFileFav); if (!combined) { if (f == currFileFav) { AppendMenu(m, MF_POPUP | MF_STRING, (UINT_PTR)sub, _TR("Current file")); } else { AutoFreeW tmp; tmp.SetCopy(path::GetBaseName(filePath)); auto fileName = win::menu::ToSafeString(tmp); AppendMenuW(m, MF_POPUP | MF_STRING, (UINT_PTR)sub, fileName); } } } }
// Find a record corresponding to the given source file, line number and optionally column number. // (at the moment the column parameter is ignored) // // If there are several *consecutively declared* records for the same line then they are all returned. // The list of records is added to the vector 'records' // // If there is no record for that line, the record corresponding to the nearest line is selected // (within a range of EPSILON_LINE) // // The function returns PDFSYNCERR_SUCCESS if a matching record was found. UINT Pdfsync::SourceToRecord(const WCHAR* srcfilename, UINT line, UINT col, Vec<size_t>& records) { UNUSED(col); if (!srcfilename) return PDFSYNCERR_INVALID_ARGUMENT; AutoFreeW srcfilepath; // convert the source file to an absolute path if (PathIsRelative(srcfilename)) srcfilepath.Set(PrependDir(srcfilename)); else srcfilepath.SetCopy(srcfilename); if (!srcfilepath) return PDFSYNCERR_OUTOFMEMORY; // find the source file entry size_t isrc; for (isrc = 0; isrc < srcfiles.size(); isrc++) if (path::IsSame(srcfilepath, srcfiles.at(isrc))) break; if (isrc == srcfiles.size()) return PDFSYNCERR_UNKNOWN_SOURCEFILE; if (fileIndex.at(isrc).start == fileIndex.at(isrc).end) return PDFSYNCERR_NORECORD_IN_SOURCEFILE; // there is not any record declaration for that particular source file // look for sections belonging to the specified file // starting with the first section that is declared within the scope of the file. UINT min_distance = EPSILON_LINE; // distance to the closest record size_t lineIx = (size_t)-1; // closest record-line index for (size_t isec = fileIndex.at(isrc).start; isec < fileIndex.at(isrc).end; isec++) { // does this section belong to the desired file? if (lines.at(isec).file != isrc) continue; UINT d = abs((int)lines.at(isec).line - (int)line); if (d < min_distance) { min_distance = d; lineIx = isec; if (0 == d) break; // We have found a record for the requested line! } } if (lineIx == (size_t)-1) return PDFSYNCERR_NORECORD_FOR_THATLINE; // we read all the consecutive records until we reach a record belonging to another line for (size_t i = lineIx; i < lines.size() && lines.at(i).line == lines.at(lineIx).line; i++) records.Push(lines.at(i).record); return PDFSYNCERR_SUCCESS; }
void SetMsg(const WCHAR* msg, Color color) { gMsg.SetCopy(msg); gMsgColor = color; }
int SyncTex::SourceToDoc(const WCHAR* srcfilename, UINT line, UINT col, UINT* page, Vec<RectI>& rects) { if (IsIndexDiscarded()) { if (RebuildIndex() != PDFSYNCERR_SUCCESS) return PDFSYNCERR_SYNCFILE_CANNOT_BE_OPENED; } AssertCrash(this->scanner); AutoFreeW srcfilepath; // convert the source file to an absolute path if (PathIsRelative(srcfilename)) srcfilepath.Set(PrependDir(srcfilename)); else srcfilepath.SetCopy(srcfilename); if (!srcfilepath) return PDFSYNCERR_OUTOFMEMORY; bool isUtf8 = true; char* mb_srcfilepath = str::conv::ToUtf8(srcfilepath).StealData(); TryAgainAnsi: if (!mb_srcfilepath) return PDFSYNCERR_OUTOFMEMORY; int ret = synctex_display_query(this->scanner, mb_srcfilepath, line, col); free(mb_srcfilepath); // recent SyncTeX versions encode in UTF-8 instead of ANSI if (isUtf8 && -1 == ret) { isUtf8 = false; mb_srcfilepath = str::conv::ToAnsi(srcfilepath).StealData(); goto TryAgainAnsi; } if (-1 == ret) return PDFSYNCERR_UNKNOWN_SOURCEFILE; if (0 == ret) return PDFSYNCERR_NOSYNCPOINT_FOR_LINERECORD; synctex_node_t node; int firstpage = -1; rects.Reset(); while ((node = synctex_next_result(this->scanner)) != nullptr) { if (firstpage == -1) { firstpage = synctex_node_page(node); if (firstpage <= 0 || firstpage > engine->PageCount()) continue; *page = (UINT)firstpage; } if (synctex_node_page(node) != firstpage) continue; RectD rc; rc.x = synctex_node_box_visible_h(node); rc.y = synctex_node_box_visible_v(node) - synctex_node_box_visible_height(node); rc.dx = synctex_node_box_visible_width(node), rc.dy = synctex_node_box_visible_height(node) + synctex_node_box_visible_depth(node); rects.Push(rc.Round()); } if (firstpage <= 0) return PDFSYNCERR_NOSYNCPOINT_FOR_LINERECORD; return PDFSYNCERR_SUCCESS; }