diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-12-29 07:18:29 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-12-29 07:18:29 +0200 |
commit | e8f06bd0985ce2c9ac5ef02525672a426d559d18 (patch) | |
tree | 5617f7e65d81ae0b5bcf3c0c6a49711662e658f2 | |
parent | 336b9d7272ed8b1a9dccee27dec20e3377ee0c74 (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.po | 4 | ||||
-rw-r--r-- | res/lang/cs.bin | bin | 31549 -> 31582 bytes | |||
-rw-r--r-- | res/lang/de.bin | bin | 30556 -> 30589 bytes | |||
-rw-r--r-- | res/lang/en.bin | bin | 26642 -> 26675 bytes | |||
-rw-r--r-- | res/lang/eo.bin | bin | 25606 -> 25639 bytes | |||
-rw-r--r-- | res/lang/es.bin | bin | 30380 -> 30413 bytes | |||
-rw-r--r-- | res/lang/es_MX.bin | bin | 27712 -> 27745 bytes | |||
-rw-r--r-- | res/lang/fi.bin | bin | 30213 -> 30246 bytes | |||
-rw-r--r-- | res/lang/fr.bin | bin | 31360 -> 31393 bytes | |||
-rw-r--r-- | res/lang/gl.bin | bin | 29565 -> 29598 bytes | |||
-rw-r--r-- | res/lang/hu.bin | bin | 31385 -> 31418 bytes | |||
-rw-r--r-- | res/lang/ia.bin | bin | 28712 -> 28745 bytes | |||
-rw-r--r-- | res/lang/ie.bin | bin | 29300 -> 29333 bytes | |||
-rw-r--r-- | res/lang/isv.bin | bin | 25363 -> 25396 bytes | |||
-rw-r--r-- | res/lang/pl.bin | bin | 29988 -> 30021 bytes | |||
-rw-r--r-- | res/lang/ru.bin | bin | 44748 -> 44781 bytes | |||
-rw-r--r-- | res/lang/sk.bin | bin | 25699 -> 25732 bytes | |||
-rw-r--r-- | res/lang/sr.bin | bin | 44174 -> 44207 bytes | |||
-rw-r--r-- | res/lang/tok.bin | bin | 27422 -> 27455 bytes | |||
-rw-r--r-- | res/lang/tr.bin | bin | 29606 -> 29639 bytes | |||
-rw-r--r-- | res/lang/uk.bin | bin | 44093 -> 44126 bytes | |||
-rw-r--r-- | res/lang/zh_Hans.bin | bin | 25607 -> 25640 bytes | |||
-rw-r--r-- | res/lang/zh_Hant.bin | bin | 25805 -> 25838 bytes | |||
-rw-r--r-- | src/app.c | 68 | ||||
-rw-r--r-- | src/app.h | 1 | ||||
-rw-r--r-- | src/gmcerts.c | 2 | ||||
-rw-r--r-- | src/ui/certlistwidget.c | 10 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 66 | ||||
-rw-r--r-- | src/ui/linkinfo.c | 2 | ||||
-rw-r--r-- | src/ui/mediaui.c | 38 |
30 files changed, 142 insertions, 49 deletions
@@ -854,6 +854,10 @@ msgstr "Bookmark Link…" | |||
854 | msgid "link.download" | 854 | msgid "link.download" |
855 | msgstr "Download Linked File" | 855 | msgstr "Download Linked File" |
856 | 856 | ||
857 | # Shows where a local file is using the Finder. | ||
858 | msgid "menu.reveal.macos" | ||
859 | msgstr "Show in Finder" | ||
860 | |||
857 | msgid "link.file.delete" | 861 | msgid "link.file.delete" |
858 | msgstr "Delete File" | 862 | msgstr "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 | |||
@@ -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 */ | |||
119 | struct Impl_App { | 120 | struct 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 | ||
1012 | const iString *execPath_App(void) { | 1020 | const 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 | ||
1093 | const 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 | |||
1085 | const iString *debugInfo_App(void) { | 1104 | const 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 | ||
3355 | void revealPath_App(const iString *path) { | 3388 | void 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; |
@@ -111,6 +111,7 @@ 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 * fileNameForUrl_App (const iString *url, const iString *mime); |
114 | const iString * temporaryPathForUrl_App(const iString *url, const iString *mime); /* deleted before quitting */ | ||
114 | const iString * downloadPathForUrl_App(const iString *url, const iString *mime); | 115 | const iString * downloadPathForUrl_App(const iString *url, const iString *mime); |
115 | 116 | ||
116 | typedef void (*iTickerFunc)(iAny *); | 117 | typedef 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 | ||
656 | static const char *certPath_GmCerts_(const iGmCerts *d, const iGmIdentity *identity) { | 656 | static 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 | ||
240 | iBool processEvent_DownloadUI(iDownloadUI *d, const SDL_Event *ev) { | 240 | iBool 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 | ||