diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-12-28 07:52:25 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-12-28 07:52:30 +0200 |
commit | 336b9d7272ed8b1a9dccee27dec20e3377ee0c74 (patch) | |
tree | e789b1406574de3e72b15a4d060890548096f4fa | |
parent | a0111b0560c96fcc25758d2384f6ee53171ccd69 (diff) |
Viewing unsupported files in another app
After receiving content of unsupported type, show a footer action button to open it in another app.
IssueID #135
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | po/en.po | 3 | ||||
-rw-r--r-- | res/lang/cs.bin | bin | 31510 -> 31549 bytes | |||
-rw-r--r-- | res/lang/de.bin | bin | 30517 -> 30556 bytes | |||
-rw-r--r-- | res/lang/en.bin | bin | 26603 -> 26642 bytes | |||
-rw-r--r-- | res/lang/eo.bin | bin | 25567 -> 25606 bytes | |||
-rw-r--r-- | res/lang/es.bin | bin | 30341 -> 30380 bytes | |||
-rw-r--r-- | res/lang/es_MX.bin | bin | 27673 -> 27712 bytes | |||
-rw-r--r-- | res/lang/fi.bin | bin | 30174 -> 30213 bytes | |||
-rw-r--r-- | res/lang/fr.bin | bin | 31321 -> 31360 bytes | |||
-rw-r--r-- | res/lang/gl.bin | bin | 29526 -> 29565 bytes | |||
-rw-r--r-- | res/lang/hu.bin | bin | 31346 -> 31385 bytes | |||
-rw-r--r-- | res/lang/ia.bin | bin | 28673 -> 28712 bytes | |||
-rw-r--r-- | res/lang/ie.bin | bin | 29261 -> 29300 bytes | |||
-rw-r--r-- | res/lang/isv.bin | bin | 25324 -> 25363 bytes | |||
-rw-r--r-- | res/lang/pl.bin | bin | 29949 -> 29988 bytes | |||
-rw-r--r-- | res/lang/ru.bin | bin | 44709 -> 44748 bytes | |||
-rw-r--r-- | res/lang/sk.bin | bin | 25660 -> 25699 bytes | |||
-rw-r--r-- | res/lang/sr.bin | bin | 44135 -> 44174 bytes | |||
-rw-r--r-- | res/lang/tok.bin | bin | 27383 -> 27422 bytes | |||
-rw-r--r-- | res/lang/tr.bin | bin | 29567 -> 29606 bytes | |||
-rw-r--r-- | res/lang/uk.bin | bin | 44054 -> 44093 bytes | |||
-rw-r--r-- | res/lang/zh_Hans.bin | bin | 25568 -> 25607 bytes | |||
-rw-r--r-- | res/lang/zh_Hant.bin | bin | 25766 -> 25805 bytes | |||
-rw-r--r-- | src/app.c | 19 | ||||
-rw-r--r-- | src/app.h | 1 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 74 |
27 files changed, 68 insertions, 30 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 94b70ea0..37afe9d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
@@ -330,6 +330,7 @@ target_include_directories (app PUBLIC | |||
330 | target_compile_options (app PUBLIC | 330 | target_compile_options (app PUBLIC |
331 | -Werror=implicit-function-declaration | 331 | -Werror=implicit-function-declaration |
332 | -Werror=incompatible-pointer-types | 332 | -Werror=incompatible-pointer-types |
333 | -Wno-deprecated-declarations | ||
333 | ${SDL2_CFLAGS} | 334 | ${SDL2_CFLAGS} |
334 | -DSTB_VORBIS_NO_STDIO=1 | 335 | -DSTB_VORBIS_NO_STDIO=1 |
335 | -DSTB_VORBIS_NO_INTEGER_CONVERSION=1 | 336 | -DSTB_VORBIS_NO_INTEGER_CONVERSION=1 |
@@ -196,6 +196,9 @@ msgstr "Find on Page" | |||
196 | msgid "macos.menu.find" | 196 | msgid "macos.menu.find" |
197 | msgstr "Find" | 197 | msgstr "Find" |
198 | 198 | ||
199 | msgid "menu.open.external" | ||
200 | msgstr "Open in Another App" | ||
201 | |||
199 | # Used on iOS. "Files" refers to Apple's iOS app where you can pick an iCloud folder. | 202 | # Used on iOS. "Files" refers to Apple's iOS app where you can pick an iCloud folder. |
200 | msgid "menu.save.files" | 203 | msgid "menu.save.files" |
201 | msgstr "Save to Files" | 204 | msgstr "Save to Files" |
diff --git a/res/lang/cs.bin b/res/lang/cs.bin index 97197051..ec75dc3a 100644 --- a/res/lang/cs.bin +++ b/res/lang/cs.bin | |||
Binary files differ | |||
diff --git a/res/lang/de.bin b/res/lang/de.bin index d2dceda6..96fd003f 100644 --- a/res/lang/de.bin +++ b/res/lang/de.bin | |||
Binary files differ | |||
diff --git a/res/lang/en.bin b/res/lang/en.bin index 942dfdc2..26e3c36a 100644 --- a/res/lang/en.bin +++ b/res/lang/en.bin | |||
Binary files differ | |||
diff --git a/res/lang/eo.bin b/res/lang/eo.bin index 513c3a0b..2df0e6b8 100644 --- a/res/lang/eo.bin +++ b/res/lang/eo.bin | |||
Binary files differ | |||
diff --git a/res/lang/es.bin b/res/lang/es.bin index 92037aea..be919a78 100644 --- a/res/lang/es.bin +++ b/res/lang/es.bin | |||
Binary files differ | |||
diff --git a/res/lang/es_MX.bin b/res/lang/es_MX.bin index 7b7e1219..d88b3386 100644 --- a/res/lang/es_MX.bin +++ b/res/lang/es_MX.bin | |||
Binary files differ | |||
diff --git a/res/lang/fi.bin b/res/lang/fi.bin index 243e9740..5dba618c 100644 --- a/res/lang/fi.bin +++ b/res/lang/fi.bin | |||
Binary files differ | |||
diff --git a/res/lang/fr.bin b/res/lang/fr.bin index 507cbb4c..830bbc72 100644 --- a/res/lang/fr.bin +++ b/res/lang/fr.bin | |||
Binary files differ | |||
diff --git a/res/lang/gl.bin b/res/lang/gl.bin index 447ed72d..4df0c57c 100644 --- a/res/lang/gl.bin +++ b/res/lang/gl.bin | |||
Binary files differ | |||
diff --git a/res/lang/hu.bin b/res/lang/hu.bin index c2a085ec..f4c826c8 100644 --- a/res/lang/hu.bin +++ b/res/lang/hu.bin | |||
Binary files differ | |||
diff --git a/res/lang/ia.bin b/res/lang/ia.bin index 5305304c..6a18bd7d 100644 --- a/res/lang/ia.bin +++ b/res/lang/ia.bin | |||
Binary files differ | |||
diff --git a/res/lang/ie.bin b/res/lang/ie.bin index 69ac42d4..84cc2cd7 100644 --- a/res/lang/ie.bin +++ b/res/lang/ie.bin | |||
Binary files differ | |||
diff --git a/res/lang/isv.bin b/res/lang/isv.bin index 26c23dc5..1a31a1f7 100644 --- a/res/lang/isv.bin +++ b/res/lang/isv.bin | |||
Binary files differ | |||
diff --git a/res/lang/pl.bin b/res/lang/pl.bin index 1ad99e5d..a003ce03 100644 --- a/res/lang/pl.bin +++ b/res/lang/pl.bin | |||
Binary files differ | |||
diff --git a/res/lang/ru.bin b/res/lang/ru.bin index 03092acf..807e032d 100644 --- a/res/lang/ru.bin +++ b/res/lang/ru.bin | |||
Binary files differ | |||
diff --git a/res/lang/sk.bin b/res/lang/sk.bin index 4d91e8a6..33c83c09 100644 --- a/res/lang/sk.bin +++ b/res/lang/sk.bin | |||
Binary files differ | |||
diff --git a/res/lang/sr.bin b/res/lang/sr.bin index 823818e2..7c4c8de1 100644 --- a/res/lang/sr.bin +++ b/res/lang/sr.bin | |||
Binary files differ | |||
diff --git a/res/lang/tok.bin b/res/lang/tok.bin index 50743783..601b07f3 100644 --- a/res/lang/tok.bin +++ b/res/lang/tok.bin | |||
Binary files differ | |||
diff --git a/res/lang/tr.bin b/res/lang/tr.bin index 543739cd..8cbaa556 100644 --- a/res/lang/tr.bin +++ b/res/lang/tr.bin | |||
Binary files differ | |||
diff --git a/res/lang/uk.bin b/res/lang/uk.bin index f866f86f..48adc59b 100644 --- a/res/lang/uk.bin +++ b/res/lang/uk.bin | |||
Binary files differ | |||
diff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin index 179d87db..759e618a 100644 --- a/res/lang/zh_Hans.bin +++ b/res/lang/zh_Hans.bin | |||
Binary files differ | |||
diff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin index aad28410..2bfc817b 100644 --- a/res/lang/zh_Hant.bin +++ b/res/lang/zh_Hant.bin | |||
Binary files differ | |||
@@ -1021,7 +1021,7 @@ const iString *downloadDir_App(void) { | |||
1021 | return collect_String(cleaned_Path(&app_.prefs.strings[downloadDir_PrefsString])); | 1021 | return collect_String(cleaned_Path(&app_.prefs.strings[downloadDir_PrefsString])); |
1022 | } | 1022 | } |
1023 | 1023 | ||
1024 | const iString *downloadPathForUrl_App(const iString *url, const iString *mime) { | 1024 | const iString *fileNameForUrl_App(const iString *url, const iString *mime) { |
1025 | /* Figure out a file name from the URL. */ | 1025 | /* Figure out a file name from the URL. */ |
1026 | iUrl parts; | 1026 | iUrl parts; |
1027 | init_Url(&parts, url); | 1027 | init_Url(&parts, url); |
@@ -1047,22 +1047,27 @@ const iString *downloadPathForUrl_App(const iString *url, const iString *mime) { | |||
1047 | } | 1047 | } |
1048 | } | 1048 | } |
1049 | if (startsWith_String(name, "~")) { | 1049 | if (startsWith_String(name, "~")) { |
1050 | /* This would be interpreted as a reference to a home directory. */ | 1050 | /* This might be interpreted as a reference to a home directory. */ |
1051 | remove_Block(&name->chars, 0, 1); | 1051 | remove_Block(&name->chars, 0, 1); |
1052 | } | 1052 | } |
1053 | iString *savePath = concat_Path(downloadDir_App(), name); | 1053 | if (lastIndexOfCStr_String(name, ".") == iInvalidPos) { |
1054 | if (lastIndexOfCStr_String(savePath, ".") == iInvalidPos) { | 1054 | /* TODO: Needs the inverse of `mediaTypeFromFileExtension_String()`. */ |
1055 | /* No extension specified in URL. */ | 1055 | /* No extension specified in URL. */ |
1056 | if (startsWith_String(mime, "text/gemini")) { | 1056 | if (startsWith_String(mime, "text/gemini")) { |
1057 | appendCStr_String(savePath, ".gmi"); | 1057 | appendCStr_String(name, ".gmi"); |
1058 | } | 1058 | } |
1059 | else if (startsWith_String(mime, "text/")) { | 1059 | else if (startsWith_String(mime, "text/")) { |
1060 | appendCStr_String(savePath, ".txt"); | 1060 | appendCStr_String(name, ".txt"); |
1061 | } | 1061 | } |
1062 | else if (startsWith_String(mime, "image/")) { | 1062 | else if (startsWith_String(mime, "image/")) { |
1063 | appendCStr_String(savePath, cstr_String(mime) + 6); | 1063 | appendCStr_String(name, cstr_String(mime) + 6); |
1064 | } | 1064 | } |
1065 | } | 1065 | } |
1066 | return name; | ||
1067 | } | ||
1068 | |||
1069 | const iString *downloadPathForUrl_App(const iString *url, const iString *mime) { | ||
1070 | iString *savePath = concat_Path(downloadDir_App(), fileNameForUrl_App(url, mime)); | ||
1066 | if (fileExists_FileInfo(savePath)) { | 1071 | if (fileExists_FileInfo(savePath)) { |
1067 | /* Make it unique. */ | 1072 | /* Make it unique. */ |
1068 | iDate now; | 1073 | iDate now; |
@@ -110,6 +110,7 @@ enum iColorTheme colorTheme_App (void); | |||
110 | const iString * schemeProxy_App (iRangecc scheme); | 110 | const iString * schemeProxy_App (iRangecc scheme); |
111 | iBool willUseProxy_App (const iRangecc scheme); | 111 | iBool willUseProxy_App (const iRangecc scheme); |
112 | const iString * searchQueryUrl_App (const iString *queryStringUnescaped); | 112 | const iString * searchQueryUrl_App (const iString *queryStringUnescaped); |
113 | const iString * fileNameForUrl_App (const iString *url, const iString *mime); | ||
113 | const iString * downloadPathForUrl_App(const iString *url, const iString *mime); | 114 | const iString * downloadPathForUrl_App(const iString *url, const iString *mime); |
114 | 115 | ||
115 | typedef void (*iTickerFunc)(iAny *); | 116 | typedef void (*iTickerFunc)(iAny *); |
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 04257a1c..3771dd6c 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -2260,6 +2260,11 @@ static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode | |||
2260 | 0, | 2260 | 0, |
2261 | format_CStr("document.setmediatype mime:%s", mtype) }); | 2261 | format_CStr("document.setmediatype mime:%s", mtype) }); |
2262 | } | 2262 | } |
2263 | pushBack_Array(&items, | ||
2264 | &(iMenuItem){ export_Icon " ${menu.open.external}", | ||
2265 | SDLK_RETURN, | ||
2266 | KMOD_PRIMARY, | ||
2267 | "document.save extview:1" }); | ||
2263 | pushBack_Array( | 2268 | pushBack_Array( |
2264 | &items, | 2269 | &items, |
2265 | &(iMenuItem){ translateCStr_Lang(download_Icon " " saveToDownloads_Label), | 2270 | &(iMenuItem){ translateCStr_Lang(download_Icon " " saveToDownloads_Label), |
@@ -3337,9 +3342,8 @@ static iBool fetchNextUnfetchedImage_DocumentWidget_(iDocumentWidget *d) { | |||
3337 | return iFalse; | 3342 | return iFalse; |
3338 | } | 3343 | } |
3339 | 3344 | ||
3340 | static const iString *saveToDownloads_(const iString *url, const iString *mime, const iBlock *content, | 3345 | static iBool saveToFile_(const iString *savePath, const iBlock *content, iBool showDialog) { |
3341 | iBool showDialog) { | 3346 | iBool ok = iFalse; |
3342 | const iString *savePath = downloadPathForUrl_App(url, mime); | ||
3343 | /* Write the file. */ { | 3347 | /* Write the file. */ { |
3344 | iFile *f = new_File(savePath); | 3348 | iFile *f = new_File(savePath); |
3345 | if (open_File(f, writeOnly_FileMode)) { | 3349 | if (open_File(f, writeOnly_FileMode)) { |
@@ -3351,21 +3355,21 @@ static const iString *saveToDownloads_(const iString *url, const iString *mime, | |||
3351 | exportDownloadedFile_iOS(savePath); | 3355 | exportDownloadedFile_iOS(savePath); |
3352 | #else | 3356 | #else |
3353 | if (showDialog) { | 3357 | if (showDialog) { |
3354 | const iMenuItem items[2] = { | 3358 | const iMenuItem items[2] = { |
3355 | { "${dlg.save.opendownload}", 0, 0, | 3359 | { "${dlg.save.opendownload}", 0, 0, |
3356 | format_CStr("!open url:%s", cstrCollect_String(makeFileUrl_String(savePath))) }, | 3360 | format_CStr("!open url:%s", cstrCollect_String(makeFileUrl_String(savePath))) }, |
3357 | { "${dlg.message.ok}", 0, 0, "message.ok" }, | 3361 | { "${dlg.message.ok}", 0, 0, "message.ok" }, |
3358 | }; | 3362 | }; |
3359 | makeMessage_Widget(uiHeading_ColorEscape "${heading.save}", | 3363 | makeMessage_Widget(uiHeading_ColorEscape "${heading.save}", |
3360 | format_CStr("%s\n${dlg.save.size} %.3f %s", | 3364 | format_CStr("%s\n${dlg.save.size} %.3f %s", |
3361 | cstr_String(path_File(f)), | 3365 | cstr_String(path_File(f)), |
3362 | isMega ? size / 1.0e6f : (size / 1.0e3f), | 3366 | isMega ? size / 1.0e6f : (size / 1.0e3f), |
3363 | isMega ? "${mb}" : "${kb}"), | 3367 | isMega ? "${mb}" : "${kb}"), |
3364 | items, | 3368 | items, |
3365 | iElemCount(items)); | 3369 | iElemCount(items)); |
3366 | } | 3370 | } |
3367 | #endif | 3371 | #endif |
3368 | return savePath; | 3372 | ok = iTrue; |
3369 | } | 3373 | } |
3370 | else { | 3374 | else { |
3371 | makeSimpleMessage_Widget(uiTextCaution_ColorEscape "${heading.save.error}", | 3375 | makeSimpleMessage_Widget(uiTextCaution_ColorEscape "${heading.save.error}", |
@@ -3373,7 +3377,16 @@ static const iString *saveToDownloads_(const iString *url, const iString *mime, | |||
3373 | } | 3377 | } |
3374 | iRelease(f); | 3378 | iRelease(f); |
3375 | } | 3379 | } |
3376 | return collectNew_String(); | 3380 | return ok; |
3381 | } | ||
3382 | |||
3383 | static const iString *saveToDownloads_(const iString *url, const iString *mime, const iBlock *content, | ||
3384 | iBool showDialog) { | ||
3385 | const iString *savePath = downloadPathForUrl_App(url, mime); | ||
3386 | if (!saveToFile_(savePath, content, showDialog)) { | ||
3387 | return collectNew_String(); | ||
3388 | } | ||
3389 | return savePath; | ||
3377 | } | 3390 | } |
3378 | 3391 | ||
3379 | static void addAllLinks_(void *context, const iGmRun *run) { | 3392 | static void addAllLinks_(void *context, const iGmRun *run) { |
@@ -4111,12 +4124,27 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
4111 | "${dlg.save.incomplete}"); | 4124 | "${dlg.save.incomplete}"); |
4112 | } | 4125 | } |
4113 | else if (!isEmpty_Block(&d->sourceContent)) { | 4126 | else if (!isEmpty_Block(&d->sourceContent)) { |
4114 | const iBool doOpen = argLabel_Command(cmd, "open"); | 4127 | if (argLabel_Command(cmd, "extview")) { |
4115 | const iString *savePath = saveToDownloads_(d->mod.url, &d->sourceMime, | 4128 | iString *tmpPath = collectNewCStr_String(tmpnam(NULL)); |
4116 | &d->sourceContent, !doOpen); | 4129 | const iRangecc tmpDir = dirName_Path(tmpPath); |
4117 | if (!isEmpty_String(savePath) && doOpen) { | 4130 | set_String( |
4118 | postCommandf_Root( | 4131 | tmpPath, |
4119 | w->root, "!open url:%s", cstrCollect_String(makeFileUrl_String(savePath))); | 4132 | collect_String(concat_Path(collectNewRange_String(tmpDir), |
4133 | fileNameForUrl_App(d->mod.url, &d->sourceMime)))); | ||
4134 | if (saveToFile_(tmpPath, &d->sourceContent, iFalse)) { | ||
4135 | /* TODO: Remember this temporary path and delete it when quitting the app. */ | ||
4136 | postCommandf_Root(w->root, "!open default:1 url:%s", | ||
4137 | cstrCollect_String(makeFileUrl_String(tmpPath))); | ||
4138 | } | ||
4139 | } | ||
4140 | else { | ||
4141 | const iBool doOpen = argLabel_Command(cmd, "open"); | ||
4142 | const iString *savePath = saveToDownloads_(d->mod.url, &d->sourceMime, | ||
4143 | &d->sourceContent, !doOpen); | ||
4144 | if (!isEmpty_String(savePath) && doOpen) { | ||
4145 | postCommandf_Root( | ||
4146 | w->root, "!open url:%s", cstrCollect_String(makeFileUrl_String(savePath))); | ||
4147 | } | ||
4120 | } | 4148 | } |
4121 | } | 4149 | } |
4122 | return iTrue; | 4150 | return iTrue; |