summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--po/en.po4
-rw-r--r--res/lang/cs.binbin31435 -> 31510 bytes
-rw-r--r--res/lang/de.binbin30442 -> 30517 bytes
-rw-r--r--res/lang/en.binbin26539 -> 26614 bytes
-rw-r--r--res/lang/eo.binbin25493 -> 25568 bytes
-rw-r--r--res/lang/es.binbin30266 -> 30341 bytes
-rw-r--r--res/lang/es_MX.binbin27598 -> 27673 bytes
-rw-r--r--res/lang/fi.binbin30099 -> 30174 bytes
-rw-r--r--res/lang/fr.binbin31246 -> 31321 bytes
-rw-r--r--res/lang/gl.binbin29451 -> 29526 bytes
-rw-r--r--res/lang/hu.binbin31271 -> 31346 bytes
-rw-r--r--res/lang/ia.binbin28598 -> 28673 bytes
-rw-r--r--res/lang/ie.binbin29186 -> 29261 bytes
-rw-r--r--res/lang/isv.binbin25259 -> 25334 bytes
-rw-r--r--res/lang/pl.binbin29874 -> 29949 bytes
-rw-r--r--res/lang/ru.binbin44634 -> 44709 bytes
-rw-r--r--res/lang/sk.binbin25595 -> 25670 bytes
-rw-r--r--res/lang/sr.binbin44060 -> 44135 bytes
-rw-r--r--res/lang/tok.binbin27308 -> 27383 bytes
-rw-r--r--res/lang/tr.binbin29492 -> 29567 bytes
-rw-r--r--res/lang/uk.binbin43979 -> 44054 bytes
-rw-r--r--res/lang/zh_Hans.binbin25493 -> 25568 bytes
-rw-r--r--res/lang/zh_Hant.binbin25691 -> 25766 bytes
-rw-r--r--src/app.c21
-rw-r--r--src/ui/documentwidget.c98
-rw-r--r--src/ui/documentwidget.h1
-rw-r--r--src/ui/inputwidget.c10
-rw-r--r--src/ui/inputwidget.h1
-rw-r--r--src/ui/mobile.c12
-rw-r--r--src/ui/root.c2
-rw-r--r--src/ui/util.c12
-rw-r--r--src/ui/window.c6
-rw-r--r--src/ui/window.h1
33 files changed, 128 insertions, 40 deletions
diff --git a/po/en.po b/po/en.po
index 3fc5423d..481545cf 100644
--- a/po/en.po
+++ b/po/en.po
@@ -760,6 +760,10 @@ msgstr "Line break"
760msgid "dlg.input.send" 760msgid "dlg.input.send"
761msgstr "Send" 761msgstr "Send"
762 762
763# Paste the line preceding the clicked link into the input prompt.
764msgid "menu.input.precedingline"
765msgstr "Paste Preceding Line"
766
763msgid "heading.save" 767msgid "heading.save"
764msgstr "FILE SAVED" 768msgstr "FILE SAVED"
765 769
diff --git a/res/lang/cs.bin b/res/lang/cs.bin
index 3a5b5820..97197051 100644
--- a/res/lang/cs.bin
+++ b/res/lang/cs.bin
Binary files differ
diff --git a/res/lang/de.bin b/res/lang/de.bin
index 57a6c945..d2dceda6 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 6c921d9b..28c11997 100644
--- a/res/lang/en.bin
+++ b/res/lang/en.bin
Binary files differ
diff --git a/res/lang/eo.bin b/res/lang/eo.bin
index b0ad3a58..d148e2f0 100644
--- a/res/lang/eo.bin
+++ b/res/lang/eo.bin
Binary files differ
diff --git a/res/lang/es.bin b/res/lang/es.bin
index 61a5b870..92037aea 100644
--- a/res/lang/es.bin
+++ b/res/lang/es.bin
Binary files differ
diff --git a/res/lang/es_MX.bin b/res/lang/es_MX.bin
index 6658bbcb..7b7e1219 100644
--- a/res/lang/es_MX.bin
+++ b/res/lang/es_MX.bin
Binary files differ
diff --git a/res/lang/fi.bin b/res/lang/fi.bin
index 287de822..243e9740 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 495a1b30..507cbb4c 100644
--- a/res/lang/fr.bin
+++ b/res/lang/fr.bin
Binary files differ
diff --git a/res/lang/gl.bin b/res/lang/gl.bin
index efafd3a1..447ed72d 100644
--- a/res/lang/gl.bin
+++ b/res/lang/gl.bin
Binary files differ
diff --git a/res/lang/hu.bin b/res/lang/hu.bin
index e0095569..c2a085ec 100644
--- a/res/lang/hu.bin
+++ b/res/lang/hu.bin
Binary files differ
diff --git a/res/lang/ia.bin b/res/lang/ia.bin
index fb4f482c..5305304c 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 d397cdea..69ac42d4 100644
--- a/res/lang/ie.bin
+++ b/res/lang/ie.bin
Binary files differ
diff --git a/res/lang/isv.bin b/res/lang/isv.bin
index 012e624f..56ed525c 100644
--- a/res/lang/isv.bin
+++ b/res/lang/isv.bin
Binary files differ
diff --git a/res/lang/pl.bin b/res/lang/pl.bin
index 60b5d77c..1ad99e5d 100644
--- a/res/lang/pl.bin
+++ b/res/lang/pl.bin
Binary files differ
diff --git a/res/lang/ru.bin b/res/lang/ru.bin
index 8cda6497..03092acf 100644
--- a/res/lang/ru.bin
+++ b/res/lang/ru.bin
Binary files differ
diff --git a/res/lang/sk.bin b/res/lang/sk.bin
index 9a5355d9..bf13f594 100644
--- a/res/lang/sk.bin
+++ b/res/lang/sk.bin
Binary files differ
diff --git a/res/lang/sr.bin b/res/lang/sr.bin
index 7d5183e5..823818e2 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 e21e8201..50743783 100644
--- a/res/lang/tok.bin
+++ b/res/lang/tok.bin
Binary files differ
diff --git a/res/lang/tr.bin b/res/lang/tr.bin
index 9dd4764e..543739cd 100644
--- a/res/lang/tr.bin
+++ b/res/lang/tr.bin
Binary files differ
diff --git a/res/lang/uk.bin b/res/lang/uk.bin
index a48258b6..f866f86f 100644
--- a/res/lang/uk.bin
+++ b/res/lang/uk.bin
Binary files differ
diff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin
index 1ad2db27..179d87db 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 f4263b3b..aad28410 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 e807f09d..accdd991 100644
--- a/src/app.c
+++ b/src/app.c
@@ -356,7 +356,7 @@ static void loadPrefs_App_(iApp *d) {
356 setUiScale_Window(get_Window(), argf_Command(cmd)); 356 setUiScale_Window(get_Window(), argf_Command(cmd));
357 } 357 }
358 else if (equal_Command(cmd, "uilang")) { 358 else if (equal_Command(cmd, "uilang")) {
359 const char *id = cstr_Rangecc(range_Command(cmd, "id")); 359 const char *id = cstr_Command(cmd, "id");
360 setCStr_String(&d->prefs.strings[uiLanguage_PrefsString], id); 360 setCStr_String(&d->prefs.strings[uiLanguage_PrefsString], id);
361 setCurrent_Lang(id); 361 setCurrent_Lang(id);
362 } 362 }
@@ -2190,6 +2190,7 @@ iBool handleCommand_App(const char *cmd) {
2190 return iTrue; 2190 return iTrue;
2191 } 2191 }
2192 else if (equal_Command(cmd, "fontpack.suggest.classic")) { 2192 else if (equal_Command(cmd, "fontpack.suggest.classic")) {
2193 /* TODO: Don't use this when system fonts are accessible. */
2193 if (!isInstalled_Fonts("classic-set") && !isInstalled_Fonts("cjk")) { 2194 if (!isInstalled_Fonts("classic-set") && !isInstalled_Fonts("cjk")) {
2194 makeQuestion_Widget( 2195 makeQuestion_Widget(
2195 uiHeading_ColorEscape "${heading.fontpack.classic}", 2196 uiHeading_ColorEscape "${heading.fontpack.classic}",
@@ -2243,6 +2244,9 @@ iBool handleCommand_App(const char *cmd) {
2243 (argLabel_Command(cmd, "axis") ? vertical_WindowSplit : 0) | (arg_Command(cmd) << 1); 2244 (argLabel_Command(cmd, "axis") ? vertical_WindowSplit : 0) | (arg_Command(cmd) << 1);
2244 const char *url = suffixPtr_Command(cmd, "url"); 2245 const char *url = suffixPtr_Command(cmd, "url");
2245 setCStr_String(d->window->pendingSplitUrl, url ? url : ""); 2246 setCStr_String(d->window->pendingSplitUrl, url ? url : "");
2247 if (hasLabel_Command(cmd, "origin")) {
2248 set_String(d->window->pendingSplitOrigin, string_Command(cmd, "origin"));
2249 }
2246 postRefresh_App(); 2250 postRefresh_App();
2247 return iTrue; 2251 return iTrue;
2248 } 2252 }
@@ -2732,12 +2736,21 @@ iBool handleCommand_App(const char *cmd) {
2732 openInDefaultBrowser_App(url); 2736 openInDefaultBrowser_App(url);
2733 return iTrue; 2737 return iTrue;
2734 } 2738 }
2739 iDocumentWidget *doc = document_Command(cmd);
2740 iDocumentWidget *origin = doc;
2741 if (hasLabel_Command(cmd, "origin")) {
2742 iDocumentWidget *cmdOrig = findWidget_App(cstr_Command(cmd, "origin"));
2743 if (cmdOrig) {
2744 origin = cmdOrig;
2745 }
2746 }
2735 const int newTab = argLabel_Command(cmd, "newtab"); 2747 const int newTab = argLabel_Command(cmd, "newtab");
2736 if (newTab & otherRoot_OpenTabFlag && numRoots_Window(get_Window()) == 1) { 2748 if (newTab & otherRoot_OpenTabFlag && numRoots_Window(get_Window()) == 1) {
2737 /* Need to split first. */ 2749 /* Need to split first. */
2738 const iInt2 winSize = get_Window()->size; 2750 const iInt2 winSize = get_Window()->size;
2739 postCommandf_App("ui.split arg:3 axis:%d newtab:%d url:%s", 2751 postCommandf_App("ui.split arg:3 axis:%d origin:%s newtab:%d url:%s",
2740 (float) winSize.x / (float) winSize.y < 0.7f ? 1 : 0, 2752 (float) winSize.x / (float) winSize.y < 0.7f ? 1 : 0,
2753 cstr_String(id_Widget(as_Widget(origin))),
2741 newTab & ~otherRoot_OpenTabFlag, 2754 newTab & ~otherRoot_OpenTabFlag,
2742 cstr_String(url)); 2755 cstr_String(url));
2743 return iTrue; 2756 return iTrue;
@@ -2749,7 +2762,6 @@ iBool handleCommand_App(const char *cmd) {
2749 setKeyRoot_Window(as_Window(d->window), root); 2762 setKeyRoot_Window(as_Window(d->window), root);
2750 setCurrent_Root(root); /* need to change for widget creation */ 2763 setCurrent_Root(root); /* need to change for widget creation */
2751 } 2764 }
2752 iDocumentWidget *doc = document_Command(cmd);
2753 if (newTab & (new_OpenTabFlag | newBackground_OpenTabFlag)) { 2765 if (newTab & (new_OpenTabFlag | newBackground_OpenTabFlag)) {
2754 doc = newTab_App(NULL, (newTab & new_OpenTabFlag) != 0); /* `newtab:2` to open in background */ 2766 doc = newTab_App(NULL, (newTab & new_OpenTabFlag) != 0); /* `newtab:2` to open in background */
2755 } 2767 }
@@ -2766,13 +2778,14 @@ iBool handleCommand_App(const char *cmd) {
2766 } 2778 }
2767 setInitialScroll_DocumentWidget(doc, argfLabel_Command(cmd, "scroll")); 2779 setInitialScroll_DocumentWidget(doc, argfLabel_Command(cmd, "scroll"));
2768 setRedirectCount_DocumentWidget(doc, redirectCount); 2780 setRedirectCount_DocumentWidget(doc, redirectCount);
2781 setOrigin_DocumentWidget(doc, origin);
2769 showCollapsed_Widget(findWidget_App("document.progress"), iFalse); 2782 showCollapsed_Widget(findWidget_App("document.progress"), iFalse);
2770 if (prefs_App()->decodeUserVisibleURLs) { 2783 if (prefs_App()->decodeUserVisibleURLs) {
2771 urlDecodePath_String(url); 2784 urlDecodePath_String(url);
2772 } 2785 }
2773 else { 2786 else {
2774 urlEncodePath_String(url); 2787 urlEncodePath_String(url);
2775 } 2788 }
2776 setUrlFlags_DocumentWidget(doc, url, 2789 setUrlFlags_DocumentWidget(doc, url,
2777 (isHistory ? useCachedContentIfAvailable_DocumentWidgetSetUrlFlag : 0) | 2790 (isHistory ? useCachedContentIfAvailable_DocumentWidgetSetUrlFlag : 0) |
2778 (fromSidebar ? openedFromSidebar_DocumentWidgetSetUrlFlag : 0)); 2791 (fromSidebar ? openedFromSidebar_DocumentWidgetSetUrlFlag : 0));
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 384c51b5..50df05f5 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -264,6 +264,7 @@ struct Impl_DocumentWidget {
264 int pinchZoomPosted; 264 int pinchZoomPosted;
265 float swipeSpeed; /* points/sec */ 265 float swipeSpeed; /* points/sec */
266 iString pendingGotoHeading; 266 iString pendingGotoHeading;
267 iString linePrecedingLink;
267 268
268 /* Network request: */ 269 /* Network request: */
269 enum iRequestState state; 270 enum iRequestState state;
@@ -391,6 +392,7 @@ void init_DocumentWidget(iDocumentWidget *d) {
391 d->grabbedPlayer = NULL; 392 d->grabbedPlayer = NULL;
392 d->mediaTimer = 0; 393 d->mediaTimer = 0;
393 init_String(&d->pendingGotoHeading); 394 init_String(&d->pendingGotoHeading);
395 init_String(&d->linePrecedingLink);
394 init_Click(&d->click, d, SDL_BUTTON_LEFT); 396 init_Click(&d->click, d, SDL_BUTTON_LEFT);
395 addChild_Widget(w, iClob(d->scroll = new_ScrollWidget())); 397 addChild_Widget(w, iClob(d->scroll = new_ScrollWidget()));
396 d->menu = NULL; /* created when clicking */ 398 d->menu = NULL; /* created when clicking */
@@ -437,6 +439,7 @@ void deinit_DocumentWidget(iDocumentWidget *d) {
437 iRelease(d->media); 439 iRelease(d->media);
438 iRelease(d->request); 440 iRelease(d->request);
439 delete_Gempub(d->sourceGempub); 441 delete_Gempub(d->sourceGempub);
442 deinit_String(&d->linePrecedingLink);
440 deinit_String(&d->pendingGotoHeading); 443 deinit_String(&d->pendingGotoHeading);
441 deinit_Block(&d->sourceContent); 444 deinit_Block(&d->sourceContent);
442 deinit_String(&d->sourceMime); 445 deinit_String(&d->sourceMime);
@@ -1893,7 +1896,7 @@ static void addBannerWarnings_DocumentWidget_(iDocumentWidget *d) {
1893 1896
1894static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float normScrollY, 1897static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float normScrollY,
1895 const iGmResponse *resp, iGmDocument *cachedDoc) { 1898 const iGmResponse *resp, iGmDocument *cachedDoc) {
1896 iAssert(width_Widget(d) > 0); /* must be laid out by now */ 1899// iAssert(width_Widget(d) > 0); /* must be laid out by now */
1897 setLinkNumberMode_DocumentWidget_(d, iFalse); 1900 setLinkNumberMode_DocumentWidget_(d, iFalse);
1898 clear_ObjectList(d->media); 1901 clear_ObjectList(d->media);
1899 delete_Gempub(d->sourceGempub); 1902 delete_Gempub(d->sourceGempub);
@@ -1918,9 +1921,6 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n
1918 setWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d), width_Widget(d)); 1921 setWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d), width_Widget(d));
1919 } 1922 }
1920 updateDocument_DocumentWidget_(d, resp, cachedDoc, iTrue); 1923 updateDocument_DocumentWidget_(d, resp, cachedDoc, iTrue);
1921// if (!cachedDoc) {
1922// setCachedDocument_History(d->mod.history, d->doc, iFalse);
1923// }
1924 clear_Banner(d->banner); 1924 clear_Banner(d->banner);
1925 updateBanner_DocumentWidget_(d); 1925 updateBanner_DocumentWidget_(d);
1926 addBannerWarnings_DocumentWidget_(d); 1926 addBannerWarnings_DocumentWidget_(d);
@@ -2242,6 +2242,16 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) {
2242 if (lineBreak && deviceType_App() != desktop_AppDeviceType) { 2242 if (lineBreak && deviceType_App() != desktop_AppDeviceType) {
2243 addChildPos_Widget(buttons, iClob(lineBreak), front_WidgetAddPos); 2243 addChildPos_Widget(buttons, iClob(lineBreak), front_WidgetAddPos);
2244 } 2244 }
2245 /* Menu for additional actions, past entries. */ {
2246 iMenuItem items[] = { { "${menu.input.precedingline}",
2247 SDLK_v,
2248 KMOD_PRIMARY | KMOD_SHIFT,
2249 format_CStr("!valueinput.set ptr:%p text:%s",
2250 buttons,
2251 cstr_String(&d->linePrecedingLink)) } };
2252 iLabelWidget *menu = makeMenuButton_LabelWidget(midEllipsis_Icon, items, 1);
2253 addChildPos_Widget(buttons, iClob(menu), front_WidgetAddPos);
2254 }
2245 setValidator_InputWidget(findChild_Widget(dlg, "input"), inputQueryValidator_, d); 2255 setValidator_InputWidget(findChild_Widget(dlg, "input"), inputQueryValidator_, d);
2246 setSensitiveContent_InputWidget(findChild_Widget(dlg, "input"), 2256 setSensitiveContent_InputWidget(findChild_Widget(dlg, "input"),
2247 statusCode == sensitiveInput_GmStatusCode); 2257 statusCode == sensitiveInput_GmStatusCode);
@@ -3836,6 +3846,30 @@ static void beginMarkingSelection_DocumentWidget_(iDocumentWidget *d, iInt2 pos)
3836 refresh_Widget(as_Widget(d)); 3846 refresh_Widget(as_Widget(d));
3837} 3847}
3838 3848
3849static void linkWasTriggered_DocumentWidget_(iDocumentWidget *d, iGmLinkId id) {
3850 iRangecc loc = linkUrlRange_GmDocument(d->doc, id);
3851 if (!loc.start) {
3852 clear_String(&d->linePrecedingLink);
3853 return;
3854 }
3855 const char *start = range_String(source_GmDocument(d->doc)).start;
3856 /* Find the preceding line. This is offered as a prefill option for a possible input query. */
3857 while (loc.start > start && *loc.start != '\n') {
3858 loc.start--;
3859 }
3860 loc.end = loc.start; /* End of the preceding line. */
3861 if (loc.start > start) {
3862 loc.start--;
3863 }
3864 while (loc.start > start && *loc.start != '\n') {
3865 loc.start--;
3866 }
3867 if (*loc.start == '\n') {
3868 loc.start++; /* Start of the preceding line. */
3869 }
3870 setRange_String(&d->linePrecedingLink, loc);
3871}
3872
3839static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) { 3873static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) {
3840 iWidget *w = as_Widget(d); 3874 iWidget *w = as_Widget(d);
3841 if (isMetricsChange_UserEvent(ev)) { 3875 if (isMetricsChange_UserEvent(ev)) {
@@ -3878,6 +3912,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
3878 : (d->flags & newTabViaHomeKeys_DocumentWidgetFlag ? 1 : 0)), 3912 : (d->flags & newTabViaHomeKeys_DocumentWidgetFlag ? 1 : 0)),
3879 cstr_String(absoluteUrl_String( 3913 cstr_String(absoluteUrl_String(
3880 d->mod.url, linkUrl_GmDocument(d->doc, run->linkId)))); 3914 d->mod.url, linkUrl_GmDocument(d->doc, run->linkId))));
3915 linkWasTriggered_DocumentWidget_(d, run->linkId);
3881 } 3916 }
3882 setLinkNumberMode_DocumentWidget_(d, iFalse); 3917 setLinkNumberMode_DocumentWidget_(d, iFalse);
3883 invalidateVisibleLinks_DocumentWidget_(d); 3918 invalidateVisibleLinks_DocumentWidget_(d);
@@ -3995,6 +4030,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
3995 return iTrue; 4030 return iTrue;
3996 } 4031 }
3997 if (ev->button.button == SDL_BUTTON_MIDDLE && d->hoverLink) { 4032 if (ev->button.button == SDL_BUTTON_MIDDLE && d->hoverLink) {
4033 linkWasTriggered_DocumentWidget_(d, d->hoverLink->linkId);
3998 postCommandf_Root(w->root, "open newtab:%d url:%s", 4034 postCommandf_Root(w->root, "open newtab:%d url:%s",
3999 (isPinned_DocumentWidget_(d) ? otherRoot_OpenTabFlag : 0) | 4035 (isPinned_DocumentWidget_(d) ? otherRoot_OpenTabFlag : 0) |
4000 (modState_Keys() & KMOD_SHIFT ? new_OpenTabFlag : newBackground_OpenTabFlag), 4036 (modState_Keys() & KMOD_SHIFT ? new_OpenTabFlag : newBackground_OpenTabFlag),
@@ -4015,6 +4051,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
4015 init_Array(&items, sizeof(iMenuItem)); 4051 init_Array(&items, sizeof(iMenuItem));
4016 if (d->contextLink) { 4052 if (d->contextLink) {
4017 /* Context menu for a link. */ 4053 /* Context menu for a link. */
4054 linkWasTriggered_DocumentWidget_(d, d->contextLink->linkId); /* perhaps will be triggered */
4018 const iString *linkUrl = linkUrl_GmDocument(d->doc, d->contextLink->linkId); 4055 const iString *linkUrl = linkUrl_GmDocument(d->doc, d->contextLink->linkId);
4019// const int linkFlags = linkFlags_GmDocument(d->doc, d->contextLink->linkId); 4056// const int linkFlags = linkFlags_GmDocument(d->doc, d->contextLink->linkId);
4020 const iRangecc scheme = urlScheme_String(linkUrl); 4057 const iRangecc scheme = urlScheme_String(linkUrl);
@@ -4034,23 +4071,30 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
4034 /* Regular links that we can open. */ 4071 /* Regular links that we can open. */
4035 pushBackN_Array( 4072 pushBackN_Array(
4036 &items, 4073 &items,
4037 (iMenuItem[]){ 4074 (iMenuItem[]){ { openTab_Icon " ${link.newtab}",
4038 { openTab_Icon " ${link.newtab}", 4075 0,
4039 0, 4076 0,
4040 0, 4077 format_CStr("!open newtab:1 origin:%s url:%s",
4041 format_CStr("!open newtab:1 url:%s", cstr_String(linkUrl)) }, 4078 cstr_String(id_Widget(w)),
4042 { openTabBg_Icon " ${link.newtab.background}", 4079 cstr_String(linkUrl)) },
4043 0, 4080 { openTabBg_Icon " ${link.newtab.background}",
4044 0, 4081 0,
4045 format_CStr("!open newtab:2 url:%s", cstr_String(linkUrl)) }, 4082 0,
4046 { "${link.side}", 4083 format_CStr("!open newtab:2 origin:%s url:%s",
4047 0, 4084 cstr_String(id_Widget(w)),
4048 0, 4085 cstr_String(linkUrl)) },
4049 format_CStr("!open newtab:4 url:%s", cstr_String(linkUrl)) }, 4086 { "${link.side}",
4050 { "${link.side.newtab}", 4087 0,
4051 0, 4088 0,
4052 0, 4089 format_CStr("!open newtab:4 origin:%s url:%s",
4053 format_CStr("!open newtab:5 url:%s", cstr_String(linkUrl)) } }, 4090 cstr_String(id_Widget(w)),
4091 cstr_String(linkUrl)) },
4092 { "${link.side.newtab}",
4093 0,
4094 0,
4095 format_CStr("!open newtab:5 origin:%s url:%s",
4096 cstr_String(id_Widget(w)),
4097 cstr_String(linkUrl)) } },
4054 4); 4098 4);
4055 if (deviceType_App() == phone_AppDeviceType) { 4099 if (deviceType_App() == phone_AppDeviceType) {
4056 removeN_Array(&items, size_Array(&items) - 2, iInvalidSize); 4100 removeN_Array(&items, size_Array(&items) - 2, iInvalidSize);
@@ -4072,7 +4116,9 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
4072 { isGemini ? "${link.noproxy}" : openExt_Icon " ${link.browser}", 4116 { isGemini ? "${link.noproxy}" : openExt_Icon " ${link.browser}",
4073 0, 4117 0,
4074 0, 4118 0,
4075 format_CStr("!open noproxy:1 url:%s", cstr_String(linkUrl)) } }, 4119 format_CStr("!open origin:%s noproxy:1 url:%s",
4120 cstr_String(id_Widget(w)),
4121 cstr_String(linkUrl)) } },
4076 2); 4122 2);
4077 } 4123 }
4078 iString *linkLabel = collectNewRange_String( 4124 iString *linkLabel = collectNewRange_String(
@@ -4414,6 +4460,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
4414 if (isPinned_DocumentWidget_(d)) { 4460 if (isPinned_DocumentWidget_(d)) {
4415 tabMode ^= otherRoot_OpenTabFlag; 4461 tabMode ^= otherRoot_OpenTabFlag;
4416 } 4462 }
4463 linkWasTriggered_DocumentWidget_(d, linkId);
4417 postCommandf_Root(w->root, "open newtab:%d url:%s", 4464 postCommandf_Root(w->root, "open newtab:%d url:%s",
4418 tabMode, 4465 tabMode,
4419 cstr_String(absoluteUrl_String( 4466 cstr_String(absoluteUrl_String(
@@ -5495,6 +5542,13 @@ iDocumentWidget *duplicate_DocumentWidget(const iDocumentWidget *orig) {
5495 return d; 5542 return d;
5496} 5543}
5497 5544
5545void setOrigin_DocumentWidget(iDocumentWidget *d, const iDocumentWidget *other) {
5546 if (d != other) {
5547 /* TODO: Could remember the other's ID? */
5548 set_String(&d->linePrecedingLink, &other->linePrecedingLink);
5549 }
5550}
5551
5498void setUrl_DocumentWidget(iDocumentWidget *d, const iString *url) { 5552void setUrl_DocumentWidget(iDocumentWidget *d, const iString *url) {
5499 setUrlFlags_DocumentWidget(d, url, 0); 5553 setUrlFlags_DocumentWidget(d, url, 0);
5500} 5554}
diff --git a/src/ui/documentwidget.h b/src/ui/documentwidget.h
index c97fa0ba..1405f19d 100644
--- a/src/ui/documentwidget.h
+++ b/src/ui/documentwidget.h
@@ -53,6 +53,7 @@ enum iDocumentWidgetSetUrlFlags {
53 openedFromSidebar_DocumentWidgetSetUrlFlag = iBit(2), 53 openedFromSidebar_DocumentWidgetSetUrlFlag = iBit(2),
54}; 54};
55 55
56void setOrigin_DocumentWidget (iDocumentWidget *, const iDocumentWidget *other);
56void setUrl_DocumentWidget (iDocumentWidget *, const iString *url); 57void setUrl_DocumentWidget (iDocumentWidget *, const iString *url);
57void setUrlFlags_DocumentWidget (iDocumentWidget *, const iString *url, int setUrlFlags); 58void setUrlFlags_DocumentWidget (iDocumentWidget *, const iString *url, int setUrlFlags);
58void setUrlAndSource_DocumentWidget (iDocumentWidget *, const iString *url, const iString *mime, const iBlock *source); 59void setUrlAndSource_DocumentWidget (iDocumentWidget *, const iString *url, const iString *mime, const iBlock *source);
diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c
index b910f905..b94e0c27 100644
--- a/src/ui/inputwidget.c
+++ b/src/ui/inputwidget.c
@@ -1163,6 +1163,12 @@ void selectAll_InputWidget(iInputWidget *d) {
1163#endif 1163#endif
1164} 1164}
1165 1165
1166void validate_InputWidget(iInputWidget *d) {
1167 if (d->validator) {
1168 d->validator(d, d->validatorContext); /* this may change the contents */
1169 }
1170}
1171
1166iLocalDef iBool isEditing_InputWidget_(const iInputWidget *d) { 1172iLocalDef iBool isEditing_InputWidget_(const iInputWidget *d) {
1167 return (flags_Widget(constAs_Widget(d)) & selected_WidgetFlag) != 0; 1173 return (flags_Widget(constAs_Widget(d)) & selected_WidgetFlag) != 0;
1168} 1174}
@@ -1653,9 +1659,7 @@ void setEatEscape_InputWidget(iInputWidget *d, iBool eatEscape) {
1653} 1659}
1654 1660
1655static void contentsWereChanged_InputWidget_(iInputWidget *d) { 1661static void contentsWereChanged_InputWidget_(iInputWidget *d) {
1656 if (d->validator) { 1662 validate_InputWidget(d);
1657 d->validator(d, d->validatorContext); /* this may change the contents */
1658 }
1659 if (d->inFlags & notifyEdits_InputWidgetFlag) { 1663 if (d->inFlags & notifyEdits_InputWidgetFlag) {
1660 postCommand_Widget(d, "input.edited id:%s", cstr_String(id_Widget(constAs_Widget(d)))); 1664 postCommand_Widget(d, "input.edited id:%s", cstr_String(id_Widget(constAs_Widget(d))));
1661 } 1665 }
diff --git a/src/ui/inputwidget.h b/src/ui/inputwidget.h
index f70c81af..5a61ec22 100644
--- a/src/ui/inputwidget.h
+++ b/src/ui/inputwidget.h
@@ -57,6 +57,7 @@ void setBackupFileName_InputWidget (iInputWidget *, const char *fileName);
57void begin_InputWidget (iInputWidget *); 57void begin_InputWidget (iInputWidget *);
58void end_InputWidget (iInputWidget *, iBool accept); 58void end_InputWidget (iInputWidget *, iBool accept);
59void selectAll_InputWidget (iInputWidget *); 59void selectAll_InputWidget (iInputWidget *);
60void validate_InputWidget (iInputWidget *);
60 61
61void setSelectAllOnFocus_InputWidget (iInputWidget *, iBool selectAllOnFocus); 62void setSelectAllOnFocus_InputWidget (iInputWidget *, iBool selectAllOnFocus);
62void setSensitiveContent_InputWidget (iInputWidget *, iBool isSensitive); 63void setSensitiveContent_InputWidget (iInputWidget *, iBool isSensitive);
diff --git a/src/ui/mobile.c b/src/ui/mobile.c
index bf3eb425..df2a661a 100644
--- a/src/ui/mobile.c
+++ b/src/ui/mobile.c
@@ -499,7 +499,7 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
499 iLabelWidget *heading = NULL; 499 iLabelWidget *heading = NULL;
500 iWidget * value = NULL; 500 iWidget * value = NULL;
501 const char * spec = item->label; 501 const char * spec = item->label;
502 const char * id = cstr_Rangecc(range_Command(spec, "id")); 502 const char * id = cstr_Command(spec, "id");
503 const char * label = hasLabel_Command(spec, "text") 503 const char * label = hasLabel_Command(spec, "text")
504 ? suffixPtr_Command(spec, "text") 504 ? suffixPtr_Command(spec, "text")
505 : format_CStr("${%s}", id); 505 : format_CStr("${%s}", id);
@@ -580,7 +580,7 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
580 addChildFlags_Widget(widget, iClob(sep), 0); 580 addChildFlags_Widget(widget, iClob(sep), 0);
581 } 581 }
582 isFirst = iFalse; 582 isFirst = iFalse;
583 const char * radId = cstr_Rangecc(range_Command(radioItem->label, "id")); 583 const char * radId = cstr_Command(radioItem->label, "id");
584 int64_t flags = noBackground_WidgetFlag | frameless_WidgetFlag; 584 int64_t flags = noBackground_WidgetFlag | frameless_WidgetFlag;
585 if (!isHorizontal) { 585 if (!isHorizontal) {
586 flags |= alignLeft_WidgetFlag; 586 flags |= alignLeft_WidgetFlag;
@@ -590,7 +590,7 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
590 const char *radLabel = 590 const char *radLabel =
591 hasLabel_Command(radioItem->label, "label") 591 hasLabel_Command(radioItem->label, "label")
592 ? format_CStr("${%s}", 592 ? format_CStr("${%s}",
593 cstr_Rangecc(range_Command(radioItem->label, "label"))) 593 cstr_Command(radioItem->label, "label"))
594 : suffixPtr_Command(radioItem->label, "text"); 594 : suffixPtr_Command(radioItem->label, "text");
595 button = new_LabelWidget(radLabel, radioItem->command); 595 button = new_LabelWidget(radLabel, radioItem->command);
596 flags |= radio_WidgetFlag; 596 flags |= radio_WidgetFlag;
@@ -613,7 +613,7 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
613 else if (equal_Command(spec, "input")) { 613 else if (equal_Command(spec, "input")) {
614 iInputWidget *input = new_InputWidget(argU32Label_Command(spec, "maxlen")); 614 iInputWidget *input = new_InputWidget(argU32Label_Command(spec, "maxlen"));
615 if (hasLabel_Command(spec, "hint")) { 615 if (hasLabel_Command(spec, "hint")) {
616 setHint_InputWidget(input, cstr_Lang(cstr_Rangecc(range_Command(spec, "hint")))); 616 setHint_InputWidget(input, cstr_Lang(cstr_Command(spec, "hint")));
617 } 617 }
618 setId_Widget(as_Widget(input), id); 618 setId_Widget(as_Widget(input), id);
619 setUrlContent_InputWidget(input, argLabel_Command(spec, "url")); 619 setUrlContent_InputWidget(input, argLabel_Command(spec, "url"));
@@ -630,7 +630,7 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
630 iWidget *unit = addChildFlags_Widget( 630 iWidget *unit = addChildFlags_Widget(
631 as_Widget(input), 631 as_Widget(input),
632 iClob(new_LabelWidget( 632 iClob(new_LabelWidget(
633 format_CStr("${%s}", cstr_Rangecc(range_Command(spec, "unit"))), NULL)), 633 format_CStr("${%s}", cstr_Command(spec, "unit")), NULL)),
634 frameless_WidgetFlag | moveToParentRightEdge_WidgetFlag | 634 frameless_WidgetFlag | moveToParentRightEdge_WidgetFlag |
635 resizeToParentHeight_WidgetFlag); 635 resizeToParentHeight_WidgetFlag);
636 setContentPadding_InputWidget(input, -1, width_Widget(unit) - 4 * gap_UI); 636 setContentPadding_InputWidget(input, -1, width_Widget(unit) - 4 * gap_UI);
@@ -800,7 +800,7 @@ void initPanels_Mobile(iWidget *panels, iWidget *parentWidget,
800 const iMenuItem *item = &itemsNullTerminated[i]; 800 const iMenuItem *item = &itemsNullTerminated[i];
801 if (equal_Command(item->label, "panel")) { 801 if (equal_Command(item->label, "panel")) {
802 haveDetailPanels = iTrue; 802 haveDetailPanels = iTrue;
803 const char *id = cstr_Rangecc(range_Command(item->label, "id")); 803 const char *id = cstr_Command(item->label, "id");
804 const iString *label = hasLabel_Command(item->label, "text") 804 const iString *label = hasLabel_Command(item->label, "text")
805 ? collect_String(suffix_Command(item->label, "text")) 805 ? collect_String(suffix_Command(item->label, "text"))
806 : collectNewFormat_String("${%s}", id); 806 : collectNewFormat_String("${%s}", id);
diff --git a/src/ui/root.c b/src/ui/root.c
index 04586bac..780dc6e7 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -441,7 +441,7 @@ static iBool handleRootCommands_(iWidget *root, const char *cmd) {
441 return iFalse; 441 return iFalse;
442 } 442 }
443 else if (equal_Command(cmd, "focus.set")) { 443 else if (equal_Command(cmd, "focus.set")) {
444 setFocus_Widget(findWidget_App(cstr_Rangecc(range_Command(cmd, "id")))); 444 setFocus_Widget(findWidget_App(cstr_Command(cmd, "id")));
445 return iTrue; 445 return iTrue;
446 } 446 }
447 else if (equal_Command(cmd, "input.resized")) { 447 else if (equal_Command(cmd, "input.resized")) {
diff --git a/src/ui/util.c b/src/ui/util.c
index a46c7f80..58e49230 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -1392,7 +1392,7 @@ static iBool tabSwitcher_(iWidget *tabs, const char *cmd) {
1392 if (equal_Command(cmd, "tabs.switch")) { 1392 if (equal_Command(cmd, "tabs.switch")) {
1393 iWidget *target = pointerLabel_Command(cmd, "page"); 1393 iWidget *target = pointerLabel_Command(cmd, "page");
1394 if (!target) { 1394 if (!target) {
1395 target = findChild_Widget(tabs, cstr_Rangecc(range_Command(cmd, "id"))); 1395 target = findChild_Widget(tabs, cstr_Command(cmd, "id"));
1396 } 1396 }
1397 if (!target) return iFalse; 1397 if (!target) return iFalse;
1398 unfocusFocusInsideTabPage_(currentTabPage_Widget(tabs)); 1398 unfocusFocusInsideTabPage_(currentTabPage_Widget(tabs));
@@ -1720,6 +1720,12 @@ iBool valueInputHandler_(iWidget *dlg, const char *cmd) {
1720 } 1720 }
1721 return iFalse; 1721 return iFalse;
1722 } 1722 }
1723 else if (equal_Command(cmd, "valueinput.set")) {
1724 iInputWidget *input = findChild_Widget(dlg, "input");
1725 setTextCStr_InputWidget(input, suffixPtr_Command(cmd, "text"));
1726 validate_InputWidget(input);
1727 return iTrue;
1728 }
1723 else if (equal_Command(cmd, "valueinput.cancel")) { 1729 else if (equal_Command(cmd, "valueinput.cancel")) {
1724 postCommandf_App("valueinput.cancelled id:%s", cstr_String(id_Widget(dlg))); 1730 postCommandf_App("valueinput.cancelled id:%s", cstr_String(id_Widget(dlg)));
1725 setId_Widget(dlg, ""); /* no further commands to emit */ 1731 setId_Widget(dlg, ""); /* no further commands to emit */
@@ -3317,7 +3323,7 @@ static const iMenuItem languages[] = {
3317static iBool translationHandler_(iWidget *dlg, const char *cmd) { 3323static iBool translationHandler_(iWidget *dlg, const char *cmd) {
3318 iUnused(dlg); 3324 iUnused(dlg);
3319 if (equal_Command(cmd, "xlt.lang")) { 3325 if (equal_Command(cmd, "xlt.lang")) {
3320 const iMenuItem *langItem = &languages[languageIndex_CStr(cstr_Rangecc(range_Command(cmd, "id")))]; 3326 const iMenuItem *langItem = &languages[languageIndex_CStr(cstr_Command(cmd, "id"))];
3321 iWidget *widget = pointer_Command(cmd); 3327 iWidget *widget = pointer_Command(cmd);
3322 iLabelWidget *drop; 3328 iLabelWidget *drop;
3323 if (flags_Widget(widget) & nativeMenu_WidgetFlag) { 3329 if (flags_Widget(widget) & nativeMenu_WidgetFlag) {
@@ -3337,7 +3343,7 @@ const char *languageId_String(const iString *menuItemLabel) {
3337 iForIndices(i, languages) { 3343 iForIndices(i, languages) {
3338 if (!languages[i].label) break; 3344 if (!languages[i].label) break;
3339 if (!cmp_String(menuItemLabel, translateCStr_Lang(languages[i].label))) { 3345 if (!cmp_String(menuItemLabel, translateCStr_Lang(languages[i].label))) {
3340 return cstr_Rangecc(range_Command(languages[i].command, "id")); 3346 return cstr_Command(languages[i].command, "id");
3341 } 3347 }
3342 } 3348 }
3343 return ""; 3349 return "";
diff --git a/src/ui/window.c b/src/ui/window.c
index a4929f51..0e13a57f 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -552,6 +552,7 @@ void init_MainWindow(iMainWindow *d, iRect rect) {
552 d->splitMode = 0; 552 d->splitMode = 0;
553 d->pendingSplitMode = 0; 553 d->pendingSplitMode = 0;
554 d->pendingSplitUrl = new_String(); 554 d->pendingSplitUrl = new_String();
555 d->pendingSplitOrigin = new_String();
555 d->place.initialPos = rect.pos; 556 d->place.initialPos = rect.pos;
556 d->place.normalRect = rect; 557 d->place.normalRect = rect;
557 d->place.lastNotifiedSize = zero_I2(); 558 d->place.lastNotifiedSize = zero_I2();
@@ -634,6 +635,7 @@ void deinit_MainWindow(iMainWindow *d) {
634 if (theMainWindow_ == d) { 635 if (theMainWindow_ == d) {
635 theMainWindow_ = NULL; 636 theMainWindow_ = NULL;
636 } 637 }
638 delete_String(d->pendingSplitOrigin);
637 delete_String(d->pendingSplitUrl); 639 delete_String(d->pendingSplitUrl);
638 deinit_Window(&d->base); 640 deinit_Window(&d->base);
639} 641}
@@ -1528,9 +1530,11 @@ void setSplitMode_MainWindow(iMainWindow *d, int splitFlags) {
1528 } 1530 }
1529 } 1531 }
1530 if (!isEmpty_String(d->pendingSplitUrl)) { 1532 if (!isEmpty_String(d->pendingSplitUrl)) {
1531 postCommandf_Root(w->roots[newRootIndex], "open url:%s", 1533 postCommandf_Root(w->roots[newRootIndex], "open origin:%s url:%s",
1534 cstr_String(d->pendingSplitOrigin),
1532 cstr_String(d->pendingSplitUrl)); 1535 cstr_String(d->pendingSplitUrl));
1533 clear_String(d->pendingSplitUrl); 1536 clear_String(d->pendingSplitUrl);
1537 clear_String(d->pendingSplitOrigin);
1534 } 1538 }
1535 else if (~splitFlags & noEvents_WindowSplit) { 1539 else if (~splitFlags & noEvents_WindowSplit) {
1536 iWidget *docTabs0 = findChild_Widget(w->roots[newRootIndex ^ 1]->widget, "doctabs"); 1540 iWidget *docTabs0 = findChild_Widget(w->roots[newRootIndex ^ 1]->widget, "doctabs");
diff --git a/src/ui/window.h b/src/ui/window.h
index 6c921f09..ae111f4c 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -114,6 +114,7 @@ struct Impl_MainWindow {
114 int splitMode; 114 int splitMode;
115 int pendingSplitMode; 115 int pendingSplitMode;
116 iString * pendingSplitUrl; /* URL to open in a newly opened split */ 116 iString * pendingSplitUrl; /* URL to open in a newly opened split */
117 iString * pendingSplitOrigin; /* tab from where split was initiated, if any */
117 SDL_Texture * appIcon; 118 SDL_Texture * appIcon;
118 int keyboardHeight; /* mobile software keyboards */ 119 int keyboardHeight; /* mobile software keyboards */
119}; 120};