summaryrefslogtreecommitdiff
path: root/src/ui
diff options
context:
space:
mode:
authorJaakko Keränen <jaakko.keranen@iki.fi>2021-07-17 15:36:37 +0300
committerJaakko Keränen <jaakko.keranen@iki.fi>2021-07-17 15:36:37 +0300
commitc5463b0aec571b8d9451ddadb656a061e22e7f2f (patch)
treefdc36a94f74e9933d8d2064ebc3070c6f12530a2 /src/ui
parent635eabab8b932ca01fc5b391cda20e23e40a6532 (diff)
Working on Titan uploads
There may be a bug in `TlsRequest` when sending out large amounts of data.
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/documentwidget.c63
-rw-r--r--src/ui/documentwidget.h3
-rw-r--r--src/ui/keys.c1
-rw-r--r--src/ui/root.c1
-rw-r--r--src/ui/uploadwidget.c110
-rw-r--r--src/ui/util.c15
-rw-r--r--src/ui/util.h4
7 files changed, 179 insertions, 18 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 8072a025..0c4dee89 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -48,6 +48,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
48#include "scrollwidget.h" 48#include "scrollwidget.h"
49#include "touch.h" 49#include "touch.h"
50#include "translation.h" 50#include "translation.h"
51#include "uploadwidget.h"
51#include "util.h" 52#include "util.h"
52#include "visbuf.h" 53#include "visbuf.h"
53#include "visited.h" 54#include "visited.h"
@@ -2426,6 +2427,24 @@ static iBool handleSwipe_DocumentWidget_(iDocumentWidget *d, const char *cmd) {
2426 return iFalse; 2427 return iFalse;
2427} 2428}
2428 2429
2430static iBool cancelRequest_DocumentWidget_(iDocumentWidget *d, iBool postBack) {
2431 if (d->request) {
2432 iWidget *w = as_Widget(d);
2433 postCommandf_Root(w->root,
2434 "document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url));
2435 iReleasePtr(&d->request);
2436 if (d->state != ready_RequestState) {
2437 d->state = ready_RequestState;
2438 if (postBack) {
2439 postCommand_Root(w->root, "navigate.back");
2440 }
2441 }
2442 updateFetchProgress_DocumentWidget_(d);
2443 return iTrue;
2444 }
2445 return iFalse;
2446}
2447
2429static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { 2448static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) {
2430 iWidget *w = as_Widget(d); 2449 iWidget *w = as_Widget(d);
2431 if (equal_Command(cmd, "document.openurls.changed")) { 2450 if (equal_Command(cmd, "document.openurls.changed")) {
@@ -2792,6 +2811,18 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2792 } 2811 }
2793 return wasHandled; 2812 return wasHandled;
2794 } 2813 }
2814 else if (equal_Command(cmd, "document.upload") && d == document_App()) {
2815 if (equalCase_Rangecc(urlScheme_String(d->mod.url), "gemini") ||
2816 equalCase_Rangecc(urlScheme_String(d->mod.url), "titan")) {
2817 iUploadWidget *upload = new_UploadWidget();
2818 setUrl_UploadWidget(upload, d->mod.url);
2819 setResponseViewer_UploadWidget(upload, d);
2820 addChild_Widget(get_Root()->widget, iClob(upload));
2821 finalizeSheet_Mobile(as_Widget(upload));
2822 postRefresh_App();
2823 }
2824 return iTrue;
2825 }
2795 else if (equal_Command(cmd, "media.updated") || equal_Command(cmd, "media.finished")) { 2826 else if (equal_Command(cmd, "media.updated") || equal_Command(cmd, "media.finished")) {
2796 return handleMediaCommand_DocumentWidget_(d, cmd); 2827 return handleMediaCommand_DocumentWidget_(d, cmd);
2797 } 2828 }
@@ -2812,15 +2843,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2812 return iFalse; 2843 return iFalse;
2813 } 2844 }
2814 else if (equal_Command(cmd, "document.stop") && document_App() == d) { 2845 else if (equal_Command(cmd, "document.stop") && document_App() == d) {
2815 if (d->request) { 2846 if (cancelRequest_DocumentWidget_(d, iTrue /* navigate back */)) {
2816 postCommandf_Root(w->root,
2817 "document.request.cancelled doc:%p url:%s", d, cstr_String(d->mod.url));
2818 iReleasePtr(&d->request);
2819 if (d->state != ready_RequestState) {
2820 d->state = ready_RequestState;
2821 postCommand_Root(w->root, "navigate.back");
2822 }
2823 updateFetchProgress_DocumentWidget_(d);
2824 return iTrue; 2847 return iTrue;
2825 } 2848 }
2826 } 2849 }
@@ -3613,6 +3636,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
3613 { "---", 0, 0, NULL }, 3636 { "---", 0, 0, NULL },
3614 { book_Icon " ${menu.page.import}", 0, 0, "bookmark.links confirm:1" }, 3637 { book_Icon " ${menu.page.import}", 0, 0, "bookmark.links confirm:1" },
3615 { globe_Icon " ${menu.page.translate}", 0, 0, "document.translate" }, 3638 { globe_Icon " ${menu.page.translate}", 0, 0, "document.translate" },
3639 { upload_Icon " ${menu.page.upload}", 0, 0, "document.upload" },
3616 { "---", 0, 0, NULL }, 3640 { "---", 0, 0, NULL },
3617 { "${menu.page.copyurl}", 0, 0, "document.copylink" } }, 3641 { "${menu.page.copyurl}", 0, 0, "document.copylink" } },
3618 15); 3642 15);
@@ -3642,6 +3666,11 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
3642 } 3666 }
3643 d->menu = makeMenu_Widget(w, data_Array(&items), size_Array(&items)); 3667 d->menu = makeMenu_Widget(w, data_Array(&items), size_Array(&items));
3644 deinit_Array(&items); 3668 deinit_Array(&items);
3669 setMenuItemDisabled_Widget(
3670 d->menu,
3671 "document.upload",
3672 !equalCase_Rangecc(urlScheme_String(d->mod.url), "gemini") &&
3673 !equalCase_Rangecc(urlScheme_String(d->mod.url), "titan"));
3645 } 3674 }
3646 processContextMenuEvent_Widget(d->menu, ev, {}); 3675 processContextMenuEvent_Widget(d->menu, ev, {});
3647 } 3676 }
@@ -5004,6 +5033,22 @@ iBool isRequestOngoing_DocumentWidget(const iDocumentWidget *d) {
5004 return d->request != NULL; 5033 return d->request != NULL;
5005} 5034}
5006 5035
5036void takeRequest_DocumentWidget(iDocumentWidget *d, iGmRequest *finishedRequest) {
5037 cancelRequest_DocumentWidget_(d, iFalse /* don't post anything */);
5038 const iString *url = url_GmRequest(finishedRequest);
5039
5040 add_History(d->mod.history, url);
5041 setUrl_DocumentWidget_(d, url);
5042 d->state = fetching_RequestState;
5043 iAssert(d->request == NULL);
5044 d->request = finishedRequest;
5045 postCommand_Widget(d,
5046 "document.request.finished doc:%p reqid:%u request:%p",
5047 d,
5048 id_GmRequest(d->request),
5049 d->request);
5050}
5051
5007void updateSize_DocumentWidget(iDocumentWidget *d) { 5052void updateSize_DocumentWidget(iDocumentWidget *d) {
5008 updateDocumentWidthRetainingScrollPosition_DocumentWidget_(d, iFalse); 5053 updateDocumentWidthRetainingScrollPosition_DocumentWidget_(d, iFalse);
5009 resetWideRuns_DocumentWidget_(d); 5054 resetWideRuns_DocumentWidget_(d);
diff --git a/src/ui/documentwidget.h b/src/ui/documentwidget.h
index 1921b25a..cc09c72d 100644
--- a/src/ui/documentwidget.h
+++ b/src/ui/documentwidget.h
@@ -26,6 +26,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
26#include <the_Foundation/stream.h> 26#include <the_Foundation/stream.h>
27 27
28iDeclareType(GmDocument) 28iDeclareType(GmDocument)
29iDeclareType(GmRequest)
29iDeclareType(History) 30iDeclareType(History)
30 31
31iDeclareWidgetClass(DocumentWidget) 32iDeclareWidgetClass(DocumentWidget)
@@ -58,4 +59,6 @@ void setRedirectCount_DocumentWidget (iDocumentWidget *, int count);
58void setSource_DocumentWidget (iDocumentWidget *, const iString *sourceText); 59void setSource_DocumentWidget (iDocumentWidget *, const iString *sourceText);
59void setOpenedFromSidebar_DocumentWidget(iDocumentWidget *, iBool fromSidebar); 60void setOpenedFromSidebar_DocumentWidget(iDocumentWidget *, iBool fromSidebar);
60 61
62void takeRequest_DocumentWidget (iDocumentWidget *, iGmRequest *finishedRequest); /* ownership given */
63
61void updateSize_DocumentWidget (iDocumentWidget *); 64void updateSize_DocumentWidget (iDocumentWidget *);
diff --git a/src/ui/keys.c b/src/ui/keys.c
index 5fcfbea2..6de30f57 100644
--- a/src/ui/keys.c
+++ b/src/ui/keys.c
@@ -238,6 +238,7 @@ static const struct { int id; iMenuItem bind; int flags; } defaultBindings_[] =
238 { 99, { "${keys.split.item} ${menu.split.vertical} 2:1", SDLK_r, 0, "ui.split arg:2 axis:1", }, noDirectTrigger_BindFlag }, 238 { 99, { "${keys.split.item} ${menu.split.vertical} 2:1", SDLK_r, 0, "ui.split arg:2 axis:1", }, noDirectTrigger_BindFlag },
239 { 100,{ "${keys.hoverurl}", '/', KMOD_PRIMARY, "prefs.hoverlink.toggle" }, 0 }, 239 { 100,{ "${keys.hoverurl}", '/', KMOD_PRIMARY, "prefs.hoverlink.toggle" }, 0 },
240 { 110,{ "${menu.save.downloads}", SDLK_s, KMOD_PRIMARY, "document.save" }, 0 }, 240 { 110,{ "${menu.save.downloads}", SDLK_s, KMOD_PRIMARY, "document.save" }, 0 },
241 { 120,{ "${keys.upload}", SDLK_u, KMOD_PRIMARY, "document.upload" }, 0 },
241 /* The following cannot currently be changed (built-in duplicates). */ 242 /* The following cannot currently be changed (built-in duplicates). */
242#if defined (iPlatformApple) 243#if defined (iPlatformApple)
243 { 1002, { NULL, SDLK_LEFTBRACKET, KMOD_PRIMARY, "navigate.back" }, 0 }, 244 { 1002, { NULL, SDLK_LEFTBRACKET, KMOD_PRIMARY, "navigate.back" }, 0 },
diff --git a/src/ui/root.c b/src/ui/root.c
index c78c9632..91077019 100644
--- a/src/ui/root.c
+++ b/src/ui/root.c
@@ -1161,6 +1161,7 @@ void createUserInterface_Root(iRoot *d) {
1161 { star_Icon " ${menu.page.subscribe}", subscribeToPage_KeyModifier, "feeds.subscribe" }, 1161 { star_Icon " ${menu.page.subscribe}", subscribeToPage_KeyModifier, "feeds.subscribe" },
1162 { book_Icon " ${menu.page.import}", 0, 0, "bookmark.links confirm:1" }, 1162 { book_Icon " ${menu.page.import}", 0, 0, "bookmark.links confirm:1" },
1163 { globe_Icon " ${menu.page.translate}", 0, 0, "document.translate" }, 1163 { globe_Icon " ${menu.page.translate}", 0, 0, "document.translate" },
1164 { upload_Icon " ${menu.page.upload}", 0, 0, "document.upload" },
1164 { "---", 0, 0, NULL }, 1165 { "---", 0, 0, NULL },
1165 { "${menu.page.copyurl}", 0, 0, "document.copylink" }, 1166 { "${menu.page.copyurl}", 0, 0, "document.copylink" },
1166 { "${menu.page.copysource}", 'c', KMOD_PRIMARY, "copy" }, 1167 { "${menu.page.copysource}", 'c', KMOD_PRIMARY, "copy" },
diff --git a/src/ui/uploadwidget.c b/src/ui/uploadwidget.c
index 036571a5..968b3775 100644
--- a/src/ui/uploadwidget.c
+++ b/src/ui/uploadwidget.c
@@ -25,9 +25,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
25#include "inputwidget.h" 25#include "inputwidget.h"
26#include "documentwidget.h" 26#include "documentwidget.h"
27#include "color.h" 27#include "color.h"
28#include "command.h"
28#include "gmrequest.h" 29#include "gmrequest.h"
29#include "app.h" 30#include "app.h"
30 31
32#include <the_Foundation/file.h>
33#include <the_Foundation/fileinfo.h>
34
31iDefineObjectConstruction(UploadWidget) 35iDefineObjectConstruction(UploadWidget)
32 36
33struct Impl_UploadWidget { 37struct Impl_UploadWidget {
@@ -39,6 +43,11 @@ struct Impl_UploadWidget {
39 iInputWidget * mime; 43 iInputWidget * mime;
40 iInputWidget * token; 44 iInputWidget * token;
41 iInputWidget * input; 45 iInputWidget * input;
46 iLabelWidget * filePathLabel;
47 iLabelWidget * fileSizeLabel;
48 iString filePath;
49 size_t fileSize;
50 iAtomicInt isRequestUpdated;
42}; 51};
43 52
44void init_UploadWidget(iUploadWidget *d) { 53void init_UploadWidget(iUploadWidget *d) {
@@ -49,6 +58,8 @@ void init_UploadWidget(iUploadWidget *d) {
49 init_String(&d->url); 58 init_String(&d->url);
50 d->viewer = NULL; 59 d->viewer = NULL;
51 d->request = NULL; 60 d->request = NULL;
61 init_String(&d->filePath);
62 d->fileSize = 0;
52 addChildFlags_Widget(w, 63 addChildFlags_Widget(w,
53 iClob(new_LabelWidget(uiHeading_ColorEscape "${heading.upload}", NULL)), 64 iClob(new_LabelWidget(uiHeading_ColorEscape "${heading.upload}", NULL)),
54 frameless_WidgetFlag); 65 frameless_WidgetFlag);
@@ -64,10 +75,11 @@ void init_UploadWidget(iUploadWidget *d) {
64 iWidget *page = new_Widget(); 75 iWidget *page = new_Widget();
65 setFlags_Widget(page, arrangeSize_WidgetFlag, iTrue); 76 setFlags_Widget(page, arrangeSize_WidgetFlag, iTrue);
66 d->input = new_InputWidget(0); 77 d->input = new_InputWidget(0);
78 setHint_InputWidget(d->input, "${hint.upload.text}");
67 setEnterInsertsLF_InputWidget(d->input, iTrue); 79 setEnterInsertsLF_InputWidget(d->input, iTrue);
68 setFixedSize_Widget(as_Widget(d->input), init_I2(120 * gap_UI, -1)); 80 setFixedSize_Widget(as_Widget(d->input), init_I2(120 * gap_UI, -1));
69 addChild_Widget(page, iClob(d->input)); 81 addChild_Widget(page, iClob(d->input));
70 appendTabPage_Widget(tabs, iClob(page), "${heading.upload.text}", '1', 0); 82 appendFramelessTabPage_Widget(tabs, iClob(page), "${heading.upload.text}", '1', 0);
71 } 83 }
72 /* File content. */ { 84 /* File content. */ {
73 appendTwoColumnTabPage_Widget(tabs, "${heading.upload.file}", '2', &headings, &values); 85 appendTwoColumnTabPage_Widget(tabs, "${heading.upload.file}", '2', &headings, &values);
@@ -75,9 +87,9 @@ void init_UploadWidget(iUploadWidget *d) {
75// iWidget *hint = addChild_Widget(values, iClob(new_LabelWidget("${upload.file.drophint}", NULL))); 87// iWidget *hint = addChild_Widget(values, iClob(new_LabelWidget("${upload.file.drophint}", NULL)));
76// pad->sizeRef = hint; 88// pad->sizeRef = hint;
77 addChild_Widget(headings, iClob(new_LabelWidget("${upload.file.name}", NULL))); 89 addChild_Widget(headings, iClob(new_LabelWidget("${upload.file.name}", NULL)));
78 addChild_Widget(values, iClob(new_LabelWidget("filename.ext", NULL))); 90 d->filePathLabel = addChild_Widget(values, iClob(new_LabelWidget("${upload.file.drophere}", NULL)));
79 addChild_Widget(headings, iClob(new_LabelWidget("${upload.file.size}", NULL))); 91 addChild_Widget(headings, iClob(new_LabelWidget("${upload.file.size}", NULL)));
80 addChild_Widget(values, iClob(new_LabelWidget("0 KB", NULL))); 92 d->fileSizeLabel = addChild_Widget(values, iClob(new_LabelWidget("\u2014", NULL)));
81 d->mime = new_InputWidget(0); 93 d->mime = new_InputWidget(0);
82 setFixedSize_Widget(as_Widget(d->mime), init_I2(50 * gap_UI, -1)); 94 setFixedSize_Widget(as_Widget(d->mime), init_I2(50 * gap_UI, -1));
83 addTwoColumnDialogInputField_Widget(headings, values, "${upload.mime}", "upload.mime", iClob(d->mime)); 95 addTwoColumnDialogInputField_Widget(headings, values, "${upload.mime}", "upload.mime", iClob(d->mime));
@@ -100,6 +112,10 @@ void init_UploadWidget(iUploadWidget *d) {
100 KMOD_PRIMARY, 112 KMOD_PRIMARY,
101 "upload.accept" } }, 113 "upload.accept" } },
102 2); 114 2);
115 setId_Widget(addChildPosFlags_Widget(buttons,
116 iClob(new_LabelWidget("0", NULL)),
117 front_WidgetAddPos, frameless_WidgetFlag),
118 "upload.pending");
103 addChild_Widget(w, iClob(buttons)); 119 addChild_Widget(w, iClob(buttons));
104 } 120 }
105 resizeToLargestPage_Widget(tabs); 121 resizeToLargestPage_Widget(tabs);
@@ -107,12 +123,16 @@ void init_UploadWidget(iUploadWidget *d) {
107} 123}
108 124
109void deinit_UploadWidget(iUploadWidget *d) { 125void deinit_UploadWidget(iUploadWidget *d) {
126 deinit_String(&d->filePath);
110 deinit_String(&d->url); 127 deinit_String(&d->url);
111 iRelease(d->request); 128 iRelease(d->request);
112} 129}
113 130
114void setUrl_UploadWidget(iUploadWidget *d, const iString *url) { 131void setUrl_UploadWidget(iUploadWidget *d, const iString *url) {
115 set_String(&d->url, url); 132 iUrl parts;
133 init_Url(&parts, url);
134 setCStr_String(&d->url, "titan");
135 appendRange_String(&d->url, (iRangecc){ parts.scheme.end, constEnd_String(url) });
116 setText_LabelWidget(d->info, &d->url); 136 setText_LabelWidget(d->info, &d->url);
117} 137}
118 138
@@ -120,6 +140,20 @@ void setResponseViewer_UploadWidget(iUploadWidget *d, iDocumentWidget *doc) {
120 d->viewer = doc; 140 d->viewer = doc;
121} 141}
122 142
143static iWidget *acceptButton_UploadWidget_(iUploadWidget *d) {
144 return lastChild_Widget(findChild_Widget(as_Widget(d), "dialogbuttons"));
145}
146
147static void requestUpdated_UploadWidget_(iUploadWidget *d, iGmRequest *req) {
148 if (!exchange_Atomic(&d->isRequestUpdated, iTrue)) {
149 postCommand_Widget(d, "upload.request.updated reqid:%u", id_GmRequest(req));
150 }
151}
152
153static void requestFinished_UploadWidget_(iUploadWidget *d, iGmRequest *req) {
154 postCommand_Widget(d, "upload.request.finished reqid:%u", id_GmRequest(req));
155}
156
123static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) { 157static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) {
124 iWidget *w = as_Widget(d); 158 iWidget *w = as_Widget(d);
125 if (isCommand_Widget(w, ev, "upload.cancel")) { 159 if (isCommand_Widget(w, ev, "upload.cancel")) {
@@ -128,12 +162,80 @@ static iBool processEvent_UploadWidget_(iUploadWidget *d, const SDL_Event *ev) {
128 destroy_Widget(w); 162 destroy_Widget(w);
129 return iTrue; 163 return iTrue;
130 } 164 }
165 const char *cmd = command_UserEvent(ev);
131 if (isCommand_Widget(w, ev, "upload.accept")) { 166 if (isCommand_Widget(w, ev, "upload.accept")) {
167 iWidget * tabs = findChild_Widget(w, "upload.tabs");
168 const int tabIndex = tabPageIndex_Widget(tabs, currentTabPage_Widget(tabs));
132 /* Make a GmRequest and send the data. */ 169 /* Make a GmRequest and send the data. */
170 iAssert(d->request == NULL);
171 iAssert(!isEmpty_String(&d->url));
172 d->request = new_GmRequest(certs_App());
173 setUrl_GmRequest(d->request, &d->url);
174 if (tabIndex == 0) {
175 /* Uploading text. */
176 setTitanData_GmRequest(d->request,
177 collectNewCStr_String("text/plain"),
178 utf8_String(text_InputWidget(d->input)),
179 text_InputWidget(d->token));
180 }
181 else {
182 /* Uploading a file. */
183 iFile *f = iClob(new_File(&d->filePath));
184 if (!open_File(f, readOnly_FileMode)) {
185 makeMessage_Widget("${heading.upload.error.file}",
186 "${upload.error.msg}",
187 (iMenuItem[]){ "${dlg.message.ok}", 0, 0, "message.ok" }, 1);
188 iReleasePtr(&d->request);
189 return iTrue;
190 }
191 setTitanData_GmRequest(d->request,
192 text_InputWidget(d->mime),
193 collect_Block(readAll_File(f)),
194 text_InputWidget(d->token));
195 close_File(f);
196 }
197 iConnect(GmRequest, d->request, updated, d, requestUpdated_UploadWidget_);
198 iConnect(GmRequest, d->request, finished, d, requestFinished_UploadWidget_);
199 submit_GmRequest(d->request);
133 /* The dialog will remain open until the request finishes, showing upload progress. */ 200 /* The dialog will remain open until the request finishes, showing upload progress. */
201 setFocus_Widget(NULL);
202 setFlags_Widget(tabs, disabled_WidgetFlag, iTrue);
203 setFlags_Widget(as_Widget(d->token), disabled_WidgetFlag, iTrue);
204 setFlags_Widget(acceptButton_UploadWidget_(d), disabled_WidgetFlag, iTrue);
205 return iTrue;
206 }
207 else if (isCommand_Widget(w, ev, "upload.request.updated")) {
208 /* TODO: Upload progress update? */
209 }
210 else if (isCommand_Widget(w, ev, "upload.request.finished") &&
211 id_GmRequest(d->request) == argU32Label_Command(cmd, "reqid")) {
212 if (d->viewer) {
213 takeRequest_DocumentWidget(d->viewer, d->request);
214 d->request = NULL; /* DocumentWidget has it now. */
215 }
216 setupSheetTransition_Mobile(w, iFalse);
217 destroy_Widget(w);
218 return iTrue;
134 } 219 }
135 if (ev->type == SDL_DROPFILE) { 220 if (ev->type == SDL_DROPFILE) {
136 /* Switch to File tab. */ 221 /* Switch to File tab. */
222 iWidget *tabs = findChild_Widget(w, "upload.tabs");
223 showTabPage_Widget(tabs, tabPage_Widget(tabs, 1));
224 setCStr_String(&d->filePath, ev->drop.file);
225 iFileInfo *info = iClob(new_FileInfo(&d->filePath));
226 if (isDirectory_FileInfo(info)) {
227 makeMessage_Widget("${heading.upload.error.file}",
228 "${upload.error.directory}",
229 (iMenuItem[]){ "${dlg.message.ok}", 0, 0, "message.ok" }, 1);
230 clear_String(&d->filePath);
231 d->fileSize = 0;
232 return iTrue;
233 }
234 d->fileSize = size_FileInfo(info);
235 setText_LabelWidget(d->filePathLabel, &d->filePath);
236 setTextCStr_LabelWidget(d->fileSizeLabel, formatCStrs_Lang("num.bytes.n", d->fileSize));
237 setTextCStr_InputWidget(d->mime, mediaType_Path(&d->filePath));
238 return iTrue;
137 } 239 }
138 return processEvent_Widget(w, ev); 240 return processEvent_Widget(w, ev);
139} 241}
diff --git a/src/ui/util.c b/src/ui/util.c
index 6bc358de..da7a69b4 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -864,6 +864,13 @@ iLabelWidget *findMenuItem_Widget(iWidget *menu, const char *command) {
864 return NULL; 864 return NULL;
865} 865}
866 866
867void setMenuItemDisabled_Widget(iWidget *menu, const char *command, iBool disable) {
868 iLabelWidget *item = findMenuItem_Widget(menu, command);
869 if (item) {
870 setFlags_Widget(as_Widget(item), disabled_WidgetFlag, disable);
871 }
872}
873
867int checkContextMenu_Widget(iWidget *menu, const SDL_Event *ev) { 874int checkContextMenu_Widget(iWidget *menu, const SDL_Event *ev) {
868 if (menu && ev->type == SDL_MOUSEBUTTONDOWN && ev->button.button == SDL_BUTTON_RIGHT) { 875 if (menu && ev->type == SDL_MOUSEBUTTONDOWN && ev->button.button == SDL_BUTTON_RIGHT) {
869 if (isVisible_Widget(menu)) { 876 if (isVisible_Widget(menu)) {
@@ -1428,8 +1435,8 @@ iWidget *makeToggle_Widget(const char *id) {
1428 return toggle; 1435 return toggle;
1429} 1436}
1430 1437
1431static void appendFramelessTabPage_(iWidget *tabs, iWidget *page, const char *title, int shortcut, 1438void appendFramelessTabPage_Widget(iWidget *tabs, iWidget *page, const char *title, int shortcut,
1432 int kmods) { 1439 int kmods) {
1433 appendTabPage_Widget(tabs, page, title, shortcut, kmods); 1440 appendTabPage_Widget(tabs, page, title, shortcut, kmods);
1434 setFlags_Widget( 1441 setFlags_Widget(
1435 (iWidget *) back_ObjectList(children_Widget(findChild_Widget(tabs, "tabs.buttons"))), 1442 (iWidget *) back_ObjectList(children_Widget(findChild_Widget(tabs, "tabs.buttons"))),
@@ -1461,7 +1468,7 @@ iWidget *appendTwoColumnTabPage_Widget(iWidget *tabs, const char *title, int sho
1461 *values = addChildFlags_Widget( 1468 *values = addChildFlags_Widget(
1462 columns, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag); 1469 columns, iClob(new_Widget()), arrangeVertical_WidgetFlag | arrangeSize_WidgetFlag);
1463 addChildFlags_Widget(page, iClob(new_Widget()), expand_WidgetFlag); 1470 addChildFlags_Widget(page, iClob(new_Widget()), expand_WidgetFlag);
1464 appendFramelessTabPage_(tabs, iClob(page), title, shortcut, shortcut ? KMOD_PRIMARY : 0); 1471 appendFramelessTabPage_Widget(tabs, iClob(page), title, shortcut, shortcut ? KMOD_PRIMARY : 0);
1465 return page; 1472 return page;
1466} 1473}
1467 1474
@@ -1888,7 +1895,7 @@ iWidget *makePreferences_Widget(void) {
1888 /* Keybindings. */ 1895 /* Keybindings. */
1889 if (deviceType_App() == desktop_AppDeviceType) { 1896 if (deviceType_App() == desktop_AppDeviceType) {
1890 iBindingsWidget *bind = new_BindingsWidget(); 1897 iBindingsWidget *bind = new_BindingsWidget();
1891 appendFramelessTabPage_(tabs, iClob(bind), "${heading.prefs.keys}", '7', KMOD_PRIMARY); 1898 appendFramelessTabPage_Widget(tabs, iClob(bind), "${heading.prefs.keys}", '7', KMOD_PRIMARY);
1892 } 1899 }
1893 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI))); 1900 addChild_Widget(dlg, iClob(makePadding_Widget(gap_UI)));
1894 updatePreferencesLayout_Widget(dlg); 1901 updatePreferencesLayout_Widget(dlg);
diff --git a/src/ui/util.h b/src/ui/util.h
index 5b02a4b3..021f72d1 100644
--- a/src/ui/util.h
+++ b/src/ui/util.h
@@ -226,7 +226,8 @@ void openMenu_Widget (iWidget *, iInt2 windowCoord);
226void openMenuFlags_Widget(iWidget *, iInt2 windowCoord, iBool postCommands); 226void openMenuFlags_Widget(iWidget *, iInt2 windowCoord, iBool postCommands);
227void closeMenu_Widget (iWidget *); 227void closeMenu_Widget (iWidget *);
228 228
229iLabelWidget * findMenuItem_Widget (iWidget *menu, const char *command); 229iLabelWidget * findMenuItem_Widget (iWidget *menu, const char *command);
230void setMenuItemDisabled_Widget (iWidget *menu, const char *command, iBool disable);
230 231
231int checkContextMenu_Widget (iWidget *, const SDL_Event *ev); /* see macro below */ 232int checkContextMenu_Widget (iWidget *, const SDL_Event *ev); /* see macro below */
232 233
@@ -242,6 +243,7 @@ iLabelWidget * makeMenuButton_LabelWidget (const char *label, const iMenuItem
242 243
243iWidget * makeTabs_Widget (iWidget *parent); 244iWidget * makeTabs_Widget (iWidget *parent);
244void appendTabPage_Widget (iWidget *tabs, iWidget *page, const char *label, int key, int kmods); 245void appendTabPage_Widget (iWidget *tabs, iWidget *page, const char *label, int key, int kmods);
246void appendFramelessTabPage_Widget(iWidget *tabs, iWidget *page, const char *title, int shortcut, int kmods);
245iWidget * appendTwoColumnTabPage_Widget(iWidget *tabs, const char *title, int shortcut, iWidget **headings, 247iWidget * appendTwoColumnTabPage_Widget(iWidget *tabs, const char *title, int shortcut, iWidget **headings,
246 iWidget **values); 248 iWidget **values);
247void prependTabPage_Widget (iWidget *tabs, iWidget *page, const char *label, int key, int kmods); 249void prependTabPage_Widget (iWidget *tabs, iWidget *page, const char *label, int key, int kmods);