=> a24657899111ce21e13bbae0b8dfdd0feba497a3
[1mdiff --git a/po/en.po b/po/en.po[m [1mindex 8977fb17..ce5ec830 100644[m [1m--- a/po/en.po[m [1m+++ b/po/en.po[m [36m@@ -954,6 +954,12 @@[m [mmsgstr "Error Saving File"[m msgid "heading.import.bookmarks"[m msgstr "Import Bookmarks"[m [m [32m+[m[32mmsgid "dlg.import.intofolder"[m [32m+[m[32mmsgstr "Add to Folder:"[m [32m+[m [32m+[m[32mmsgid "dlg.import.headings"[m [32m+[m[32mmsgstr "Subfolders from Headings:"[m [32m+[m #, c-format[m msgid "dlg.import.found"[m msgid_plural "dlg.import.found.n"[m [36m@@ -969,6 +975,9 @@[m [mmsgstr[1] "%sAdd %d Bookmarks"[m msgid "dlg.import.notnew"[m msgstr "All links on this page are already bookmarked."[m [m [32m+[m[32mmsgid "dlg.import.notfound"[m [32m+[m[32mmsgstr "There are no links on this page."[m [32m+[m msgid "heading.autoreload"[m msgstr "Auto-Reload"[m [m [1mdiff --git a/res/lang/cs.bin b/res/lang/cs.bin[m [1mindex 1b8c0e08..01bd8529 100644[m Binary files a/res/lang/cs.bin and b/res/lang/cs.bin differ [1mdiff --git a/res/lang/de.bin b/res/lang/de.bin[m [1mindex d6d47f51..fe140b86 100644[m Binary files a/res/lang/de.bin and b/res/lang/de.bin differ [1mdiff --git a/res/lang/en.bin b/res/lang/en.bin[m [1mindex f8b4ffca..09229bfe 100644[m Binary files a/res/lang/en.bin and b/res/lang/en.bin differ [1mdiff --git a/res/lang/eo.bin b/res/lang/eo.bin[m [1mindex 865910c0..0a5dd9e8 100644[m Binary files a/res/lang/eo.bin and b/res/lang/eo.bin differ [1mdiff --git a/res/lang/es.bin b/res/lang/es.bin[m [1mindex c42c7c70..8ed2fa37 100644[m Binary files a/res/lang/es.bin and b/res/lang/es.bin differ [1mdiff --git a/res/lang/es_MX.bin b/res/lang/es_MX.bin[m [1mindex e9bafe94..a6288c90 100644[m Binary files a/res/lang/es_MX.bin and b/res/lang/es_MX.bin differ [1mdiff --git a/res/lang/eu.bin b/res/lang/eu.bin[m [1mindex 67bfccbc..4472a069 100644[m Binary files a/res/lang/eu.bin and b/res/lang/eu.bin differ [1mdiff --git a/res/lang/fi.bin b/res/lang/fi.bin[m [1mindex 802860a4..c0d480c8 100644[m Binary files a/res/lang/fi.bin and b/res/lang/fi.bin differ [1mdiff --git a/res/lang/fr.bin b/res/lang/fr.bin[m [1mindex 99bdc5c5..8505078e 100644[m Binary files a/res/lang/fr.bin and b/res/lang/fr.bin differ [1mdiff --git a/res/lang/gl.bin b/res/lang/gl.bin[m [1mindex 5acfecfc..03b09939 100644[m Binary files a/res/lang/gl.bin and b/res/lang/gl.bin differ [1mdiff --git a/res/lang/hu.bin b/res/lang/hu.bin[m [1mindex f07665ca..f19f2e96 100644[m Binary files a/res/lang/hu.bin and b/res/lang/hu.bin differ [1mdiff --git a/res/lang/ia.bin b/res/lang/ia.bin[m [1mindex 1e95f965..b23b441a 100644[m Binary files a/res/lang/ia.bin and b/res/lang/ia.bin differ [1mdiff --git a/res/lang/ie.bin b/res/lang/ie.bin[m [1mindex 94220e80..57439c4c 100644[m Binary files a/res/lang/ie.bin and b/res/lang/ie.bin differ [1mdiff --git a/res/lang/isv.bin b/res/lang/isv.bin[m [1mindex e290eb93..e06b83c2 100644[m Binary files a/res/lang/isv.bin and b/res/lang/isv.bin differ [1mdiff --git a/res/lang/it.bin b/res/lang/it.bin[m [1mindex 3b1bd5d3..9894f60c 100644[m Binary files a/res/lang/it.bin and b/res/lang/it.bin differ [1mdiff --git a/res/lang/ja.bin b/res/lang/ja.bin[m [1mindex d66a9f07..b74b54d8 100644[m Binary files a/res/lang/ja.bin and b/res/lang/ja.bin differ [1mdiff --git a/res/lang/nl.bin b/res/lang/nl.bin[m [1mindex 944e0414..d05f2ac8 100644[m Binary files a/res/lang/nl.bin and b/res/lang/nl.bin differ [1mdiff --git a/res/lang/pl.bin b/res/lang/pl.bin[m [1mindex 95d963fa..03cc3f7a 100644[m Binary files a/res/lang/pl.bin and b/res/lang/pl.bin differ [1mdiff --git a/res/lang/ru.bin b/res/lang/ru.bin[m [1mindex c3ed24ad..2b719706 100644[m Binary files a/res/lang/ru.bin and b/res/lang/ru.bin differ [1mdiff --git a/res/lang/sk.bin b/res/lang/sk.bin[m [1mindex 7f03e531..d0dc7df9 100644[m Binary files a/res/lang/sk.bin and b/res/lang/sk.bin differ [1mdiff --git a/res/lang/sr.bin b/res/lang/sr.bin[m [1mindex 77786e29..edc6e4a9 100644[m Binary files a/res/lang/sr.bin and b/res/lang/sr.bin differ [1mdiff --git a/res/lang/tok.bin b/res/lang/tok.bin[m [1mindex 8a857f68..3f2b05f4 100644[m Binary files a/res/lang/tok.bin and b/res/lang/tok.bin differ [1mdiff --git a/res/lang/tr.bin b/res/lang/tr.bin[m [1mindex 5a10e7c4..0db87aab 100644[m Binary files a/res/lang/tr.bin and b/res/lang/tr.bin differ [1mdiff --git a/res/lang/uk.bin b/res/lang/uk.bin[m [1mindex 26da3aad..15c0c180 100644[m Binary files a/res/lang/uk.bin and b/res/lang/uk.bin differ [1mdiff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin[m [1mindex 7cb820a4..4c3ae410 100644[m Binary files a/res/lang/zh_Hans.bin and b/res/lang/zh_Hans.bin differ [1mdiff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin[m [1mindex 24753c7e..607a8c98 100644[m Binary files a/res/lang/zh_Hant.bin and b/res/lang/zh_Hant.bin differ [1mdiff --git a/src/app.c b/src/app.c[m [1mindex fd4d53ff..f84d0acc 100644[m [1m--- a/src/app.c[m [1m+++ b/src/app.c[m [36m@@ -5051,7 +5051,7 @@[m [miBool handleCommand_App(const char *cmd) {[m iArchive *zip = iClob(new_Archive());[m if (openFile_Archive(zip, path)) {[m if (!arg_Command(cmd)) {[m [31m- makeUserDataImporter_Dialog(path);[m [32m+[m[32m makeUserDataImporter_Widget(path);[m return iTrue;[m }[m const int bookmarks = argLabel_Command(cmd, "bookmarks");[m [1mdiff --git a/src/bookmarks.c b/src/bookmarks.c[m [1mindex bfc47783..f01c9c54 100644[m [1m--- a/src/bookmarks.c[m [1m+++ b/src/bookmarks.c[m [36m@@ -163,7 +163,7 @@[m [mstruct Impl_Bookmarks {[m int idEnum;[m iHash bookmarks; /* bookmark ID is the hash key */[m uint32_t recentFolderId; /* recently interacted with */[m [31m- iPtrArray remoteRequests; [m [32m+[m[32m iPtrArray remoteRequests;[m };[m [m iDefineTypeConstruction(Bookmarks)[m [36m@@ -252,7 +252,7 @@[m [mstatic void loadOldFormat_Bookmarks(iBookmarks *d, const char *dirPath) {[m /*----------------------------------------------------------------------------------------------*/[m [m iDeclareType(BookmarkLoader)[m [31m- [m [32m+[m struct Impl_BookmarkLoader {[m iTomlParser *toml;[m iBookmarks *bookmarks;[m [36m@@ -368,7 +368,7 @@[m [mstatic void load_BookmarkLoader(iBookmarkLoader *d, iStream *stream) {[m }[m [m iDefineTypeConstructionArgs(BookmarkLoader, (iBookmarks *b), b)[m [31m- [m [32m+[m /*----------------------------------------------------------------------------------------------*/[m [m static iBool isMatchingParent_Bookmark_(void *context, const iBookmark *bm) {[m [36m@@ -522,6 +522,11 @@[m [mstatic iRangei orderRange_Bookmarks_(const iBookmarks *d) {[m [m uint32_t add_Bookmarks(iBookmarks *d, const iString *url, const iString *title, const iString *tags,[m iChar icon) {[m [32m+[m[32m return addToFolder_Bookmarks(d, url, title, tags, icon, 0);[m [32m+[m[32m}[m [32m+[m [32m+[m[32muint32_t addToFolder_Bookmarks(iBookmarks *d, const iString *url, const iString *title,[m [32m+[m[32m const iString *tags, iChar icon, uint32_t folderId) {[m lock_Mutex(d->mtx);[m iBookmark *bm = new_Bookmark();[m if (url) {[m [36m@@ -540,6 +545,7 @@[m [muint32_t add_Bookmarks(iBookmarks *d, const iString *url, const iString *title,[m else {[m bm->order = ord.start - 1; /* First in lists. */[m }[m [32m+[m[32m bm->parentId = folderId;[m insert_Bookmarks_(d, bm);[m unlock_Mutex(d->mtx);[m return id_Bookmark(bm);[m [36m@@ -563,7 +569,7 @@[m [miBool updateBookmarkIcon_Bookmarks(iBookmarks *d, const iString *url, iChar icon[m iBool changed = iFalse;[m lock_Mutex(d->mtx);[m iForEach(Hash, i, &d->bookmarks) {[m [31m- iBookmark *bm = (iBookmark *) i.value; [m [32m+[m[32m iBookmark *bm = (iBookmark *) i.value;[m if (~bm->flags & remote_BookmarkFlag && ~bm->flags & userIcon_BookmarkFlag) {[m if (equalCase_String(&bm->url, url) && icon != bm->icon) {[m bm->icon = icon;[m [36m@@ -640,10 +646,10 @@[m [muint32_t findUrl_Bookmarks(const iBookmarks *d, const iString *url) {[m /*----------------------------------------------------------------------------------------------*/[m [m iDeclareType(MatchUrlArgs)[m [31m- [m [32m+[m struct Impl_MatchUrlArgs {[m const iString *url;[m [31m- const iString *identityFp; [m [32m+[m[32m const iString *identityFp;[m };[m [m static iBool matchUrlAndIdent_(iMatchUrlArgs *args, const iBookmark *bm) {[m [36m@@ -664,7 +670,7 @@[m [muint32_t findUrlIdent_Bookmarks(const iBookmarks *d, const iString *url, const i[m if (isEmpty_PtrArray(found)) {[m return 0;[m }[m [31m- return id_Bookmark(constFront_PtrArray(found)); [m [32m+[m[32m return id_Bookmark(constFront_PtrArray(found));[m }[m [m /*----------------------------------------------------------------------------------------------*/[m [1mdiff --git a/src/bookmarks.h b/src/bookmarks.h[m [1mindex bdc8f600..b4b3517d 100644[m [1m--- a/src/bookmarks.h[m [1m+++ b/src/bookmarks.h[m [36m@@ -32,7 +32,7 @@[m [miDeclareType(GmRequest)[m [m iDeclareType(Bookmark)[m iDeclareTypeConstruction(Bookmark)[m [31m- [m [32m+[m /* These values are not serialized as-is in bookmarks.ini. Instead, they are included in `tags`[m with a dot prefix. This helps retain backwards and forwards compatibility. */[m enum iBookmarkFlags {[m [36m@@ -88,6 +88,8 @@[m [mvoid deserialize_Bookmarks (iBookmarks *, iStream *ins, enum iImpor[m [m uint32_t add_Bookmarks (iBookmarks *, const iString *url, const iString *title,[m const iString *tags, iChar icon);[m [32m+[m[32muint32_t addToFolder_Bookmarks (iBookmarks *, const iString *url, const iString *title,[m [32m+[m[32m const iString *tags, iChar icon, uint32_t folderId);[m iBool remove_Bookmarks (iBookmarks *, uint32_t id);[m iBookmark * get_Bookmarks (iBookmarks *, uint32_t id);[m void reorder_Bookmarks (iBookmarks *, uint32_t id, int newOrder);[m [1mdiff --git a/src/gmdocument.c b/src/gmdocument.c[m [1mindex 0aaf5868..043b93e7 100644[m [1m--- a/src/gmdocument.c[m [1m+++ b/src/gmdocument.c[m [36m@@ -2687,6 +2687,10 @@[m [mstatic const iGmLink *link_GmDocument_(const iGmDocument *d, iGmLinkId id) {[m return NULL;[m }[m [m [32m+[m[32msize_t numLinks_GmDocument(const iGmDocument *d) {[m [32m+[m[32m return size_PtrArray(&d->links);[m [32m+[m[32m}[m [32m+[m const iString *linkUrl_GmDocument(const iGmDocument *d, iGmLinkId linkId) {[m const iGmLink *link = link_GmDocument_(d, linkId);[m return link ? &link->url : NULL;[m [1mdiff --git a/src/gmdocument.h b/src/gmdocument.h[m [1mindex 96db60db..34e8358a 100644[m [1m--- a/src/gmdocument.h[m [1m+++ b/src/gmdocument.h[m [36m@@ -248,6 +248,7 @@[m [menum iGmLinkPart {[m const iGmRun * findRun_GmDocument (const iGmDocument *, iInt2 pos);[m iRangecc findLoc_GmDocument (const iGmDocument *, iInt2 pos);[m const iGmRun * findRunAtLoc_GmDocument (const iGmDocument *, const char *loc);[m [32m+[m[32msize_t numLinks_GmDocument (const iGmDocument *); /* link IDs: 1...numLinks (inclusive) */[m const iString * linkUrl_GmDocument (const iGmDocument *, iGmLinkId linkId);[m iRangecc linkUrlRange_GmDocument (const iGmDocument *, iGmLinkId linkId);[m iRangecc linkLabel_GmDocument (const iGmDocument *, iGmLinkId linkId);[m [1mdiff --git a/src/macos.m b/src/macos.m[m [1mindex 15481c55..d42bdaf7 100644[m [1m--- a/src/macos.m[m [1m+++ b/src/macos.m[m [36m@@ -853,15 +853,23 @@[m [mstatic NSMenuItem *makeMenuItems_(NSMenu *menu, MenuCommands *commands, int atIn[m if (!subwidget) {[m subwidget = findWidget_Root(submenuId);[m }[m [31m- iAssert(subwidget);[m [31m- const iArray *items = userData_Object(subwidget);[m [31m- iAssert(items);[m [31m- makeMenuItems_(sub, commands, 0, isBookmarksMenu, constData_Array(items), size_Array(items));[m [31m- [item setSubmenu:sub];[m [31m- if (isBookmarksMenu) {[m [31m-#if defined (__MAC_10_13)[m [31m- [item setImage:[NSImage imageWithSystemSymbolName:@"folder" accessibilityDescription:nil]];[m [31m-#endif[m [32m+[m[32m if (subwidget) {[m [32m+[m[32m const iArray *items = userData_Object(subwidget);[m [32m+[m[32m iAssert(items);[m [32m+[m[32m makeMenuItems_(sub, commands, 0, isBookmarksMenu, constData_Array(items),[m [32m+[m[32m size_Array(items));[m [32m+[m[32m [item setSubmenu:sub];[m [32m+[m[32m if (isBookmarksMenu) {[m [32m+[m[32m #if defined (__MAC_10_13)[m [32m+[m[32m [item setImage:[NSImage imageWithSystemSymbolName:@"folder"[m [32m+[m[32m accessibilityDescription:nil]];[m [32m+[m[32m #endif[m [32m+[m[32m }[m [32m+[m[32m }[m [32m+[m[32m else {[m [32m+[m[32m [sub release];[m [32m+[m[32m [item release];[m [32m+[m[32m continue;[m }[m [sub release];[m }[m [36m@@ -869,7 +877,8 @@[m [mstatic NSMenuItem *makeMenuItems_(NSMenu *menu, MenuCommands *commands, int atIn[m item.action = (hasCommand ? @selector(postMenuItemCommand:) : nil);[m if (isBookmarksMenu && hasCommand && startsWith_CStr(items[i].command, "!open ")) {[m #if defined (__MAC_10_13)[m [31m- [item setImage:[NSImage imageWithSystemSymbolName:@"bookmark.fill" accessibilityDescription:nil]];[m [32m+[m[32m [item setImage:[NSImage imageWithSystemSymbolName:@"bookmark.fill"[m [32m+[m[32m accessibilityDescription:nil]];[m #endif[m }[m }[m [1mdiff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c[m [1mindex 4229989d..42711739 100644[m [1m--- a/src/ui/documentwidget.c[m [1m+++ b/src/ui/documentwidget.c[m [36m@@ -71,6 +71,7 @@[m [mSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */[m #include[m #include [m #include [m [32m+[m[32m#include [m #include [m #include [m #include [m [36m@@ -2510,13 +2511,6 @@[m [mstatic const iString *saveToDownloads_(const iString *url, const iString *mime,[m return savePath;[m }[m [m [31m-static void addAllLinks_(void *context, const iGmRun *run) {[m [31m- iPtrArray *links = context;[m [31m- if (~run->flags & decoration_GmRunFlag && run->linkId) {[m [31m- pushBack_PtrArray(links, run);[m [31m- }[m [31m-}[m [31m-[m static iBool handlePinch_DocumentWidget_(iDocumentWidget *d, const char *cmd) {[m if (equal_Command(cmd, "pinch.began")) {[m d->pinchZoomInitial = d->pinchZoomPosted = prefs_App()->zoomPercent;[m [36m@@ -3593,51 +3587,113 @@[m [mstatic iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)[m return iTrue;[m }[m else if (equal_Command(cmd, "bookmark.links") && document_App() == d) {[m [31m- iPtrArray *links = collectNew_PtrArray();[m [31m- render_GmDocument(d->view->doc, (iRangei){ 0, size_GmDocument(d->view->doc).y }, addAllLinks_, links);[m [32m+[m[32m iIntSet *linkIds = collectNew_IntSet();[m /* Find links that aren't already bookmarked. */[m [31m- iForEach(PtrArray, i, links) {[m [31m- const iGmRun *run = i.ptr;[m [31m- uint32_t bmid;[m [31m- if ((bmid = findUrl_Bookmarks(bookmarks_App(),[m [31m- linkUrl_GmDocument(d->view->doc, run->linkId))) != 0) {[m [32m+[m[32m const iGmDocument *doc = d->view->doc;[m [32m+[m[32m for (size_t linkId = 1; linkId <= numLinks_GmDocument(doc); linkId++) {[m [32m+[m[32m uint32_t bmid;[m [32m+[m[32m if ((bmid = findUrl_Bookmarks(bookmarks_App(), linkUrl_GmDocument(doc, linkId))) != 0) {[m const iBookmark *bm = get_Bookmarks(bookmarks_App(), bmid);[m /* We can import local copies of remote bookmarks. */[m if (~bm->flags & remote_BookmarkFlag) {[m [31m- remove_PtrArrayIterator(&i);[m [32m+[m[32m continue; /* This one is bookmarked. */[m }[m }[m [32m+[m[32m insert_IntSet(linkIds, linkId);[m }[m [31m- if (!isEmpty_PtrArray(links)) {[m [32m+[m[32m if (!isEmpty_IntSet(linkIds)) {[m if (argLabel_Command(cmd, "confirm")) {[m [31m- const size_t count = size_PtrArray(links);[m [31m- makeQuestion_Widget([m [31m- uiHeading_ColorEscape "${heading.import.bookmarks}",[m [31m- formatCStrs_Lang("dlg.import.found.n", count),[m [31m- (iMenuItem[]){ { "${cancel}" },[m [31m- { format_CStr(cstrCount_Lang("dlg.import.add.n", (int) count),[m [31m- uiTextAction_ColorEscape,[m [31m- count),[m [31m- 0,[m [31m- 0,[m [31m- "bookmark.links" } },[m [31m- 2);[m [32m+[m[32m const size_t count = size_IntSet(linkIds);[m [32m+[m[32m makeLinkImporter_Widget(count);[m }[m else {[m [31m- iConstForEach(PtrArray, j, links) {[m [31m- const iGmRun *run = j.ptr;[m [31m- add_Bookmarks(bookmarks_App(),[m [31m- linkUrl_GmDocument(d->view->doc, run->linkId),[m [31m- collect_String(newRange_String(run->text)),[m [31m- NULL,[m [31m- 0x1f588 /* pin */);[m [32m+[m[32m const uint32_t intoFolder = argLabel_Command(cmd, "folder");[m [32m+[m[32m const iBool withHeadings = argLabel_Command(cmd, "headings");[m [32m+[m[32m /* We need to prepare some auxiliary bookkeeping to keep track of the folders[m [32m+[m[32m that are created for each section of the page. */[m [32m+[m[32m uint32_t parentId = intoFolder;[m [32m+[m[32m uint32_t hierarchy[] = { intoFolder, 0, 0, 0, 0, 0 };[m [32m+[m[32m const iPtrArray *headings = headings_GmDocument(doc);[m [32m+[m[32m const iGmHeading *head = isEmpty_Array(headings) ? NULL : constData_Array(headings);[m [32m+[m[32m const iGmHeading *headFirst = head;[m [32m+[m[32m const iGmHeading *headEnd = isEmpty_Array(headings) ? NULL : constEnd_Array(headings);[m [32m+[m[32m uint32_t *headingBookmarkIds = calloc(size_Array(headings), sizeof(uint32_t));[m [32m+[m[32m /* We will create folders as we go and afterwards delete the ones that[m [32m+[m[32m didn't end up containing any links. */[m [32m+[m[32m iDeclareType(InfoNode);[m [32m+[m[32m struct Impl_InfoNode {[m [32m+[m[32m iHashNode node;[m [32m+[m[32m size_t numChildren;[m [32m+[m[32m };[m [32m+[m[32m iHash *folderInfo = new_Hash();[m [32m+[m[32m /* `linkIds` only contains the new links that need to be bookmarked. */[m [32m+[m[32m iConstForEach(IntSet, j, linkIds) {[m [32m+[m[32m const iGmLinkId linkId = *j.value;[m [32m+[m[32m iRangecc linkRange = linkUrlRange_GmDocument(doc, linkId);[m [32m+[m[32m /* Advance in the headings until we reach the one that this link is under. */[m [32m+[m[32m while (withHeadings && head < headEnd && linkRange.start > head->text.start) {[m [32m+[m[32m if (!headingBookmarkIds[head - headFirst]) {[m [32m+[m[32m const int hlev = head->level + 1;[m [32m+[m[32m parentId = addToFolder_Bookmarks(bookmarks_App(),[m [32m+[m[32m NULL,[m [32m+[m[32m collectNewRange_String(head->text),[m [32m+[m[32m NULL,[m [32m+[m[32m 0,[m [32m+[m[32m hierarchy[hlev - 1]);[m [32m+[m[32m hierarchy[hlev] = parentId;[m [32m+[m[32m iInfoNode *info = iMalloc(InfoNode); {[m [32m+[m[32m info->node.key = parentId;[m [32m+[m[32m info->numChildren = 0;[m [32m+[m[32m }[m [32m+[m[32m insert_Hash(folderInfo, &info->node);[m [32m+[m[32m headingBookmarkIds[head - headFirst] = parentId;[m [32m+[m[32m parentId = parentId;[m [32m+[m[32m /* Keep track of the hierarchy so we know at any time the parent[m [32m+[m[32m of each heading level. */[m [32m+[m[32m for (int k = 1; k < hlev; k++) {[m [32m+[m[32m if (hierarchy[k] == 0) {[m [32m+[m[32m hierarchy[k] = parentId;[m [32m+[m[32m }[m [32m+[m[32m }[m [32m+[m[32m hierarchy[hlev + 1] = parentId;[m [32m+[m[32m hierarchy[hlev + 2] = parentId;[m [32m+[m[32m }[m [32m+[m[32m head++;[m [32m+[m[32m }[m [32m+[m[32m addToFolder_Bookmarks(bookmarks_App(),[m [32m+[m[32m linkUrl_GmDocument(doc, linkId),[m [32m+[m[32m collectNewRange_String(linkLabel_GmDocument(doc, linkId)),[m [32m+[m[32m NULL,[m [32m+[m[32m 0x1f588 /* pin */,[m [32m+[m[32m withHeadings ? parentId : intoFolder);[m [32m+[m[32m /* Count children. */[m [32m+[m[32m if (withHeadings) {[m [32m+[m[32m for (uint32_t pid = parentId;[m [32m+[m[32m pid && pid != intoFolder;[m [32m+[m[32m pid = get_Bookmarks(bookmarks_App(), pid)->parentId) {[m [32m+[m[32m iInfoNode *n = (iInfoNode *) value_Hash(folderInfo, pid);[m [32m+[m[32m iAssert(n);[m [32m+[m[32m n->numChildren++;[m [32m+[m[32m }[m [32m+[m[32m }[m [32m+[m[32m }[m [32m+[m[32m iForEach(Hash, iter, folderInfo) {[m [32m+[m[32m iInfoNode *n = (iInfoNode *) iter.value;[m [32m+[m[32m if (n->numChildren == 0) {[m [32m+[m[32m /* This folder was not needed. */[m [32m+[m[32m remove_Bookmarks(bookmarks_App(), n->node.key);[m [32m+[m[32m }[m [32m+[m[32m free(remove_HashIterator(&iter));[m }[m [32m+[m[32m delete_Hash(folderInfo);[m [32m+[m[32m free(headingBookmarkIds);[m postCommand_App("bookmarks.changed");[m }[m }[m else {[m makeSimpleMessage_Widget(uiHeading_ColorEscape "${heading.import.bookmarks}",[m [31m- "${dlg.import.notnew}");[m [32m+[m[32m numLinks_GmDocument(doc) == 0 ? "${dlg.import.notfound}"[m [32m+[m[32m : "${dlg.import.notnew}");[m }[m return iTrue;[m }[m [1mdiff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c[m [1mindex d6870278..3378671a 100644[m [1m--- a/src/ui/sidebarwidget.c[m [1m+++ b/src/ui/sidebarwidget.c[m [36m@@ -1790,6 +1790,7 @@[m [mstatic iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)[m postCommand_App("bookmarks.changed");[m }[m else {[m [32m+[m[32m setFocus_Widget(NULL);[m const size_t numBookmarks = numBookmarks_(list);[m makeQuestion_Widget(uiHeading_ColorEscape "${heading.confirm.bookmarks.delete}",[m formatCStrs_Lang("dlg.confirm.bookmarks.delete.n", numBookmarks),[m [36m@@ -2276,6 +2277,7 @@[m [mstatic void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect,[m const iListWidget *list) {[m const iSidebarWidget *sidebar = findParentClass_Widget(constAs_Widget(list),[m &Class_SidebarWidget);[m [32m+[m[32m const iBool isListFocus = isFocused_Widget(list);[m const iBool isMenuVisible = isVisible_Widget(sidebar->menu);[m const iBool isDragging = constDragItem_ListWidget(list) == d;[m const iBool isEditing = sidebar->isEditing; /* only on mobile */[m [36m@@ -2530,6 +2532,10 @@[m [mstatic void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect,[m }[m iEndCollect();[m }[m [32m+[m[32m if (isListFocus && isHover && !isTerminal_Platform()) {[m [32m+[m[32m /* Visualize the keyboard cursor. */[m [32m+[m[32m drawRect_Paint(p, shrunk_Rect(itemRect, one_I2()), uiTextAction_ColorId);[m [32m+[m[32m }[m }[m [m iBeginDefineSubclass(SidebarWidget, Widget)[m [1mdiff --git a/src/ui/util.c b/src/ui/util.c[m [1mindex 296d0280..59866400 100644[m [1m--- a/src/ui/util.c[m [1m+++ b/src/ui/util.c[m [36m@@ -2948,10 +2948,12 @@[m [mstatic void addPrefsInputWithHeading_(iWidget *headings, iWidget *values,[m addDialogInputWithHeading_(headings, values, format_CStr("${%s}", id), id, input);[m }[m [m [31m-static void addDialogToggle_(iWidget *headings, iWidget *values,[m [32m+[m[32mstatic iWidget *addDialogToggle_(iWidget *headings, iWidget *values,[m const char *heading, const char *toggleId) {[m [32m+[m[32m iWidget *toggle;[m addChild_Widget(headings, iClob(makeHeading_Widget(heading)));[m [31m- addChild_Widget(values, iClob(makeToggle_Widget(toggleId)));[m [32m+[m[32m addChild_Widget(values, toggle = iClob(makeToggle_Widget(toggleId)));[m [32m+[m[32m return toggle;[m }[m [m static void addDialogToggleGroup_(iWidget *headings, iWidget *values, const char *title,[m [36m@@ -4367,6 +4369,74 @@[m [miWidget *makeSnippetCreation_Widget(void) {[m [m /*----------------------------------------------------------------------------------------------*/[m [m [32m+[m[32mstatic iBool handleLinkImporterCommands_(iWidget *dlg, const char *cmd) {[m [32m+[m[32m if (equalWidget_Command(cmd, dlg, "cancel")) {[m [32m+[m[32m setupSheetTransition_Mobile(dlg, dialogTransitionDir_Widget(dlg));[m [32m+[m[32m destroy_Widget(dlg);[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equalWidget_Command(cmd, dlg, "dlg.import.intofolder")) {[m [32m+[m[32m updateDropdownSelection_LabelWidget(findChild_Widget(dlg, "dlg.import.intofolder"),[m [32m+[m[32m format_CStr(" arg:%d", arg_Command(cmd)));[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m else if (equalWidget_Command(cmd, dlg, "dlg.import.accept")) {[m [32m+[m[32m const char *intoFolder =[m [32m+[m[32m selectedDropdownCommand_LabelWidget(findChild_Widget(dlg, "dlg.import.intofolder"));[m [32m+[m[32m const iBool headings = isSelected_Widget(findChild_Widget(dlg, "dlg.import.headings"));[m [32m+[m[32m postCommandf_App("bookmark.links folder:%d headings:%d", arg_Command(intoFolder), headings);[m [32m+[m[32m setupSheetTransition_Mobile(dlg, dialogTransitionDir_Widget(dlg));[m [32m+[m[32m destroy_Widget(dlg);[m [32m+[m[32m return iTrue;[m [32m+[m[32m }[m [32m+[m[32m return iFalse;[m [32m+[m[32m}[m [32m+[m [32m+[m[32miWidget *makeLinkImporter_Widget(size_t count) {[m [32m+[m[32m const iMenuItem actions[] = {[m [32m+[m[32m { "${cancel}" },[m [32m+[m[32m { format_CStr([m [32m+[m[32m cstrCount_Lang("dlg.import.add.n", (int) count), uiTextAction_ColorEscape, count),[m [32m+[m[32m 0,[m [32m+[m[32m 0,[m [32m+[m[32m "dlg.import.accept" },[m [32m+[m[32m };[m [32m+[m[32m iWidget *dlg = NULL;[m [32m+[m[32m if (isUsingPanelLayout_Mobile()) {[m [32m+[m[32m /* TODO */[m [32m+[m[32m }[m [32m+[m[32m else {[m [32m+[m[32m iWidget *headings, *values;[m [32m+[m[32m dlg = makeSheet_Widget("linkbookmarking");[m [32m+[m[32m addDialogTitle_(dlg, "${heading.import.bookmarks}", "heading.import.bookmarks");[m [32m+[m[32m addWrappedLabel_Widget(dlg, formatCStrs_Lang("dlg.import.found.n", count), NULL);[m [32m+[m[32m addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI)));[m [32m+[m[32m addChild_Widget(dlg, iClob(makeTwoColumns_Widget(&headings, &values)));[m [32m+[m[32m const iArray *folders = makeBookmarkFolderActions_MenuItem("dlg.import.intofolder", iTrue, 0);[m [32m+[m[32m iLabelWidget *intoFolder = addDialogDropMenu_(headings,[m [32m+[m[32m values,[m [32m+[m[32m "${dlg.import.intofolder}",[m [32m+[m[32m constData_Array(folders),[m [32m+[m[32m iInvalidSize,[m [32m+[m[32m "dlg.import.intofolder");[m [32m+[m[32m updateDropdownSelection_LabelWidget([m [32m+[m[32m intoFolder, format_CStr(" arg:%zu", recentFolder_Bookmarks(bookmarks_App())));[m [32m+[m[32m setToggle_Widget([m [32m+[m[32m addDialogToggle_(headings, values, "${dlg.import.headings}", "dlg.import.headings"),[m [32m+[m[32m iTrue);[m [32m+[m[32m addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI)));[m [32m+[m[32m addChild_Widget(dlg, iClob(makeDialogButtons_Widget(actions, iElemCount(actions))));[m [32m+[m[32m addChild_Widget(get_Root()->widget, iClob(dlg));[m [32m+[m[32m arrange_Widget(dlg);[m [32m+[m[32m arrange_Widget(dlg);[m [32m+[m[32m }[m [32m+[m[32m setCommandHandler_Widget(dlg, handleLinkImporterCommands_);[m [32m+[m[32m setupSheetTransition_Mobile(dlg, incoming_TransitionFlag | dialogTransitionDir_Widget(dlg));[m [32m+[m[32m return dlg;[m [32m+[m[32m}[m [32m+[m [32m+[m[32m/*----------------------------------------------------------------------------------------------*/[m [32m+[m iWidget *makeIdentityCreation_Widget(void) {[m const iMenuItem actions[] = { { "${dlg.newident.more}", 0, 0, "ident.showmore" },[m { "---" },[m [36m@@ -4731,7 +4801,7 @@[m [mstatic iBool handleUserDataImporterCommands_(iWidget *dlg, const char *cmd) {[m return iFalse;[m }[m [m [31m-iWidget *makeUserDataImporter_Dialog(const iString *archivePath) {[m [32m+[m[32miWidget *makeUserDataImporter_Widget(const iString *archivePath) {[m iWidget *dlg;[m const iMenuItem actions[] = {[m { "${menu.selectall}", 0, 0, "importer.selectall" },[m [1mdiff --git a/src/ui/util.h b/src/ui/util.h[m [1mindex 6d23ad00..88835167 100644[m [1m--- a/src/ui/util.h[m [1m+++ b/src/ui/util.h[m [36m@@ -407,7 +407,8 @@[m [miWidget * makeSiteSpecificSettings_Widget (const iString *url);[m iWidget * makeSnippetCreation_Widget (void);[m iWidget * makeTranslation_Widget (iWidget *parent);[m iWidget * makeGlyphFinder_Widget (void);[m [31m-iWidget * makeUserDataImporter_Dialog (const iString *archivePath);[m [32m+[m[32miWidget * makeUserDataImporter_Widget (const iString *archivePath);[m [32m+[m[32miWidget * makeLinkImporter_Widget (size_t count);[m [m const char * languageId_String (const iString *menuItemLabel);[m int languageIndex_CStr (const char *langId);[m
text/gemini; charset=utf-8
This content has been proxied by September (3851b).