Lagrange [work/v1.17]

Hierarchical navigation with Gopher and Titan

=> 07804493ac17ff430302c7940f44b7525c0620e8

diff --git a/res/about/version.gmi b/res/about/version.gmi
index a0cbe637..df532ab3 100644
--- a/res/about/version.gmi
+++ b/res/about/version.gmi
@@ -9,6 +9,8 @@
 ## 1.10.3
 * Added a man page.
 * "/index.gmi" is considered equal to "/" when navigating to parent directory.
+* Gopher: Fixed navigating to root, e.g., when clicking on the page top banner. Set item type to 1 to show a gophermap and not the plain source.
+* Titan: When navigating to parent/root, switch URL scheme to "gemini". This action occurs on a Titan response page, so initiating a new upload with the parent/root URL is probably not appropriate.
 * Fixed crash when a media player is active and a new download is started.
 * Fixed crash when a line contains nothing but an ANSI escape sequence.
 * Fixed a possible crash when saving state of subscribed feeds.
diff --git a/src/gmutil.c b/src/gmutil.c
index 98e4d4d6..b984950e 100644
--- a/src/gmutil.c
+++ b/src/gmutil.c
@@ -131,6 +131,16 @@ static iRangecc prevPathSeg_(const char *end, const char *start) {
     return seg;
 }
 
+void stripUrlPort_String(iString *d) {
+    iUrl parts;
+    init_Url(&parts, d);
+    if (!isEmpty_Range(&parts.port)) {
+        /* Always preceded by a colon. */
+        remove_Block(&d->chars, parts.port.start - 1 - constBegin_String(d),
+                     size_Range(&parts.port) + 1);
+    }
+}
+
 void stripDefaultUrlPort_String(iString *d) {
     iUrl parts;
     init_Url(&parts, d);
@@ -681,6 +691,17 @@ const iString *withSpacesEncoded_String(const iString *d) {
     return d;
 }
 
+const iString *withScheme_String(const iString *d, const char *scheme) {
+    iUrl parts;
+    init_Url(&parts, d);
+    if (!equalCase_Rangecc(parts.scheme, scheme)) {
+        iString *repl = collectNewCStr_String(scheme);
+        appendRange_String(repl, (iRangecc){ parts.scheme.end, constEnd_String(d) });
+        return repl;
+    }
+    return d;
+}
+
 const iString *canonicalUrl_String(const iString *d) {
     /* The "canonical" form, used for internal storage and comparisons, is:
        - all non-reserved characters decoded (i.e., it's an IRI)
diff --git a/src/gmutil.h b/src/gmutil.h
index 15bb7b2e..1594afc4 100644
--- a/src/gmutil.h
+++ b/src/gmutil.h
@@ -127,6 +127,7 @@ iBool           isKnownScheme_Rangecc   (iRangecc scheme); /* any URI scheme */
 iBool           isKnownUrlScheme_Rangecc(iRangecc scheme); /* URL schemes only */
 void            punyEncodeDomain_Rangecc(iRangecc domain, iString *encoded_out);
 void            punyEncodeUrlHost_String(iString *absoluteUrl);
+void            stripUrlPort_String     (iString *);
 void            stripDefaultUrlPort_String(iString *);
 const iString * urlFragmentStripped_String(const iString *);
 const iString * urlQueryStripped_String (const iString *);
@@ -138,6 +139,7 @@ const char *    makeFileUrl_CStr        (const char *localFilePath);
 iString *       localFilePathFromUrl_String(const iString *);
 void            urlEncodeSpaces_String  (iString *);
 const iString * withSpacesEncoded_String(const iString *);
+const iString * withScheme_String       (const iString *, const char *scheme); /* replace URI scheme */
 const iString * canonicalUrl_String     (const iString *);
 
 const char *    mediaType_Path                      (const iString *path);
diff --git a/src/gopher.c b/src/gopher.c
index 008a7743..0e34fe6a 100644
--- a/src/gopher.c
+++ b/src/gopher.c
@@ -299,3 +299,13 @@ iBool processResponse_Gopher(iGopher *d, const iBlock *data) {
     }
     return changed;
 }
+
+void setUrlItemType_Gopher(iString *url, char itemType) {
+    iUrl parts;
+    init_Url(&parts, url);
+    if (equalCase_Rangecc(parts.scheme, "gopher")) {
+        if (parts.path.start && size_Range(&parts.path) >= 2) {
+            ((char *) parts.path.start)[1] = itemType;
+        }
+    }   
+}
diff --git a/src/gopher.h b/src/gopher.h
index 3ad7e374..3cad0c21 100644
--- a/src/gopher.h
+++ b/src/gopher.h
@@ -44,3 +44,5 @@ iDeclareTypeConstruction(Gopher)
 void    open_Gopher             (iGopher *, const iString *url);
 iBool   processResponse_Gopher  (iGopher *, const iBlock *data);
 void    cancel_Gopher           (iGopher *);
+
+void    setUrlItemType_Gopher   (iString *url, char itemType);
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 2e15cdce..86513368 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -36,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
 #include "gmdocument.h"
 #include "gmrequest.h"
 #include "gmutil.h"
+#include "gopher.h"
 #include "history.h"
 #include "indicatorwidget.h"
 #include "inputwidget.h"
@@ -4315,20 +4316,31 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
             }
             iString *parentUrl = collectNewRange_String((iRangecc){ constBegin_String(d->mod.url),
                                                                     parts.path.end });
-            if (equalCase_Rangecc(parts.scheme, "gopher")) {
-                /* Always go to a gophermap. */
-                iZap(parts);
-                init_Url(&parts, parentUrl);
-                if (parts.path.start && size_Range(&parts.path) >= 2) {
-                    ((char *) parts.path.start)[1] = '1';
-                }
+            /* Always go to a gophermap. */
+            setUrlItemType_Gopher(parentUrl, '1');
+            /* Hierarchical navigation doesn't make sense with Titan. */
+            if (startsWith_String(parentUrl, "titan://")) {
+                /* We have no way of knowing if the corresponding URL is valid for Gemini,
+                   but let's try anyway. */                
+                set_String(parentUrl, withScheme_String(parentUrl, "gemini"));
+                stripUrlPort_String(parentUrl);
             }
             postCommandf_Root(w->root, "open url:%s", cstr_String(parentUrl));
         }
         return iTrue;
     }
     else if (equal_Command(cmd, "navigate.root") && document_App() == d) {
-        postCommandf_Root(w->root, "open url:%s/", cstr_Rangecc(urlRoot_String(d->mod.url)));
+        iString *rootUrl = collectNewRange_String(urlRoot_String(d->mod.url));
+        /* Always go to a gophermap. */
+        setUrlItemType_Gopher(rootUrl, '1');
+        /* Hierarchical navigation doesn't make sense with Titan. */
+        if (startsWith_String(rootUrl, "titan://")) {
+            /* We have no way of knowing if the corresponding URL is valid for Gemini,
+               but let's try anyway. */                
+            set_String(rootUrl, withScheme_String(rootUrl, "gemini"));
+            stripUrlPort_String(rootUrl);
+        }        
+        postCommandf_Root(w->root, "open url:%s/", cstr_String(rootUrl));
         return iTrue;
     }
     else if (equalWidget_Command(cmd, w, "scroll.moved")) {
Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/work%2Fv1.17/cdiff/07804493ac17ff430302c7940f44b7525c0620e8
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
28.883676 milliseconds
Gemini-to-HTML Time
0.252289 milliseconds

This content has been proxied by September (ba2dc).