Lagrange [work/v1.10]

DocumentWidget: Paste preceding line

=> a5a59eb664cdfb5fe37fb7d8c82d11c7008cbac7

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"
 msgid "dlg.input.send"
 msgstr "Send"
 
+# Paste the line preceding the clicked link into the input prompt.
+msgid "menu.input.precedingline"
+msgstr "Paste Preceding Line"
+
 msgid "heading.save"
 msgstr "FILE SAVED"
 
diff --git a/res/lang/cs.bin b/res/lang/cs.bin
index 3a5b5820..97197051 100644
Binary files a/res/lang/cs.bin and b/res/lang/cs.bin differ
diff --git a/res/lang/de.bin b/res/lang/de.bin
index 57a6c945..d2dceda6 100644
Binary files a/res/lang/de.bin and b/res/lang/de.bin differ
diff --git a/res/lang/en.bin b/res/lang/en.bin
index 6c921d9b..28c11997 100644
Binary files a/res/lang/en.bin and b/res/lang/en.bin differ
diff --git a/res/lang/eo.bin b/res/lang/eo.bin
index b0ad3a58..d148e2f0 100644
Binary files a/res/lang/eo.bin and b/res/lang/eo.bin differ
diff --git a/res/lang/es.bin b/res/lang/es.bin
index 61a5b870..92037aea 100644
Binary files a/res/lang/es.bin and b/res/lang/es.bin differ
diff --git a/res/lang/es_MX.bin b/res/lang/es_MX.bin
index 6658bbcb..7b7e1219 100644
Binary files a/res/lang/es_MX.bin and b/res/lang/es_MX.bin differ
diff --git a/res/lang/fi.bin b/res/lang/fi.bin
index 287de822..243e9740 100644
Binary files a/res/lang/fi.bin and b/res/lang/fi.bin differ
diff --git a/res/lang/fr.bin b/res/lang/fr.bin
index 495a1b30..507cbb4c 100644
Binary files a/res/lang/fr.bin and b/res/lang/fr.bin differ
diff --git a/res/lang/gl.bin b/res/lang/gl.bin
index efafd3a1..447ed72d 100644
Binary files a/res/lang/gl.bin and b/res/lang/gl.bin differ
diff --git a/res/lang/hu.bin b/res/lang/hu.bin
index e0095569..c2a085ec 100644
Binary files a/res/lang/hu.bin and b/res/lang/hu.bin differ
diff --git a/res/lang/ia.bin b/res/lang/ia.bin
index fb4f482c..5305304c 100644
Binary files a/res/lang/ia.bin and b/res/lang/ia.bin differ
diff --git a/res/lang/ie.bin b/res/lang/ie.bin
index d397cdea..69ac42d4 100644
Binary files a/res/lang/ie.bin and b/res/lang/ie.bin differ
diff --git a/res/lang/isv.bin b/res/lang/isv.bin
index 012e624f..56ed525c 100644
Binary files a/res/lang/isv.bin and b/res/lang/isv.bin differ
diff --git a/res/lang/pl.bin b/res/lang/pl.bin
index 60b5d77c..1ad99e5d 100644
Binary files a/res/lang/pl.bin and b/res/lang/pl.bin differ
diff --git a/res/lang/ru.bin b/res/lang/ru.bin
index 8cda6497..03092acf 100644
Binary files a/res/lang/ru.bin and b/res/lang/ru.bin differ
diff --git a/res/lang/sk.bin b/res/lang/sk.bin
index 9a5355d9..bf13f594 100644
Binary files a/res/lang/sk.bin and b/res/lang/sk.bin differ
diff --git a/res/lang/sr.bin b/res/lang/sr.bin
index 7d5183e5..823818e2 100644
Binary files a/res/lang/sr.bin and b/res/lang/sr.bin differ
diff --git a/res/lang/tok.bin b/res/lang/tok.bin
index e21e8201..50743783 100644
Binary files a/res/lang/tok.bin and b/res/lang/tok.bin differ
diff --git a/res/lang/tr.bin b/res/lang/tr.bin
index 9dd4764e..543739cd 100644
Binary files a/res/lang/tr.bin and b/res/lang/tr.bin differ
diff --git a/res/lang/uk.bin b/res/lang/uk.bin
index a48258b6..f866f86f 100644
Binary files a/res/lang/uk.bin and b/res/lang/uk.bin differ
diff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin
index 1ad2db27..179d87db 100644
Binary files a/res/lang/zh_Hans.bin and b/res/lang/zh_Hans.bin differ
diff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin
index f4263b3b..aad28410 100644
Binary files a/res/lang/zh_Hant.bin and b/res/lang/zh_Hant.bin 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) {
                 setUiScale_Window(get_Window(), argf_Command(cmd));
             }
             else if (equal_Command(cmd, "uilang")) {
-                const char *id = cstr_Rangecc(range_Command(cmd, "id"));
+                const char *id = cstr_Command(cmd, "id");
                 setCStr_String(&d->prefs.strings[uiLanguage_PrefsString], id);
                 setCurrent_Lang(id);
             }
