=> 336b9d7272ed8b1a9dccee27dec20e3377ee0c74
[1mdiff --git a/CMakeLists.txt b/CMakeLists.txt[m [1mindex 94b70ea0..37afe9d2 100644[m [1m--- a/CMakeLists.txt[m [1m+++ b/CMakeLists.txt[m [36m@@ -330,6 +330,7 @@[m [mtarget_include_directories (app PUBLIC[m target_compile_options (app PUBLIC[m -Werror=implicit-function-declaration[m -Werror=incompatible-pointer-types[m [32m+[m[32m -Wno-deprecated-declarations[m ${SDL2_CFLAGS}[m -DSTB_VORBIS_NO_STDIO=1[m -DSTB_VORBIS_NO_INTEGER_CONVERSION=1[m [1mdiff --git a/po/en.po b/po/en.po[m [1mindex 04b6ac19..61d7d3f7 100644[m [1m--- a/po/en.po[m [1m+++ b/po/en.po[m [36m@@ -196,6 +196,9 @@[m [mmsgstr "Find on Page"[m msgid "macos.menu.find"[m msgstr "Find"[m [m [32m+[m[32mmsgid "menu.open.external"[m [32m+[m[32mmsgstr "Open in Another App"[m [32m+[m # Used on iOS. "Files" refers to Apple's iOS app where you can pick an iCloud folder.[m msgid "menu.save.files"[m msgstr "Save to Files"[m [1mdiff --git a/res/lang/cs.bin b/res/lang/cs.bin[m [1mindex 97197051..ec75dc3a 100644[m Binary files a/res/lang/cs.bin and b/res/lang/cs.bin differ [1mdiff --git a/res/lang/de.bin b/res/lang/de.bin[m [1mindex d2dceda6..96fd003f 100644[m Binary files a/res/lang/de.bin and b/res/lang/de.bin differ [1mdiff --git a/res/lang/en.bin b/res/lang/en.bin[m [1mindex 942dfdc2..26e3c36a 100644[m Binary files a/res/lang/en.bin and b/res/lang/en.bin differ [1mdiff --git a/res/lang/eo.bin b/res/lang/eo.bin[m [1mindex 513c3a0b..2df0e6b8 100644[m Binary files a/res/lang/eo.bin and b/res/lang/eo.bin differ [1mdiff --git a/res/lang/es.bin b/res/lang/es.bin[m [1mindex 92037aea..be919a78 100644[m Binary files a/res/lang/es.bin and b/res/lang/es.bin differ [1mdiff --git a/res/lang/es_MX.bin b/res/lang/es_MX.bin[m [1mindex 7b7e1219..d88b3386 100644[m Binary files a/res/lang/es_MX.bin and b/res/lang/es_MX.bin differ [1mdiff --git a/res/lang/fi.bin b/res/lang/fi.bin[m [1mindex 243e9740..5dba618c 100644[m Binary files a/res/lang/fi.bin and b/res/lang/fi.bin differ [1mdiff --git a/res/lang/fr.bin b/res/lang/fr.bin[m [1mindex 507cbb4c..830bbc72 100644[m Binary files a/res/lang/fr.bin and b/res/lang/fr.bin differ [1mdiff --git a/res/lang/gl.bin b/res/lang/gl.bin[m [1mindex 447ed72d..4df0c57c 100644[m Binary files a/res/lang/gl.bin and b/res/lang/gl.bin differ [1mdiff --git a/res/lang/hu.bin b/res/lang/hu.bin[m [1mindex c2a085ec..f4c826c8 100644[m Binary files a/res/lang/hu.bin and b/res/lang/hu.bin differ [1mdiff --git a/res/lang/ia.bin b/res/lang/ia.bin[m [1mindex 5305304c..6a18bd7d 100644[m Binary files a/res/lang/ia.bin and b/res/lang/ia.bin differ [1mdiff --git a/res/lang/ie.bin b/res/lang/ie.bin[m [1mindex 69ac42d4..84cc2cd7 100644[m Binary files a/res/lang/ie.bin and b/res/lang/ie.bin differ [1mdiff --git a/res/lang/isv.bin b/res/lang/isv.bin[m [1mindex 26c23dc5..1a31a1f7 100644[m Binary files a/res/lang/isv.bin and b/res/lang/isv.bin differ [1mdiff --git a/res/lang/pl.bin b/res/lang/pl.bin[m [1mindex 1ad99e5d..a003ce03 100644[m Binary files a/res/lang/pl.bin and b/res/lang/pl.bin differ [1mdiff --git a/res/lang/ru.bin b/res/lang/ru.bin[m [1mindex 03092acf..807e032d 100644[m Binary files a/res/lang/ru.bin and b/res/lang/ru.bin differ [1mdiff --git a/res/lang/sk.bin b/res/lang/sk.bin[m [1mindex 4d91e8a6..33c83c09 100644[m Binary files a/res/lang/sk.bin and b/res/lang/sk.bin differ [1mdiff --git a/res/lang/sr.bin b/res/lang/sr.bin[m [1mindex 823818e2..7c4c8de1 100644[m Binary files a/res/lang/sr.bin and b/res/lang/sr.bin differ [1mdiff --git a/res/lang/tok.bin b/res/lang/tok.bin[m [1mindex 50743783..601b07f3 100644[m Binary files a/res/lang/tok.bin and b/res/lang/tok.bin differ [1mdiff --git a/res/lang/tr.bin b/res/lang/tr.bin[m [1mindex 543739cd..8cbaa556 100644[m Binary files a/res/lang/tr.bin and b/res/lang/tr.bin differ [1mdiff --git a/res/lang/uk.bin b/res/lang/uk.bin[m [1mindex f866f86f..48adc59b 100644[m Binary files a/res/lang/uk.bin and b/res/lang/uk.bin differ [1mdiff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin[m [1mindex 179d87db..759e618a 100644[m Binary files a/res/lang/zh_Hans.bin and b/res/lang/zh_Hans.bin differ [1mdiff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin[m [1mindex aad28410..2bfc817b 100644[m Binary files a/res/lang/zh_Hant.bin and b/res/lang/zh_Hant.bin differ [1mdiff --git a/src/app.c b/src/app.c[m [1mindex b04b6388..b0b36783 100644[m [1m--- a/src/app.c[m [1m+++ b/src/app.c[m [36m@@ -1021,7 +1021,7 @@[m [mconst iString *downloadDir_App(void) {[m return collect_String(cleaned_Path(&app_.prefs.strings[downloadDir_PrefsString]));[m }[m [m [31m-const iString *downloadPathForUrl_App(const iString *url, const iString *mime) {[m [32m+[m[32mconst iString *fileNameForUrl_App(const iString *url, const iString *mime) {[m /* Figure out a file name from the URL. */[m iUrl parts;[m init_Url(&parts, url);[m [36m@@ -1047,22 +1047,27 @@[m [mconst iString *downloadPathForUrl_App(const iString *url, const iString *mime) {[m }[m }[m if (startsWith_String(name, "~")) {[m [31m- /* This would be interpreted as a reference to a home directory. */[m [32m+[m[32m /* This might be interpreted as a reference to a home directory. */[m remove_Block(&name->chars, 0, 1);[m }[m [31m- iString *savePath = concat_Path(downloadDir_App(), name);[m [31m- if (lastIndexOfCStr_String(savePath, ".") == iInvalidPos) {[m [32m+[m[32m if (lastIndexOfCStr_String(name, ".") == iInvalidPos) {[m [32m+[m[32m /* TODO: Needs the inverse of `mediaTypeFromFileExtension_String()`. */[m /* No extension specified in URL. */[m if (startsWith_String(mime, "text/gemini")) {[m [31m- appendCStr_String(savePath, ".gmi");[m [32m+[m[32m appendCStr_String(name, ".gmi");[m }[m else if (startsWith_String(mime, "text/")) {[m [31m- appendCStr_String(savePath, ".txt");[m [32m+[m[32m appendCStr_String(name, ".txt");[m }[m else if (startsWith_String(mime, "image/")) {[m [31m- appendCStr_String(savePath, cstr_String(mime) + 6);[m [32m+[m[32m appendCStr_String(name, cstr_String(mime) + 6);[m }[m }[m [32m+[m[32m return name;[m [32m+[m[32m}[m [32m+[m [32m+[m[32mconst iString *downloadPathForUrl_App(const iString *url, const iString *mime) {[m [32m+[m[32m iString *savePath = concat_Path(downloadDir_App(), fileNameForUrl_App(url, mime));[m if (fileExists_FileInfo(savePath)) {[m /* Make it unique. */[m iDate now;[m [1mdiff --git a/src/app.h b/src/app.h[m [1mindex 50d3ac6b..0c336e65 100644[m [1m--- a/src/app.h[m [1m+++ b/src/app.h[m [36m@@ -110,6 +110,7 @@[m [menum iColorTheme colorTheme_App (void);[m const iString * schemeProxy_App (iRangecc scheme);[m iBool willUseProxy_App (const iRangecc scheme);[m const iString * searchQueryUrl_App (const iString *queryStringUnescaped);[m [32m+[m[32mconst iString * fileNameForUrl_App (const iString *url, const iString *mime);[m const iString * downloadPathForUrl_App(const iString *url, const iString *mime);[m [m typedef void (*iTickerFunc)(iAny *);[m [1mdiff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c[m [1mindex 04257a1c..3771dd6c 100644[m [1m--- a/src/ui/documentwidget.c[m [1m+++ b/src/ui/documentwidget.c[m [36m@@ -2260,6 +2260,11 @@[m [mstatic void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode[m 0,[m format_CStr("document.setmediatype mime:%s", mtype) });[m }[m [32m+[m[32m pushBack_Array(&items,[m [32m+[m[32m &(iMenuItem){ export_Icon " ${menu.open.external}",[m [32m+[m[32m SDLK_RETURN,[m [32m+[m[32m KMOD_PRIMARY,[m [32m+[m[32m "document.save extview:1" });[m pushBack_Array([m &items,[m &(iMenuItem){ translateCStr_Lang(download_Icon " " saveToDownloads_Label),[m [36m@@ -3337,9 +3342,8 @@[m [mstatic iBool fetchNextUnfetchedImage_DocumentWidget_(iDocumentWidget *d) {[m return iFalse;[m }[m [m [31m-static const iString *saveToDownloads_(const iString *url, const iString *mime, const iBlock *content,[m [31m- iBool showDialog) {[m [31m- const iString *savePath = downloadPathForUrl_App(url, mime);[m [32m+[m[32mstatic iBool saveToFile_(const iString *savePath, const iBlock *content, iBool showDialog) {[m [32m+[m[32m iBool ok = iFalse;[m /* Write the file. */ {[m iFile *f = new_File(savePath);[m if (open_File(f, writeOnly_FileMode)) {[m [36m@@ -3351,21 +3355,21 @@[m [mstatic const iString *saveToDownloads_(const iString *url, const iString *mime,[m exportDownloadedFile_iOS(savePath);[m #else[m if (showDialog) {[m [31m- const iMenuItem items[2] = {[m [31m- { "${dlg.save.opendownload}", 0, 0,[m [31m- format_CStr("!open url:%s", cstrCollect_String(makeFileUrl_String(savePath))) },[m [31m- { "${dlg.message.ok}", 0, 0, "message.ok" },[m [31m- };[m [31m- makeMessage_Widget(uiHeading_ColorEscape "${heading.save}",[m [31m- format_CStr("%s\n${dlg.save.size} %.3f %s",[m [31m- cstr_String(path_File(f)),[m [31m- isMega ? size / 1.0e6f : (size / 1.0e3f),[m [31m- isMega ? "${mb}" : "${kb}"),[m [31m- items,[m [31m- iElemCount(items));[m [32m+[m[32m const iMenuItem items[2] = {[m [32m+[m[32m { "${dlg.save.opendownload}", 0, 0,[m [32m+[m[32m format_CStr("!open url:%s", cstrCollect_String(makeFileUrl_String(savePath))) },[m [32m+[m[32m { "${dlg.message.ok}", 0, 0, "message.ok" },[m [32m+[m[32m };[m [32m+[m[32m makeMessage_Widget(uiHeading_ColorEscape "${heading.save}",[m [32m+[m[32m format_CStr("%s\n${dlg.save.size} %.3f %s",[m [32m+[m[32m cstr_String(path_File(f)),[m [32m+[m[32m isMega ? size / 1.0e6f : (size / 1.0e3f),[m [32m+[m[32m isMega ? "${mb}" : "${kb}"),[m [32m+[m[32m items,[m [32m+[m[32m iElemCount(items));[m }[m #endif[m [31m- return savePath;[m [32m+[m[32m ok = iTrue;[m }[m else {[m makeSimpleMessage_Widget(uiTextCaution_ColorEscape "${heading.save.error}",[m [36m@@ -3373,7 +3377,16 @@[m [mstatic const iString *saveToDownloads_(const iString *url, const iString *mime,[m }[m iRelease(f);[m }[m [31m- return collectNew_String();[m [32m+[m[32m return ok;[m [32m+[m[32m}[m [32m+[m [32m+[m[32mstatic const iString *saveToDownloads_(const iString *url, const iString *mime, const iBlock *content,[m [32m+[m[32m iBool showDialog) {[m [32m+[m[32m const iString *savePath = downloadPathForUrl_App(url, mime);[m [32m+[m[32m if (!saveToFile_(savePath, content, showDialog)) {[m [32m+[m[32m return collectNew_String();[m [32m+[m[32m }[m [32m+[m[32m return savePath;[m }[m [m static void addAllLinks_(void *context, const iGmRun *run) {[m [36m@@ -4111,12 +4124,27 @@[m [mstatic iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)[m "${dlg.save.incomplete}");[m }[m else if (!isEmpty_Block(&d->sourceContent)) {[m [31m- const iBool doOpen = argLabel_Command(cmd, "open");[m [31m- const iString *savePath = saveToDownloads_(d->mod.url, &d->sourceMime,[m [31m- &d->sourceContent, !doOpen);[m [31m- if (!isEmpty_String(savePath) && doOpen) {[m [31m- postCommandf_Root([m [31m- w->root, "!open url:%s", cstrCollect_String(makeFileUrl_String(savePath)));[m [32m+[m[32m if (argLabel_Command(cmd, "extview")) {[m [32m+[m[32m iString *tmpPath = collectNewCStr_String(tmpnam(NULL));[m [32m+[m[32m const iRangecc tmpDir = dirName_Path(tmpPath);[m [32m+[m[32m set_String([m [32m+[m[32m tmpPath,[m [32m+[m[32m collect_String(concat_Path(collectNewRange_String(tmpDir),[m [32m+[m[32m fileNameForUrl_App(d->mod.url, &d->sourceMime))));[m [32m+[m[32m if (saveToFile_(tmpPath, &d->sourceContent, iFalse)) {[m [32m+[m[32m /* TODO: Remember this temporary path and delete it when quitting the app. */[m [32m+[m[32m postCommandf_Root(w->root, "!open default:1 url:%s",[m [32m+[m[32m cstrCollect_String(makeFileUrl_String(tmpPath)));[m [32m+[m[32m }[m [32m+[m[32m }[m [32m+[m[32m else {[m [32m+[m[32m const iBool doOpen = argLabel_Command(cmd, "open");[m [32m+[m[32m const iString *savePath = saveToDownloads_(d->mod.url, &d->sourceMime,[m [32m+[m[32m &d->sourceContent, !doOpen);[m [32m+[m[32m if (!isEmpty_String(savePath) && doOpen) {[m [32m+[m[32m postCommandf_Root([m [32m+[m[32m w->root, "!open url:%s", cstrCollect_String(makeFileUrl_String(savePath)));[m [32m+[m[32m }[m }[m }[m return iTrue;[m
text/gemini; charset=utf-8
This content has been proxied by September (ba2dc).