diff options
Diffstat (limited to 'src/ui/documentwidget.c')
-rw-r--r-- | src/ui/documentwidget.c | 483 |
1 files changed, 268 insertions, 215 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 93b2166b..ee669c1a 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c | |||
@@ -400,7 +400,18 @@ void init_DocumentWidget(iDocumentWidget *d) { | |||
400 | addAction_Widget(w, navigateRoot_KeyShortcut, "navigate.root"); | 400 | addAction_Widget(w, navigateRoot_KeyShortcut, "navigate.root"); |
401 | } | 401 | } |
402 | 402 | ||
403 | void cancelAllRequests_DocumentWidget(iDocumentWidget *d) { | ||
404 | iForEach(ObjectList, i, d->media) { | ||
405 | iMediaRequest *mr = i.object; | ||
406 | cancel_GmRequest(mr->req); | ||
407 | } | ||
408 | if (d->request) { | ||
409 | cancel_GmRequest(d->request); | ||
410 | } | ||
411 | } | ||
412 | |||
403 | void deinit_DocumentWidget(iDocumentWidget *d) { | 413 | void deinit_DocumentWidget(iDocumentWidget *d) { |
414 | cancelAllRequests_DocumentWidget(d); | ||
404 | pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); | 415 | pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue); |
405 | removeTicker_App(animate_DocumentWidget_, d); | 416 | removeTicker_App(animate_DocumentWidget_, d); |
406 | removeTicker_App(prerender_DocumentWidget_, d); | 417 | removeTicker_App(prerender_DocumentWidget_, d); |
@@ -496,7 +507,8 @@ static int documentWidth_DocumentWidget_(const iDocumentWidget *d) { | |||
496 | -1.0f, 10.0f); /* adapt to width */ | 507 | -1.0f, 10.0f); /* adapt to width */ |
497 | //printf("%f\n", adjust); fflush(stdout); | 508 | //printf("%f\n", adjust); fflush(stdout); |
498 | return iMini(iMax(minWidth, bounds.size.x - gap_UI * (d->pageMargin + adjust) * 2), | 509 | return iMini(iMax(minWidth, bounds.size.x - gap_UI * (d->pageMargin + adjust) * 2), |
499 | fontSize_UI * prefs->lineWidth * prefs->zoomPercent / 100); | 510 | fontSize_UI * //emRatio_Text(paragraph_FontId) * /* dependent on avg. glyph width */ |
511 | prefs->lineWidth * prefs->zoomPercent / 100); | ||
500 | } | 512 | } |
501 | 513 | ||
502 | static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { | 514 | static iRect documentBounds_DocumentWidget_(const iDocumentWidget *d) { |
@@ -563,7 +575,8 @@ static void addVisible_DocumentWidget_(void *context, const iGmRun *run) { | |||
563 | pushBack_PtrArray(&d->visibleWideRuns, run); | 575 | pushBack_PtrArray(&d->visibleWideRuns, run); |
564 | } | 576 | } |
565 | } | 577 | } |
566 | if (run->mediaType == audio_GmRunMediaType || run->mediaType == download_GmRunMediaType) { | 578 | /* Image runs are static so they're drawn as part of the content. */ |
579 | if (run->mediaType && run->mediaType != image_MediaType) { | ||
567 | iAssert(run->mediaId); | 580 | iAssert(run->mediaId); |
568 | pushBack_PtrArray(&d->visibleMedia, run); | 581 | pushBack_PtrArray(&d->visibleMedia, run); |
569 | } | 582 | } |
@@ -757,14 +770,14 @@ static uint32_t mediaUpdateInterval_DocumentWidget_(const iDocumentWidget *d) { | |||
757 | uint32_t interval = invalidInterval_; | 770 | uint32_t interval = invalidInterval_; |
758 | iConstForEach(PtrArray, i, &d->visibleMedia) { | 771 | iConstForEach(PtrArray, i, &d->visibleMedia) { |
759 | const iGmRun *run = i.ptr; | 772 | const iGmRun *run = i.ptr; |
760 | if (run->mediaType == audio_GmRunMediaType) { | 773 | if (run->mediaType == audio_MediaType) { |
761 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), run->mediaId); | 774 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)); |
762 | if (flags_Player(plr) & adjustingVolume_PlayerFlag || | 775 | if (flags_Player(plr) & adjustingVolume_PlayerFlag || |
763 | (isStarted_Player(plr) && !isPaused_Player(plr))) { | 776 | (isStarted_Player(plr) && !isPaused_Player(plr))) { |
764 | interval = iMin(interval, 1000 / 15); | 777 | interval = iMin(interval, 1000 / 15); |
765 | } | 778 | } |
766 | } | 779 | } |
767 | else if (run->mediaType == download_GmRunMediaType) { | 780 | else if (run->mediaType == download_MediaType) { |
768 | interval = iMin(interval, 1000); | 781 | interval = iMin(interval, 1000); |
769 | } | 782 | } |
770 | } | 783 | } |
@@ -783,8 +796,8 @@ static void updateMedia_DocumentWidget_(iDocumentWidget *d) { | |||
783 | refresh_Widget(d); | 796 | refresh_Widget(d); |
784 | iConstForEach(PtrArray, i, &d->visibleMedia) { | 797 | iConstForEach(PtrArray, i, &d->visibleMedia) { |
785 | const iGmRun *run = i.ptr; | 798 | const iGmRun *run = i.ptr; |
786 | if (run->mediaType == audio_GmRunMediaType) { | 799 | if (run->mediaType == audio_MediaType) { |
787 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), run->mediaId); | 800 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)); |
788 | if (idleTimeMs_Player(plr) > 3000 && ~flags_Player(plr) & volumeGrabbed_PlayerFlag && | 801 | if (idleTimeMs_Player(plr) > 3000 && ~flags_Player(plr) & volumeGrabbed_PlayerFlag && |
789 | flags_Player(plr) & adjustingVolume_PlayerFlag) { | 802 | flags_Player(plr) & adjustingVolume_PlayerFlag) { |
790 | setFlags_Player(plr, adjustingVolume_PlayerFlag, iFalse); | 803 | setFlags_Player(plr, adjustingVolume_PlayerFlag, iFalse); |
@@ -928,8 +941,9 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) { | |||
928 | pushBackCStr_StringArray(title, "Lagrange"); | 941 | pushBackCStr_StringArray(title, "Lagrange"); |
929 | } | 942 | } |
930 | /* Take away parts if it doesn't fit. */ | 943 | /* Take away parts if it doesn't fit. */ |
931 | const int avail = bounds_Widget(as_Widget(tabButton)).size.x - 3 * gap_UI; | 944 | const int avail = bounds_Widget(as_Widget(tabButton)).size.x - 3 * gap_UI; |
932 | iBool setWindow = (document_App() == d && isUnderKeyRoot_Widget(d)); | 945 | iBool setWindow = (document_App() == d && isUnderKeyRoot_Widget(d)); |
946 | const int font = uiLabel_FontId; | ||
933 | for (;;) { | 947 | for (;;) { |
934 | iString *text = collect_String(joinCStr_StringArray(title, " \u2014 ")); | 948 | iString *text = collect_String(joinCStr_StringArray(title, " \u2014 ")); |
935 | if (setWindow) { | 949 | if (setWindow) { |
@@ -945,7 +959,7 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) { | |||
945 | prependChar_String(text, siteIcon); | 959 | prependChar_String(text, siteIcon); |
946 | prependCStr_String(text, escape_Color(uiIcon_ColorId)); | 960 | prependCStr_String(text, escape_Color(uiIcon_ColorId)); |
947 | } | 961 | } |
948 | const int width = measureRange_Text(default_FontId, range_String(text)).advance.x; | 962 | const int width = measureRange_Text(font, range_String(text)).advance.x; |
949 | if (width <= avail || | 963 | if (width <= avail || |
950 | isEmpty_StringArray(title)) { | 964 | isEmpty_StringArray(title)) { |
951 | updateText_LabelWidget(tabButton, text); | 965 | updateText_LabelWidget(tabButton, text); |
@@ -954,9 +968,9 @@ static void updateWindowTitle_DocumentWidget_(const iDocumentWidget *d) { | |||
954 | if (size_StringArray(title) == 1) { | 968 | if (size_StringArray(title) == 1) { |
955 | /* Just truncate to fit. */ | 969 | /* Just truncate to fit. */ |
956 | const char *endPos; | 970 | const char *endPos; |
957 | tryAdvanceNoWrap_Text(default_FontId, | 971 | tryAdvanceNoWrap_Text(font, |
958 | range_String(text), | 972 | range_String(text), |
959 | avail - measure_Text(default_FontId, "...").advance.x, | 973 | avail - measure_Text(font, "...").advance.x, |
960 | &endPos); | 974 | &endPos); |
961 | updateText_LabelWidget( | 975 | updateText_LabelWidget( |
962 | tabButton, | 976 | tabButton, |
@@ -1242,6 +1256,9 @@ static const char *zipPageHeading_(const iRangecc mime) { | |||
1242 | if (equalCase_Rangecc(mime, "application/gpub+zip")) { | 1256 | if (equalCase_Rangecc(mime, "application/gpub+zip")) { |
1243 | return book_Icon " Gempub"; | 1257 | return book_Icon " Gempub"; |
1244 | } | 1258 | } |
1259 | else if (equalCase_Rangecc(mime, mimeType_FontPack)) { | ||
1260 | return "\U0001f520 Fontpack"; | ||
1261 | } | ||
1245 | iRangecc type = iNullRange; | 1262 | iRangecc type = iNullRange; |
1246 | nextSplit_Rangecc(mime, "/", &type); /* skip the part before the slash */ | 1263 | nextSplit_Rangecc(mime, "/", &type); /* skip the part before the slash */ |
1247 | nextSplit_Rangecc(mime, "/", &type); | 1264 | nextSplit_Rangecc(mime, "/", &type); |
@@ -1256,165 +1273,175 @@ static const char *zipPageHeading_(const iRangecc mime) { | |||
1256 | 1273 | ||
1257 | static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool isCached) { | 1274 | static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool isCached) { |
1258 | iWidget *w = as_Widget(d); | 1275 | iWidget *w = as_Widget(d); |
1259 | delete_Gempub(d->sourceGempub); | 1276 | /* Gempub page behavior and footer actions. */ { |
1260 | d->sourceGempub = NULL; | 1277 | /* TODO: move this to gempub.c */ |
1261 | if (!cmpCase_String(&d->sourceMime, "application/octet-stream") || | 1278 | delete_Gempub(d->sourceGempub); |
1262 | !cmpCase_String(&d->sourceMime, mimeType_Gempub) || | 1279 | d->sourceGempub = NULL; |
1263 | endsWithCase_String(d->mod.url, ".gpub")) { | 1280 | if (!cmpCase_String(&d->sourceMime, "application/octet-stream") || |
1264 | iGempub *gempub = new_Gempub(); | 1281 | !cmpCase_String(&d->sourceMime, mimeType_Gempub) || |
1265 | if (open_Gempub(gempub, &d->sourceContent)) { | 1282 | endsWithCase_String(d->mod.url, ".gpub")) { |
1266 | setBaseUrl_Gempub(gempub, d->mod.url); | ||
1267 | setSource_DocumentWidget(d, collect_String(coverPageSource_Gempub(gempub))); | ||
1268 | setCStr_String(&d->sourceMime, mimeType_Gempub); | ||
1269 | d->sourceGempub = gempub; | ||
1270 | } | ||
1271 | else { | ||
1272 | delete_Gempub(gempub); | ||
1273 | } | ||
1274 | } | ||
1275 | if (!d->sourceGempub) { | ||
1276 | const iString *localPath = collect_String(localFilePathFromUrl_String(d->mod.url)); | ||
1277 | iBool isInside = iFalse; | ||
1278 | if (localPath && !fileExists_FileInfo(localPath)) { | ||
1279 | /* This URL may refer to a file inside the archive. */ | ||
1280 | localPath = findContainerArchive_Path(localPath); | ||
1281 | isInside = iTrue; | ||
1282 | } | ||
1283 | if (localPath && equal_CStr(mediaType_Path(localPath), "application/gpub+zip")) { | ||
1284 | iGempub *gempub = new_Gempub(); | 1283 | iGempub *gempub = new_Gempub(); |
1285 | if (openFile_Gempub(gempub, localPath)) { | 1284 | if (open_Gempub(gempub, &d->sourceContent)) { |
1286 | setBaseUrl_Gempub(gempub, collect_String(makeFileUrl_String(localPath))); | 1285 | setBaseUrl_Gempub(gempub, d->mod.url); |
1287 | if (!isInside) { | 1286 | setSource_DocumentWidget(d, collect_String(coverPageSource_Gempub(gempub))); |
1288 | setSource_DocumentWidget(d, collect_String(coverPageSource_Gempub(gempub))); | 1287 | setCStr_String(&d->sourceMime, mimeType_Gempub); |
1289 | setCStr_String(&d->sourceMime, mimeType_Gempub); | ||
1290 | } | ||
1291 | d->sourceGempub = gempub; | 1288 | d->sourceGempub = gempub; |
1292 | } | 1289 | } |
1293 | else { | 1290 | else { |
1294 | delete_Gempub(gempub); | 1291 | delete_Gempub(gempub); |
1295 | } | 1292 | } |
1296 | } | 1293 | } |
1297 | } | 1294 | if (!d->sourceGempub) { |
1298 | if (d->sourceGempub) { | 1295 | const iString *localPath = collect_String(localFilePathFromUrl_String(d->mod.url)); |
1299 | if (equal_String(d->mod.url, coverPageUrl_Gempub(d->sourceGempub))) { | 1296 | iBool isInside = iFalse; |
1300 | if (!isRemote_Gempub(d->sourceGempub)) { | 1297 | if (localPath && !fileExists_FileInfo(localPath)) { |
1301 | iArray *items = collectNew_Array(sizeof(iMenuItem)); | 1298 | /* This URL may refer to a file inside the archive. */ |
1302 | pushBack_Array( | 1299 | localPath = findContainerArchive_Path(localPath); |
1303 | items, | 1300 | isInside = iTrue; |
1304 | &(iMenuItem){ book_Icon " ${gempub.cover.view}", | ||
1305 | 0, | ||
1306 | 0, | ||
1307 | format_CStr("!open url:%s", | ||
1308 | cstr_String(indexPageUrl_Gempub(d->sourceGempub))) }); | ||
1309 | if (navSize_Gempub(d->sourceGempub) > 0) { | ||
1310 | pushBack_Array( | ||
1311 | items, | ||
1312 | &(iMenuItem){ | ||
1313 | format_CStr(forwardArrow_Icon " %s", | ||
1314 | cstr_String(navLinkLabel_Gempub(d->sourceGempub, 0))), | ||
1315 | SDLK_RIGHT, | ||
1316 | 0, | ||
1317 | format_CStr("!open url:%s", | ||
1318 | cstr_String(navLinkUrl_Gempub(d->sourceGempub, 0))) }); | ||
1319 | } | ||
1320 | makeFooterButtons_DocumentWidget_(d, constData_Array(items), size_Array(items)); | ||
1321 | } | 1301 | } |
1322 | else { | 1302 | if (localPath && equal_CStr(mediaType_Path(localPath), mimeType_Gempub)) { |
1323 | makeFooterButtons_DocumentWidget_( | 1303 | iGempub *gempub = new_Gempub(); |
1324 | d, | 1304 | if (openFile_Gempub(gempub, localPath)) { |
1325 | (iMenuItem[]){ { book_Icon " ${menu.save.downloads.open}", | 1305 | setBaseUrl_Gempub(gempub, collect_String(makeFileUrl_String(localPath))); |
1326 | SDLK_s, | 1306 | if (!isInside) { |
1327 | KMOD_PRIMARY | KMOD_SHIFT, | 1307 | setSource_DocumentWidget(d, collect_String(coverPageSource_Gempub(gempub))); |
1328 | "document.save open:1" }, | 1308 | setCStr_String(&d->sourceMime, mimeType_Gempub); |
1329 | { download_Icon " " saveToDownloads_Label, | 1309 | } |
1330 | SDLK_s, | 1310 | d->sourceGempub = gempub; |
1331 | KMOD_PRIMARY, | 1311 | } |
1332 | "document.save" } }, | 1312 | else { |
1333 | 2); | 1313 | delete_Gempub(gempub); |
1334 | } | 1314 | } |
1335 | if (preloadCoverImage_Gempub(d->sourceGempub, d->doc)) { | ||
1336 | redoLayout_GmDocument(d->doc); | ||
1337 | updateVisible_DocumentWidget_(d); | ||
1338 | invalidate_DocumentWidget_(d); | ||
1339 | } | 1315 | } |
1340 | } | 1316 | } |
1341 | else if (equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) { | 1317 | if (d->sourceGempub) { |
1342 | makeFooterButtons_DocumentWidget_( | 1318 | if (equal_String(d->mod.url, coverPageUrl_Gempub(d->sourceGempub))) { |
1343 | d, | 1319 | if (!isRemote_Gempub(d->sourceGempub)) { |
1344 | (iMenuItem[]){ { format_CStr(book_Icon " %s", | 1320 | iArray *items = collectNew_Array(sizeof(iMenuItem)); |
1345 | cstr_String(property_Gempub(d->sourceGempub, | ||
1346 | title_GempubProperty))), | ||
1347 | SDLK_LEFT, | ||
1348 | 0, | ||
1349 | format_CStr("!open url:%s", | ||
1350 | cstr_String(coverPageUrl_Gempub(d->sourceGempub))) } }, | ||
1351 | 1); | ||
1352 | } | ||
1353 | else { | ||
1354 | /* Navigation buttons. */ | ||
1355 | iArray *items = collectNew_Array(sizeof(iMenuItem)); | ||
1356 | const size_t navIndex = navIndex_Gempub(d->sourceGempub, d->mod.url); | ||
1357 | if (navIndex != iInvalidPos) { | ||
1358 | if (navIndex < navSize_Gempub(d->sourceGempub) - 1) { | ||
1359 | pushBack_Array( | 1321 | pushBack_Array( |
1360 | items, | 1322 | items, |
1361 | &(iMenuItem){ | 1323 | &(iMenuItem){ book_Icon " ${gempub.cover.view}", |
1362 | format_CStr(forwardArrow_Icon " %s", | 1324 | 0, |
1363 | cstr_String(navLinkLabel_Gempub(d->sourceGempub, navIndex + 1))), | 1325 | 0, |
1364 | SDLK_RIGHT, | 1326 | format_CStr("!open url:%s", |
1365 | 0, | 1327 | cstr_String(indexPageUrl_Gempub(d->sourceGempub))) }); |
1366 | format_CStr("!open url:%s", | 1328 | if (navSize_Gempub(d->sourceGempub) > 0) { |
1367 | cstr_String(navLinkUrl_Gempub(d->sourceGempub, navIndex + 1))) }); | 1329 | pushBack_Array( |
1330 | items, | ||
1331 | &(iMenuItem){ | ||
1332 | format_CStr(forwardArrow_Icon " %s", | ||
1333 | cstr_String(navLinkLabel_Gempub(d->sourceGempub, 0))), | ||
1334 | SDLK_RIGHT, | ||
1335 | 0, | ||
1336 | format_CStr("!open url:%s", | ||
1337 | cstr_String(navLinkUrl_Gempub(d->sourceGempub, 0))) }); | ||
1338 | } | ||
1339 | makeFooterButtons_DocumentWidget_(d, constData_Array(items), size_Array(items)); | ||
1368 | } | 1340 | } |
1369 | if (navIndex > 0) { | 1341 | else { |
1370 | pushBack_Array( | 1342 | makeFooterButtons_DocumentWidget_( |
1371 | items, | 1343 | d, |
1372 | &(iMenuItem){ | 1344 | (iMenuItem[]){ { book_Icon " ${menu.save.downloads.open}", |
1373 | format_CStr(backArrow_Icon " %s", | 1345 | SDLK_s, |
1374 | cstr_String(navLinkLabel_Gempub(d->sourceGempub, navIndex - 1))), | 1346 | KMOD_PRIMARY | KMOD_SHIFT, |
1375 | SDLK_LEFT, | 1347 | "document.save open:1" }, |
1376 | 0, | 1348 | { download_Icon " " saveToDownloads_Label, |
1377 | format_CStr("!open url:%s", | 1349 | SDLK_s, |
1378 | cstr_String(navLinkUrl_Gempub(d->sourceGempub, navIndex - 1))) }); | 1350 | KMOD_PRIMARY, |
1351 | "document.save" } }, | ||
1352 | 2); | ||
1379 | } | 1353 | } |
1380 | else if (!equalCase_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) { | 1354 | if (preloadCoverImage_Gempub(d->sourceGempub, d->doc)) { |
1381 | pushBack_Array( | 1355 | redoLayout_GmDocument(d->doc); |
1382 | items, | 1356 | updateVisible_DocumentWidget_(d); |
1383 | &(iMenuItem){ | 1357 | invalidate_DocumentWidget_(d); |
1384 | format_CStr(book_Icon " %s", | ||
1385 | cstr_String(property_Gempub(d->sourceGempub, title_GempubProperty))), | ||
1386 | SDLK_LEFT, | ||
1387 | 0, | ||
1388 | format_CStr("!open url:%s", | ||
1389 | cstr_String(coverPageUrl_Gempub(d->sourceGempub))) }); | ||
1390 | } | 1358 | } |
1391 | } | 1359 | } |
1392 | if (!isEmpty_Array(items)) { | 1360 | else if (equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) { |
1393 | makeFooterButtons_DocumentWidget_(d, constData_Array(items), size_Array(items)); | 1361 | makeFooterButtons_DocumentWidget_( |
1362 | d, | ||
1363 | (iMenuItem[]){ { format_CStr(book_Icon " %s", | ||
1364 | cstr_String(property_Gempub(d->sourceGempub, | ||
1365 | title_GempubProperty))), | ||
1366 | SDLK_LEFT, | ||
1367 | 0, | ||
1368 | format_CStr("!open url:%s", | ||
1369 | cstr_String(coverPageUrl_Gempub(d->sourceGempub))) } }, | ||
1370 | 1); | ||
1394 | } | 1371 | } |
1395 | } | 1372 | else { |
1396 | if (!isCached && prefs_App()->pinSplit && | 1373 | /* Navigation buttons. */ |
1397 | equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) { | 1374 | iArray *items = collectNew_Array(sizeof(iMenuItem)); |
1398 | const iString *navStart = navStartLinkUrl_Gempub(d->sourceGempub); | 1375 | const size_t navIndex = navIndex_Gempub(d->sourceGempub, d->mod.url); |
1399 | if (navStart) { | 1376 | if (navIndex != iInvalidPos) { |
1400 | iWindow *win = get_Window(); | 1377 | if (navIndex < navSize_Gempub(d->sourceGempub) - 1) { |
1401 | /* Auto-split to show index and the first navigation link. */ | 1378 | pushBack_Array( |
1402 | if (numRoots_Window(win) == 2) { | 1379 | items, |
1403 | /* This document is showing the index page. */ | 1380 | &(iMenuItem){ |
1404 | iRoot *other = otherRoot_Window(win, w->root); | 1381 | format_CStr(forwardArrow_Icon " %s", |
1405 | postCommandf_Root(other, "open url:%s", cstr_String(navStart)); | 1382 | cstr_String(navLinkLabel_Gempub(d->sourceGempub, navIndex + 1))), |
1406 | if (prefs_App()->pinSplit == 1 && w->root == win->roots[1]) { | 1383 | SDLK_RIGHT, |
1407 | /* On the wrong side. */ | 1384 | 0, |
1408 | postCommand_App("ui.split swap:1"); | 1385 | format_CStr("!open url:%s", |
1386 | cstr_String(navLinkUrl_Gempub(d->sourceGempub, navIndex + 1))) }); | ||
1387 | } | ||
1388 | if (navIndex > 0) { | ||
1389 | pushBack_Array( | ||
1390 | items, | ||
1391 | &(iMenuItem){ | ||
1392 | format_CStr(backArrow_Icon " %s", | ||
1393 | cstr_String(navLinkLabel_Gempub(d->sourceGempub, navIndex - 1))), | ||
1394 | SDLK_LEFT, | ||
1395 | 0, | ||
1396 | format_CStr("!open url:%s", | ||
1397 | cstr_String(navLinkUrl_Gempub(d->sourceGempub, navIndex - 1))) }); | ||
1398 | } | ||
1399 | else if (!equalCase_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) { | ||
1400 | pushBack_Array( | ||
1401 | items, | ||
1402 | &(iMenuItem){ | ||
1403 | format_CStr(book_Icon " %s", | ||
1404 | cstr_String(property_Gempub(d->sourceGempub, title_GempubProperty))), | ||
1405 | SDLK_LEFT, | ||
1406 | 0, | ||
1407 | format_CStr("!open url:%s", | ||
1408 | cstr_String(coverPageUrl_Gempub(d->sourceGempub))) }); | ||
1409 | } | 1409 | } |
1410 | } | 1410 | } |
1411 | else { | 1411 | if (!isEmpty_Array(items)) { |
1412 | postCommandf_App( | 1412 | makeFooterButtons_DocumentWidget_(d, constData_Array(items), size_Array(items)); |
1413 | "open newtab:%d url:%s", otherRoot_OpenTabFlag, cstr_String(navStart)); | 1413 | } |
1414 | } | ||
1415 | if (!isCached && prefs_App()->pinSplit && | ||
1416 | equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) { | ||
1417 | const iString *navStart = navStartLinkUrl_Gempub(d->sourceGempub); | ||
1418 | if (navStart) { | ||
1419 | iWindow *win = get_Window(); | ||
1420 | /* Auto-split to show index and the first navigation link. */ | ||
1421 | if (numRoots_Window(win) == 2) { | ||
1422 | /* This document is showing the index page. */ | ||
1423 | iRoot *other = otherRoot_Window(win, w->root); | ||
1424 | postCommandf_Root(other, "open url:%s", cstr_String(navStart)); | ||
1425 | if (prefs_App()->pinSplit == 1 && w->root == win->roots[1]) { | ||
1426 | /* On the wrong side. */ | ||
1427 | postCommand_App("ui.split swap:1"); | ||
1428 | } | ||
1429 | } | ||
1430 | else { | ||
1431 | postCommandf_App( | ||
1432 | "open newtab:%d url:%s", otherRoot_OpenTabFlag, cstr_String(navStart)); | ||
1433 | } | ||
1414 | } | 1434 | } |
1415 | } | 1435 | } |
1416 | } | 1436 | } |
1417 | } | 1437 | } |
1438 | /* Local fontpacks are automatically shown. */ | ||
1439 | if (preloadLocalFontpackForPreview_Fonts(d->doc)) { | ||
1440 | documentRunsInvalidated_DocumentWidget_(d); | ||
1441 | redoLayout_GmDocument(d->doc); | ||
1442 | updateVisible_DocumentWidget_(d); | ||
1443 | invalidate_DocumentWidget_(d); | ||
1444 | } | ||
1418 | } | 1445 | } |
1419 | 1446 | ||
1420 | static void updateDocument_DocumentWidget_(iDocumentWidget *d, | 1447 | static void updateDocument_DocumentWidget_(iDocumentWidget *d, |
@@ -1482,7 +1509,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, | |||
1482 | } | 1509 | } |
1483 | delete_String(localPath); | 1510 | delete_String(localPath); |
1484 | if (equalCase_Rangecc(urlScheme_String(d->mod.url), "file")) { | 1511 | if (equalCase_Rangecc(urlScheme_String(d->mod.url), "file")) { |
1485 | appendFormat_String(&str, "=> %s/ ${doc.archive.view}\n", | 1512 | appendFormat_String(&str, "=> %s/ " folder_Icon " ${doc.archive.view}\n", |
1486 | cstr_String(withSpacesEncoded_String(d->mod.url))); | 1513 | cstr_String(withSpacesEncoded_String(d->mod.url))); |
1487 | } | 1514 | } |
1488 | translate_Lang(&str); | 1515 | translate_Lang(&str); |
@@ -1619,7 +1646,7 @@ static void parseUser_DocumentWidget_(iDocumentWidget *d) { | |||
1619 | static void cacheRunGlyphs_(void *data, const iGmRun *run) { | 1646 | static void cacheRunGlyphs_(void *data, const iGmRun *run) { |
1620 | iUnused(data); | 1647 | iUnused(data); |
1621 | if (!isEmpty_Range(&run->text)) { | 1648 | if (!isEmpty_Range(&run->text)) { |
1622 | cache_Text(run->textParams.font, run->text); | 1649 | cache_Text(run->font, run->text); |
1623 | } | 1650 | } |
1624 | } | 1651 | } |
1625 | 1652 | ||
@@ -2087,7 +2114,7 @@ static iBool requestMedia_DocumentWidget_(iDocumentWidget *d, iGmLinkId linkId, | |||
2087 | } | 2114 | } |
2088 | 2115 | ||
2089 | static iBool isDownloadRequest_DocumentWidget(const iDocumentWidget *d, const iMediaRequest *req) { | 2116 | static iBool isDownloadRequest_DocumentWidget(const iDocumentWidget *d, const iMediaRequest *req) { |
2090 | return findLinkDownload_Media(constMedia_GmDocument(d->doc), req->linkId) != 0; | 2117 | return findMediaForLink_Media(constMedia_GmDocument(d->doc), req->linkId, download_MediaType).type != 0; |
2091 | } | 2118 | } |
2092 | 2119 | ||
2093 | static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | 2120 | static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { |
@@ -2172,7 +2199,7 @@ static void allocVisBuffer_DocumentWidget_(const iDocumentWidget *d) { | |||
2172 | static iBool fetchNextUnfetchedImage_DocumentWidget_(iDocumentWidget *d) { | 2199 | static iBool fetchNextUnfetchedImage_DocumentWidget_(iDocumentWidget *d) { |
2173 | iConstForEach(PtrArray, i, &d->visibleLinks) { | 2200 | iConstForEach(PtrArray, i, &d->visibleLinks) { |
2174 | const iGmRun *run = i.ptr; | 2201 | const iGmRun *run = i.ptr; |
2175 | if (run->linkId && run->mediaType == none_GmRunMediaType && | 2202 | if (run->linkId && run->mediaType == none_MediaType && |
2176 | ~run->flags & decoration_GmRunFlag) { | 2203 | ~run->flags & decoration_GmRunFlag) { |
2177 | const int linkFlags = linkFlags_GmDocument(d->doc, run->linkId); | 2204 | const int linkFlags = linkFlags_GmDocument(d->doc, run->linkId); |
2178 | if (isMediaLink_GmDocument(d->doc, run->linkId) && | 2205 | if (isMediaLink_GmDocument(d->doc, run->linkId) && |
@@ -2761,8 +2788,10 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2761 | else if (equalWidget_Command(cmd, w, "document.downloadlink")) { | 2788 | else if (equalWidget_Command(cmd, w, "document.downloadlink")) { |
2762 | if (d->contextLink) { | 2789 | if (d->contextLink) { |
2763 | const iGmLinkId linkId = d->contextLink->linkId; | 2790 | const iGmLinkId linkId = d->contextLink->linkId; |
2764 | setDownloadUrl_Media( | 2791 | setUrl_Media(media_GmDocument(d->doc), |
2765 | media_GmDocument(d->doc), linkId, linkUrl_GmDocument(d->doc, linkId)); | 2792 | linkId, |
2793 | download_MediaType, | ||
2794 | linkUrl_GmDocument(d->doc, linkId)); | ||
2766 | requestMedia_DocumentWidget_(d, linkId, iFalse /* no filters */); | 2795 | requestMedia_DocumentWidget_(d, linkId, iFalse /* no filters */); |
2767 | redoLayout_GmDocument(d->doc); /* inline downloader becomes visible */ | 2796 | redoLayout_GmDocument(d->doc); /* inline downloader becomes visible */ |
2768 | updateVisible_DocumentWidget_(d); | 2797 | updateVisible_DocumentWidget_(d); |
@@ -2872,7 +2901,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2872 | const iMedia * media = media_GmDocument(d->doc); | 2901 | const iMedia * media = media_GmDocument(d->doc); |
2873 | const size_t num = numAudio_Media(media); | 2902 | const size_t num = numAudio_Media(media); |
2874 | for (size_t id = 1; id <= num; id++) { | 2903 | for (size_t id = 1; id <= num; id++) { |
2875 | iPlayer *plr = audioPlayer_Media(media, id); | 2904 | iPlayer *plr = audioPlayer_Media(media, (iMediaId){ audio_MediaType, id }); |
2876 | if (plr != startedPlr) { | 2905 | if (plr != startedPlr) { |
2877 | setPaused_Player(plr, iTrue); | 2906 | setPaused_Player(plr, iTrue); |
2878 | } | 2907 | } |
@@ -3210,8 +3239,8 @@ static iRect runRect_DocumentWidget_(const iDocumentWidget *d, const iGmRun *run | |||
3210 | } | 3239 | } |
3211 | 3240 | ||
3212 | static void setGrabbedPlayer_DocumentWidget_(iDocumentWidget *d, const iGmRun *run) { | 3241 | static void setGrabbedPlayer_DocumentWidget_(iDocumentWidget *d, const iGmRun *run) { |
3213 | if (run && run->mediaType == audio_GmRunMediaType) { | 3242 | if (run && run->mediaType == audio_MediaType) { |
3214 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), run->mediaId); | 3243 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)); |
3215 | setFlags_Player(plr, volumeGrabbed_PlayerFlag, iTrue); | 3244 | setFlags_Player(plr, volumeGrabbed_PlayerFlag, iTrue); |
3216 | d->grabbedStartVolume = volume_Player(plr); | 3245 | d->grabbedStartVolume = volume_Player(plr); |
3217 | d->grabbedPlayer = run; | 3246 | d->grabbedPlayer = run; |
@@ -3219,7 +3248,7 @@ static void setGrabbedPlayer_DocumentWidget_(iDocumentWidget *d, const iGmRun *r | |||
3219 | } | 3248 | } |
3220 | else if (d->grabbedPlayer) { | 3249 | else if (d->grabbedPlayer) { |
3221 | setFlags_Player( | 3250 | setFlags_Player( |
3222 | audioPlayer_Media(media_GmDocument(d->doc), d->grabbedPlayer->mediaId), | 3251 | audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(d->grabbedPlayer)), |
3223 | volumeGrabbed_PlayerFlag, | 3252 | volumeGrabbed_PlayerFlag, |
3224 | iFalse); | 3253 | iFalse); |
3225 | d->grabbedPlayer = NULL; | 3254 | d->grabbedPlayer = NULL; |
@@ -3247,11 +3276,21 @@ static iBool processMediaEvents_DocumentWidget_(iDocumentWidget *d, const SDL_Ev | |||
3247 | const iInt2 mouse = init_I2(ev->button.x, ev->button.y); | 3276 | const iInt2 mouse = init_I2(ev->button.x, ev->button.y); |
3248 | iConstForEach(PtrArray, i, &d->visibleMedia) { | 3277 | iConstForEach(PtrArray, i, &d->visibleMedia) { |
3249 | const iGmRun *run = i.ptr; | 3278 | const iGmRun *run = i.ptr; |
3250 | if (run->mediaType != audio_GmRunMediaType) { | 3279 | if (run->mediaType == fontpack_MediaType) { |
3280 | iFontpackUI ui; | ||
3281 | init_FontpackUI(&ui, media_GmDocument(d->doc), run->mediaId, | ||
3282 | runRect_DocumentWidget_(d, run)); | ||
3283 | if (processEvent_FontpackUI(&ui, ev)) { | ||
3284 | refresh_Widget(d); | ||
3285 | return iTrue; | ||
3286 | } | ||
3287 | } | ||
3288 | if (run->mediaType != audio_MediaType) { | ||
3251 | continue; | 3289 | continue; |
3252 | } | 3290 | } |
3291 | /* TODO: move this to mediaui.c */ | ||
3253 | const iRect rect = runRect_DocumentWidget_(d, run); | 3292 | const iRect rect = runRect_DocumentWidget_(d, run); |
3254 | iPlayer * plr = audioPlayer_Media(media_GmDocument(d->doc), run->mediaId); | 3293 | iPlayer * plr = audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)); |
3255 | if (contains_Rect(rect, mouse)) { | 3294 | if (contains_Rect(rect, mouse)) { |
3256 | iPlayerUI ui; | 3295 | iPlayerUI ui; |
3257 | init_PlayerUI(&ui, plr, rect); | 3296 | init_PlayerUI(&ui, plr, rect); |
@@ -3439,7 +3478,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
3439 | return iTrue; | 3478 | return iTrue; |
3440 | } | 3479 | } |
3441 | break; | 3480 | break; |
3442 | #if 1 | 3481 | #if !defined (NDEBUG) |
3443 | case SDLK_KP_1: | 3482 | case SDLK_KP_1: |
3444 | case '`': { | 3483 | case '`': { |
3445 | iBlock *seed = new_Block(64); | 3484 | iBlock *seed = new_Block(64); |
@@ -3631,7 +3670,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
3631 | cstr_String(linkUrl)) }, | 3670 | cstr_String(linkUrl)) }, |
3632 | }, | 3671 | }, |
3633 | 3); | 3672 | 3); |
3634 | if (isNative && d->contextLink->mediaType != download_GmRunMediaType) { | 3673 | if (isNative && d->contextLink->mediaType != download_MediaType) { |
3635 | pushBackN_Array(&items, (iMenuItem[]){ | 3674 | pushBackN_Array(&items, (iMenuItem[]){ |
3636 | { "---" }, | 3675 | { "---" }, |
3637 | { download_Icon " ${link.download}", 0, 0, "document.downloadlink" }, | 3676 | { download_Icon " ${link.download}", 0, 0, "document.downloadlink" }, |
@@ -3639,7 +3678,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
3639 | } | 3678 | } |
3640 | iMediaRequest *mediaReq; | 3679 | iMediaRequest *mediaReq; |
3641 | if ((mediaReq = findMediaRequest_DocumentWidget_(d, d->contextLink->linkId)) != NULL && | 3680 | if ((mediaReq = findMediaRequest_DocumentWidget_(d, d->contextLink->linkId)) != NULL && |
3642 | d->contextLink->mediaType != download_GmRunMediaType) { | 3681 | d->contextLink->mediaType != download_MediaType) { |
3643 | if (isFinished_GmRequest(mediaReq->req)) { | 3682 | if (isFinished_GmRequest(mediaReq->req)) { |
3644 | pushBack_Array(&items, | 3683 | pushBack_Array(&items, |
3645 | &(iMenuItem){ download_Icon " " saveToDownloads_Label, | 3684 | &(iMenuItem){ download_Icon " " saveToDownloads_Label, |
@@ -3761,7 +3800,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
3761 | case drag_ClickResult: { | 3800 | case drag_ClickResult: { |
3762 | if (d->grabbedPlayer) { | 3801 | if (d->grabbedPlayer) { |
3763 | iPlayer *plr = | 3802 | iPlayer *plr = |
3764 | audioPlayer_Media(media_GmDocument(d->doc), d->grabbedPlayer->mediaId); | 3803 | audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(d->grabbedPlayer)); |
3765 | iPlayerUI ui; | 3804 | iPlayerUI ui; |
3766 | init_PlayerUI(&ui, plr, runRect_DocumentWidget_(d, d->grabbedPlayer)); | 3805 | init_PlayerUI(&ui, plr, runRect_DocumentWidget_(d, d->grabbedPlayer)); |
3767 | float off = (float) delta_Click(&d->click).x / (float) width_Rect(ui.volumeSlider); | 3806 | float off = (float) delta_Click(&d->click).x / (float) width_Rect(ui.volumeSlider); |
@@ -3887,8 +3926,9 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
3887 | } | 3926 | } |
3888 | if (d->hoverLink) { | 3927 | if (d->hoverLink) { |
3889 | /* TODO: Move this to a method. */ | 3928 | /* TODO: Move this to a method. */ |
3890 | const iGmLinkId linkId = d->hoverLink->linkId; | 3929 | const iGmLinkId linkId = d->hoverLink->linkId; |
3891 | const int linkFlags = linkFlags_GmDocument(d->doc, linkId); | 3930 | const iMediaId linkMedia = mediaId_GmRun(d->hoverLink); |
3931 | const int linkFlags = linkFlags_GmDocument(d->doc, linkId); | ||
3892 | iAssert(linkId); | 3932 | iAssert(linkId); |
3893 | /* Media links are opened inline by default. */ | 3933 | /* Media links are opened inline by default. */ |
3894 | if (isMediaLink_GmDocument(d->doc, linkId)) { | 3934 | if (isMediaLink_GmDocument(d->doc, linkId)) { |
@@ -3941,6 +3981,12 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
3941 | } | 3981 | } |
3942 | refresh_Widget(w); | 3982 | refresh_Widget(w); |
3943 | } | 3983 | } |
3984 | else if (linkMedia.type == download_MediaType || | ||
3985 | findMediaRequest_DocumentWidget_(d, linkId)) { | ||
3986 | /* TODO: What should be done when clicking on an inline download? | ||
3987 | Maybe dismiss if finished? */ | ||
3988 | return iTrue; | ||
3989 | } | ||
3944 | else if (linkFlags & supportedScheme_GmLinkFlag) { | 3990 | else if (linkFlags & supportedScheme_GmLinkFlag) { |
3945 | int tabMode = openTabMode_Sym(modState_Keys()); | 3991 | int tabMode = openTabMode_Sym(modState_Keys()); |
3946 | if (isPinned_DocumentWidget_(d)) { | 3992 | if (isPinned_DocumentWidget_(d)) { |
@@ -4026,7 +4072,7 @@ static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iCol | |||
4026 | contains_Range(&mark, run->text.start))) { | 4072 | contains_Range(&mark, run->text.start))) { |
4027 | int x = 0; | 4073 | int x = 0; |
4028 | if (!*isInside) { | 4074 | if (!*isInside) { |
4029 | x = measureRange_Text(run->textParams.font, | 4075 | x = measureRange_Text(run->font, |
4030 | (iRangecc){ run->text.start, iMax(run->text.start, mark.start) }) | 4076 | (iRangecc){ run->text.start, iMax(run->text.start, mark.start) }) |
4031 | .advance.x; | 4077 | .advance.x; |
4032 | } | 4078 | } |
@@ -4035,7 +4081,7 @@ static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iCol | |||
4035 | iRangecc mk = !*isInside ? mark | 4081 | iRangecc mk = !*isInside ? mark |
4036 | : (iRangecc){ run->text.start, iMax(run->text.start, mark.end) }; | 4082 | : (iRangecc){ run->text.start, iMax(run->text.start, mark.end) }; |
4037 | mk.start = iMax(mk.start, run->text.start); | 4083 | mk.start = iMax(mk.start, run->text.start); |
4038 | w = measureRange_Text(run->textParams.font, mk).advance.x; | 4084 | w = measureRange_Text(run->font, mk).advance.x; |
4039 | *isInside = iFalse; | 4085 | *isInside = iFalse; |
4040 | } | 4086 | } |
4041 | else { | 4087 | else { |
@@ -4074,7 +4120,7 @@ static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iCol | |||
4074 | 4120 | ||
4075 | static void drawMark_DrawContext_(void *context, const iGmRun *run) { | 4121 | static void drawMark_DrawContext_(void *context, const iGmRun *run) { |
4076 | iDrawContext *d = context; | 4122 | iDrawContext *d = context; |
4077 | if (run->mediaType == none_GmRunMediaType) { | 4123 | if (run->mediaType == none_MediaType) { |
4078 | fillRange_DrawContext_(d, run, uiMatching_ColorId, d->widget->foundMark, &d->inFoundMark); | 4124 | fillRange_DrawContext_(d, run, uiMatching_ColorId, d->widget->foundMark, &d->inFoundMark); |
4079 | fillRange_DrawContext_(d, run, uiMarked_ColorId, d->widget->selectMark, &d->inSelectMark); | 4125 | fillRange_DrawContext_(d, run, uiMarked_ColorId, d->widget->selectMark, &d->inSelectMark); |
4080 | } | 4126 | } |
@@ -4088,15 +4134,15 @@ static void drawBannerRun_DrawContext_(iDrawContext *d, const iGmRun *run, iInt2 | |||
4088 | iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2)); | 4134 | iInt2 bpos = add_I2(visPos, init_I2(0, lineHeight_Text(banner_FontId) / 2)); |
4089 | if (icon) { | 4135 | if (icon) { |
4090 | appendChar_String(&str, icon); | 4136 | appendChar_String(&str, icon); |
4091 | const iRect iconRect = visualBounds_Text(run->textParams.font, range_String(&str)); | 4137 | const iRect iconRect = visualBounds_Text(run->font, range_String(&str)); |
4092 | drawRange_Text( | 4138 | drawRange_Text( |
4093 | run->textParams.font, | 4139 | run->font, |
4094 | addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->textParams.font) / 2), | 4140 | addY_I2(bpos, -mid_Rect(iconRect).y + lineHeight_Text(run->font) / 2), |
4095 | tmBannerIcon_ColorId, | 4141 | tmBannerIcon_ColorId, |
4096 | range_String(&str)); | 4142 | range_String(&str)); |
4097 | bpos.x += right_Rect(iconRect) + 3 * gap_Text; | 4143 | bpos.x += right_Rect(iconRect) + 3 * gap_Text; |
4098 | } | 4144 | } |
4099 | drawRange_Text(run->textParams.font, | 4145 | drawRange_Text(run->font, |
4100 | bpos, | 4146 | bpos, |
4101 | tmBannerTitle_ColorId, | 4147 | tmBannerTitle_ColorId, |
4102 | bannerText_DocumentWidget_(d->widget)); | 4148 | bannerText_DocumentWidget_(d->widget)); |
@@ -4181,8 +4227,8 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4181 | d->runsDrawn.end = run; | 4227 | d->runsDrawn.end = run; |
4182 | } | 4228 | } |
4183 | } | 4229 | } |
4184 | if (run->mediaType == image_GmRunMediaType) { | 4230 | if (run->mediaType == image_MediaType) { |
4185 | SDL_Texture *tex = imageTexture_Media(media_GmDocument(d->widget->doc), run->mediaId); | 4231 | SDL_Texture *tex = imageTexture_Media(media_GmDocument(d->widget->doc), mediaId_GmRun(run)); |
4186 | const iRect dst = moved_Rect(run->visBounds, origin); | 4232 | const iRect dst = moved_Rect(run->visBounds, origin); |
4187 | if (tex) { | 4233 | if (tex) { |
4188 | fillRect_Paint(&d->paint, dst, tmBackground_ColorId); /* in case the image has alpha */ | 4234 | fillRect_Paint(&d->paint, dst, tmBackground_ColorId); /* in case the image has alpha */ |
@@ -4203,7 +4249,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4203 | /* Media UIs are drawn afterwards as a dynamic overlay. */ | 4249 | /* Media UIs are drawn afterwards as a dynamic overlay. */ |
4204 | return; | 4250 | return; |
4205 | } | 4251 | } |
4206 | enum iColorId fg = run->textParams.color; | 4252 | enum iColorId fg = run->color; |
4207 | const iGmDocument *doc = d->widget->doc; | 4253 | const iGmDocument *doc = d->widget->doc; |
4208 | const int linkFlags = linkFlags_GmDocument(doc, run->linkId); | 4254 | const int linkFlags = linkFlags_GmDocument(doc, run->linkId); |
4209 | /* Hover state of a link. */ | 4255 | /* Hover state of a link. */ |
@@ -4270,10 +4316,10 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4270 | const iInt2 margin = preRunMargin_GmDocument(doc, run->preId); | 4316 | const iInt2 margin = preRunMargin_GmDocument(doc, run->preId); |
4271 | fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackgroundAltText_ColorId); | 4317 | fillRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmBackgroundAltText_ColorId); |
4272 | drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmQuoteIcon_ColorId); | 4318 | drawRect_Paint(&d->paint, (iRect){ visPos, run->visBounds.size }, tmQuoteIcon_ColorId); |
4273 | drawWrapRange_Text(run->textParams.font, | 4319 | drawWrapRange_Text(run->font, |
4274 | add_I2(visPos, margin), | 4320 | add_I2(visPos, margin), |
4275 | run->visBounds.size.x - 2 * margin.x, | 4321 | run->visBounds.size.x - 2 * margin.x, |
4276 | run->textParams.color, | 4322 | run->color, |
4277 | run->text); | 4323 | run->text); |
4278 | } | 4324 | } |
4279 | else if (run->flags & siteBanner_GmRunFlag) { | 4325 | else if (run->flags & siteBanner_GmRunFlag) { |
@@ -4292,17 +4338,17 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4292 | linkOrdinalChar_DocumentWidget_(d->widget, ord - d->widget->ordinalBase); | 4338 | linkOrdinalChar_DocumentWidget_(d->widget, ord - d->widget->ordinalBase); |
4293 | if (ordChar) { | 4339 | if (ordChar) { |
4294 | const char *circle = "\u25ef"; /* Large Circle */ | 4340 | const char *circle = "\u25ef"; /* Large Circle */ |
4295 | const int circleFont = defaultContentRegular_FontId; | 4341 | const int circleFont = FONT_ID(default_FontId, regular_FontStyle, contentRegular_FontSize); |
4296 | iRect nbArea = { init_I2(d->viewPos.x - gap_UI / 3, visPos.y), | 4342 | iRect nbArea = { init_I2(d->viewPos.x - gap_UI / 3, visPos.y), |
4297 | init_I2(3.95f * gap_Text, 1.0f * lineHeight_Text(circleFont)) }; | 4343 | init_I2(3.95f * gap_Text, 1.0f * lineHeight_Text(circleFont)) }; |
4298 | drawRange_Text( | 4344 | drawRange_Text( |
4299 | circleFont, topLeft_Rect(nbArea), tmQuote_ColorId, range_CStr(circle)); | 4345 | circleFont, topLeft_Rect(nbArea), tmQuote_ColorId, range_CStr(circle)); |
4300 | iRect circleArea = visualBounds_Text(circleFont, range_CStr(circle)); | 4346 | iRect circleArea = visualBounds_Text(circleFont, range_CStr(circle)); |
4301 | addv_I2(&circleArea.pos, topLeft_Rect(nbArea)); | 4347 | addv_I2(&circleArea.pos, topLeft_Rect(nbArea)); |
4302 | drawCentered_Text(defaultContentSmall_FontId, | 4348 | drawCentered_Text(FONT_ID(default_FontId, regular_FontStyle, contentSmall_FontSize), |
4303 | circleArea, | 4349 | circleArea, |
4304 | iTrue, | 4350 | iTrue, |
4305 | tmQuote_ColorId, | 4351 | tmQuote_ColorId, |
4306 | "%lc", | 4352 | "%lc", |
4307 | (int) ordChar); | 4353 | (int) ordChar); |
4308 | goto runDrawn; | 4354 | goto runDrawn; |
@@ -4312,15 +4358,15 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4312 | if (run->flags & quoteBorder_GmRunFlag) { | 4358 | if (run->flags & quoteBorder_GmRunFlag) { |
4313 | drawVLine_Paint(&d->paint, | 4359 | drawVLine_Paint(&d->paint, |
4314 | addX_I2(visPos, | 4360 | addX_I2(visPos, |
4315 | !run->textParams.isRTL | 4361 | !run->isRTL |
4316 | ? -gap_Text * 5 / 2 | 4362 | ? -gap_Text * 5 / 2 |
4317 | : (width_Rect(run->visBounds) + gap_Text * 5 / 2)), | 4363 | : (width_Rect(run->visBounds) + gap_Text * 5 / 2)), |
4318 | height_Rect(run->visBounds), | 4364 | height_Rect(run->visBounds), |
4319 | tmQuoteIcon_ColorId); | 4365 | tmQuoteIcon_ColorId); |
4320 | } | 4366 | } |
4321 | drawBoundRange_Text(run->textParams.font, | 4367 | drawBoundRange_Text(run->font, |
4322 | visPos, | 4368 | visPos, |
4323 | (run->textParams.isRTL ? -1 : 1) * width_Rect(run->visBounds), | 4369 | (run->isRTL ? -1 : 1) * width_Rect(run->visBounds), |
4324 | fg, | 4370 | fg, |
4325 | run->text); | 4371 | run->text); |
4326 | runDrawn:; | 4372 | runDrawn:; |
@@ -4337,31 +4383,31 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4337 | fg = linkColor_GmDocument(doc, run->linkId, textHover_GmLinkPart); | 4383 | fg = linkColor_GmDocument(doc, run->linkId, textHover_GmLinkPart); |
4338 | iString text; | 4384 | iString text; |
4339 | init_String(&text); | 4385 | init_String(&text); |
4340 | iMediaId imageId = linkImage_GmDocument(doc, run->linkId); | 4386 | const iMediaId linkMedia = findMediaForLink_Media(constMedia_GmDocument(doc), |
4341 | iMediaId audioId = !imageId ? linkAudio_GmDocument(doc, run->linkId) : 0; | 4387 | run->linkId, none_MediaType); |
4342 | iMediaId downloadId = !imageId && !audioId ? | 4388 | iAssert(linkMedia.type != none_MediaType); |
4343 | findLinkDownload_Media(constMedia_GmDocument(doc), run->linkId) : 0; | 4389 | iGmMediaInfo info; |
4344 | iAssert(imageId || audioId || downloadId); | 4390 | info_Media(constMedia_GmDocument(doc), linkMedia, &info); |
4345 | if (imageId) { | 4391 | switch (linkMedia.type) { |
4346 | iAssert(!isEmpty_Rect(run->bounds)); | 4392 | case image_MediaType: { |
4347 | iGmMediaInfo info; | 4393 | iAssert(!isEmpty_Rect(run->bounds)); |
4348 | imageInfo_Media(constMedia_GmDocument(doc), imageId, &info); | 4394 | const iInt2 imgSize = imageSize_Media(constMedia_GmDocument(doc), linkMedia); |
4349 | const iInt2 imgSize = imageSize_Media(constMedia_GmDocument(doc), imageId); | 4395 | format_String(&text, "%s \u2014 %d x %d \u2014 %.1f%s", |
4350 | format_String(&text, "%s \u2014 %d x %d \u2014 %.1f%s", | 4396 | info.type, imgSize.x, imgSize.y, info.numBytes / 1.0e6f, |
4351 | info.type, imgSize.x, imgSize.y, info.numBytes / 1.0e6f, | 4397 | cstr_Lang("mb")); |
4352 | cstr_Lang("mb")); | 4398 | break; |
4353 | } | 4399 | } |
4354 | else if (audioId) { | 4400 | case audio_MediaType: |
4355 | iGmMediaInfo info; | 4401 | format_String(&text, "%s", info.type); |
4356 | audioInfo_Media(constMedia_GmDocument(doc), audioId, &info); | 4402 | break; |
4357 | format_String(&text, "%s", info.type); | 4403 | case download_MediaType: |
4358 | } | 4404 | format_String(&text, "%s", info.type); |
4359 | else if (downloadId) { | 4405 | break; |
4360 | iGmMediaInfo info; | 4406 | default: |
4361 | downloadInfo_Media(constMedia_GmDocument(doc), downloadId, &info); | 4407 | break; |
4362 | format_String(&text, "%s", info.type); | ||
4363 | } | 4408 | } |
4364 | if (findMediaRequest_DocumentWidget_(d->widget, run->linkId)) { | 4409 | if (linkMedia.type != download_MediaType && /* can't cancel downloads currently */ |
4410 | findMediaRequest_DocumentWidget_(d->widget, run->linkId)) { | ||
4365 | appendFormat_String( | 4411 | appendFormat_String( |
4366 | &text, " %s" close_Icon, isHover ? escape_Color(tmLinkText_ColorId) : ""); | 4412 | &text, " %s" close_Icon, isHover ? escape_Color(tmLinkText_ColorId) : ""); |
4367 | } | 4413 | } |
@@ -4436,7 +4482,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4436 | append_String(&str, collect_String(format_Date(&date, "%b %d"))); | 4482 | append_String(&str, collect_String(format_Date(&date, "%b %d"))); |
4437 | } | 4483 | } |
4438 | if (!isEmpty_String(&str)) { | 4484 | if (!isEmpty_String(&str)) { |
4439 | if (run->textParams.isRTL) { | 4485 | if (run->isRTL) { |
4440 | appendCStr_String(&str, " \u2014 "); | 4486 | appendCStr_String(&str, " \u2014 "); |
4441 | } | 4487 | } |
4442 | else { | 4488 | else { |
@@ -4445,7 +4491,7 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4445 | const iInt2 textSize = measure_Text(metaFont, cstr_String(&str)).bounds.size; | 4491 | const iInt2 textSize = measure_Text(metaFont, cstr_String(&str)).bounds.size; |
4446 | int tx = topRight_Rect(linkRect).x; | 4492 | int tx = topRight_Rect(linkRect).x; |
4447 | const char *msg = cstr_String(&str); | 4493 | const char *msg = cstr_String(&str); |
4448 | if (run->textParams.isRTL) { | 4494 | if (run->isRTL) { |
4449 | tx = topLeft_Rect(linkRect).x - textSize.x; | 4495 | tx = topLeft_Rect(linkRect).x - textSize.x; |
4450 | } | 4496 | } |
4451 | if (tx + textSize.x > right_Rect(d->widgetBounds)) { | 4497 | if (tx + textSize.x > right_Rect(d->widgetBounds)) { |
@@ -4592,18 +4638,25 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { | |||
4592 | static void drawMedia_DocumentWidget_(const iDocumentWidget *d, iPaint *p) { | 4638 | static void drawMedia_DocumentWidget_(const iDocumentWidget *d, iPaint *p) { |
4593 | iConstForEach(PtrArray, i, &d->visibleMedia) { | 4639 | iConstForEach(PtrArray, i, &d->visibleMedia) { |
4594 | const iGmRun * run = i.ptr; | 4640 | const iGmRun * run = i.ptr; |
4595 | if (run->mediaType == audio_GmRunMediaType) { | 4641 | if (run->mediaType == audio_MediaType) { |
4596 | iPlayerUI ui; | 4642 | iPlayerUI ui; |
4597 | init_PlayerUI(&ui, | 4643 | init_PlayerUI(&ui, |
4598 | audioPlayer_Media(media_GmDocument(d->doc), run->mediaId), | 4644 | audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)), |
4599 | runRect_DocumentWidget_(d, run)); | 4645 | runRect_DocumentWidget_(d, run)); |
4600 | draw_PlayerUI(&ui, p); | 4646 | draw_PlayerUI(&ui, p); |
4601 | } | 4647 | } |
4602 | else if (run->mediaType == download_GmRunMediaType) { | 4648 | else if (run->mediaType == download_MediaType) { |
4603 | iDownloadUI ui; | 4649 | iDownloadUI ui; |
4604 | init_DownloadUI(&ui, d, run->mediaId, runRect_DocumentWidget_(d, run)); | 4650 | init_DownloadUI(&ui, constMedia_GmDocument(d->doc), run->mediaId, |
4651 | runRect_DocumentWidget_(d, run)); | ||
4605 | draw_DownloadUI(&ui, p); | 4652 | draw_DownloadUI(&ui, p); |
4606 | } | 4653 | } |
4654 | else if (run->mediaType == fontpack_MediaType) { | ||
4655 | iFontpackUI ui; | ||
4656 | init_FontpackUI(&ui, constMedia_GmDocument(d->doc), run->mediaId, | ||
4657 | runRect_DocumentWidget_(d, run)); | ||
4658 | draw_FontpackUI(&ui, p); | ||
4659 | } | ||
4607 | } | 4660 | } |
4608 | } | 4661 | } |
4609 | 4662 | ||
@@ -4914,7 +4967,7 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { | |||
4914 | } | 4967 | } |
4915 | /* Pinch zoom indicator. */ | 4968 | /* Pinch zoom indicator. */ |
4916 | if (d->flags & pinchZoom_DocumentWidgetFlag) { | 4969 | if (d->flags & pinchZoom_DocumentWidgetFlag) { |
4917 | const int font = defaultLargeBold_FontId; | 4970 | const int font = uiLabelLargeBold_FontId; |
4918 | const int height = lineHeight_Text(font) * 2; | 4971 | const int height = lineHeight_Text(font) * 2; |
4919 | const iInt2 size = init_I2(height * 2, height); | 4972 | const iInt2 size = init_I2(height * 2, height); |
4920 | const iRect rect = { sub_I2(mid_Rect(bounds), divi_I2(size, 2)), size }; | 4973 | const iRect rect = { sub_I2(mid_Rect(bounds), divi_I2(size, 2)), size }; |