summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-12-29 07:18:29 +0200
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-12-29 07:18:29 +0200
commite8f06bd0985ce2c9ac5ef02525672a426d559d18 (patch)
tree5617f7e65d81ae0b5bcf3c0c6a49711662e658f2
parent336b9d7272ed8b1a9dccee27dec20e3377ee0c74 (diff)
Inline download context menu; macOS: Show in Finder
The inline downloads UI finally has some interactivity: left-clicking on a finished download opens it in the default viewer app, and right-clicking shows a context menu with relevant actions.
-rw-r--r--po/en.po4
-rw-r--r--res/lang/cs.binbin31549 -> 31582 bytes
-rw-r--r--res/lang/de.binbin30556 -> 30589 bytes
-rw-r--r--res/lang/en.binbin26642 -> 26675 bytes
-rw-r--r--res/lang/eo.binbin25606 -> 25639 bytes
-rw-r--r--res/lang/es.binbin30380 -> 30413 bytes
-rw-r--r--res/lang/es_MX.binbin27712 -> 27745 bytes
-rw-r--r--res/lang/fi.binbin30213 -> 30246 bytes
-rw-r--r--res/lang/fr.binbin31360 -> 31393 bytes
-rw-r--r--res/lang/gl.binbin29565 -> 29598 bytes
-rw-r--r--res/lang/hu.binbin31385 -> 31418 bytes
-rw-r--r--res/lang/ia.binbin28712 -> 28745 bytes
-rw-r--r--res/lang/ie.binbin29300 -> 29333 bytes
-rw-r--r--res/lang/isv.binbin25363 -> 25396 bytes
-rw-r--r--res/lang/pl.binbin29988 -> 30021 bytes
-rw-r--r--res/lang/ru.binbin44748 -> 44781 bytes
-rw-r--r--res/lang/sk.binbin25699 -> 25732 bytes
-rw-r--r--res/lang/sr.binbin44174 -> 44207 bytes
-rw-r--r--res/lang/tok.binbin27422 -> 27455 bytes
-rw-r--r--res/lang/tr.binbin29606 -> 29639 bytes
-rw-r--r--res/lang/uk.binbin44093 -> 44126 bytes
-rw-r--r--res/lang/zh_Hans.binbin25607 -> 25640 bytes
-rw-r--r--res/lang/zh_Hant.binbin25805 -> 25838 bytes
-rw-r--r--src/app.c68
-rw-r--r--src/app.h1
-rw-r--r--src/gmcerts.c2
-rw-r--r--src/ui/certlistwidget.c10
-rw-r--r--src/ui/documentwidget.c66
-rw-r--r--src/ui/linkinfo.c2
-rw-r--r--src/ui/mediaui.c38
30 files changed, 142 insertions, 49 deletions
diff --git a/po/en.po b/po/en.po
index 61d7d3f7..49f78eb8 100644
--- a/po/en.po
+++ b/po/en.po
@@ -854,6 +854,10 @@ msgstr "Bookmark Link…"
854msgid "link.download" 854msgid "link.download"
855msgstr "Download Linked File" 855msgstr "Download Linked File"
856 856
857# Shows where a local file is using the Finder.
858msgid "menu.reveal.macos"
859msgstr "Show in Finder"
860
857msgid "link.file.delete" 861msgid "link.file.delete"
858msgstr "Delete File" 862msgstr "Delete File"
859 863
diff --git a/res/lang/cs.bin b/res/lang/cs.bin
index ec75dc3a..1c30a0a9 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 96fd003f..bf05a72e 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 26e3c36a..bd858ade 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 2df0e6b8..cc829562 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 be919a78..f62291f1 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 d88b3386..f6f88d6c 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 5dba618c..e69245be 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 830bbc72..7be665e5 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 4df0c57c..8c1fdf24 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 f4c826c8..b06c8676 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 6a18bd7d..4c3b403d 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 84cc2cd7..5e431c29 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 1a31a1f7..54cc6774 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 a003ce03..cf6a6b23 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 807e032d..87c82a7d 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 33c83c09..3ac6e18c 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 7c4c8de1..5c66460d 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 601b07f3..19b6d9df 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 8cbaa556..9a8babc9 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 48adc59b..ebcb11be 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 759e618a..ab4b338a 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 2bfc817b..f166156f 100644
--- a/res/lang/zh_Hant.bin
+++ b/res/lang/zh_Hant.bin
Binary files differ
diff --git a/src/app.c b/src/app.c
index b0b36783..e5f9a41c 100644
--- a/src/app.c
+++ b/src/app.c
@@ -55,6 +55,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
55#include <the_Foundation/path.h> 55#include <the_Foundation/path.h>
56#include <the_Foundation/process.h> 56#include <the_Foundation/process.h>
57#include <the_Foundation/sortedarray.h> 57#include <the_Foundation/sortedarray.h>
58#include <the_Foundation/stringset.h>
58#include <the_Foundation/time.h> 59#include <the_Foundation/time.h>
59#include <the_Foundation/version.h> 60#include <the_Foundation/version.h>
60#include <SDL.h> 61#include <SDL.h>
@@ -119,6 +120,7 @@ static const int idleThreshold_App_ = 1000; /* ms */
119struct Impl_App { 120struct Impl_App {
120 iCommandLine args; 121 iCommandLine args;
121 iString * execPath; 122 iString * execPath;
123 iStringSet * tempFilesPendingDeletion;
122 iMimeHooks * mimehooks; 124 iMimeHooks * mimehooks;
123 iGmCerts * certs; 125 iGmCerts * certs;
124 iVisited * visited; 126 iVisited * visited;
@@ -735,6 +737,7 @@ static void init_App_(iApp *d, int argc, char **argv) {
735#endif 737#endif
736 d->isDarkSystemTheme = iTrue; /* will be updated by system later on, if supported */ 738 d->isDarkSystemTheme = iTrue; /* will be updated by system later on, if supported */
737 d->isSuspended = iFalse; 739 d->isSuspended = iFalse;
740 d->tempFilesPendingDeletion = new_StringSet();
738 init_CommandLine(&d->args, argc, argv); 741 init_CommandLine(&d->args, argc, argv);
739 /* Where was the app started from? We ask SDL first because the command line alone 742 /* Where was the app started from? We ask SDL first because the command line alone
740 cannot be relied on (behavior differs depending on OS). */ { 743 cannot be relied on (behavior differs depending on OS). */ {
@@ -1005,8 +1008,13 @@ static void deinit_App(iApp *d) {
1005#endif 1008#endif
1006 deinit_SortedArray(&d->tickers); 1009 deinit_SortedArray(&d->tickers);
1007 deinit_Periodic(&d->periodic); 1010 deinit_Periodic(&d->periodic);
1008 deinit_Lang(); 1011 deinit_Lang();
1009 iRecycle(); 1012 iRecycle();
1013 /* Delete all temporary files created while running. */
1014 iConstForEach(StringSet, tmp, d->tempFilesPendingDeletion) {
1015 remove(cstr_String(tmp.value));
1016 }
1017 iRelease(d->tempFilesPendingDeletion);
1010} 1018}
1011 1019
1012const iString *execPath_App(void) { 1020const iString *execPath_App(void) {
@@ -1082,6 +1090,17 @@ const iString *downloadPathForUrl_App(const iString *url, const iString *mime) {
1082 return collect_String(savePath); 1090 return collect_String(savePath);
1083} 1091}
1084 1092
1093const iString *temporaryPathForUrl_App(const iString *url, const iString *mime) {
1094 iApp *d = &app_;
1095 iString *tmpPath = collectNewCStr_String(tmpnam(NULL));
1096 const iRangecc tmpDir = dirName_Path(tmpPath);
1097 set_String(
1098 tmpPath,
1099 collect_String(concat_Path(collectNewRange_String(tmpDir), fileNameForUrl_App(url, mime))));
1100 insert_StringSet(d->tempFilesPendingDeletion, tmpPath); /* deleted in `deinit_App` */
1101 return tmpPath;
1102}
1103
1085const iString *debugInfo_App(void) { 1104const iString *debugInfo_App(void) {
1086 extern char **environ; /* The environment variables. */ 1105 extern char **environ; /* The environment variables. */
1087 iApp *d = &app_; 1106 iApp *d = &app_;
@@ -2687,7 +2706,9 @@ iBool handleCommand_App(const char *cmd) {
2687 } 2706 }
2688#endif 2707#endif
2689 else if (equal_Command(cmd, "downloads.open")) { 2708 else if (equal_Command(cmd, "downloads.open")) {
2690 postCommandf_App("open url:%s", cstrCollect_String(makeFileUrl_String(downloadDir_App()))); 2709 postCommandf_App("open newtab:%d url:%s",
2710 argLabel_Command(cmd, "newtab"),
2711 cstrCollect_String(makeFileUrl_String(downloadDir_App())));
2691 return iTrue; 2712 return iTrue;
2692 } 2713 }
2693 else if (equal_Command(cmd, "ca.file")) { 2714 else if (equal_Command(cmd, "ca.file")) {
@@ -2718,6 +2739,19 @@ iBool handleCommand_App(const char *cmd) {
2718 } 2739 }
2719 return iTrue; 2740 return iTrue;
2720 } 2741 }
2742 else if (equal_Command(cmd, "reveal")) {
2743 const iString *path = NULL;
2744 if (hasLabel_Command(cmd, "path")) {
2745 path = suffix_Command(cmd, "path");
2746 }
2747 else if (hasLabel_Command(cmd, "url")) {
2748 path = collect_String(localFilePathFromUrl_String(suffix_Command(cmd, "url")));
2749 }
2750 if (path) {
2751 revealPath_App(path);
2752 }
2753 return iTrue;
2754 }
2721 else if (equal_Command(cmd, "open")) { 2755 else if (equal_Command(cmd, "open")) {
2722 const char *urlArg = suffixPtr_Command(cmd, "url"); 2756 const char *urlArg = suffixPtr_Command(cmd, "url");
2723 if (!urlArg) { 2757 if (!urlArg) {
@@ -2726,9 +2760,8 @@ iBool handleCommand_App(const char *cmd) {
2726 if (findWidget_App("prefs")) { 2760 if (findWidget_App("prefs")) {
2727 postCommand_App("prefs.dismiss"); 2761 postCommand_App("prefs.dismiss");
2728 } 2762 }
2729 iString *url = collectNewCStr_String(urlArg); 2763 iString *url = collectNewCStr_String(urlArg);
2730 const iBool noProxy = argLabel_Command(cmd, "noproxy") != 0; 2764 const iBool noProxy = argLabel_Command(cmd, "noproxy") != 0;
2731 const iBool fromSidebar = argLabel_Command(cmd, "fromsidebar") != 0;
2732 iUrl parts; 2765 iUrl parts;
2733 init_Url(&parts, url); 2766 init_Url(&parts, url);
2734 if (equal_Rangecc(parts.scheme, "about") && equal_Rangecc(parts.path, "command") && 2767 if (equal_Rangecc(parts.scheme, "about") && equal_Rangecc(parts.path, "command") &&
@@ -3354,26 +3387,11 @@ void openInDefaultBrowser_App(const iString *url) {
3354 3387
3355void revealPath_App(const iString *path) { 3388void revealPath_App(const iString *path) {
3356#if defined (iPlatformAppleDesktop) 3389#if defined (iPlatformAppleDesktop)
3357 const char *scriptPath = concatPath_CStr(dataDir_App_(), "revealfile.scpt"); 3390 iProcess *proc = new_Process();
3358 iFile *f = newCStr_File(scriptPath); 3391 setArguments_Process(
3359 if (open_File(f, writeOnly_FileMode | text_FileMode)) { 3392 proc, iClob(newStringsCStr_StringList("/usr/bin/open", "-R", cstr_String(path), NULL)));
3360 /* AppleScript to select a specific file. */ 3393 start_Process(proc);
3361 write_File(f, collect_Block(newCStr_Block("on run argv\n" 3394 iRelease(proc);
3362 " tell application \"Finder\"\n"
3363 " activate\n"
3364 " reveal POSIX file (item 1 of argv) as text\n"
3365 " end tell\n"
3366 "end run\n")));
3367 close_File(f);
3368 iProcess *proc = new_Process();
3369 setArguments_Process(
3370 proc,
3371 iClob(newStringsCStr_StringList(
3372 "/usr/bin/osascript", scriptPath, cstr_String(path), NULL)));
3373 start_Process(proc);
3374 iRelease(proc);
3375 }
3376 iRelease(f);
3377#elif defined (iPlatformLinux) || defined (iPlatformHaiku) 3395#elif defined (iPlatformLinux) || defined (iPlatformHaiku)
3378 iFileInfo *inf = iClob(new_FileInfo(path)); 3396 iFileInfo *inf = iClob(new_FileInfo(path));
3379 iRangecc target; 3397 iRangecc target;
diff --git a/src/app.h b/src/app.h
index 0c336e65..d15e1f21 100644
--- a/src/app.h
+++ b/src/app.h
@@ -111,6 +111,7 @@ const iString * schemeProxy_App (iRangecc scheme);
111iBool willUseProxy_App (const iRangecc scheme); 111iBool willUseProxy_App (const iRangecc scheme);
112const iString * searchQueryUrl_App (const iString *queryStringUnescaped); 112const iString * searchQueryUrl_App (const iString *queryStringUnescaped);
113const iString * fileNameForUrl_App (const iString *url, const iString *mime); 113const iString * fileNameForUrl_App (const iString *url, const iString *mime);
114const iString * temporaryPathForUrl_App(const iString *url, const iString *mime); /* deleted before quitting */
114const iString * downloadPathForUrl_App(const iString *url, const iString *mime); 115const iString * downloadPathForUrl_App(const iString *url, const iString *mime);
115 116
116typedef void (*iTickerFunc)(iAny *); 117typedef void (*iTickerFunc)(iAny *);
diff --git a/src/gmcerts.c b/src/gmcerts.c
index 8f7bf181..7b05103b 100644
--- a/src/gmcerts.c
+++ b/src/gmcerts.c
@@ -654,7 +654,7 @@ void importIdentity_GmCerts(iGmCerts *d, iTlsCertificate *cert, const iString *n
654} 654}
655 655
656static const char *certPath_GmCerts_(const iGmCerts *d, const iGmIdentity *identity) { 656static const char *certPath_GmCerts_(const iGmCerts *d, const iGmIdentity *identity) {
657 if (!(identity->flags & (temporary_GmIdentityFlag | imported_GmIdentityFlag))) { 657 if (!(identity->flags & temporary_GmIdentityFlag)) {
658 const char *finger = cstrCollect_String(hexEncode_Block(&identity->fingerprint)); 658 const char *finger = cstrCollect_String(hexEncode_Block(&identity->fingerprint));
659 return concatPath_CStr(cstr_String(&d->saveDir), format_CStr("idents/%s", finger)); 659 return concatPath_CStr(cstr_String(&d->saveDir), format_CStr("idents/%s", finger));
660 } 660 }
diff --git a/src/ui/certlistwidget.c b/src/ui/certlistwidget.c
index 31d8bac6..5a1c481b 100644
--- a/src/ui/certlistwidget.c
+++ b/src/ui/certlistwidget.c
@@ -97,17 +97,21 @@ static void updateContextMenu_CertListWidget_(iCertListWidget *d) {
97 pushBack_Array(items, &(iMenuItem){ format_CStr("```%s", cstr_String(docUrl)) }); 97 pushBack_Array(items, &(iMenuItem){ format_CStr("```%s", cstr_String(docUrl)) });
98 firstIndex = 1; 98 firstIndex = 1;
99 } 99 }
100 pushBackN_Array(items, (iMenuItem[]){ 100 const iMenuItem ctxItems[] = {
101 { person_Icon " ${ident.use}", 0, 0, "ident.use arg:1" }, 101 { person_Icon " ${ident.use}", 0, 0, "ident.use arg:1" },
102 { close_Icon " ${ident.stopuse}", 0, 0, "ident.use arg:0" }, 102 { close_Icon " ${ident.stopuse}", 0, 0, "ident.use arg:0" },
103 { close_Icon " ${ident.stopuse.all}", 0, 0, "ident.use arg:0 clear:1" }, 103 { close_Icon " ${ident.stopuse.all}", 0, 0, "ident.use arg:0 clear:1" },
104 { "---", 0, 0, NULL }, 104 { "---", 0, 0, NULL },
105 { edit_Icon " ${menu.edit.notes}", 0, 0, "ident.edit" }, 105 { edit_Icon " ${menu.edit.notes}", 0, 0, "ident.edit" },
106 { "${ident.fingerprint}", 0, 0, "ident.fingerprint" }, 106 { "${ident.fingerprint}", 0, 0, "ident.fingerprint" },
107#if defined (iPlatformAppleDesktop)
108 { magnifyingGlass_Icon " ${menu.reveal.macos}", 0, 0, "ident.reveal" },
109#endif
107 { export_Icon " ${ident.export}", 0, 0, "ident.export" }, 110 { export_Icon " ${ident.export}", 0, 0, "ident.export" },
108 { "---", 0, 0, NULL }, 111 { "---", 0, 0, NULL },
109 { delete_Icon " " uiTextCaution_ColorEscape "${ident.delete}", 0, 0, "ident.delete confirm:1" }, 112 { delete_Icon " " uiTextCaution_ColorEscape "${ident.delete}", 0, 0, "ident.delete confirm:1" },
110 }, 9); 113 };
114 pushBackN_Array(items, ctxItems, iElemCount(ctxItems));
111 /* Used URLs. */ 115 /* Used URLs. */
112 const iGmIdentity *ident = menuIdentity_CertListWidget_(d); 116 const iGmIdentity *ident = menuIdentity_CertListWidget_(d);
113 if (ident) { 117 if (ident) {
@@ -244,7 +248,7 @@ static iBool processEvent_CertListWidget_(iCertListWidget *d, const SDL_Event *e
244 if (ident) { 248 if (ident) {
245 const iString *crtPath = certificatePath_GmCerts(certs_App(), ident); 249 const iString *crtPath = certificatePath_GmCerts(certs_App(), ident);
246 if (crtPath) { 250 if (crtPath) {
247 revealPath_App(crtPath); 251 postCommandf_App("reveal path:%s", cstr_String(crtPath));
248 } 252 }
249 } 253 }
250 return iTrue; 254 return iTrue;
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 3771dd6c..9e5e6ea3 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -4125,16 +4125,16 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
4125 } 4125 }
4126 else if (!isEmpty_Block(&d->sourceContent)) { 4126 else if (!isEmpty_Block(&d->sourceContent)) {
4127 if (argLabel_Command(cmd, "extview")) { 4127 if (argLabel_Command(cmd, "extview")) {
4128 iString *tmpPath = collectNewCStr_String(tmpnam(NULL)); 4128 if (equalCase_Rangecc(urlScheme_String(d->mod.url), "file")) {
4129 const iRangecc tmpDir = dirName_Path(tmpPath); 4129 /* Already a file so just open it directly. */
4130 set_String( 4130 postCommandf_Root(w->root, "!open default:1 url:%s", cstr_String(d->mod.url));
4131 tmpPath, 4131 }
4132 collect_String(concat_Path(collectNewRange_String(tmpDir), 4132 else {
4133 fileNameForUrl_App(d->mod.url, &d->sourceMime)))); 4133 const iString *tmpPath = temporaryPathForUrl_App(d->mod.url, &d->sourceMime);
4134 if (saveToFile_(tmpPath, &d->sourceContent, iFalse)) { 4134 if (saveToFile_(tmpPath, &d->sourceContent, iFalse)) {
4135 /* TODO: Remember this temporary path and delete it when quitting the app. */ 4135 postCommandf_Root(w->root, "!open default:1 url:%s",
4136 postCommandf_Root(w->root, "!open default:1 url:%s", 4136 cstrCollect_String(makeFileUrl_String(tmpPath)));
4137 cstrCollect_String(makeFileUrl_String(tmpPath))); 4137 }
4138 } 4138 }
4139 } 4139 }
4140 else { 4140 else {
@@ -4484,11 +4484,6 @@ static iBool processMediaEvents_DocumentWidget_(iDocumentWidget *d, const SDL_Ev
4484 ev->type != SDL_MOUSEMOTION) { 4484 ev->type != SDL_MOUSEMOTION) {
4485 return iFalse; 4485 return iFalse;
4486 } 4486 }
4487 if (ev->type == SDL_MOUSEBUTTONDOWN || ev->type == SDL_MOUSEBUTTONUP) {
4488 if (ev->button.button != SDL_BUTTON_LEFT) {
4489 return iFalse;
4490 }
4491 }
4492 if (d->grabbedPlayer) { 4487 if (d->grabbedPlayer) {
4493 /* Updated in the drag. */ 4488 /* Updated in the drag. */
4494 return iFalse; 4489 return iFalse;
@@ -4496,9 +4491,23 @@ static iBool processMediaEvents_DocumentWidget_(iDocumentWidget *d, const SDL_Ev
4496 const iInt2 mouse = init_I2(ev->button.x, ev->button.y); 4491 const iInt2 mouse = init_I2(ev->button.x, ev->button.y);
4497 iConstForEach(PtrArray, i, &d->view.visibleMedia) { 4492 iConstForEach(PtrArray, i, &d->view.visibleMedia) {
4498 const iGmRun *run = i.ptr; 4493 const iGmRun *run = i.ptr;
4494 if (run->mediaType == download_MediaType) {
4495 iDownloadUI ui;
4496 init_DownloadUI(&ui, media_GmDocument(d->view.doc), mediaId_GmRun(run).id,
4497 runRect_DocumentView_(&d->view, run));
4498 if (processEvent_DownloadUI(&ui, ev)) {
4499 return iTrue;
4500 }
4501 continue;
4502 }
4499 if (run->mediaType != audio_MediaType) { 4503 if (run->mediaType != audio_MediaType) {
4500 continue; 4504 continue;
4501 } 4505 }
4506 if (ev->type == SDL_MOUSEBUTTONDOWN || ev->type == SDL_MOUSEBUTTONUP) {
4507 if (ev->button.button != SDL_BUTTON_LEFT) {
4508 return iFalse;
4509 }
4510 }
4502 /* TODO: move this to mediaui.c */ 4511 /* TODO: move this to mediaui.c */
4503 const iRect rect = runRect_DocumentView_(&d->view, run); 4512 const iRect rect = runRect_DocumentView_(&d->view, run);
4504 iPlayer * plr = audioPlayer_Media(media_GmDocument(d->view.doc), mediaId_GmRun(run)); 4513 iPlayer * plr = audioPlayer_Media(media_GmDocument(d->view.doc), mediaId_GmRun(run));
@@ -4842,6 +4851,9 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
4842 iChangeFlags(d->flags, noHoverWhileScrolling_DocumentWidgetFlag, iFalse); 4851 iChangeFlags(d->flags, noHoverWhileScrolling_DocumentWidgetFlag, iFalse);
4843 return iTrue; 4852 return iTrue;
4844 } 4853 }
4854 if (processMediaEvents_DocumentWidget_(d, ev)) {
4855 return iTrue;
4856 }
4845 if (ev->type == SDL_MOUSEBUTTONDOWN) { 4857 if (ev->type == SDL_MOUSEBUTTONDOWN) {
4846 if (ev->button.button == SDL_BUTTON_X1) { 4858 if (ev->button.button == SDL_BUTTON_X1) {
4847 postCommand_Root(w->root, "navigate.back"); 4859 postCommand_Root(w->root, "navigate.back");
@@ -4923,6 +4935,23 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
4923 if (deviceType_App() == phone_AppDeviceType) { 4935 if (deviceType_App() == phone_AppDeviceType) {
4924 removeN_Array(&items, size_Array(&items) - 2, iInvalidSize); 4936 removeN_Array(&items, size_Array(&items) - 2, iInvalidSize);
4925 } 4937 }
4938 if (equalCase_Rangecc(scheme, "file")) {
4939 pushBack_Array(&items, &(iMenuItem){ "---" });
4940 pushBack_Array(&items,
4941 &(iMenuItem){ export_Icon " ${menu.open.external}",
4942 0,
4943 0,
4944 format_CStr("!open default:1 url:%s",
4945 cstr_String(linkUrl)) });
4946#if defined (iPlatformAppleDesktop)
4947 pushBack_Array(&items,
4948 &(iMenuItem){ "${menu.reveal.macos}",
4949 0,
4950 0,
4951 format_CStr("!reveal url:%s",
4952 cstr_String(linkUrl)) });
4953#endif
4954 }
4926 } 4955 }
4927 else if (!willUseProxy_App(scheme)) { 4956 else if (!willUseProxy_App(scheme)) {
4928 pushBack_Array( 4957 pushBack_Array(
@@ -4959,7 +4988,8 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
4959 cstr_String(linkUrl)) }, 4988 cstr_String(linkUrl)) },
4960 }, 4989 },
4961 3); 4990 3);
4962 if (isNative && d->contextLink->mediaType != download_MediaType) { 4991 if (isNative && d->contextLink->mediaType != download_MediaType &&
4992 !equalCase_Rangecc(scheme, "file")) {
4963 pushBackN_Array(&items, (iMenuItem[]){ 4993 pushBackN_Array(&items, (iMenuItem[]){
4964 { "---" }, 4994 { "---" },
4965 { download_Icon " ${link.download}", 0, 0, "document.downloadlink" }, 4995 { download_Icon " ${link.download}", 0, 0, "document.downloadlink" },
@@ -4979,6 +5009,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
4979 } 5009 }
4980 if (equalCase_Rangecc(scheme, "file")) { 5010 if (equalCase_Rangecc(scheme, "file")) {
4981 /* Local files may be deleted. */ 5011 /* Local files may be deleted. */
5012 pushBack_Array(&items, &(iMenuItem){ "---" });
4982 pushBack_Array( 5013 pushBack_Array(
4983 &items, 5014 &items,
4984 &(iMenuItem){ delete_Icon " " uiTextCaution_ColorEscape 5015 &(iMenuItem){ delete_Icon " " uiTextCaution_ColorEscape
@@ -5052,9 +5083,6 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
5052 processContextMenuEvent_Widget(d->menu, ev, {}); 5083 processContextMenuEvent_Widget(d->menu, ev, {});
5053 } 5084 }
5054 } 5085 }
5055 if (processMediaEvents_DocumentWidget_(d, ev)) {
5056 return iTrue;
5057 }
5058 if (processEvent_Banner(d->banner, ev)) { 5086 if (processEvent_Banner(d->banner, ev)) {
5059 return iTrue; 5087 return iTrue;
5060 } 5088 }
diff --git a/src/ui/linkinfo.c b/src/ui/linkinfo.c
index cb48c7ea..5102f9b3 100644
--- a/src/ui/linkinfo.c
+++ b/src/ui/linkinfo.c
@@ -92,7 +92,7 @@ void infoText_LinkInfo(const iGmDocument *doc, iGmLinkId linkId, iString *text_o
92 appendRange_String(text_out, (iRangecc){ parts.path.start, constEnd_String(url) }); 92 appendRange_String(text_out, (iRangecc){ parts.path.start, constEnd_String(url) });
93 } 93 }
94 else if (scheme != gemini_GmLinkScheme) { 94 else if (scheme != gemini_GmLinkScheme) {
95 appendCStr_String(text_out, globe_Icon " "); 95 appendCStr_String(text_out, scheme == file_GmLinkScheme ? "" : globe_Icon " ");
96 append_String(text_out, url); 96 append_String(text_out, url);
97 } 97 }
98 else { 98 else {
diff --git a/src/ui/mediaui.c b/src/ui/mediaui.c
index 1c828194..2aec568f 100644
--- a/src/ui/mediaui.c
+++ b/src/ui/mediaui.c
@@ -238,6 +238,44 @@ void init_DownloadUI(iDownloadUI *d, const iMedia *media, uint16_t mediaId, iRec
238/*----------------------------------------------------------------------------------------------*/ 238/*----------------------------------------------------------------------------------------------*/
239 239
240iBool processEvent_DownloadUI(iDownloadUI *d, const SDL_Event *ev) { 240iBool processEvent_DownloadUI(iDownloadUI *d, const SDL_Event *ev) {
241 if (ev->type == SDL_MOUSEBUTTONDOWN || ev->type == SDL_MOUSEBUTTONUP) {
242 const iInt2 mouse = init_I2(ev->button.x, ev->button.y);
243 if (!contains_Rect(d->bounds, mouse)) {
244 return iFalse;
245 }
246 float bytesPerSecond;
247 const iString *path;
248 iBool isFinished;
249 downloadStats_Media(d->media, (iMediaId){ download_MediaType, d->mediaId },
250 &path, &bytesPerSecond, &isFinished);
251 if (isFinished) {
252 if (ev->button.button == SDL_BUTTON_RIGHT && ev->type == SDL_MOUSEBUTTONDOWN) {
253 const iMenuItem items[] = {
254 /* Items related to the file */
255 { openTab_Icon " ${menu.opentab}",
256 0,
257 0,
258 format_CStr("!open newtab:1 url:%s",
259 cstrCollect_String(makeFileUrl_String(path))) },
260#if defined (iPlatformAppleDesktop)
261 { "${menu.reveal.macos}",
262 0,
263 0,
264 format_CStr("!reveal path:%s", cstr_String(path)) },
265#endif
266 { "---" },
267 /* Generic items */
268 { "${menu.downloads}", 0, 0, "downloads.open newtab:1" },
269 };
270 openMenu_Widget(makeMenu_Widget(get_Root()->widget, items, iElemCount(items)),
271 mouse);
272 return iTrue;
273 }
274 else if (ev->button.button == SDL_BUTTON_LEFT && ev->type == SDL_MOUSEBUTTONUP) {
275 postCommandf_App("open default:1 url:%s", cstrCollect_String(makeFileUrl_String(path)));
276 }
277 }
278 }
241 return iFalse; 279 return iFalse;
242} 280}
243 281