summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gempub.c19
-rw-r--r--src/gempub.h1
-rw-r--r--src/ui/documentwidget.c70
3 files changed, 64 insertions, 26 deletions
diff --git a/src/gempub.c b/src/gempub.c
index f3021add..1f5d58ce 100644
--- a/src/gempub.c
+++ b/src/gempub.c
@@ -94,6 +94,9 @@ static void parseNavigationLinks_Gempub_(const iGempub *d) {
94 set_String(&link.url, absoluteUrl_String(url_GmRequest(index), collectNewRange_String(url))); 94 set_String(&link.url, absoluteUrl_String(url_GmRequest(index), collectNewRange_String(url)));
95 setRange_String(&link.label, capturedRange_RegExpMatch(&m, 2)); 95 setRange_String(&link.label, capturedRange_RegExpMatch(&m, 2));
96 trim_String(&link.label); 96 trim_String(&link.label);
97 if (isEmpty_String(&link.label)) {
98 setRange_String(&link.label, url);
99 }
97 pushBack_Array(d->navLinks, &link); 100 pushBack_Array(d->navLinks, &link);
98 } 101 }
99 iEndCollect(); 102 iEndCollect();
@@ -280,7 +283,7 @@ static void appendProperty_Gempub_(const iGempub *d, const char *label,
280 } 283 }
281} 284}
282 285
283static iBool isRemote_Gempub_(const iGempub *d) { 286iBool isRemote_Gempub(const iGempub *d) {
284 return !equalCase_Rangecc(urlScheme_String(&d->baseUrl), "file"); 287 return !equalCase_Rangecc(urlScheme_String(&d->baseUrl), "file");
285} 288}
286 289
@@ -295,7 +298,7 @@ iString *coverPageSource_Gempub(const iGempub *d) {
295 } 298 }
296 appendCStr_String(out, "\n"); 299 appendCStr_String(out, "\n");
297 appendProperty_Gempub_(d, "${gempub.meta.author}:", author_GempubProperty, out); 300 appendProperty_Gempub_(d, "${gempub.meta.author}:", author_GempubProperty, out);
298 if (!isRemote_Gempub_(d)) { 301 if (!isRemote_Gempub(d)) {
299 appendFormat_String(out, "\n=> %s " book_Icon " ${gempub.cover.view}\n", 302 appendFormat_String(out, "\n=> %s " book_Icon " ${gempub.cover.view}\n",
300 cstr_String(indexPageUrl_Gempub(d))); 303 cstr_String(indexPageUrl_Gempub(d)));
301 if (hasProperty_Gempub_(d, cover_GempubProperty)) { 304 if (hasProperty_Gempub_(d, cover_GempubProperty)) {
@@ -306,12 +309,12 @@ iString *coverPageSource_Gempub(const iGempub *d) {
306 else { 309 else {
307 iString *key = collectNew_String(); /* TODO: add a helper for this */ 310 iString *key = collectNew_String(); /* TODO: add a helper for this */
308 toString_Sym(SDLK_s, KMOD_PRIMARY, key); 311 toString_Sym(SDLK_s, KMOD_PRIMARY, key);
309 appendCStr_String(out, "\n${gempub.cover.viewlocal} "); 312 appendCStr_String(out, "\n${gempub.cover.viewlocal}\n");
310 appendFormat_String(out, 313// appendFormat_String(out,
311 cstr_Lang("error.unsupported.suggestsave"), 314// cstr_Lang("error.unsupported.suggestsave"),
312 cstr_String(key), 315// cstr_String(key),
313 saveToDownloads_Label); 316// saveToDownloads_Label);
314 appendCStr_String(out, "\n"); 317// appendCStr_String(out, "\n");
315 } 318 }
316 appendCStr_String(out, "\n## ${gempub.cover.aboutbook}\n"); 319 appendCStr_String(out, "\n## ${gempub.cover.aboutbook}\n");
317 appendProperty_Gempub_(d, "${gempub.meta.version}:", version_GempubProperty, out); 320 appendProperty_Gempub_(d, "${gempub.meta.version}:", version_GempubProperty, out);
diff --git a/src/gempub.h b/src/gempub.h
index c03cabe6..e5f1b8eb 100644
--- a/src/gempub.h
+++ b/src/gempub.h
@@ -53,6 +53,7 @@ void close_Gempub (iGempub *);
53void setBaseUrl_Gempub (iGempub *, const iString *baseUrl); 53void setBaseUrl_Gempub (iGempub *, const iString *baseUrl);
54 54
55iBool isOpen_Gempub (const iGempub *); 55iBool isOpen_Gempub (const iGempub *);
56iBool isRemote_Gempub (const iGempub *);
56iString * coverPageSource_Gempub (const iGempub *); 57iString * coverPageSource_Gempub (const iGempub *);
57iBool preloadCoverImage_Gempub(const iGempub *, iGmDocument *doc); 58iBool preloadCoverImage_Gempub(const iGempub *, iGmDocument *doc);
58 59
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index c8aad02b..a02e0bca 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -820,6 +820,13 @@ static iRangecc currentHeading_DocumentWidget_(const iDocumentWidget *d) {
820 return heading; 820 return heading;
821} 821}
822 822
823static int updateScrollMax_DocumentWidget_(iDocumentWidget *d) {
824 arrange_Widget(d->footerButtons); /* scrollMax depends on footer height */
825 const int scrollMax = scrollMax_DocumentWidget_(d);
826 setMax_SmoothScroll(&d->scrollY, scrollMax);
827 return scrollMax;
828}
829
823static void updateVisible_DocumentWidget_(iDocumentWidget *d) { 830static void updateVisible_DocumentWidget_(iDocumentWidget *d) {
824 iChangeFlags(d->flags, 831 iChangeFlags(d->flags,
825 centerVertically_DocumentWidgetFlag, 832 centerVertically_DocumentWidgetFlag,
@@ -827,7 +834,7 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) {
827 !isSuccess_GmStatusCode(d->sourceStatus)); 834 !isSuccess_GmStatusCode(d->sourceStatus));
828 const iRangei visRange = visibleRange_DocumentWidget_(d); 835 const iRangei visRange = visibleRange_DocumentWidget_(d);
829 const iRect bounds = bounds_Widget(as_Widget(d)); 836 const iRect bounds = bounds_Widget(as_Widget(d));
830 const int scrollMax = scrollMax_DocumentWidget_(d); 837 const int scrollMax = updateScrollMax_DocumentWidget_(d);
831 /* Reposition the footer buttons as appropriate. */ 838 /* Reposition the footer buttons as appropriate. */
832 /* TODO: You can just position `footerButtons` here completely without having to get 839 /* TODO: You can just position `footerButtons` here completely without having to get
833 `Widget` involved with the offset in any way. */ 840 `Widget` involved with the offset in any way. */
@@ -837,7 +844,6 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) {
837 const int hPad = (width_Rect(bounds) - iMin(120 * gap_UI, width_Rect(docBounds))) / 2; 844 const int hPad = (width_Rect(bounds) - iMin(120 * gap_UI, width_Rect(docBounds))) / 2;
838 const int vPad = 3 * gap_UI; 845 const int vPad = 3 * gap_UI;
839 setPadding_Widget(d->footerButtons, hPad, vPad, hPad, vPad); 846 setPadding_Widget(d->footerButtons, hPad, vPad, hPad, vPad);
840 arrange_Widget(d->footerButtons);
841 d->footerButtons->animOffsetRef = (scrollMax > 0 ? &d->scrollY.pos : NULL); 847 d->footerButtons->animOffsetRef = (scrollMax > 0 ? &d->scrollY.pos : NULL);
842 if (scrollMax <= 0) { 848 if (scrollMax <= 0) {
843 d->footerButtons->animOffsetRef = NULL; 849 d->footerButtons->animOffsetRef = NULL;
@@ -848,7 +854,6 @@ static void updateVisible_DocumentWidget_(iDocumentWidget *d) {
848 d->footerButtons->rect.pos.y = size_GmDocument(d->doc).y + 2 * gap_UI * d->pageMargin; 854 d->footerButtons->rect.pos.y = size_GmDocument(d->doc).y + 2 * gap_UI * d->pageMargin;
849 } 855 }
850 } 856 }
851 setMax_SmoothScroll(&d->scrollY, scrollMax);
852 setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax }); 857 setRange_ScrollWidget(d->scroll, (iRangei){ 0, scrollMax });
853 const int docSize = size_GmDocument(d->doc).y; 858 const int docSize = size_GmDocument(d->doc).y;
854 setThumb_ScrollWidget(d->scroll, 859 setThumb_ScrollWidget(d->scroll,
@@ -1066,7 +1071,7 @@ static void makeFooterButtons_DocumentWidget_(iDocumentWidget *d, const iMenuIte
1066 resizeWidthOfChildren_WidgetFlag | arrangeHeight_WidgetFlag | 1071 resizeWidthOfChildren_WidgetFlag | arrangeHeight_WidgetFlag |
1067 fixedPosition_WidgetFlag | resizeToParentWidth_WidgetFlag, 1072 fixedPosition_WidgetFlag | resizeToParentWidth_WidgetFlag,
1068 iTrue); 1073 iTrue);
1069 setBackgroundColor_Widget(d->footerButtons, tmBackground_ColorId); 1074 //setBackgroundColor_Widget(d->footerButtons, tmBackground_ColorId);
1070 for (size_t i = 0; i < count; ++i) { 1075 for (size_t i = 0; i < count; ++i) {
1071 iLabelWidget *button = addChildFlags_Widget( 1076 iLabelWidget *button = addChildFlags_Widget(
1072 d->footerButtons, 1077 d->footerButtons,
@@ -1079,6 +1084,7 @@ static void makeFooterButtons_DocumentWidget_(iDocumentWidget *d, const iMenuIte
1079 addChild_Widget(as_Widget(d), iClob(d->footerButtons)); 1084 addChild_Widget(as_Widget(d), iClob(d->footerButtons));
1080 arrange_Widget(d->footerButtons); 1085 arrange_Widget(d->footerButtons);
1081 arrange_Widget(w); 1086 arrange_Widget(w);
1087 updateVisible_DocumentWidget_(d); /* final placement for the buttons */
1082} 1088}
1083 1089
1084static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode code, 1090static void showErrorPage_DocumentWidget_(iDocumentWidget *d, enum iGmStatusCode code,
@@ -1222,7 +1228,7 @@ static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool
1222 } 1228 }
1223 if (d->sourceGempub) { 1229 if (d->sourceGempub) {
1224 if (equal_String(d->mod.url, coverPageUrl_Gempub(d->sourceGempub))) { 1230 if (equal_String(d->mod.url, coverPageUrl_Gempub(d->sourceGempub))) {
1225 if (equalCase_Rangecc(urlScheme_String(d->mod.url), "file")) { 1231 if (!isRemote_Gempub(d->sourceGempub)) {
1226 iArray *items = collectNew_Array(sizeof(iMenuItem)); 1232 iArray *items = collectNew_Array(sizeof(iMenuItem));
1227 pushBack_Array( 1233 pushBack_Array(
1228 items, 1234 items,
@@ -1244,6 +1250,19 @@ static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool
1244 } 1250 }
1245 makeFooterButtons_DocumentWidget_(d, constData_Array(items), size_Array(items)); 1251 makeFooterButtons_DocumentWidget_(d, constData_Array(items), size_Array(items));
1246 } 1252 }
1253 else {
1254 makeFooterButtons_DocumentWidget_(
1255 d,
1256 (iMenuItem[]){ { book_Icon " ${menu.save.downloads.open}",
1257 SDLK_s,
1258 KMOD_PRIMARY | KMOD_SHIFT,
1259 "document.save open:1" },
1260 { download_Icon " " saveToDownloads_Label,
1261 SDLK_s,
1262 KMOD_PRIMARY,
1263 "document.save" } },
1264 2);
1265 }
1247 if (preloadCoverImage_Gempub(d->sourceGempub, d->doc)) { 1266 if (preloadCoverImage_Gempub(d->sourceGempub, d->doc)) {
1248 redoLayout_GmDocument(d->doc); 1267 redoLayout_GmDocument(d->doc);
1249 updateVisible_DocumentWidget_(d); 1268 updateVisible_DocumentWidget_(d);
@@ -2048,7 +2067,8 @@ static iBool fetchNextUnfetchedImage_DocumentWidget_(iDocumentWidget *d) {
2048 return iFalse; 2067 return iFalse;
2049} 2068}
2050 2069
2051static void saveToDownloads_(const iString *url, const iString *mime, const iBlock *content) { 2070static const iString *saveToDownloads_(const iString *url, const iString *mime, const iBlock *content,
2071 iBool showDialog) {
2052 const iString *savePath = downloadPathForUrl_App(url, mime); 2072 const iString *savePath = downloadPathForUrl_App(url, mime);
2053 /* Write the file. */ { 2073 /* Write the file. */ {
2054 iFile *f = new_File(savePath); 2074 iFile *f = new_File(savePath);
@@ -2060,17 +2080,22 @@ static void saveToDownloads_(const iString *url, const iString *mime, const iBlo
2060#if defined (iPlatformAppleMobile) 2080#if defined (iPlatformAppleMobile)
2061 exportDownloadedFile_iOS(savePath); 2081 exportDownloadedFile_iOS(savePath);
2062#else 2082#else
2063 const iMenuItem items[2] = { 2083 if (showDialog) {
2064 { "${dlg.save.opendownload}", 0, 0, 2084 const iMenuItem items[2] = {
2065 format_CStr("!open url:%s", cstrCollect_String(makeFileUrl_String(savePath))) }, 2085 { "${dlg.save.opendownload}", 0, 0,
2066 { "${dlg.message.ok}", 0, 0, "message.ok" }, 2086 format_CStr("!open url:%s", cstrCollect_String(makeFileUrl_String(savePath))) },
2067 }; 2087 { "${dlg.message.ok}", 0, 0, "message.ok" },
2068 makeMessage_Widget(uiHeading_ColorEscape "${heading.save}", 2088 };
2069 format_CStr("%s\n${dlg.save.size} %.3f %s", cstr_String(path_File(f)), 2089 makeMessage_Widget(uiHeading_ColorEscape "${heading.save}",
2070 isMega ? size / 1.0e6f : (size / 1.0e3f), 2090 format_CStr("%s\n${dlg.save.size} %.3f %s",
2071 isMega ? "${mb}" : "${kb}"), 2091 cstr_String(path_File(f)),
2072 items, iElemCount(items)); 2092 isMega ? size / 1.0e6f : (size / 1.0e3f),
2093 isMega ? "${mb}" : "${kb}"),
2094 items,
2095 iElemCount(items));
2096 }
2073#endif 2097#endif
2098 return savePath;
2074 } 2099 }
2075 else { 2100 else {
2076 makeSimpleMessage_Widget(uiTextCaution_ColorEscape "${heading.save.error}", 2101 makeSimpleMessage_Widget(uiTextCaution_ColorEscape "${heading.save.error}",
@@ -2078,6 +2103,7 @@ static void saveToDownloads_(const iString *url, const iString *mime, const iBlo
2078 } 2103 }
2079 iRelease(f); 2104 iRelease(f);
2080 } 2105 }
2106 return collectNew_String();
2081} 2107}
2082 2108
2083static void addAllLinks_(void *context, const iGmRun *run) { 2109static void addAllLinks_(void *context, const iGmRun *run) {
@@ -2556,7 +2582,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2556 const iMediaRequest *media = findMediaRequest_DocumentWidget_(d, linkId); 2582 const iMediaRequest *media = findMediaRequest_DocumentWidget_(d, linkId);
2557 if (media) { 2583 if (media) {
2558 saveToDownloads_(url_GmRequest(media->req), meta_GmRequest(media->req), 2584 saveToDownloads_(url_GmRequest(media->req), meta_GmRequest(media->req),
2559 body_GmRequest(media->req)); 2585 body_GmRequest(media->req), iTrue);
2560 } 2586 }
2561 } 2587 }
2562 else if (equal_Command(cmd, "document.save") && document_App() == d) { 2588 else if (equal_Command(cmd, "document.save") && document_App() == d) {
@@ -2565,7 +2591,13 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2565 "${dlg.save.incomplete}"); 2591 "${dlg.save.incomplete}");
2566 } 2592 }
2567 else if (!isEmpty_Block(&d->sourceContent)) { 2593 else if (!isEmpty_Block(&d->sourceContent)) {
2568 saveToDownloads_(d->mod.url, &d->sourceMime, &d->sourceContent); 2594 const iBool doOpen = argLabel_Command(cmd, "open");
2595 const iString *savePath = saveToDownloads_(d->mod.url, &d->sourceMime,
2596 &d->sourceContent, !doOpen);
2597 if (!isEmpty_String(savePath) && doOpen) {
2598 postCommandf_Root(
2599 w->root, "!open url:%s", cstrCollect_String(makeFileUrl_String(savePath)));
2600 }
2569 } 2601 }
2570 return iTrue; 2602 return iTrue;
2571 } 2603 }
@@ -2675,6 +2707,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2675 return iTrue; 2707 return iTrue;
2676 } 2708 }
2677 else if (equal_Command(cmd, "scroll.bottom") && document_App() == d) { 2709 else if (equal_Command(cmd, "scroll.bottom") && document_App() == d) {
2710 updateScrollMax_DocumentWidget_(d); /* scrollY.max might not be fully updated */
2678 init_Anim(&d->scrollY.pos, d->scrollY.max); 2711 init_Anim(&d->scrollY.pos, d->scrollY.max);
2679 invalidate_VisBuf(d->visBuf); 2712 invalidate_VisBuf(d->visBuf);
2680 clampScroll_DocumentWidget_(d); 2713 clampScroll_DocumentWidget_(d);
@@ -3188,6 +3221,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
3188 iArray items; 3221 iArray items;
3189 init_Array(&items, sizeof(iMenuItem)); 3222 init_Array(&items, sizeof(iMenuItem));
3190 if (d->contextLink) { 3223 if (d->contextLink) {
3224 /* Context menu for a link. */
3191 const iString *linkUrl = linkUrl_GmDocument(d->doc, d->contextLink->linkId); 3225 const iString *linkUrl = linkUrl_GmDocument(d->doc, d->contextLink->linkId);
3192// const int linkFlags = linkFlags_GmDocument(d->doc, d->contextLink->linkId); 3226// const int linkFlags = linkFlags_GmDocument(d->doc, d->contextLink->linkId);
3193 const iRangecc scheme = urlScheme_String(linkUrl); 3227 const iRangecc scheme = urlScheme_String(linkUrl);