Lagrange [work/v1.11]

Adding context items to open in new window

=> ed14d3467d6046ddfa66c3143a340428aeef66ae

diff --git a/po/en.po b/po/en.po
index bff4c495..6647324e 100644
--- a/po/en.po
+++ b/po/en.po
@@ -148,6 +148,9 @@ msgstr "Identity"
 msgid "menu.title.help"
 msgstr "Help"
 
+msgid "menu.newwindow"
+msgstr "New Window"
+
 msgid "menu.newtab"
 msgstr "New Tab"
 
@@ -519,9 +522,6 @@ msgstr "%b. %d, %Y"
 msgid "feeds.today"
 msgstr "Today"
 
-msgid "feeds.entry.newtab"
-msgstr "Open Entry in New Tab"
-
 msgid "feeds.entry.markread"
 msgstr "Mark as Read"
 
@@ -558,6 +558,9 @@ msgstr "Open in New Tab"
 msgid "menu.opentab.background"
 msgstr "Open in Background Tab"
 
+msgid "menu.openwindow"
+msgstr "Open in New Window"
+
 msgid "menu.openfile"
 msgstr "Open File…"
 
@@ -870,6 +873,9 @@ msgstr "Open Link to the Side"
 msgid "link.side.newtab"
 msgstr "Open Link in New Tab to the Side"
 
+msgid "link.newwindow"
+msgstr "Open Link in New Window"
+
 msgid "link.browser"
 msgstr "Open Link in Default Browser"
 