@@ -2190,6 +2190,7 @@ iBool handleCommand_App(const char *cmd) {
         return iTrue;
     }
     else if (equal_Command(cmd, "fontpack.suggest.classic")) {
+        /* TODO: Don't use this when system fonts are accessible. */
         if (!isInstalled_Fonts("classic-set") && !isInstalled_Fonts("cjk")) {
             makeQuestion_Widget(
                 uiHeading_ColorEscape "${heading.fontpack.classic}",
@@ -2243,6 +2244,9 @@ iBool handleCommand_App(const char *cmd) {
             (argLabel_Command(cmd, "axis") ? vertical_WindowSplit : 0) | (arg_Command(cmd) << 1);
         const char *url = suffixPtr_Command(cmd, "url");
         setCStr_String(d->window->pendingSplitUrl, url ? url : "");
+        if (hasLabel_Command(cmd, "origin")) {
+            set_String(d->window->pendingSplitOrigin, string_Command(cmd, "origin"));
+        }
         postRefresh_App();
         return iTrue;
     }
@@ -2732,12 +2736,21 @@ iBool handleCommand_App(const char *cmd) {
             openInDefaultBrowser_App(url);
             return iTrue;
         }
+        iDocumentWidget *doc = document_Command(cmd);
+        iDocumentWidget *origin = doc;
+        if (hasLabel_Command(cmd, "origin")) {
+            iDocumentWidget *cmdOrig = findWidget_App(cstr_Command(cmd, "origin"));
+            if (cmdOrig) {
+                origin = cmdOrig;
+            }
+        }
         const int newTab = argLabel_Command(cmd, "newtab");
         if (newTab & otherRoot_OpenTabFlag && numRoots_Window(get_Window()) == 1) {
             /* Need to split first. */
             const iInt2 winSize = get_Window()->size;
-            postCommandf_App("ui.split arg:3 axis:%d newtab:%d url:%s",
+            postCommandf_App("ui.split arg:3 axis:%d origin:%s newtab:%d url:%s",
                              (float) winSize.x / (float) winSize.y < 0.7f ? 1 : 0,
+                             cstr_String(id_Widget(as_Widget(origin))),
                              newTab & ~otherRoot_OpenTabFlag,
                              cstr_String(url));
             return iTrue;
@@ -2749,7 +2762,6 @@ iBool handleCommand_App(const char *cmd) {
             setKeyRoot_Window(as_Window(d->window), root);
             setCurrent_Root(root); /* need to change for widget creation */
         }
-        iDocumentWidget *doc = document_Command(cmd);
         if (newTab & (new_OpenTabFlag | newBackground_OpenTabFlag)) {
             doc = newTab_App(NULL, (newTab & new_OpenTabFlag) != 0); /* `newtab:2` to open in background */
         }
@@ -2766,13 +2778,14 @@ iBool handleCommand_App(const char *cmd) {
         }
         setInitialScroll_DocumentWidget(doc, argfLabel_Command(cmd, "scroll"));
         setRedirectCount_DocumentWidget(doc, redirectCount);
+        setOrigin_DocumentWidget(doc, origin);
         showCollapsed_Widget(findWidget_App("document.progress"), iFalse);
         if (prefs_App()->decodeUserVisibleURLs) {
             urlDecodePath_String(url);
         }
         else {
             urlEncodePath_String(url);
-        }
+        }            
         setUrlFlags_DocumentWidget(doc, url,
            (isHistory   ? useCachedContentIfAvailable_DocumentWidgetSetUrlFlag : 0) |
            (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 {
     int            pinchZoomPosted;
     float          swipeSpeed; /* points/sec */
     iString        pendingGotoHeading;
+    iString        linePrecedingLink;
     
     /* Network request: */
     enum iRequestState state;
@@ -391,6 +392,7 @@ void init_DocumentWidget(iDocumentWidget *d) {
     d->grabbedPlayer = NULL;
     d->mediaTimer    = 0;
     init_String(&d->pendingGotoHeading);
+    init_String(&d->linePrecedingLink);
     init_Click(&d->click, d, SDL_BUTTON_LEFT);
     addChild_Widget(w, iClob(d->scroll = new_ScrollWidget()));
     d->menu         = NULL; /* created when clicking */
@@ -437,6 +439,7 @@ void deinit_DocumentWidget(iDocumentWidget *d) {
     iRelease(d->media);
     iRelease(d->request);
     delete_Gempub(d->sourceGempub);
+    deinit_String(&d->linePrecedingLink);
     deinit_String(&d->pendingGotoHeading);
     deinit_Block(&d->sourceContent);
     deinit_String(&d->sourceMime);
@@ -1893,7 +1896,7 @@ static void addBannerWarnings_DocumentWidget_(iDocumentWidget *d) {
 
 static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float normScrollY,
                                                      const iGmResponse *resp, iGmDocument *cachedDoc) {
-    iAssert(width_Widget(d) > 0); /* must be laid out by now */
+//    iAssert(width_Widget(d) > 0); /* must be laid out by now */
     setLinkNumberMode_DocumentWidget_(d, iFalse);
     clear_ObjectList(d->media);
     delete_Gempub(d->sourceGempub);
@@ -1918,9 +1921,6 @@ static void updateFromCachedResponse_DocumentWidget_(iDocumentWidget *d, float n
             setWidth_GmDocument(d->doc, documentWidth_DocumentWidget_(d), width_Widget(d));
         }
         updateDocument_DocumentWidget_(d, resp, cachedDoc, iTrue);
-//        if (!cachedDoc) {
-//            setCachedDocument_History(d->mod.history, d->doc, iFalse);
-//        }
         clear_Banner(d->banner);
         updateBanner_DocumentWidget_(d);
         addBannerWarnings_DocumentWidget_(d);
@@ -2242,6 +2242,16 @@ static void checkResponse_DocumentWidget_(iDocumentWidget *d) {
                 if (lineBreak && deviceType_App() != desktop_AppDeviceType) {
                     addChildPos_Widget(buttons, iClob(lineBreak), front_WidgetAddPos);
                 }
+                /* Menu for additional actions, past entries. */ {
+                    iMenuItem items[] = { { "${menu.input.precedingline}",
+                                            SDLK_v,
+                                            KMOD_PRIMARY | KMOD_SHIFT,
+                                            format_CStr("!valueinput.set ptr:%p text:%s",
+                                                        buttons,
+                                                        cstr_String(&d->linePrecedingLink)) } };
+                    iLabelWidget *menu = makeMenuButton_LabelWidget(midEllipsis_Icon, items, 1);
+                    addChildPos_Widget(buttons, iClob(menu), front_WidgetAddPos);
+                }                
                 setValidator_InputWidget(findChild_Widget(dlg, "input"), inputQueryValidator_, d);
                 setSensitiveContent_InputWidget(findChild_Widget(dlg, "input"),
                                                 statusCode == sensitiveInput_GmStatusCode);
@@ -3836,6 +3846,30 @@ static void beginMarkingSelection_DocumentWidget_(iDocumentWidget *d, iInt2 pos)
     refresh_Widget(as_Widget(d));
 }
 
+static void linkWasTriggered_DocumentWidget_(iDocumentWidget *d, iGmLinkId id) {
+    iRangecc loc = linkUrlRange_GmDocument(d->doc, id);
+    if (!loc.start) {
+        clear_String(&d->linePrecedingLink);
+        return;
+    }
+    const char *start = range_String(source_GmDocument(d->doc)).start;
+    /* Find the preceding line. This is offered as a prefill option for a possible input query. */
+    while (loc.start > start && *loc.start != '\n') {
+        loc.start--;
+    }
+    loc.end = loc.start; /* End of the preceding line. */
+    if (loc.start > start) {
+        loc.start--;
+    }
+    while (loc.start > start && *loc.start != '\n') {
+        loc.start--;
+    }
+    if (*loc.start == '\n') {
+        loc.start++; /* Start of the preceding line. */
+    }
+    setRange_String(&d->linePrecedingLink, loc);
+}
+
 static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *ev) {
     iWidget *w = as_Widget(d);
     if (isMetricsChange_UserEvent(ev)) {
@@ -3878,6 +3912,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
                                              : (d->flags & newTabViaHomeKeys_DocumentWidgetFlag ? 1 : 0)),
                                           cstr_String(absoluteUrl_String(
                                              d->mod.url, linkUrl_GmDocument(d->doc, run->linkId))));
+                        linkWasTriggered_DocumentWidget_(d, run->linkId);
                     }
                     setLinkNumberMode_DocumentWidget_(d, iFalse);
                     invalidateVisibleLinks_DocumentWidget_(d);
@@ -3995,6 +4030,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
             return iTrue;
         }
         if (ev->button.button == SDL_BUTTON_MIDDLE && d->hoverLink) {
+            linkWasTriggered_DocumentWidget_(d, d->hoverLink->linkId);
             postCommandf_Root(w->root, "open newtab:%d url:%s",
                               (isPinned_DocumentWidget_(d) ? otherRoot_OpenTabFlag : 0) |
                               (modState_Keys() & KMOD_SHIFT ? new_OpenTabFlag : newBackground_OpenTabFlag),
@@ -4015,6 +4051,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
                 init_Array(&items, sizeof(iMenuItem));
                 if (d->contextLink) {
                     /* Context menu for a link. */
+                    linkWasTriggered_DocumentWidget_(d, d->contextLink->linkId); /* perhaps will be triggered */
                     const iString *linkUrl  = linkUrl_GmDocument(d->doc, d->contextLink->linkId);
 //                    const int      linkFlags = linkFlags_GmDocument(d->doc, d->contextLink->linkId);
                     const iRangecc scheme   = urlScheme_String(linkUrl);
@@ -4034,23 +4071,30 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
                         /* Regular links that we can open. */
                         pushBackN_Array(
                             &items,
-                            (iMenuItem[]){
-                                { openTab_Icon " ${link.newtab}",
-                                  0,
-                                  0,
-                                  format_CStr("!open newtab:1 url:%s", cstr_String(linkUrl)) },
-                                { openTabBg_Icon " ${link.newtab.background}",
-                                  0,
-                                  0,
-                                  format_CStr("!open newtab:2 url:%s", cstr_String(linkUrl)) },
-                                { "${link.side}",
-                                  0,
-                                  0,
-                                  format_CStr("!open newtab:4 url:%s", cstr_String(linkUrl)) },
-                                { "${link.side.newtab}",
-                                  0,
-                                  0,
-                                  format_CStr("!open newtab:5 url:%s", cstr_String(linkUrl)) } },
+                            (iMenuItem[]){ { openTab_Icon " ${link.newtab}",
+                                             0,
+                                             0,
+                                             format_CStr("!open newtab:1 origin:%s url:%s",
+                                                         cstr_String(id_Widget(w)),
+                                                         cstr_String(linkUrl)) },
+                                           { openTabBg_Icon " ${link.newtab.background}",
+                                             0,
+                                             0,
+                                             format_CStr("!open newtab:2 origin:%s url:%s",
+                                                         cstr_String(id_Widget(w)),
+                                                         cstr_String(linkUrl)) },
+                                           { "${link.side}",
+                                             0,
+                                             0,
+                                             format_CStr("!open newtab:4 origin:%s url:%s",
+                                                         cstr_String(id_Widget(w)),
+                                                         cstr_String(linkUrl)) },
+                                           { "${link.side.newtab}",
+                                             0,
+                                             0,
+                                             format_CStr("!open newtab:5 origin:%s url:%s",
+                                                         cstr_String(id_Widget(w)),
+                                                         cstr_String(linkUrl)) } },
                             4);
                         if (deviceType_App() == phone_AppDeviceType) {
                             removeN_Array(&items, size_Array(&items) - 2, iInvalidSize);
@@ -4072,7 +4116,9 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
                                 { isGemini ? "${link.noproxy}" : openExt_Icon " ${link.browser}",
                                   0,
                                   0,
-                                  format_CStr("!open noproxy:1 url:%s", cstr_String(linkUrl)) } },
+                                  format_CStr("!open origin:%s noproxy:1 url:%s",
+                                              cstr_String(id_Widget(w)),
+                                              cstr_String(linkUrl)) } },
                             2);
                     }
                     iString *linkLabel = collectNewRange_String(
@@ -4414,6 +4460,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
                         if (isPinned_DocumentWidget_(d)) {
                             tabMode ^= otherRoot_OpenTabFlag;
                         }
+                        linkWasTriggered_DocumentWidget_(d, linkId);
                         postCommandf_Root(w->root, "open newtab:%d url:%s",
                                          tabMode,
                                          cstr_String(absoluteUrl_String(
@@ -5495,6 +5542,13 @@ iDocumentWidget *duplicate_DocumentWidget(const iDocumentWidget *orig) {
     return d;
 }
 
+void setOrigin_DocumentWidget(iDocumentWidget *d, const iDocumentWidget *other) {
+    if (d != other) {
+        /* TODO: Could remember the other's ID? */
+        set_String(&d->linePrecedingLink, &other->linePrecedingLink);
+    }
+}
+
 void setUrl_DocumentWidget(iDocumentWidget *d, const iString *url) {
     setUrlFlags_DocumentWidget(d, url, 0);
 }
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 {
     openedFromSidebar_DocumentWidgetSetUrlFlag           = iBit(2),
 };
 
+void    setOrigin_DocumentWidget        (iDocumentWidget *, const iDocumentWidget *other);
 void    setUrl_DocumentWidget           (iDocumentWidget *, const iString *url);
 void    setUrlFlags_DocumentWidget      (iDocumentWidget *, const iString *url, int setUrlFlags);
 void    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) {
 #endif
 }
 
+void validate_InputWidget(iInputWidget *d) {
+    if (d->validator) {
+        d->validator(d, d->validatorContext); /* this may change the contents */
+    }    
+}
+
 iLocalDef iBool isEditing_InputWidget_(const iInputWidget *d) {
     return (flags_Widget(constAs_Widget(d)) & selected_WidgetFlag) != 0;
 }
@@ -1653,9 +1659,7 @@ void setEatEscape_InputWidget(iInputWidget *d, iBool eatEscape) {
 }
 
 static void contentsWereChanged_InputWidget_(iInputWidget *d) {
-    if (d->validator) {
-        d->validator(d, d->validatorContext); /* this may change the contents */
-    }
+    validate_InputWidget(d);
     if (d->inFlags & notifyEdits_InputWidgetFlag) {
         postCommand_Widget(d, "input.edited id:%s", cstr_String(id_Widget(constAs_Widget(d))));
     }
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);
 void    begin_InputWidget               (iInputWidget *);
 void    end_InputWidget                 (iInputWidget *, iBool accept);
 void    selectAll_InputWidget           (iInputWidget *);
+void    validate_InputWidget            (iInputWidget *);
 
 void    setSelectAllOnFocus_InputWidget (iInputWidget *, iBool selectAllOnFocus);
 void    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) {
     iLabelWidget *heading = NULL;
     iWidget *     value   = NULL;
     const char *  spec    = item->label;
-    const char *  id      = cstr_Rangecc(range_Command(spec, "id"));
+    const char *  id      = cstr_Command(spec, "id");
     const char *  label   = hasLabel_Command(spec, "text")
                                 ? suffixPtr_Command(spec, "text")
                                 : format_CStr("${%s}", id);
@@ -580,7 +580,7 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
                 addChildFlags_Widget(widget, iClob(sep), 0);
             }
             isFirst = iFalse;
-            const char *  radId = cstr_Rangecc(range_Command(radioItem->label, "id"));
+            const char *  radId = cstr_Command(radioItem->label, "id");
             int64_t       flags = noBackground_WidgetFlag | frameless_WidgetFlag;
             if (!isHorizontal) {
                 flags |= alignLeft_WidgetFlag;
@@ -590,7 +590,7 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
                 const char *radLabel =
                     hasLabel_Command(radioItem->label, "label")
                         ? format_CStr("${%s}",
-                                      cstr_Rangecc(range_Command(radioItem->label, "label")))
+                                      cstr_Command(radioItem->label, "label"))
                         : suffixPtr_Command(radioItem->label, "text");
                 button = new_LabelWidget(radLabel, radioItem->command);
                 flags |= radio_WidgetFlag;
@@ -613,7 +613,7 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
     else if (equal_Command(spec, "input")) {
         iInputWidget *input = new_InputWidget(argU32Label_Command(spec, "maxlen"));
         if (hasLabel_Command(spec, "hint")) {
-            setHint_InputWidget(input, cstr_Lang(cstr_Rangecc(range_Command(spec, "hint"))));
+            setHint_InputWidget(input, cstr_Lang(cstr_Command(spec, "hint")));
         }
         setId_Widget(as_Widget(input), id);
         setUrlContent_InputWidget(input, argLabel_Command(spec, "url"));
@@ -630,7 +630,7 @@ void makePanelItem_Mobile(iWidget *panel, const iMenuItem *item) {
                 iWidget *unit = addChildFlags_Widget(
                     as_Widget(input),
                     iClob(new_LabelWidget(
-                        format_CStr("${%s}", cstr_Rangecc(range_Command(spec, "unit"))), NULL)),
+                        format_CStr("${%s}", cstr_Command(spec, "unit")), NULL)),
                     frameless_WidgetFlag | moveToParentRightEdge_WidgetFlag |
                         resizeToParentHeight_WidgetFlag);
                 setContentPadding_InputWidget(input, -1, width_Widget(unit) - 4 * gap_UI);
@@ -800,7 +800,7 @@ void initPanels_Mobile(iWidget *panels, iWidget *parentWidget,
         const iMenuItem *item = &itemsNullTerminated[i];
         if (equal_Command(item->label, "panel")) {
             haveDetailPanels = iTrue;
-            const char *id = cstr_Rangecc(range_Command(item->label, "id"));
+            const char *id = cstr_Command(item->label, "id");
             const iString *label = hasLabel_Command(item->label, "text")
                                        ? collect_String(suffix_Command(item->label, "text"))
                                        : 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) {
         return iFalse;
     }
     else if (equal_Command(cmd, "focus.set")) {
-        setFocus_Widget(findWidget_App(cstr_Rangecc(range_Command(cmd, "id"))));
+        setFocus_Widget(findWidget_App(cstr_Command(cmd, "id")));
         return iTrue;
     }
     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) {
     if (equal_Command(cmd, "tabs.switch")) {
         iWidget *target = pointerLabel_Command(cmd, "page");
         if (!target) {
-            target = findChild_Widget(tabs, cstr_Rangecc(range_Command(cmd, "id")));
+            target = findChild_Widget(tabs, cstr_Command(cmd, "id"));
         }
         if (!target) return iFalse;
         unfocusFocusInsideTabPage_(currentTabPage_Widget(tabs));
@@ -1720,6 +1720,12 @@ iBool valueInputHandler_(iWidget *dlg, const char *cmd) {
         }
         return iFalse;
     }
+    else if (equal_Command(cmd, "valueinput.set")) {
+        iInputWidget *input = findChild_Widget(dlg, "input");
+        setTextCStr_InputWidget(input, suffixPtr_Command(cmd, "text"));
+        validate_InputWidget(input);
+        return iTrue;
+    }
     else if (equal_Command(cmd, "valueinput.cancel")) {
         postCommandf_App("valueinput.cancelled id:%s", cstr_String(id_Widget(dlg)));
         setId_Widget(dlg, ""); /* no further commands to emit */
@@ -3317,7 +3323,7 @@ static const iMenuItem languages[] = {
 static iBool translationHandler_(iWidget *dlg, const char *cmd) {
     iUnused(dlg);
     if (equal_Command(cmd, "xlt.lang")) {
-        const iMenuItem *langItem = &languages[languageIndex_CStr(cstr_Rangecc(range_Command(cmd, "id")))];
+        const iMenuItem *langItem = &languages[languageIndex_CStr(cstr_Command(cmd, "id"))];
         iWidget *widget = pointer_Command(cmd);
         iLabelWidget *drop;
         if (flags_Widget(widget) & nativeMenu_WidgetFlag) {
@@ -3337,7 +3343,7 @@ const char *languageId_String(const iString *menuItemLabel) {
     iForIndices(i, languages) {
         if (!languages[i].label) break;
         if (!cmp_String(menuItemLabel, translateCStr_Lang(languages[i].label))) {
-            return cstr_Rangecc(range_Command(languages[i].command, "id"));
+            return cstr_Command(languages[i].command, "id");
         }
     }
     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) {
     d->splitMode              = 0;
     d->pendingSplitMode       = 0;
     d->pendingSplitUrl        = new_String();
+    d->pendingSplitOrigin     = new_String();
     d->place.initialPos       = rect.pos;
     d->place.normalRect       = rect;
     d->place.lastNotifiedSize = zero_I2();
@@ -634,6 +635,7 @@ void deinit_MainWindow(iMainWindow *d) {
     if (theMainWindow_ == d) {
         theMainWindow_ = NULL;
     }
+    delete_String(d->pendingSplitOrigin);
     delete_String(d->pendingSplitUrl);
     deinit_Window(&d->base);
 }
@@ -1528,9 +1530,11 @@ void setSplitMode_MainWindow(iMainWindow *d, int splitFlags) {
                 }
             }
             if (!isEmpty_String(d->pendingSplitUrl)) {
-                postCommandf_Root(w->roots[newRootIndex], "open url:%s",
+                postCommandf_Root(w->roots[newRootIndex], "open origin:%s url:%s",
+                                  cstr_String(d->pendingSplitOrigin),
                                   cstr_String(d->pendingSplitUrl));
                 clear_String(d->pendingSplitUrl);
+                clear_String(d->pendingSplitOrigin);
             }
             else if (~splitFlags & noEvents_WindowSplit) {
                 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 {
     int           splitMode;
     int           pendingSplitMode;
     iString *     pendingSplitUrl; /* URL to open in a newly opened split */
+    iString *     pendingSplitOrigin; /* tab from where split was initiated, if any */
     SDL_Texture * appIcon;
     int           keyboardHeight; /* mobile software keyboards */    
 };
Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/work%2Fv1.10/cdiff/a5a59eb664cdfb5fe37fb7d8c82d11c7008cbac7
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
103.78799 milliseconds
Gemini-to-HTML Time
1.215915 milliseconds

This content has been proxied by September (3851b).