summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
10 files changed, 124 insertions, 40 deletions
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};