diff --git a/src/app.c b/src/app.c
index 010c6d74..543467ef 100644
--- a/src/app.c
+++ b/src/app.c
@@ -1878,7 +1878,7 @@ void postCommand_Root(iRoot *d, const char *command) {
     ev.user.data2 = d; /* all events are root-specific */
     ev.user.windowID = d ? id_Window(d->window) : 0; /* root-specific means window-specific */
     SDL_PushEvent(&ev);
-    iWindow *win = get_Window();
+    iWindow *win = d ? d->window : NULL;
 #if defined (iPlatformAndroid)
     SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "%s[command] {%d} %s",
                 app_.isLoadingPrefs ? "[Prefs] " : "",
@@ -1979,6 +1979,13 @@ size_t windowIndex_App(const iMainWindow *win) {
     return indexOf_PtrArray(&app_.mainWindows, win); 
 }
 
+iMainWindow *newMainWindow_App(void) {
+    iApp *d = &app_;
+    iMainWindow *win = new_MainWindow(initialWindowRect_App_(d, size_PtrArray(&d->mainWindows)));
+    addWindow_App(win);
+    return win;
+}
+
 const iPtrArray *mainWindows_App(void) {
     return &app_.mainWindows;
 }
@@ -3038,7 +3045,18 @@ iBool handleCommand_App(const char *cmd) {
             return iTrue; /* invalid command */
         }
         if (findWidget_App("prefs")) {
-            postCommand_App("prefs.dismiss");        
+            postCommand_App("prefs.dismiss");
+        }
+        if (argLabel_Command(cmd, "newwindow")) {
+            const iRangecc gotoheading = range_Command(cmd, "gotoheading");
+            const iRangecc gotourlheading = range_Command(cmd, "gotourlheading");
+            postCommandf_Root(get_Root(), "window.new%s%s%s%s url:%s",
+                              isEmpty_Range(&gotoheading) ? "" : " gotoheading:",
+                              isEmpty_Range(&gotoheading) ? "" : cstr_Rangecc(gotoheading),
+                              isEmpty_Range(&gotourlheading) ? "" : " gotourlheading:",
+                              isEmpty_Range(&gotourlheading) ? "" : cstr_Rangecc(gotourlheading),
+                              urlArg);
+            return iTrue;
         }
         iString    *url     = collectNewCStr_String(urlArg);
         const iBool noProxy = argLabel_Command(cmd, "noproxy") != 0;
@@ -3187,7 +3205,12 @@ iBool handleCommand_App(const char *cmd) {
         addWindow_App(newWin); /* takes ownership */
         SDL_ShowWindow(newWin->base.win);
         setCurrent_Window(newWin);
-        postCommand_Root(newWin->base.roots[0], "~navigate.home");
+        if (hasLabel_Command(cmd, "url")) {
+            postCommandf_Root(newWin->base.roots[0], "~open %s", cmd + 11 /* all arguments passed on */);
+        }
+        else {
+            postCommand_Root(newWin->base.roots[0], "~navigate.home");
+        }
         postCommand_Root(newWin->base.roots[0], "~window.unfreeze");
         return iTrue;
     }
@@ -3745,21 +3768,7 @@ void revealPath_App(const iString *path) {
 }
 
 iObjectList *listDocuments_App(const iRoot *rootOrNull) {
-    iWindow *win = get_Window();
-    iObjectList *docs = new_ObjectList();
-    iForIndices(i, win->roots) {
-        iRoot *root = win->roots[i];
-        if (!root) continue;
-        if (!rootOrNull || root == rootOrNull) {
-            const iWidget *tabs = findChild_Widget(root->widget, "doctabs");
-            iForEach(ObjectList, i, children_Widget(findChild_Widget(tabs, "tabs.pages"))) {
-                if (isInstance_Object(i.object, &Class_DocumentWidget)) {
-                    pushBack_ObjectList(docs, i.object);
-                }
-            }
-        }
-    }
-    return docs;
+    return listDocuments_MainWindow(get_MainWindow(), rootOrNull);
 }
 
 iStringSet *listOpenURLs_App(void) {
diff --git a/src/app.h b/src/app.h
index 63a477a5..5f7b506f 100644
--- a/src/app.h
+++ b/src/app.h
@@ -129,6 +129,7 @@ void        setActiveWindow_App (iMainWindow *win);
 void        closeWindow_App     (iMainWindow *win);
 size_t      numWindows_App      (void);
 size_t      windowIndex_App     (const iMainWindow *win);
+iMainWindow *newMainWindow_App  (void);
 const iPtrArray *mainWindows_App(void);
 void        addPopup_App        (iWindow *popup);
 void        removePopup_App     (iWindow *popup);
diff --git a/src/defs.h b/src/defs.h
index be5280fa..c3480bc2 100644
--- a/src/defs.h
+++ b/src/defs.h
@@ -157,8 +157,9 @@ iLocalDef int acceptKeyMod_ReturnKeyBehavior(int behavior) {
 #define bookmark_Icon       "\U0001f516"
 #define folder_Icon         "\U0001f4c1"
 #define file_Icon           "\U0001f5ce"
-#define openTab_Icon        "\u2750"
-#define openTabBg_Icon      "\u2b1a"
+#define openWindow_Icon     "\u2b1a" //"\U0001F5d4"
+#define openTab_Icon        add_Icon
+#define openTabBg_Icon      "\u2750" //"\u2b1a"
 #define openExt_Icon        "\u27a0"
 #define add_Icon            "\u2795"
 #define page_Icon           "\U00010117"
diff --git a/src/gmutil.c b/src/gmutil.c
index e862b18a..b32722ac 100644
--- a/src/gmutil.c
+++ b/src/gmutil.c
@@ -780,7 +780,7 @@ iRangecc mediaTypeWithoutParameters_Rangecc(iRangecc mime) {
     return part;
 }
 
-const iString *feedEntryOpenCommand_String(const iString *url, int newTab) {
+const iString *feedEntryOpenCommand_String(const iString *url, int newTab, int newWindow) {
     if (!isEmpty_String(url)) {
         iString *cmd = collectNew_String();
         const size_t fragPos = indexOf_String(url, '#');
@@ -788,15 +788,20 @@ const iString *feedEntryOpenCommand_String(const iString *url, int newTab) {
             iString *head = newRange_String(
                 (iRangecc){ constBegin_String(url) + fragPos + 1, constEnd_String(url) });
             format_String(cmd,
-                          "open fromsidebar:1 newtab:%d gotourlheading:%s url:%s",
+                          "open fromsidebar:1 newtab:%d newwindow:%d gotourlheading:%s url:%s",
                           newTab,
+                          newWindow,
                           cstr_String(head),
                           cstr_Rangecc((iRangecc){ constBegin_String(url),
                                                    constBegin_String(url) + fragPos }));
             delete_String(head);
         }
         else {
-            format_String(cmd, "open fromsidebar:1 newtab:%d url:%s", newTab, cstr_String(url));
+            format_String(cmd,
+                          "open fromsidebar:1 newtab:%d newwindow:%d url:%s",
+                          newTab,
+                          newWindow,
+                          cstr_String(url));
         }
         return cmd;
     }
diff --git a/src/gmutil.h b/src/gmutil.h
index 01eb8e52..e4284cfd 100644
--- a/src/gmutil.h
+++ b/src/gmutil.h
@@ -151,4 +151,4 @@ iRangecc        mediaTypeWithoutParameters_Rangecc  (iRangecc mime);
 const iString * findContainerArchive_Path           (const iString *path);
 
 
-const iString * feedEntryOpenCommand_String (const iString *url, int newTab); /* checks fragment */
+const iString * feedEntryOpenCommand_String (const iString *url, int newTab, int newWindow); /* checks fragment */
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 76c26e27..99039ff5 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -5071,10 +5071,9 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
                 iArray items;
                 init_Array(&items, sizeof(iMenuItem));
                 if (d->contextLink) {
-                    /* Context menu for a link. */
+                    /* Construct the link context menu, depending on what kind of link was clicked. */
                     interactingWithLink_DocumentWidget_(d, d->contextLink->linkId); /* perhaps will be triggered */
                     const iString *linkUrl  = linkUrl_GmDocument(view->doc, d->contextLink->linkId);
-//                    const int      linkFlags = linkFlags_GmDocument(d->doc, d->contextLink->linkId);
                     const iRangecc scheme   = urlScheme_String(linkUrl);
                     const iBool    isGemini = equalCase_Rangecc(scheme, "gemini");
                     iBool          isNative = iFalse;
@@ -5086,39 +5085,48 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
                             format_CStr("```%s", cstr_String(infoText)),
                             0, 0, NULL });
                     }
-                    if (willUseProxy_App(scheme) || isGemini ||
+                    if (isGemini ||
+                        willUseProxy_App(scheme) ||
+                        equalCase_Rangecc(scheme, "data") ||
                         equalCase_Rangecc(scheme, "file") ||
                         equalCase_Rangecc(scheme, "finger") ||
                         equalCase_Rangecc(scheme, "gopher")) {
                         isNative = iTrue;
                         /* Regular links that we can open. */
-                        pushBackN_Array(
-                            &items,
-                            (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);
+                        pushBackN_Array(&items,
+                                        (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)) },
+                                            { openWindow_Icon " ${link.newwindow}",
+                                              0,
+                                              0,
+                                              format_CStr("!open newwindow:1 origin:%s url:%s",
+                                                          cstr_String(id_Widget(w)),
+                                                          cstr_String(linkUrl)) },
+                                        },
+                                        5);
                         if (deviceType_App() == phone_AppDeviceType) {
                             removeN_Array(&items, size_Array(&items) - 2, iInvalidSize);
                         }
diff --git a/src/ui/lookupwidget.c b/src/ui/lookupwidget.c
index f14170ad..dc3264a2 100644
--- a/src/ui/lookupwidget.c
+++ b/src/ui/lookupwidget.c
@@ -568,7 +568,7 @@ static void presentResults_LookupWidget_(iLookupWidget *d) {
                               cstr_String(&res->label),
                               uiText_ColorEscape,
                               cstr_String(&res->meta));
-                const iString *cmd = feedEntryOpenCommand_String(&res->url, 0);
+                const iString *cmd = feedEntryOpenCommand_String(&res->url, 0, 0);
                 if (cmd) {
                     set_String(&item->command, cmd);
                 }
diff --git a/src/ui/root.c b/src/ui/root.c
index 7e4b4863..9dee50ae 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -56,7 +56,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
 #if defined (iPlatformPcDesktop)
 /* TODO: Submenus wouldn't hurt here. */
 static const iMenuItem navMenuItems_[] = {
-    { add_Icon " ${menu.newtab}", 't', KMOD_PRIMARY, "tabs.new" },
+    { openWindow_Icon " ${menu.newwindow}", SDLK_n, KMOD_PRIMARY, "window.new" },
+    { add_Icon " ${menu.newtab}", SDLK_t, KMOD_PRIMARY, "tabs.new" },
     { "${menu.openlocation}", SDLK_l, KMOD_PRIMARY, "navigate.focus" },
     { "---" },
     { download_Icon " " saveToDownloads_Label, SDLK_s, KMOD_PRIMARY, "document.save" },
diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c
index 73023a4f..0322b2a9 100644
--- a/src/ui/sidebarwidget.c
+++ b/src/ui/sidebarwidget.c
@@ -414,9 +414,13 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct
             }
             d->menu = makeMenu_Widget(
                 as_Widget(d),
-                (iMenuItem[]){ { openTab_Icon " ${feeds.entry.newtab}", 0, 0, "feed.entry.opentab" },
+                (iMenuItem[]){ { openTab_Icon " ${menu.opentab}", 0, 0, "feed.entry.open newtab:1" },
+                               { openTabBg_Icon " ${menu.opentab.background}", 0, 0, "feed.entry.open newtab:2" },
+                               { openWindow_Icon " ${menu.openwindow}", 0, 0, "feed.entry.open newwindow:1" },
+                               { "---", 0, 0, NULL },
                                { circle_Icon " ${feeds.entry.markread}", 0, 0, "feed.entry.toggleread" },
                                { bookmark_Icon " ${feeds.entry.bookmark}", 0, 0, "feed.entry.bookmark" },
+                               { "${menu.copyurl}", 0, 0, "feed.entry.copyurl" },
                                { "---", 0, 0, NULL },
                                { page_Icon " ${feeds.entry.openfeed}", 0, 0, "feed.entry.openfeed" },
                                { edit_Icon " ${feeds.edit}", 0, 0, "feed.entry.edit" },
@@ -424,7 +428,7 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct
                                { "---", 0, 0, NULL },
                                { check_Icon " ${feeds.markallread}", SDLK_a, KMOD_SHIFT, "feeds.markallread" },
                                { reload_Icon " ${feeds.refresh}", SDLK_r, KMOD_PRIMARY | KMOD_SHIFT, "feeds.refresh" } },
-                10);
+                13);
             d->modeMenu = makeMenu_Widget(
                 as_Widget(d),
                 (iMenuItem[]){
@@ -491,6 +495,7 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct
                 as_Widget(d),
                 (iMenuItem[]){ { openTab_Icon " ${menu.opentab}", 0, 0, "bookmark.open newtab:1" },
                                { openTabBg_Icon " ${menu.opentab.background}", 0, 0, "bookmark.open newtab:2" },
+                               { openWindow_Icon " ${menu.openwindow}", 0, 0, "bookmark.open newwindow:1" },
                                { "---", 0, 0, NULL },
                                { edit_Icon " ${menu.edit}", 0, 0, "bookmark.edit" },
                                { copy_Icon " ${menu.dup}", 0, 0, "bookmark.dup" },
@@ -502,11 +507,11 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct
                                { "---", 0, 0, NULL },
                                { delete_Icon " " uiTextCaution_ColorEscape "${bookmark.delete}", 0, 0, "bookmark.delete" },
                                { "---", 0, 0, NULL },
-                               { add_Icon " ${menu.newfolder}", 0, 0, "bookmark.addfolder" },
+                               { folder_Icon " ${menu.newfolder}", 0, 0, "bookmark.addfolder" },
                                { upDownArrow_Icon " ${menu.sort.alpha}", 0, 0, "bookmark.sortfolder" },
                                { "---", 0, 0, NULL },
                                { reload_Icon " ${bookmarks.reload}", 0, 0, "bookmarks.reload.remote" } },
-               17);
+               18);
             d->modeMenu = makeMenu_Widget(
                 as_Widget(d),
                 (iMenuItem[]){ { bookmark_Icon " ${menu.page.bookmark}", SDLK_d, KMOD_PRIMARY, "bookmark.add" },
@@ -571,13 +576,17 @@ static void updateItemsWithFlags_SidebarWidget_(iSidebarWidget *d, iBool keepAct
             d->menu = makeMenu_Widget(
                 as_Widget(d),
                 (iMenuItem[]){
+                    { openTab_Icon " ${menu.opentab}", 0, 0, "history.open newtab:1" },
+                    { openTabBg_Icon " ${menu.opentab.background}", 0, 0, "history.open newtab:2" },
+                    { openWindow_Icon " ${menu.openwindow}", 0, 0, "history.open newwindow:1" },
+                    { "---" },
                     { "${menu.copyurl}", 0, 0, "history.copy" },
                     { bookmark_Icon " ${sidebar.entry.bookmark}", 0, 0, "history.addbookmark" },
                     { "---", 0, 0, NULL },
                     { close_Icon " ${menu.forgeturl}", 0, 0, "history.delete" },
                     { "---", 0, 0, NULL },
                     { delete_Icon " " uiTextCaution_ColorEscape "${history.clear}", 0, 0, "history.clear confirm:1" },
-                }, 6);
+                }, 10);
             d->modeMenu = makeMenu_Widget(
                 as_Widget(d),
                 (iMenuItem[]){
@@ -981,7 +990,7 @@ static void itemClicked_SidebarWidget_(iSidebarWidget *d, iSidebarItem *item, si
         }
         case feeds_SidebarMode: {
             postCommandString_Root(get_Root(),
-                feedEntryOpenCommand_String(&item->url, openTabMode_Sym(modState_Keys())));
+                feedEntryOpenCommand_String(&item->url, openTabMode_Sym(modState_Keys()), 0));
             break;
         }
         case bookmarks_SidebarMode:
@@ -1641,8 +1650,11 @@ static iBool processEvent_SidebarWidget_(iSidebarWidget *d, const SDL_Event *ev)
         else if (startsWith_CStr(cmd, "feed.entry.") && d->mode == feeds_SidebarMode) {
             const iSidebarItem *item = d->contextItem;
             if (item) {
-                if (isCommand_Widget(w, ev, "feed.entry.opentab")) {
-                    postCommandString_Root(get_Root(), feedEntryOpenCommand_String(&item->url, 1));
+                if (isCommand_Widget(w, ev, "feed.entry.open")) {
+                    const char *cmd = command_UserEvent(ev);
+                    postCommandString_Root(get_Root(), feedEntryOpenCommand_String(&item->url,
+                                                                                   argLabel_Command(cmd, "newtab"),
+                                                                                   argLabel_Command(cmd, "newwindow")));
                     return iTrue;
                 }
                 if (isCommand_Widget(w, ev, "feed.entry.toggleread")) {
diff --git a/src/ui/window.c b/src/ui/window.c
index 6f680cd4..b0de0557 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -1567,6 +1567,23 @@ void setKeyboardHeight_MainWindow(iMainWindow *d, int height) {
     }
 }
 
+iObjectList *listDocuments_MainWindow(iMainWindow *d, const iRoot *rootOrNull) {
+    iObjectList *docs = new_ObjectList();
+    iForIndices(i, d->base.roots) {
+        iRoot *root = d->base.roots[i];
+        if (!root) continue;
+        if (!rootOrNull || root == rootOrNull) {
+            const iWidget *tabs = findChild_Widget(root->widget, "doctabs");
+            iForEach(ObjectList, i, children_Widget(findChild_Widget(tabs, "tabs.pages"))) {
+                if (isInstance_Object(i.object, &Class_DocumentWidget)) {
+                    pushBack_ObjectList(docs, i.object);
+                }
+            }
+        }
+    }
+    return docs;
+}
+
 void checkPendingSplit_MainWindow(iMainWindow *d) {
     if (d->splitMode != d->pendingSplitMode) {
         setSplitMode_MainWindow(d, d->pendingSplitMode);
diff --git a/src/ui/window.h b/src/ui/window.h
index c7d59380..c3c34e1b 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -187,6 +187,7 @@ void        setTitle_MainWindow             (iMainWindow *, const iString *title
 void        setSnap_MainWindow              (iMainWindow *, int snapMode);
 void        setFreezeDraw_MainWindow        (iMainWindow *, iBool freezeDraw);
 void        setKeyboardHeight_MainWindow    (iMainWindow *, int height);
+iObjectList *listDocuments_MainWindow       (iMainWindow *, const iRoot *rootOrNull);
 void        setSplitMode_MainWindow         (iMainWindow *, int splitMode);
 void        checkPendingSplit_MainWindow    (iMainWindow *);
 void        swapRoots_MainWindow            (iMainWindow *);
Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/work%2Fv1.11/cdiff/ed14d3467d6046ddfa66c3143a340428aeef66ae
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
98.072793 milliseconds
Gemini-to-HTML Time
0.926847 milliseconds

This content has been proxied by September (ba2dc).