summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-05-04 22:34:31 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-05-04 22:34:31 +0300
commit7a4d9cc39337f93cb3b9e5bca982b04f0a7c7da5 (patch)
tree1e55a93e0e69b91d4b50c75ce581e03d6b54e229
parentd827dab6cdbc376b6d33cbe5924588e92cffcf54 (diff)
Split view pinning, "linksplit" special tag
A navigation aid: when in split mode, option to keep one of the tabs pinned to the current page so all links open to the side. Added "linksplit" bookmark tag to activate view splitting when opening a link on the page.
-rw-r--r--po/en.po12
-rw-r--r--res/lang/de.binbin19904 -> 20024 bytes
-rw-r--r--res/lang/en.binbin18392 -> 18512 bytes
-rw-r--r--res/lang/es.binbin20482 -> 20602 bytes
-rw-r--r--res/lang/fi.binbin20186 -> 20306 bytes
-rw-r--r--res/lang/fr.binbin20968 -> 21088 bytes
-rw-r--r--res/lang/ia.binbin20376 -> 20496 bytes
-rw-r--r--res/lang/ie.binbin19606 -> 19726 bytes
-rw-r--r--res/lang/ru.binbin30118 -> 30238 bytes
-rw-r--r--res/lang/sr.binbin28583 -> 28703 bytes
-rw-r--r--res/lang/tok.binbin18882 -> 19002 bytes
-rw-r--r--res/lang/zh_Hans.binbin17782 -> 17902 bytes
-rw-r--r--res/lang/zh_Hant.binbin17856 -> 17976 bytes
-rw-r--r--src/app.c21
-rw-r--r--src/bookmarks.c10
-rw-r--r--src/bookmarks.h8
-rw-r--r--src/feeds.c2
-rw-r--r--src/prefs.c1
-rw-r--r--src/prefs.h1
-rw-r--r--src/ui/documentwidget.c44
-rw-r--r--src/ui/sidebarwidget.c30
-rw-r--r--src/ui/util.c25
-rw-r--r--src/ui/window.c23
23 files changed, 132 insertions, 45 deletions
diff --git a/po/en.po b/po/en.po
index e51b434c..e01d303b 100644
--- a/po/en.po
+++ b/po/en.po
@@ -1066,6 +1066,18 @@ msgstr "Collapse preformatted:"
1066msgid "prefs.archive.openindex" 1066msgid "prefs.archive.openindex"
1067msgstr "Open archive indices:" 1067msgstr "Open archive indices:"
1068 1068
1069msgid "prefs.pinsplit"
1070msgstr "Split view pinning:"
1071
1072msgid "prefs.pinsplit.none"
1073msgstr "None"
1074
1075msgid "prefs.pinsplit.left"
1076msgstr "Left Tab"
1077
1078msgid "prefs.pinsplit.right"
1079msgstr "Right Tab"
1080
1069msgid "prefs.smoothscroll" 1081msgid "prefs.smoothscroll"
1070msgstr "Smooth scrolling:" 1082msgstr "Smooth scrolling:"
1071 1083
diff --git a/res/lang/de.bin b/res/lang/de.bin
index 9ca2be68..846dc8e1 100644
--- a/res/lang/de.bin
+++ b/res/lang/de.bin
Binary files differ
diff --git a/res/lang/en.bin b/res/lang/en.bin
index 1a18bc38..64e150d5 100644
--- a/res/lang/en.bin
+++ b/res/lang/en.bin
Binary files differ
diff --git a/res/lang/es.bin b/res/lang/es.bin
index b37813d1..8fdd3d8b 100644
--- a/res/lang/es.bin
+++ b/res/lang/es.bin
Binary files differ
diff --git a/res/lang/fi.bin b/res/lang/fi.bin
index 4e2bee52..7ab951b4 100644
--- a/res/lang/fi.bin
+++ b/res/lang/fi.bin
Binary files differ
diff --git a/res/lang/fr.bin b/res/lang/fr.bin
index 8cfb9331..01cc217d 100644
--- a/res/lang/fr.bin
+++ b/res/lang/fr.bin
Binary files differ
diff --git a/res/lang/ia.bin b/res/lang/ia.bin
index 5310ac72..eb3e44a8 100644
--- a/res/lang/ia.bin
+++ b/res/lang/ia.bin
Binary files differ
diff --git a/res/lang/ie.bin b/res/lang/ie.bin
index 151cdf7d..62d74fa7 100644
--- a/res/lang/ie.bin
+++ b/res/lang/ie.bin
Binary files differ
diff --git a/res/lang/ru.bin b/res/lang/ru.bin
index 95ebe75d..1e8a4a90 100644
--- a/res/lang/ru.bin
+++ b/res/lang/ru.bin
Binary files differ
diff --git a/res/lang/sr.bin b/res/lang/sr.bin
index 0f254c81..08682c88 100644
--- a/res/lang/sr.bin
+++ b/res/lang/sr.bin
Binary files differ
diff --git a/res/lang/tok.bin b/res/lang/tok.bin
index d6b0aba7..5061520c 100644
--- a/res/lang/tok.bin
+++ b/res/lang/tok.bin
Binary files differ
diff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin
index 249d88d5..cee2f096 100644
--- a/res/lang/zh_Hans.bin
+++ b/res/lang/zh_Hans.bin
Binary files differ
diff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin
index 99e29957..67240240 100644
--- a/res/lang/zh_Hant.bin
+++ b/res/lang/zh_Hant.bin
Binary files differ
diff --git a/src/app.c b/src/app.c
index 4872799b..39f0346e 100644
--- a/src/app.c
+++ b/src/app.c
@@ -1446,6 +1446,14 @@ static void updatePrefsThemeButtons_(iWidget *d) {
1446 } 1446 }
1447} 1447}
1448 1448
1449static void updatePrefsPinSplitButtons_(iWidget *d, int value) {
1450 for (size_t i = 0; i < 3; i++) {
1451 setFlags_Widget(findChild_Widget(d, format_CStr("prefs.pinsplit.%u", i)),
1452 selected_WidgetFlag,
1453 i == value);
1454 }
1455}
1456
1449static void updateDropdownSelection_(iLabelWidget *dropButton, const char *selectedCommand) { 1457static void updateDropdownSelection_(iLabelWidget *dropButton, const char *selectedCommand) {
1450 iWidget *menu = findChild_Widget(as_Widget(dropButton), "menu"); 1458 iWidget *menu = findChild_Widget(as_Widget(dropButton), "menu");
1451 iForEach(ObjectList, i, children_Widget(menu)) { 1459 iForEach(ObjectList, i, children_Widget(menu)) {
@@ -1526,6 +1534,10 @@ static iBool handlePrefsCommands_(iWidget *d, const char *cmd) {
1526 setFlags_Widget(findChild_Widget(d, "prefs.quoteicon.1"), selected_WidgetFlag, arg == 1); 1534 setFlags_Widget(findChild_Widget(d, "prefs.quoteicon.1"), selected_WidgetFlag, arg == 1);
1527 return iFalse; 1535 return iFalse;
1528 } 1536 }
1537 else if (equal_Command(cmd, "pinsplit.set")) {
1538 updatePrefsPinSplitButtons_(d, arg_Command(cmd));
1539 return iFalse;
1540 }
1529 else if (equal_Command(cmd, "doctheme.dark.set")) { 1541 else if (equal_Command(cmd, "doctheme.dark.set")) {
1530 updateColorThemeButton_(findChild_Widget(d, "prefs.doctheme.dark"), arg_Command(cmd)); 1542 updateColorThemeButton_(findChild_Widget(d, "prefs.doctheme.dark"), arg_Command(cmd));
1531 return iFalse; 1543 return iFalse;
@@ -1824,6 +1836,10 @@ iBool handleCommand_App(const char *cmd) {
1824 } 1836 }
1825 return iTrue; 1837 return iTrue;
1826 } 1838 }
1839 else if (equal_Command(cmd, "pinsplit.set")) {
1840 d->prefs.pinSplit = arg_Command(cmd);
1841 return iTrue;
1842 }
1827 else if (equal_Command(cmd, "theme.set")) { 1843 else if (equal_Command(cmd, "theme.set")) {
1828 const int isAuto = argLabel_Command(cmd, "auto"); 1844 const int isAuto = argLabel_Command(cmd, "auto");
1829 d->prefs.theme = arg_Command(cmd); 1845 d->prefs.theme = arg_Command(cmd);
@@ -2030,6 +2046,7 @@ iBool handleCommand_App(const char *cmd) {
2030 iRoot *oldRoot = root; 2046 iRoot *oldRoot = root;
2031 if (newTab & otherRoot_OpenTabFlag) { 2047 if (newTab & otherRoot_OpenTabFlag) {
2032 root = otherRoot_Window(d->window, root); 2048 root = otherRoot_Window(d->window, root);
2049 setKeyRoot_Window(d->window, root);
2033 setCurrent_Root(root); /* need to change for widget creation */ 2050 setCurrent_Root(root); /* need to change for widget creation */
2034 } 2051 }
2035 iDocumentWidget *doc = document_Command(cmd); 2052 iDocumentWidget *doc = document_Command(cmd);
@@ -2172,6 +2189,7 @@ iBool handleCommand_App(const char *cmd) {
2172 setToggle_Widget(findChild_Widget(dlg, "prefs.archive.openindex"), d->prefs.openArchiveIndexPages); 2189 setToggle_Widget(findChild_Widget(dlg, "prefs.archive.openindex"), d->prefs.openArchiveIndexPages);
2173 setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme); 2190 setToggle_Widget(findChild_Widget(dlg, "prefs.ostheme"), d->prefs.useSystemTheme);
2174 setToggle_Widget(findChild_Widget(dlg, "prefs.customframe"), d->prefs.customFrame); 2191 setToggle_Widget(findChild_Widget(dlg, "prefs.customframe"), d->prefs.customFrame);
2192 updatePrefsPinSplitButtons_(dlg, d->prefs.pinSplit);
2175 updateDropdownSelection_(findChild_Widget(dlg, "prefs.uilang"), cstr_String(&d->prefs.uiLanguage)); 2193 updateDropdownSelection_(findChild_Widget(dlg, "prefs.uilang"), cstr_String(&d->prefs.uiLanguage));
2176 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize); 2194 setToggle_Widget(findChild_Widget(dlg, "prefs.retainwindow"), d->prefs.retainWindowSize);
2177 setText_InputWidget(findChild_Widget(dlg, "prefs.uiscale"), 2195 setText_InputWidget(findChild_Widget(dlg, "prefs.uiscale"),
@@ -2234,7 +2252,8 @@ iBool handleCommand_App(const char *cmd) {
2234 } 2252 }
2235 else if (equal_Command(cmd, "navigate.home")) { 2253 else if (equal_Command(cmd, "navigate.home")) {
2236 /* Look for bookmarks tagged "homepage". */ 2254 /* Look for bookmarks tagged "homepage". */
2237 iRegExp *pattern = iClob(new_RegExp("\\bhomepage\\b", caseInsensitive_RegExpOption)); 2255 iRegExp *pattern = iClob(new_RegExp("\\b" homepage_BookmarkTag "\\b",
2256 caseInsensitive_RegExpOption));
2238 const iPtrArray *homepages = 2257 const iPtrArray *homepages =
2239 list_Bookmarks(d->bookmarks, NULL, filterTagsRegExp_Bookmarks, pattern); 2258 list_Bookmarks(d->bookmarks, NULL, filterTagsRegExp_Bookmarks, pattern);
2240 if (isEmpty_PtrArray(homepages)) { 2259 if (isEmpty_PtrArray(homepages)) {
diff --git a/src/bookmarks.c b/src/bookmarks.c
index 5fff9c75..1f887c98 100644
--- a/src/bookmarks.c
+++ b/src/bookmarks.c
@@ -219,7 +219,7 @@ iBool remove_Bookmarks(iBookmarks *d, uint32_t id) {
219 if (bm) { 219 if (bm) {
220 /* If this is a remote source, make sure all the remote bookmarks are 220 /* If this is a remote source, make sure all the remote bookmarks are
221 removed as well. */ 221 removed as well. */
222 if (hasTag_Bookmark(bm, "remotesource")) { 222 if (hasTag_Bookmark(bm, remoteSource_BookmarkTag)) {
223 iForEach(Hash, i, &d->bookmarks) { 223 iForEach(Hash, i, &d->bookmarks) {
224 iBookmark *j = (iBookmark *) i.value; 224 iBookmark *j = (iBookmark *) i.value;
225 if (j->sourceId == id_Bookmark(bm)) { 225 if (j->sourceId == id_Bookmark(bm)) {
@@ -240,7 +240,7 @@ iBool updateBookmarkIcon_Bookmarks(iBookmarks *d, const iString *url, iChar icon
240 const uint32_t id = findUrl_Bookmarks(d, url); 240 const uint32_t id = findUrl_Bookmarks(d, url);
241 if (id) { 241 if (id) {
242 iBookmark *bm = get_Bookmarks(d, id); 242 iBookmark *bm = get_Bookmarks(d, id);
243 if (!hasTag_Bookmark(bm, "remote") && !hasTag_Bookmark(bm, "usericon")) { 243 if (!hasTag_Bookmark(bm, remote_BookmarkTag) && !hasTag_Bookmark(bm, userIcon_BookmarkTag)) {
244 if (icon != bm->icon) { 244 if (icon != bm->icon) {
245 bm->icon = icon; 245 bm->icon = icon;
246 changed = iTrue; 246 changed = iTrue;
@@ -257,7 +257,7 @@ iChar siteIcon_Bookmarks(const iBookmarks *d, const iString *url) {
257 } 257 }
258 static iRegExp *tagPattern_; 258 static iRegExp *tagPattern_;
259 if (!tagPattern_) { 259 if (!tagPattern_) {
260 tagPattern_ = new_RegExp("\\busericon\\b", caseSensitive_RegExpOption); 260 tagPattern_ = new_RegExp("\\b" userIcon_BookmarkTag "\\b", caseSensitive_RegExpOption);
261 } 261 }
262 const iRangecc urlRoot = urlRoot_String(url); 262 const iRangecc urlRoot = urlRoot_String(url);
263 size_t matchingSize = iInvalidSize; /* we'll pick the shortest matching */ 263 size_t matchingSize = iInvalidSize; /* we'll pick the shortest matching */
@@ -412,7 +412,7 @@ const iString *bookmarkListPage_Bookmarks(const iBookmarks *d, enum iBookmarkLis
412 412
413static iBool isRemoteSource_Bookmark_(void *context, const iBookmark *d) { 413static iBool isRemoteSource_Bookmark_(void *context, const iBookmark *d) {
414 iUnused(context); 414 iUnused(context);
415 return hasTag_Bookmark(d, "remotesource"); 415 return hasTag_Bookmark(d, remoteSource_BookmarkTag);
416} 416}
417 417
418void remoteRequestFinished_Bookmarks_(iBookmarks *d, iGmRequest *req) { 418void remoteRequestFinished_Bookmarks_(iBookmarks *d, iGmRequest *req) {
@@ -484,7 +484,7 @@ void fetchRemote_Bookmarks(iBookmarks *d) {
484 size_t numRemoved = 0; 484 size_t numRemoved = 0;
485 iForEach(Hash, i, &d->bookmarks) { 485 iForEach(Hash, i, &d->bookmarks) {
486 iBookmark *bm = (iBookmark *) i.value; 486 iBookmark *bm = (iBookmark *) i.value;
487 if (hasTag_Bookmark(bm, "remote")) { 487 if (hasTag_Bookmark(bm, remote_BookmarkTag)) {
488 remove_HashIterator(&i); 488 remove_HashIterator(&i);
489 delete_Bookmark(bm); 489 delete_Bookmark(bm);
490 numRemoved++; 490 numRemoved++;
diff --git a/src/bookmarks.h b/src/bookmarks.h
index ab9c683b..635682d1 100644
--- a/src/bookmarks.h
+++ b/src/bookmarks.h
@@ -32,6 +32,14 @@ iDeclareType(GmRequest)
32iDeclareType(Bookmark) 32iDeclareType(Bookmark)
33iDeclareTypeConstruction(Bookmark) 33iDeclareTypeConstruction(Bookmark)
34 34
35#define headings_BookmarkTag "headings"
36#define homepage_BookmarkTag "homepage"
37#define linkSplit_BookmarkTag "linksplit"
38#define remote_BookmarkTag "remote"
39#define remoteSource_BookmarkTag "remotesource"
40#define subscribed_BookmarkTag "subscribed"
41#define userIcon_BookmarkTag "usericon"
42
35struct Impl_Bookmark { 43struct Impl_Bookmark {
36 iHashNode node; 44 iHashNode node;
37 iString url; 45 iString url;
diff --git a/src/feeds.c b/src/feeds.c
index 2f4714f3..dcd97b43 100644
--- a/src/feeds.c
+++ b/src/feeds.c
@@ -95,7 +95,7 @@ static void init_FeedJob(iFeedJob *d, const iBookmark *bookmark) {
95 init_PtrArray(&d->results); 95 init_PtrArray(&d->results);
96 iZap(d->startTime); 96 iZap(d->startTime);
97 d->isFirstUpdate = iFalse; 97 d->isFirstUpdate = iFalse;
98 d->checkHeadings = hasTag_Bookmark(bookmark, "headings"); 98 d->checkHeadings = hasTag_Bookmark(bookmark, headings_BookmarkTag);
99} 99}
100 100
101static void deinit_FeedJob(iFeedJob *d) { 101static void deinit_FeedJob(iFeedJob *d) {
diff --git a/src/prefs.c b/src/prefs.c
index e26efaba..e6eba2a6 100644
--- a/src/prefs.c
+++ b/src/prefs.c
@@ -37,6 +37,7 @@ void init_Prefs(iPrefs *d) {
37 d->zoomPercent = 100; 37 d->zoomPercent = 100;
38 d->sideIcon = iTrue; 38 d->sideIcon = iTrue;
39 d->hideToolbarOnScroll = iTrue; 39 d->hideToolbarOnScroll = iTrue;
40 d->pinSplit = 1;
40 d->hoverLink = iFalse; 41 d->hoverLink = iFalse;
41 d->smoothScrolling = iTrue; 42 d->smoothScrolling = iTrue;
42 d->loadImageInsteadOfScrolling = iFalse; 43 d->loadImageInsteadOfScrolling = iFalse;
diff --git a/src/prefs.h b/src/prefs.h
index 7f02a100..0f604ee2 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -51,6 +51,7 @@ struct Impl_Prefs {
51 int zoomPercent; 51 int zoomPercent;
52 iBool sideIcon; 52 iBool sideIcon;
53 iBool hideToolbarOnScroll; 53 iBool hideToolbarOnScroll;
54 int pinSplit; /* 0: no pinning, 1: left doc, 2: right doc */
54 /* Behavior */ 55 /* Behavior */
55 iString downloadDir; 56 iString downloadDir;
56 iBool hoverLink; 57 iBool hoverLink;
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 6a44fd5f..8d519865 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -221,6 +221,7 @@ enum iDocumentWidgetFlag {
221 pinchZoom_DocumentWidgetFlag = iBit(9), 221 pinchZoom_DocumentWidgetFlag = iBit(9),
222 movingSelectMarkStart_DocumentWidgetFlag = iBit(10), 222 movingSelectMarkStart_DocumentWidgetFlag = iBit(10),
223 movingSelectMarkEnd_DocumentWidgetFlag = iBit(11), 223 movingSelectMarkEnd_DocumentWidgetFlag = iBit(11),
224 otherRootByDefault_DocumentWidgetFlag = iBit(12), /* links open to other root by default */
224}; 225};
225 226
226enum iDocumentLinkOrdinalMode { 227enum iDocumentLinkOrdinalMode {
@@ -956,6 +957,20 @@ static void documentRunsInvalidated_DocumentWidget_(iDocumentWidget *d) {
956 iZap(d->renderRuns); 957 iZap(d->renderRuns);
957} 958}
958 959
960iBool isPinned_DocumentWidget_(const iDocumentWidget *d) {
961 if (d->flags & otherRootByDefault_DocumentWidgetFlag) {
962 return iTrue;
963 }
964 const iWidget *w = constAs_Widget(d);
965 const iWindow *win = get_Window();
966 if (numRoots_Window(win) == 1) {
967 return iFalse;
968 }
969 const iPrefs *prefs = prefs_App();
970 return (prefs->pinSplit == 1 && w->root == win->roots[0]) ||
971 (prefs->pinSplit == 2 && w->root == win->roots[1]);
972}
973
959void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) { 974void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) {
960 setUrl_GmDocument(d->doc, d->mod.url); 975 setUrl_GmDocument(d->doc, d->mod.url);
961 setSource_GmDocument(d->doc, source, documentWidth_DocumentWidget_(d)); 976 setSource_GmDocument(d->doc, source, documentWidth_DocumentWidget_(d));
@@ -965,6 +980,15 @@ void setSource_DocumentWidget(iDocumentWidget *d, const iString *source) {
965 d->drawBufs->flags |= updateSideBuf_DrawBufsFlag; 980 d->drawBufs->flags |= updateSideBuf_DrawBufsFlag;
966 invalidate_DocumentWidget_(d); 981 invalidate_DocumentWidget_(d);
967 refresh_Widget(as_Widget(d)); 982 refresh_Widget(as_Widget(d));
983 /* Check for special bookmark tags. */
984 d->flags &= ~otherRootByDefault_DocumentWidgetFlag;
985 const uint16_t bmid = findUrl_Bookmarks(bookmarks_App(), d->mod.url);
986 if (bmid) {
987 const iBookmark *bm = get_Bookmarks(bookmarks_App(), bmid);
988 if (hasTag_Bookmark(bm, linkSplit_BookmarkTag)) {
989 d->flags |= otherRootByDefault_DocumentWidgetFlag;
990 }
991 }
968} 992}
969 993
970static void updateTheme_DocumentWidget_(iDocumentWidget *d) { 994static void updateTheme_DocumentWidget_(iDocumentWidget *d) {
@@ -2455,7 +2479,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2455 linkUrl_GmDocument(d->doc, run->linkId))) != 0) { 2479 linkUrl_GmDocument(d->doc, run->linkId))) != 0) {
2456 const iBookmark *bm = get_Bookmarks(bookmarks_App(), bmid); 2480 const iBookmark *bm = get_Bookmarks(bookmarks_App(), bmid);
2457 /* We can import local copies of remote bookmarks. */ 2481 /* We can import local copies of remote bookmarks. */
2458 if (!hasTag_Bookmark(bm, "remote")) { 2482 if (!hasTag_Bookmark(bm, remote_BookmarkTag)) {
2459 remove_PtrArrayIterator(&i); 2483 remove_PtrArrayIterator(&i);
2460 } 2484 }
2461 } 2485 }
@@ -2740,11 +2764,12 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2740 else { 2764 else {
2741 postCommandf_Root(w->root, 2765 postCommandf_Root(w->root,
2742 "open newtab:%d url:%s", 2766 "open newtab:%d url:%s",
2743 d->ordinalMode == 2767 (isPinned_DocumentWidget_(d) ? otherRoot_OpenTabFlag : 0) ^
2768 (d->ordinalMode ==
2744 numbersAndAlphabet_DocumentLinkOrdinalMode 2769 numbersAndAlphabet_DocumentLinkOrdinalMode
2745 ? openTabMode_Sym(modState_Keys()) 2770 ? openTabMode_Sym(modState_Keys())
2746 : (d->flags & newTabViaHomeKeys_DocumentWidgetFlag ? 1 : 0), 2771 : (d->flags & newTabViaHomeKeys_DocumentWidgetFlag ? 1 : 0)),
2747 cstr_String(absoluteUrl_String( 2772 cstr_String(absoluteUrl_String(
2748 d->mod.url, linkUrl_GmDocument(d->doc, run->linkId)))); 2773 d->mod.url, linkUrl_GmDocument(d->doc, run->linkId))));
2749 } 2774 }
2750 setLinkNumberMode_DocumentWidget_(d, iFalse); 2775 setLinkNumberMode_DocumentWidget_(d, iFalse);
@@ -2862,8 +2887,9 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2862 } 2887 }
2863 if (ev->button.button == SDL_BUTTON_MIDDLE && d->hoverLink) { 2888 if (ev->button.button == SDL_BUTTON_MIDDLE && d->hoverLink) {
2864 postCommandf_Root(w->root, "open newtab:%d url:%s", 2889 postCommandf_Root(w->root, "open newtab:%d url:%s",
2865 modState_Keys() & KMOD_SHIFT ? 1 : 2, 2890 (isPinned_DocumentWidget_(d) ? otherRoot_OpenTabFlag : 0) |
2866 cstr_String(linkUrl_GmDocument(d->doc, d->hoverLink->linkId))); 2891 (modState_Keys() & KMOD_SHIFT ? new_OpenTabFlag : newBackground_OpenTabFlag),
2892 cstr_String(linkUrl_GmDocument(d->doc, d->hoverLink->linkId)));
2867 return iTrue; 2893 return iTrue;
2868 } 2894 }
2869 if (ev->button.button == SDL_BUTTON_RIGHT && 2895 if (ev->button.button == SDL_BUTTON_RIGHT &&
@@ -3237,8 +3263,12 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
3237 refresh_Widget(w); 3263 refresh_Widget(w);
3238 } 3264 }
3239 else if (linkFlags & supportedProtocol_GmLinkFlag) { 3265 else if (linkFlags & supportedProtocol_GmLinkFlag) {
3266 int tabMode = openTabMode_Sym(modState_Keys());
3267 if (isPinned_DocumentWidget_(d)) {
3268 tabMode ^= otherRoot_OpenTabFlag;
3269 }
3240 postCommandf_Root(w->root, "open newtab:%d url:%s", 3270 postCommandf_Root(w->root, "open newtab:%d url:%s",
3241 openTabMode_Sym(modState_Keys()), 3271 tabMode,
3242 cstr_String(absoluteUrl_String( 3272 cstr_String(absoluteUrl_String(
3243 d->mod.url, linkUrl_GmDocument(d->doc, linkId)))); 3273 d->mod.url, linkUrl_GmDocument(d->doc, linkId))));
3244 } 3274 }
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index 7d08b83a..245b76a3 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -824,12 +824,12 @@ iBool handleBookmarkEditorCommands_SidebarWidget_(iWidget *editor, const char *c
824 set_String(&bm->url, url); 824 set_String(&bm->url, url);
825 set_String(&bm->tags, tags); 825 set_String(&bm->tags, tags);
826 if (isEmpty_String(icon)) { 826 if (isEmpty_String(icon)) {
827 removeTag_Bookmark(bm, "usericon"); 827 removeTag_Bookmark(bm, userIcon_BookmarkTag);
828 bm->icon = 0; 828 bm->icon = 0;
829 } 829 }
830 else { 830 else {
831 if (!hasTag_Bookmark(bm, "usericon")) { 831 if (!hasTag_Bookmark(bm, userIcon_BookmarkTag)) {
832 addTag_Bookmark(bm, "usericon"); 832 addTag_Bookmark(bm, userIcon_BookmarkTag);
833 } 833 }
834 bm->icon = first_String(icon); 834 bm->icon = first_String(icon);
835 } 835 }
@@ -1008,7 +1008,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1008 setText_InputWidget(findChild_Widget(dlg, "bmed.title"), &bm->title); 1008 setText_InputWidget(findChild_Widget(dlg, "bmed.title"), &bm->title);
1009 setText_InputWidget(findChild_Widget(dlg, "bmed.url"), &bm->url); 1009 setText_InputWidget(findChild_Widget(dlg, "bmed.url"), &bm->url);
1010 setText_InputWidget(findChild_Widget(dlg, "bmed.tags"), &bm->tags); 1010 setText_InputWidget(findChild_Widget(dlg, "bmed.tags"), &bm->tags);
1011 if (hasTag_Bookmark(bm, "usericon")) { 1011 if (hasTag_Bookmark(bm, userIcon_BookmarkTag)) {
1012 setText_InputWidget(findChild_Widget(dlg, "bmed.icon"), 1012 setText_InputWidget(findChild_Widget(dlg, "bmed.icon"),
1013 collect_String(newUnicodeN_String(&bm->icon, 1))); 1013 collect_String(newUnicodeN_String(&bm->icon, 1)));
1014 } 1014 }
@@ -1022,7 +1022,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1022 if (d->mode == bookmarks_SidebarMode && item) { 1022 if (d->mode == bookmarks_SidebarMode && item) {
1023 setFlags_Widget(w, disabled_WidgetFlag, iTrue); 1023 setFlags_Widget(w, disabled_WidgetFlag, iTrue);
1024 iBookmark *bm = get_Bookmarks(bookmarks_App(), item->id); 1024 iBookmark *bm = get_Bookmarks(bookmarks_App(), item->id);
1025 const iBool isRemote = hasTag_Bookmark(bm, "remote"); 1025 const iBool isRemote = hasTag_Bookmark(bm, remote_BookmarkTag);
1026 iChar icon = isRemote ? 0x1f588 : bm->icon; 1026 iChar icon = isRemote ? 0x1f588 : bm->icon;
1027 iWidget *dlg = makeBookmarkCreation_Widget(&bm->url, &bm->title, icon); 1027 iWidget *dlg = makeBookmarkCreation_Widget(&bm->url, &bm->title, icon);
1028 setId_Widget(dlg, format_CStr("bmed.%s", cstr_String(id_Widget(w)))); 1028 setId_Widget(dlg, format_CStr("bmed.%s", cstr_String(id_Widget(w))));
@@ -1040,7 +1040,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1040 iBookmark *bm = get_Bookmarks(bookmarks_App(), item->id); 1040 iBookmark *bm = get_Bookmarks(bookmarks_App(), item->id);
1041 if (hasTag_Bookmark(bm, tag)) { 1041 if (hasTag_Bookmark(bm, tag)) {
1042 removeTag_Bookmark(bm, tag); 1042 removeTag_Bookmark(bm, tag);
1043 if (!iCmpStr(tag, "subscribed")) { 1043 if (!iCmpStr(tag, subscribed_BookmarkTag)) {
1044 removeEntries_Feeds(item->id); 1044 removeEntries_Feeds(item->id);
1045 } 1045 }
1046 } 1046 }
@@ -1119,7 +1119,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1119 } 1119 }
1120 if (isCommand_Widget(w, ev, "feed.entry.unsubscribe")) { 1120 if (isCommand_Widget(w, ev, "feed.entry.unsubscribe")) {
1121 if (arg_Command(cmd)) { 1121 if (arg_Command(cmd)) {
1122 removeTag_Bookmark(feedBookmark, "subscribed"); 1122 removeTag_Bookmark(feedBookmark, subscribed_BookmarkTag);
1123 removeEntries_Feeds(id_Bookmark(feedBookmark)); 1123 removeEntries_Feeds(id_Bookmark(feedBookmark));
1124 updateItems_SidebarWidget_(d); 1124 updateItems_SidebarWidget_(d);
1125 } 1125 }
@@ -1308,7 +1308,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1308 "bookmark.tag tag:homepage"); 1308 "bookmark.tag tag:homepage");
1309 if (menuItem) { 1309 if (menuItem) {
1310 setTextCStr_LabelWidget(menuItem, 1310 setTextCStr_LabelWidget(menuItem,
1311 hasTag_Bookmark(bm, "homepage") 1311 hasTag_Bookmark(bm, homepage_BookmarkTag)
1312 ? home_Icon " ${bookmark.untag.home}" 1312 ? home_Icon " ${bookmark.untag.home}"
1313 : home_Icon " ${bookmark.tag.home}"); 1313 : home_Icon " ${bookmark.tag.home}");
1314 checkIcon_LabelWidget(menuItem); 1314 checkIcon_LabelWidget(menuItem);
@@ -1316,7 +1316,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1316 menuItem = findMenuItem_Widget(d->menu, "bookmark.tag tag:subscribed"); 1316 menuItem = findMenuItem_Widget(d->menu, "bookmark.tag tag:subscribed");
1317 if (menuItem) { 1317 if (menuItem) {
1318 setTextCStr_LabelWidget(menuItem, 1318 setTextCStr_LabelWidget(menuItem,
1319 hasTag_Bookmark(bm, "subscribed") 1319 hasTag_Bookmark(bm, subscribed_BookmarkTag)
1320 ? star_Icon " ${bookmark.untag.sub}" 1320 ? star_Icon " ${bookmark.untag.sub}"
1321 : star_Icon " ${bookmark.tag.sub}"); 1321 : star_Icon " ${bookmark.tag.sub}");
1322 checkIcon_LabelWidget(menuItem); 1322 checkIcon_LabelWidget(menuItem);
@@ -1324,7 +1324,7 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1324 menuItem = findMenuItem_Widget(d->menu, "bookmark.tag tag:remotesource"); 1324 menuItem = findMenuItem_Widget(d->menu, "bookmark.tag tag:remotesource");
1325 if (menuItem) { 1325 if (menuItem) {
1326 setTextCStr_LabelWidget(menuItem, 1326 setTextCStr_LabelWidget(menuItem,
1327 hasTag_Bookmark(bm, "remotesource") 1327 hasTag_Bookmark(bm, remoteSource_BookmarkTag)
1328 ? downArrowBar_Icon " ${bookmark.untag.remote}" 1328 ? downArrowBar_Icon " ${bookmark.untag.remote}"
1329 : downArrowBar_Icon " ${bookmark.tag.remote}"); 1329 : downArrowBar_Icon " ${bookmark.tag.remote}");
1330 checkIcon_LabelWidget(menuItem); 1330 checkIcon_LabelWidget(menuItem);
@@ -1386,13 +1386,13 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
1386 const iSidebarItem *hoverItem = hoverItem_ListWidget(d->list); 1386 const iSidebarItem *hoverItem = hoverItem_ListWidget(d->list);
1387 iAssert(hoverItem); 1387 iAssert(hoverItem);
1388 const iBookmark * bm = get_Bookmarks(bookmarks_App(), hoverItem->id); 1388 const iBookmark * bm = get_Bookmarks(bookmarks_App(), hoverItem->id);
1389 const iBool isRemote = hasTag_Bookmark(bm, "remote"); 1389 const iBool isRemote = hasTag_Bookmark(bm, remote_BookmarkTag);
1390 static const char *localOnlyCmds[] = { "bookmark.edit", 1390 static const char *localOnlyCmds[] = { "bookmark.edit",
1391 "bookmark.delete", 1391 "bookmark.delete",
1392 "bookmark.tag tag:subscribed", 1392 "bookmark.tag tag:" subscribed_BookmarkTag,
1393 "bookmark.tag tag:homepage", 1393 "bookmark.tag tag:" homepage_BookmarkTag,
1394 "bookmark.tag tag:remotesource", 1394 "bookmark.tag tag:" remoteSource_BookmarkTag,
1395 "bookmark.tag tag:subscribed" }; 1395 "bookmark.tag tag:" subscribed_BookmarkTag };
1396 iForIndices(i, localOnlyCmds) { 1396 iForIndices(i, localOnlyCmds) {
1397 setFlags_Widget(as_Widget(findMenuItem_Widget(d->menu, localOnlyCmds[i])), 1397 setFlags_Widget(as_Widget(findMenuItem_Widget(d->menu, localOnlyCmds[i])),
1398 disabled_WidgetFlag, 1398 disabled_WidgetFlag,
diff --git a/src/ui/util.c b/src/ui/util.c
index 5995f6a0..8c058ac3 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -2218,6 +2218,14 @@ iWidget *makePreferences_Widget(void) {
2218 addChild_Widget(values, iClob(makeToggle_Widget("prefs.hoverlink"))); 2218 addChild_Widget(values, iClob(makeToggle_Widget("prefs.hoverlink")));
2219 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.archive.openindex}"))); 2219 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.archive.openindex}")));
2220 addChild_Widget(values, iClob(makeToggle_Widget("prefs.archive.openindex"))); 2220 addChild_Widget(values, iClob(makeToggle_Widget("prefs.archive.openindex")));
2221 addChild_Widget(headings, iClob(makeHeading_Widget("${prefs.pinsplit}")));
2222 iWidget *pinSplit = new_Widget();
2223 /* Split mode document pinning. */ {
2224 addRadioButton_(pinSplit, "prefs.pinsplit.0", "${prefs.pinsplit.none}", "pinsplit.set arg:0");
2225 addRadioButton_(pinSplit, "prefs.pinsplit.1", "${prefs.pinsplit.left}", "pinsplit.set arg:1");
2226 addRadioButton_(pinSplit, "prefs.pinsplit.2", "${prefs.pinsplit.right}", "pinsplit.set arg:2");
2227 }
2228 addChildFlags_Widget(values, iClob(pinSplit), arrangeHorizontal_WidgetFlag | arrangeSize_WidgetFlag);
2221 addChild_Widget(headings, iClob(makePadding_Widget(bigGap))); 2229 addChild_Widget(headings, iClob(makePadding_Widget(bigGap)));
2222 addChild_Widget(values, iClob(makePadding_Widget(bigGap))); 2230 addChild_Widget(values, iClob(makePadding_Widget(bigGap)));
2223 /* UI languages. */ { 2231 /* UI languages. */ {
@@ -2457,10 +2465,21 @@ iWidget *makeBookmarkEditor_Widget(void) {
2457 setUrlContent_InputWidget(inputs[1], iTrue); 2465 setUrlContent_InputWidget(inputs[1], iTrue);
2458 addDialogInputWithHeading_(headings, values, "${dlg.bookmark.tags}", "bmed.tags", iClob(inputs[2] = new_InputWidget(0))); 2466 addDialogInputWithHeading_(headings, values, "${dlg.bookmark.tags}", "bmed.tags", iClob(inputs[2] = new_InputWidget(0)));
2459 addDialogInputWithHeading_(headings, values, "${dlg.bookmark.icon}", "bmed.icon", iClob(inputs[3] = new_InputWidget(1))); 2467 addDialogInputWithHeading_(headings, values, "${dlg.bookmark.icon}", "bmed.icon", iClob(inputs[3] = new_InputWidget(1)));
2468 /* Buttons for special tags. */
2469 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI)));
2470 addChild_Widget(dlg, iClob(makeTwoColumnWidget_(&headings, &values)));
2471 makeTwoColumnHeading_("SPECIAL TAGS", headings, values);
2472 addChild_Widget(headings, iClob(makeHeading_Widget("${bookmark.tag.home}")));
2473 addChild_Widget(values, iClob(makeToggle_Widget("bmed.tag.home")));
2474 addChild_Widget(headings, iClob(makeHeading_Widget("${bookmark.tag.remote}")));
2475 addChild_Widget(values, iClob(makeToggle_Widget("bmed.tag.remote")));
2476 addChild_Widget(headings, iClob(makeHeading_Widget("${bookmark.tag.sub}")));
2477 addChild_Widget(values, iClob(makeToggle_Widget("bmed.tag.sub")));
2460 arrange_Widget(dlg); 2478 arrange_Widget(dlg);
2461 for (int i = 0; i < 3; ++i) { 2479 for (int i = 0; i < 3; ++i) {
2462 as_Widget(inputs[i])->rect.size.x = 100 * gap_UI - headings->rect.size.x; 2480 as_Widget(inputs[i])->rect.size.x = 100 * gap_UI - headings->rect.size.x;
2463 } 2481 }
2482 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI)));
2464 addChild_Widget( 2483 addChild_Widget(
2465 dlg, 2484 dlg,
2466 iClob(makeDialogButtons_Widget((iMenuItem[]){ { "${cancel}", 0, 0, NULL }, 2485 iClob(makeDialogButtons_Widget((iMenuItem[]){ { "${cancel}", 0, 0, NULL },
@@ -2489,8 +2508,8 @@ static iBool handleBookmarkCreationCommands_SidebarWidget_(iWidget *editor, cons
2489 const uint32_t id = add_Bookmarks(bookmarks_App(), url, title, tags, first_String(icon)); 2508 const uint32_t id = add_Bookmarks(bookmarks_App(), url, title, tags, first_String(icon));
2490 if (!isEmpty_String(icon)) { 2509 if (!isEmpty_String(icon)) {
2491 iBookmark *bm = get_Bookmarks(bookmarks_App(), id); 2510 iBookmark *bm = get_Bookmarks(bookmarks_App(), id);
2492 if (!hasTag_Bookmark(bm, "usericon")) { 2511 if (!hasTag_Bookmark(bm, userIcon_BookmarkTag)) {
2493 addTag_Bookmark(bm, "usericon"); 2512 addTag_Bookmark(bm, userIcon_BookmarkTag);
2494 } 2513 }
2495 } 2514 }
2496 postCommand_App("bookmarks.changed"); 2515 postCommand_App("bookmarks.changed");
@@ -2609,7 +2628,7 @@ iWidget *makeFeedSettings_Widget(uint32_t bookmarkId) {
2609 setText_InputWidget(findChild_Widget(dlg, "feedcfg.title"), 2628 setText_InputWidget(findChild_Widget(dlg, "feedcfg.title"),
2610 bm ? &bm->title : feedTitle_DocumentWidget(document_App())); 2629 bm ? &bm->title : feedTitle_DocumentWidget(document_App()));
2611 setFlags_Widget(findChild_Widget(dlg, 2630 setFlags_Widget(findChild_Widget(dlg,
2612 hasTag_Bookmark(bm, "headings") ? "feedcfg.type.headings" 2631 hasTag_Bookmark(bm, headings_BookmarkTag) ? "feedcfg.type.headings"
2613 : "feedcfg.type.gemini"), 2632 : "feedcfg.type.gemini"),
2614 selected_WidgetFlag, 2633 selected_WidgetFlag,
2615 iTrue); 2634 iTrue);
diff --git a/src/ui/window.c b/src/ui/window.c
index 5a8a1e26..c4d06d75 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -1218,9 +1218,15 @@ void setSplitMode_Window(iWindow *d, int splitFlags) {
1218 setCurrent_Root(d->roots[1]); 1218 setCurrent_Root(d->roots[1]);
1219 d->keyRoot = d->roots[1]; 1219 d->keyRoot = d->roots[1];
1220 createUserInterface_Root(d->roots[1]); 1220 createUserInterface_Root(d->roots[1]);
1221 /* If the old root has multiple tabs, move the current one to the new split. */ { 1221 if (!isEmpty_String(d->pendingSplitUrl)) {
1222 postCommandf_Root(d->roots[1], "open url:%s",
1223 cstr_String(d->pendingSplitUrl));
1224 clear_String(d->pendingSplitUrl);
1225 }
1226 else if (~splitFlags & noEvents_WindowSplit) {
1222 iWidget *docTabs0 = findChild_Widget(d->roots[0]->widget, "doctabs"); 1227 iWidget *docTabs0 = findChild_Widget(d->roots[0]->widget, "doctabs");
1223 iWidget *docTabs1 = findChild_Widget(d->roots[1]->widget, "doctabs"); 1228 iWidget *docTabs1 = findChild_Widget(d->roots[1]->widget, "doctabs");
1229 /* If the old root has multiple tabs, move the current one to the new split. */
1224 if (tabCount_Widget(docTabs0) >= 2) { 1230 if (tabCount_Widget(docTabs0) >= 2) {
1225 int movedIndex = tabPageIndex_Widget(docTabs0, moved); 1231 int movedIndex = tabPageIndex_Widget(docTabs0, moved);
1226 removeTabPage_Widget(docTabs0, movedIndex); 1232 removeTabPage_Widget(docTabs0, movedIndex);
@@ -1228,19 +1234,10 @@ void setSplitMode_Window(iWindow *d, int splitFlags) {
1228 iRelease(removeTabPage_Widget(docTabs1, 0)); /* delete the default tab */ 1234 iRelease(removeTabPage_Widget(docTabs1, 0)); /* delete the default tab */
1229 setRoot_Widget(as_Widget(moved), d->roots[1]); 1235 setRoot_Widget(as_Widget(moved), d->roots[1]);
1230 prependTabPage_Widget(docTabs1, iClob(moved), "", 0, 0); 1236 prependTabPage_Widget(docTabs1, iClob(moved), "", 0, 0);
1231 if (~splitFlags & noEvents_WindowSplit) { 1237 postCommandf_App("tabs.switch page:%p", moved);
1232 postCommandf_App("tabs.switch page:%p", moved);
1233 }
1234 } 1238 }
1235 else if (~splitFlags & noEvents_WindowSplit) { 1239 else {
1236 if (isEmpty_String(d->pendingSplitUrl)) { 1240 postCommand_Root(d->roots[1], "navigate.home");
1237 postCommand_Root(d->roots[1], "navigate.home");
1238 }
1239 else {
1240 postCommandf_Root(d->roots[1], "open url:%s",
1241 cstr_String(d->pendingSplitUrl));
1242 clear_String(d->pendingSplitUrl);
1243 }
1244 } 1241 }
1245 } 1242 }
1246 setCurrent_Root(NULL); 1243 setCurrent_Root(NULL);