diff options
Diffstat (limited to 'src/ui/documentwidget.c')
-rw-r--r-- | src/ui/documentwidget.c | 429 |
1 files changed, 240 insertions, 189 deletions
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 45a8cf2d..44db3e5b 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); |
@@ -564,7 +575,8 @@ static void addVisible_DocumentWidget_(void *context, const iGmRun *run) { | |||
564 | pushBack_PtrArray(&d->visibleWideRuns, run); | 575 | pushBack_PtrArray(&d->visibleWideRuns, run); |
565 | } | 576 | } |
566 | } | 577 | } |
567 | 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) { | ||
568 | iAssert(run->mediaId); | 580 | iAssert(run->mediaId); |
569 | pushBack_PtrArray(&d->visibleMedia, run); | 581 | pushBack_PtrArray(&d->visibleMedia, run); |
570 | } | 582 | } |
@@ -758,14 +770,14 @@ static uint32_t mediaUpdateInterval_DocumentWidget_(const iDocumentWidget *d) { | |||
758 | uint32_t interval = invalidInterval_; | 770 | uint32_t interval = invalidInterval_; |
759 | iConstForEach(PtrArray, i, &d->visibleMedia) { | 771 | iConstForEach(PtrArray, i, &d->visibleMedia) { |
760 | const iGmRun *run = i.ptr; | 772 | const iGmRun *run = i.ptr; |
761 | if (run->mediaType == audio_GmRunMediaType) { | 773 | if (run->mediaType == audio_MediaType) { |
762 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), run->mediaId); | 774 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)); |
763 | if (flags_Player(plr) & adjustingVolume_PlayerFlag || | 775 | if (flags_Player(plr) & adjustingVolume_PlayerFlag || |
764 | (isStarted_Player(plr) && !isPaused_Player(plr))) { | 776 | (isStarted_Player(plr) && !isPaused_Player(plr))) { |
765 | interval = iMin(interval, 1000 / 15); | 777 | interval = iMin(interval, 1000 / 15); |
766 | } | 778 | } |
767 | } | 779 | } |
768 | else if (run->mediaType == download_GmRunMediaType) { | 780 | else if (run->mediaType == download_MediaType) { |
769 | interval = iMin(interval, 1000); | 781 | interval = iMin(interval, 1000); |
770 | } | 782 | } |
771 | } | 783 | } |
@@ -784,8 +796,8 @@ static void updateMedia_DocumentWidget_(iDocumentWidget *d) { | |||
784 | refresh_Widget(d); | 796 | refresh_Widget(d); |
785 | iConstForEach(PtrArray, i, &d->visibleMedia) { | 797 | iConstForEach(PtrArray, i, &d->visibleMedia) { |
786 | const iGmRun *run = i.ptr; | 798 | const iGmRun *run = i.ptr; |
787 | if (run->mediaType == audio_GmRunMediaType) { | 799 | if (run->mediaType == audio_MediaType) { |
788 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), run->mediaId); | 800 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)); |
789 | if (idleTimeMs_Player(plr) > 3000 && ~flags_Player(plr) & volumeGrabbed_PlayerFlag && | 801 | if (idleTimeMs_Player(plr) > 3000 && ~flags_Player(plr) & volumeGrabbed_PlayerFlag && |
790 | flags_Player(plr) & adjustingVolume_PlayerFlag) { | 802 | flags_Player(plr) & adjustingVolume_PlayerFlag) { |
791 | setFlags_Player(plr, adjustingVolume_PlayerFlag, iFalse); | 803 | setFlags_Player(plr, adjustingVolume_PlayerFlag, iFalse); |
@@ -1244,6 +1256,9 @@ static const char *zipPageHeading_(const iRangecc mime) { | |||
1244 | if (equalCase_Rangecc(mime, "application/gpub+zip")) { | 1256 | if (equalCase_Rangecc(mime, "application/gpub+zip")) { |
1245 | return book_Icon " Gempub"; | 1257 | return book_Icon " Gempub"; |
1246 | } | 1258 | } |
1259 | else if (equalCase_Rangecc(mime, mimeType_FontPack)) { | ||
1260 | return "\U0001f520 Fontpack"; | ||
1261 | } | ||
1247 | iRangecc type = iNullRange; | 1262 | iRangecc type = iNullRange; |
1248 | nextSplit_Rangecc(mime, "/", &type); /* skip the part before the slash */ | 1263 | nextSplit_Rangecc(mime, "/", &type); /* skip the part before the slash */ |
1249 | nextSplit_Rangecc(mime, "/", &type); | 1264 | nextSplit_Rangecc(mime, "/", &type); |
@@ -1258,165 +1273,175 @@ static const char *zipPageHeading_(const iRangecc mime) { | |||
1258 | 1273 | ||
1259 | static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool isCached) { | 1274 | static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool isCached) { |
1260 | iWidget *w = as_Widget(d); | 1275 | iWidget *w = as_Widget(d); |
1261 | delete_Gempub(d->sourceGempub); | 1276 | /* Gempub page behavior and footer actions. */ { |
1262 | d->sourceGempub = NULL; | 1277 | /* TODO: move this to gempub.c */ |
1263 | if (!cmpCase_String(&d->sourceMime, "application/octet-stream") || | 1278 | delete_Gempub(d->sourceGempub); |
1264 | !cmpCase_String(&d->sourceMime, mimeType_Gempub) || | 1279 | d->sourceGempub = NULL; |
1265 | endsWithCase_String(d->mod.url, ".gpub")) { | 1280 | if (!cmpCase_String(&d->sourceMime, "application/octet-stream") || |
1266 | iGempub *gempub = new_Gempub(); | 1281 | !cmpCase_String(&d->sourceMime, mimeType_Gempub) || |
1267 | if (open_Gempub(gempub, &d->sourceContent)) { | 1282 | endsWithCase_String(d->mod.url, ".gpub")) { |
1268 | setBaseUrl_Gempub(gempub, d->mod.url); | ||
1269 | setSource_DocumentWidget(d, collect_String(coverPageSource_Gempub(gempub))); | ||
1270 | setCStr_String(&d->sourceMime, mimeType_Gempub); | ||
1271 | d->sourceGempub = gempub; | ||
1272 | } | ||
1273 | else { | ||
1274 | delete_Gempub(gempub); | ||
1275 | } | ||
1276 | } | ||
1277 | if (!d->sourceGempub) { | ||
1278 | const iString *localPath = collect_String(localFilePathFromUrl_String(d->mod.url)); | ||
1279 | iBool isInside = iFalse; | ||
1280 | if (localPath && !fileExists_FileInfo(localPath)) { | ||
1281 | /* This URL may refer to a file inside the archive. */ | ||
1282 | localPath = findContainerArchive_Path(localPath); | ||
1283 | isInside = iTrue; | ||
1284 | } | ||
1285 | if (localPath && equal_CStr(mediaType_Path(localPath), "application/gpub+zip")) { | ||
1286 | iGempub *gempub = new_Gempub(); | 1283 | iGempub *gempub = new_Gempub(); |
1287 | if (openFile_Gempub(gempub, localPath)) { | 1284 | if (open_Gempub(gempub, &d->sourceContent)) { |
1288 | setBaseUrl_Gempub(gempub, collect_String(makeFileUrl_String(localPath))); | 1285 | setBaseUrl_Gempub(gempub, d->mod.url); |
1289 | if (!isInside) { | 1286 | setSource_DocumentWidget(d, collect_String(coverPageSource_Gempub(gempub))); |
1290 | setSource_DocumentWidget(d, collect_String(coverPageSource_Gempub(gempub))); | 1287 | setCStr_String(&d->sourceMime, mimeType_Gempub); |
1291 | setCStr_String(&d->sourceMime, mimeType_Gempub); | ||
1292 | } | ||
1293 | d->sourceGempub = gempub; | 1288 | d->sourceGempub = gempub; |
1294 | } | 1289 | } |
1295 | else { | 1290 | else { |
1296 | delete_Gempub(gempub); | 1291 | delete_Gempub(gempub); |
1297 | } | 1292 | } |
1298 | } | 1293 | } |
1299 | } | 1294 | if (!d->sourceGempub) { |
1300 | if (d->sourceGempub) { | 1295 | const iString *localPath = collect_String(localFilePathFromUrl_String(d->mod.url)); |
1301 | if (equal_String(d->mod.url, coverPageUrl_Gempub(d->sourceGempub))) { | 1296 | iBool isInside = iFalse; |
1302 | if (!isRemote_Gempub(d->sourceGempub)) { | 1297 | if (localPath && !fileExists_FileInfo(localPath)) { |
1303 | iArray *items = collectNew_Array(sizeof(iMenuItem)); | 1298 | /* This URL may refer to a file inside the archive. */ |
1304 | pushBack_Array( | 1299 | localPath = findContainerArchive_Path(localPath); |
1305 | items, | 1300 | isInside = iTrue; |
1306 | &(iMenuItem){ book_Icon " ${gempub.cover.view}", | ||
1307 | 0, | ||
1308 | 0, | ||
1309 | format_CStr("!open url:%s", | ||
1310 | cstr_String(indexPageUrl_Gempub(d->sourceGempub))) }); | ||
1311 | if (navSize_Gempub(d->sourceGempub) > 0) { | ||
1312 | pushBack_Array( | ||
1313 | items, | ||
1314 | &(iMenuItem){ | ||
1315 | format_CStr(forwardArrow_Icon " %s", | ||
1316 | cstr_String(navLinkLabel_Gempub(d->sourceGempub, 0))), | ||
1317 | SDLK_RIGHT, | ||
1318 | 0, | ||
1319 | format_CStr("!open url:%s", | ||
1320 | cstr_String(navLinkUrl_Gempub(d->sourceGempub, 0))) }); | ||
1321 | } | ||
1322 | makeFooterButtons_DocumentWidget_(d, constData_Array(items), size_Array(items)); | ||
1323 | } | 1301 | } |
1324 | else { | 1302 | if (localPath && equal_CStr(mediaType_Path(localPath), mimeType_Gempub)) { |
1325 | makeFooterButtons_DocumentWidget_( | 1303 | iGempub *gempub = new_Gempub(); |
1326 | d, | 1304 | if (openFile_Gempub(gempub, localPath)) { |
1327 | (iMenuItem[]){ { book_Icon " ${menu.save.downloads.open}", | 1305 | setBaseUrl_Gempub(gempub, collect_String(makeFileUrl_String(localPath))); |
1328 | SDLK_s, | 1306 | if (!isInside) { |
1329 | KMOD_PRIMARY | KMOD_SHIFT, | 1307 | setSource_DocumentWidget(d, collect_String(coverPageSource_Gempub(gempub))); |
1330 | "document.save open:1" }, | 1308 | setCStr_String(&d->sourceMime, mimeType_Gempub); |
1331 | { download_Icon " " saveToDownloads_Label, | 1309 | } |
1332 | SDLK_s, | 1310 | d->sourceGempub = gempub; |
1333 | KMOD_PRIMARY, | 1311 | } |
1334 | "document.save" } }, | 1312 | else { |
1335 | 2); | 1313 | delete_Gempub(gempub); |
1336 | } | 1314 | } |
1337 | if (preloadCoverImage_Gempub(d->sourceGempub, d->doc)) { | ||
1338 | redoLayout_GmDocument(d->doc); | ||
1339 | updateVisible_DocumentWidget_(d); | ||
1340 | invalidate_DocumentWidget_(d); | ||
1341 | } | 1315 | } |
1342 | } | 1316 | } |
1343 | else if (equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) { | 1317 | if (d->sourceGempub) { |
1344 | makeFooterButtons_DocumentWidget_( | 1318 | if (equal_String(d->mod.url, coverPageUrl_Gempub(d->sourceGempub))) { |
1345 | d, | 1319 | if (!isRemote_Gempub(d->sourceGempub)) { |
1346 | (iMenuItem[]){ { format_CStr(book_Icon " %s", | 1320 | iArray *items = collectNew_Array(sizeof(iMenuItem)); |
1347 | cstr_String(property_Gempub(d->sourceGempub, | ||
1348 | title_GempubProperty))), | ||
1349 | SDLK_LEFT, | ||
1350 | 0, | ||
1351 | format_CStr("!open url:%s", | ||
1352 | cstr_String(coverPageUrl_Gempub(d->sourceGempub))) } }, | ||
1353 | 1); | ||
1354 | } | ||
1355 | else { | ||
1356 | /* Navigation buttons. */ | ||
1357 | iArray *items = collectNew_Array(sizeof(iMenuItem)); | ||
1358 | const size_t navIndex = navIndex_Gempub(d->sourceGempub, d->mod.url); | ||
1359 | if (navIndex != iInvalidPos) { | ||
1360 | if (navIndex < navSize_Gempub(d->sourceGempub) - 1) { | ||
1361 | pushBack_Array( | 1321 | pushBack_Array( |
1362 | items, | 1322 | items, |
1363 | &(iMenuItem){ | 1323 | &(iMenuItem){ book_Icon " ${gempub.cover.view}", |
1364 | format_CStr(forwardArrow_Icon " %s", | 1324 | 0, |
1365 | cstr_String(navLinkLabel_Gempub(d->sourceGempub, navIndex + 1))), | 1325 | 0, |
1366 | SDLK_RIGHT, | 1326 | format_CStr("!open url:%s", |
1367 | 0, | 1327 | cstr_String(indexPageUrl_Gempub(d->sourceGempub))) }); |
1368 | format_CStr("!open url:%s", | 1328 | if (navSize_Gempub(d->sourceGempub) > 0) { |
1369 | 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)); | ||
1370 | } | 1340 | } |
1371 | if (navIndex > 0) { | 1341 | else { |
1372 | pushBack_Array( | 1342 | makeFooterButtons_DocumentWidget_( |
1373 | items, | 1343 | d, |
1374 | &(iMenuItem){ | 1344 | (iMenuItem[]){ { book_Icon " ${menu.save.downloads.open}", |
1375 | format_CStr(backArrow_Icon " %s", | 1345 | SDLK_s, |
1376 | cstr_String(navLinkLabel_Gempub(d->sourceGempub, navIndex - 1))), | 1346 | KMOD_PRIMARY | KMOD_SHIFT, |
1377 | SDLK_LEFT, | 1347 | "document.save open:1" }, |
1378 | 0, | 1348 | { download_Icon " " saveToDownloads_Label, |
1379 | format_CStr("!open url:%s", | 1349 | SDLK_s, |
1380 | cstr_String(navLinkUrl_Gempub(d->sourceGempub, navIndex - 1))) }); | 1350 | KMOD_PRIMARY, |
1351 | "document.save" } }, | ||
1352 | 2); | ||
1381 | } | 1353 | } |
1382 | else if (!equalCase_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) { | 1354 | if (preloadCoverImage_Gempub(d->sourceGempub, d->doc)) { |
1383 | pushBack_Array( | 1355 | redoLayout_GmDocument(d->doc); |
1384 | items, | 1356 | updateVisible_DocumentWidget_(d); |
1385 | &(iMenuItem){ | 1357 | invalidate_DocumentWidget_(d); |
1386 | format_CStr(book_Icon " %s", | ||
1387 | cstr_String(property_Gempub(d->sourceGempub, title_GempubProperty))), | ||
1388 | SDLK_LEFT, | ||
1389 | 0, | ||
1390 | format_CStr("!open url:%s", | ||
1391 | cstr_String(coverPageUrl_Gempub(d->sourceGempub))) }); | ||
1392 | } | 1358 | } |
1393 | } | 1359 | } |
1394 | if (!isEmpty_Array(items)) { | 1360 | else if (equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) { |
1395 | 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); | ||
1396 | } | 1371 | } |
1397 | } | 1372 | else { |
1398 | if (!isCached && prefs_App()->pinSplit && | 1373 | /* Navigation buttons. */ |
1399 | equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) { | 1374 | iArray *items = collectNew_Array(sizeof(iMenuItem)); |
1400 | const iString *navStart = navStartLinkUrl_Gempub(d->sourceGempub); | 1375 | const size_t navIndex = navIndex_Gempub(d->sourceGempub, d->mod.url); |
1401 | if (navStart) { | 1376 | if (navIndex != iInvalidPos) { |
1402 | iWindow *win = get_Window(); | 1377 | if (navIndex < navSize_Gempub(d->sourceGempub) - 1) { |
1403 | /* Auto-split to show index and the first navigation link. */ | 1378 | pushBack_Array( |
1404 | if (numRoots_Window(win) == 2) { | 1379 | items, |
1405 | /* This document is showing the index page. */ | 1380 | &(iMenuItem){ |
1406 | iRoot *other = otherRoot_Window(win, w->root); | 1381 | format_CStr(forwardArrow_Icon " %s", |
1407 | postCommandf_Root(other, "open url:%s", cstr_String(navStart)); | 1382 | cstr_String(navLinkLabel_Gempub(d->sourceGempub, navIndex + 1))), |
1408 | if (prefs_App()->pinSplit == 1 && w->root == win->roots[1]) { | 1383 | SDLK_RIGHT, |
1409 | /* On the wrong side. */ | 1384 | 0, |
1410 | 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))) }); | ||
1411 | } | 1409 | } |
1412 | } | 1410 | } |
1413 | else { | 1411 | if (!isEmpty_Array(items)) { |
1414 | postCommandf_App( | 1412 | makeFooterButtons_DocumentWidget_(d, constData_Array(items), size_Array(items)); |
1415 | "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 | } | ||
1416 | } | 1434 | } |
1417 | } | 1435 | } |
1418 | } | 1436 | } |
1419 | } | 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 | } | ||
1420 | } | 1445 | } |
1421 | 1446 | ||
1422 | static void updateDocument_DocumentWidget_(iDocumentWidget *d, | 1447 | static void updateDocument_DocumentWidget_(iDocumentWidget *d, |
@@ -1484,7 +1509,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d, | |||
1484 | } | 1509 | } |
1485 | delete_String(localPath); | 1510 | delete_String(localPath); |
1486 | if (equalCase_Rangecc(urlScheme_String(d->mod.url), "file")) { | 1511 | if (equalCase_Rangecc(urlScheme_String(d->mod.url), "file")) { |
1487 | appendFormat_String(&str, "=> %s/ ${doc.archive.view}\n", | 1512 | appendFormat_String(&str, "=> %s/ " folder_Icon " ${doc.archive.view}\n", |
1488 | cstr_String(withSpacesEncoded_String(d->mod.url))); | 1513 | cstr_String(withSpacesEncoded_String(d->mod.url))); |
1489 | } | 1514 | } |
1490 | translate_Lang(&str); | 1515 | translate_Lang(&str); |
@@ -2089,7 +2114,7 @@ static iBool requestMedia_DocumentWidget_(iDocumentWidget *d, iGmLinkId linkId, | |||
2089 | } | 2114 | } |
2090 | 2115 | ||
2091 | static iBool isDownloadRequest_DocumentWidget(const iDocumentWidget *d, const iMediaRequest *req) { | 2116 | static iBool isDownloadRequest_DocumentWidget(const iDocumentWidget *d, const iMediaRequest *req) { |
2092 | return findLinkDownload_Media(constMedia_GmDocument(d->doc), req->linkId) != 0; | 2117 | return findMediaForLink_Media(constMedia_GmDocument(d->doc), req->linkId, download_MediaType).type != 0; |
2093 | } | 2118 | } |
2094 | 2119 | ||
2095 | static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { | 2120 | static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) { |
@@ -2174,7 +2199,7 @@ static void allocVisBuffer_DocumentWidget_(const iDocumentWidget *d) { | |||
2174 | static iBool fetchNextUnfetchedImage_DocumentWidget_(iDocumentWidget *d) { | 2199 | static iBool fetchNextUnfetchedImage_DocumentWidget_(iDocumentWidget *d) { |
2175 | iConstForEach(PtrArray, i, &d->visibleLinks) { | 2200 | iConstForEach(PtrArray, i, &d->visibleLinks) { |
2176 | const iGmRun *run = i.ptr; | 2201 | const iGmRun *run = i.ptr; |
2177 | if (run->linkId && run->mediaType == none_GmRunMediaType && | 2202 | if (run->linkId && run->mediaType == none_MediaType && |
2178 | ~run->flags & decoration_GmRunFlag) { | 2203 | ~run->flags & decoration_GmRunFlag) { |
2179 | const int linkFlags = linkFlags_GmDocument(d->doc, run->linkId); | 2204 | const int linkFlags = linkFlags_GmDocument(d->doc, run->linkId); |
2180 | if (isMediaLink_GmDocument(d->doc, run->linkId) && | 2205 | if (isMediaLink_GmDocument(d->doc, run->linkId) && |
@@ -2763,8 +2788,10 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2763 | else if (equalWidget_Command(cmd, w, "document.downloadlink")) { | 2788 | else if (equalWidget_Command(cmd, w, "document.downloadlink")) { |
2764 | if (d->contextLink) { | 2789 | if (d->contextLink) { |
2765 | const iGmLinkId linkId = d->contextLink->linkId; | 2790 | const iGmLinkId linkId = d->contextLink->linkId; |
2766 | setDownloadUrl_Media( | 2791 | setUrl_Media(media_GmDocument(d->doc), |
2767 | media_GmDocument(d->doc), linkId, linkUrl_GmDocument(d->doc, linkId)); | 2792 | linkId, |
2793 | download_MediaType, | ||
2794 | linkUrl_GmDocument(d->doc, linkId)); | ||
2768 | requestMedia_DocumentWidget_(d, linkId, iFalse /* no filters */); | 2795 | requestMedia_DocumentWidget_(d, linkId, iFalse /* no filters */); |
2769 | redoLayout_GmDocument(d->doc); /* inline downloader becomes visible */ | 2796 | redoLayout_GmDocument(d->doc); /* inline downloader becomes visible */ |
2770 | updateVisible_DocumentWidget_(d); | 2797 | updateVisible_DocumentWidget_(d); |
@@ -2874,7 +2901,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) | |||
2874 | const iMedia * media = media_GmDocument(d->doc); | 2901 | const iMedia * media = media_GmDocument(d->doc); |
2875 | const size_t num = numAudio_Media(media); | 2902 | const size_t num = numAudio_Media(media); |
2876 | for (size_t id = 1; id <= num; id++) { | 2903 | for (size_t id = 1; id <= num; id++) { |
2877 | iPlayer *plr = audioPlayer_Media(media, id); | 2904 | iPlayer *plr = audioPlayer_Media(media, (iMediaId){ audio_MediaType, id }); |
2878 | if (plr != startedPlr) { | 2905 | if (plr != startedPlr) { |
2879 | setPaused_Player(plr, iTrue); | 2906 | setPaused_Player(plr, iTrue); |
2880 | } | 2907 | } |
@@ -3212,8 +3239,8 @@ static iRect runRect_DocumentWidget_(const iDocumentWidget *d, const iGmRun *run | |||
3212 | } | 3239 | } |
3213 | 3240 | ||
3214 | static void setGrabbedPlayer_DocumentWidget_(iDocumentWidget *d, const iGmRun *run) { | 3241 | static void setGrabbedPlayer_DocumentWidget_(iDocumentWidget *d, const iGmRun *run) { |
3215 | if (run && run->mediaType == audio_GmRunMediaType) { | 3242 | if (run && run->mediaType == audio_MediaType) { |
3216 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), run->mediaId); | 3243 | iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)); |
3217 | setFlags_Player(plr, volumeGrabbed_PlayerFlag, iTrue); | 3244 | setFlags_Player(plr, volumeGrabbed_PlayerFlag, iTrue); |
3218 | d->grabbedStartVolume = volume_Player(plr); | 3245 | d->grabbedStartVolume = volume_Player(plr); |
3219 | d->grabbedPlayer = run; | 3246 | d->grabbedPlayer = run; |
@@ -3221,7 +3248,7 @@ static void setGrabbedPlayer_DocumentWidget_(iDocumentWidget *d, const iGmRun *r | |||
3221 | } | 3248 | } |
3222 | else if (d->grabbedPlayer) { | 3249 | else if (d->grabbedPlayer) { |
3223 | setFlags_Player( | 3250 | setFlags_Player( |
3224 | audioPlayer_Media(media_GmDocument(d->doc), d->grabbedPlayer->mediaId), | 3251 | audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(d->grabbedPlayer)), |
3225 | volumeGrabbed_PlayerFlag, | 3252 | volumeGrabbed_PlayerFlag, |
3226 | iFalse); | 3253 | iFalse); |
3227 | d->grabbedPlayer = NULL; | 3254 | d->grabbedPlayer = NULL; |
@@ -3249,11 +3276,21 @@ static iBool processMediaEvents_DocumentWidget_(iDocumentWidget *d, const SDL_Ev | |||
3249 | const iInt2 mouse = init_I2(ev->button.x, ev->button.y); | 3276 | const iInt2 mouse = init_I2(ev->button.x, ev->button.y); |
3250 | iConstForEach(PtrArray, i, &d->visibleMedia) { | 3277 | iConstForEach(PtrArray, i, &d->visibleMedia) { |
3251 | const iGmRun *run = i.ptr; | 3278 | const iGmRun *run = i.ptr; |
3252 | 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) { | ||
3253 | continue; | 3289 | continue; |
3254 | } | 3290 | } |
3291 | /* TODO: move this to mediaui.c */ | ||
3255 | const iRect rect = runRect_DocumentWidget_(d, run); | 3292 | const iRect rect = runRect_DocumentWidget_(d, run); |
3256 | iPlayer * plr = audioPlayer_Media(media_GmDocument(d->doc), run->mediaId); | 3293 | iPlayer * plr = audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)); |
3257 | if (contains_Rect(rect, mouse)) { | 3294 | if (contains_Rect(rect, mouse)) { |
3258 | iPlayerUI ui; | 3295 | iPlayerUI ui; |
3259 | init_PlayerUI(&ui, plr, rect); | 3296 | init_PlayerUI(&ui, plr, rect); |
@@ -3633,7 +3670,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
3633 | cstr_String(linkUrl)) }, | 3670 | cstr_String(linkUrl)) }, |
3634 | }, | 3671 | }, |
3635 | 3); | 3672 | 3); |
3636 | if (isNative && d->contextLink->mediaType != download_GmRunMediaType) { | 3673 | if (isNative && d->contextLink->mediaType != download_MediaType) { |
3637 | pushBackN_Array(&items, (iMenuItem[]){ | 3674 | pushBackN_Array(&items, (iMenuItem[]){ |
3638 | { "---" }, | 3675 | { "---" }, |
3639 | { download_Icon " ${link.download}", 0, 0, "document.downloadlink" }, | 3676 | { download_Icon " ${link.download}", 0, 0, "document.downloadlink" }, |
@@ -3641,7 +3678,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
3641 | } | 3678 | } |
3642 | iMediaRequest *mediaReq; | 3679 | iMediaRequest *mediaReq; |
3643 | if ((mediaReq = findMediaRequest_DocumentWidget_(d, d->contextLink->linkId)) != NULL && | 3680 | if ((mediaReq = findMediaRequest_DocumentWidget_(d, d->contextLink->linkId)) != NULL && |
3644 | d->contextLink->mediaType != download_GmRunMediaType) { | 3681 | d->contextLink->mediaType != download_MediaType) { |
3645 | if (isFinished_GmRequest(mediaReq->req)) { | 3682 | if (isFinished_GmRequest(mediaReq->req)) { |
3646 | pushBack_Array(&items, | 3683 | pushBack_Array(&items, |
3647 | &(iMenuItem){ download_Icon " " saveToDownloads_Label, | 3684 | &(iMenuItem){ download_Icon " " saveToDownloads_Label, |
@@ -3763,7 +3800,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
3763 | case drag_ClickResult: { | 3800 | case drag_ClickResult: { |
3764 | if (d->grabbedPlayer) { | 3801 | if (d->grabbedPlayer) { |
3765 | iPlayer *plr = | 3802 | iPlayer *plr = |
3766 | audioPlayer_Media(media_GmDocument(d->doc), d->grabbedPlayer->mediaId); | 3803 | audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(d->grabbedPlayer)); |
3767 | iPlayerUI ui; | 3804 | iPlayerUI ui; |
3768 | init_PlayerUI(&ui, plr, runRect_DocumentWidget_(d, d->grabbedPlayer)); | 3805 | init_PlayerUI(&ui, plr, runRect_DocumentWidget_(d, d->grabbedPlayer)); |
3769 | 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); |
@@ -3883,8 +3920,9 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
3883 | } | 3920 | } |
3884 | if (d->hoverLink) { | 3921 | if (d->hoverLink) { |
3885 | /* TODO: Move this to a method. */ | 3922 | /* TODO: Move this to a method. */ |
3886 | const iGmLinkId linkId = d->hoverLink->linkId; | 3923 | const iGmLinkId linkId = d->hoverLink->linkId; |
3887 | const int linkFlags = linkFlags_GmDocument(d->doc, linkId); | 3924 | const iMediaId linkMedia = mediaId_GmRun(d->hoverLink); |
3925 | const int linkFlags = linkFlags_GmDocument(d->doc, linkId); | ||
3888 | iAssert(linkId); | 3926 | iAssert(linkId); |
3889 | /* Media links are opened inline by default. */ | 3927 | /* Media links are opened inline by default. */ |
3890 | if (isMediaLink_GmDocument(d->doc, linkId)) { | 3928 | if (isMediaLink_GmDocument(d->doc, linkId)) { |
@@ -3937,6 +3975,12 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e | |||
3937 | } | 3975 | } |
3938 | refresh_Widget(w); | 3976 | refresh_Widget(w); |
3939 | } | 3977 | } |
3978 | else if (linkMedia.type == download_MediaType || | ||
3979 | findMediaRequest_DocumentWidget_(d, linkId)) { | ||
3980 | /* TODO: What should be done when clicking on an inline download? | ||
3981 | Maybe dismiss if finished? */ | ||
3982 | return iTrue; | ||
3983 | } | ||
3940 | else if (linkFlags & supportedScheme_GmLinkFlag) { | 3984 | else if (linkFlags & supportedScheme_GmLinkFlag) { |
3941 | int tabMode = openTabMode_Sym(modState_Keys()); | 3985 | int tabMode = openTabMode_Sym(modState_Keys()); |
3942 | if (isPinned_DocumentWidget_(d)) { | 3986 | if (isPinned_DocumentWidget_(d)) { |
@@ -4071,7 +4115,7 @@ static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iCol | |||
4071 | 4115 | ||
4072 | static void drawMark_DrawContext_(void *context, const iGmRun *run) { | 4116 | static void drawMark_DrawContext_(void *context, const iGmRun *run) { |
4073 | iDrawContext *d = context; | 4117 | iDrawContext *d = context; |
4074 | if (run->mediaType == none_GmRunMediaType) { | 4118 | if (run->mediaType == none_MediaType) { |
4075 | fillRange_DrawContext_(d, run, uiMatching_ColorId, d->widget->foundMark, &d->inFoundMark); | 4119 | fillRange_DrawContext_(d, run, uiMatching_ColorId, d->widget->foundMark, &d->inFoundMark); |
4076 | fillRange_DrawContext_(d, run, uiMarked_ColorId, d->widget->selectMark, &d->inSelectMark); | 4120 | fillRange_DrawContext_(d, run, uiMarked_ColorId, d->widget->selectMark, &d->inSelectMark); |
4077 | } | 4121 | } |
@@ -4178,8 +4222,8 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4178 | d->runsDrawn.end = run; | 4222 | d->runsDrawn.end = run; |
4179 | } | 4223 | } |
4180 | } | 4224 | } |
4181 | if (run->mediaType == image_GmRunMediaType) { | 4225 | if (run->mediaType == image_MediaType) { |
4182 | SDL_Texture *tex = imageTexture_Media(media_GmDocument(d->widget->doc), run->mediaId); | 4226 | SDL_Texture *tex = imageTexture_Media(media_GmDocument(d->widget->doc), mediaId_GmRun(run)); |
4183 | const iRect dst = moved_Rect(run->visBounds, origin); | 4227 | const iRect dst = moved_Rect(run->visBounds, origin); |
4184 | if (tex) { | 4228 | if (tex) { |
4185 | fillRect_Paint(&d->paint, dst, tmBackground_ColorId); /* in case the image has alpha */ | 4229 | fillRect_Paint(&d->paint, dst, tmBackground_ColorId); /* in case the image has alpha */ |
@@ -4334,31 +4378,31 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) { | |||
4334 | fg = linkColor_GmDocument(doc, run->linkId, textHover_GmLinkPart); | 4378 | fg = linkColor_GmDocument(doc, run->linkId, textHover_GmLinkPart); |
4335 | iString text; | 4379 | iString text; |
4336 | init_String(&text); | 4380 | init_String(&text); |
4337 | iMediaId imageId = linkImage_GmDocument(doc, run->linkId); | 4381 | const iMediaId linkMedia = findMediaForLink_Media(constMedia_GmDocument(doc), |
4338 | iMediaId audioId = !imageId ? linkAudio_GmDocument(doc, run->linkId) : 0; | 4382 | run->linkId, none_MediaType); |
4339 | iMediaId downloadId = !imageId && !audioId ? | 4383 | iAssert(linkMedia.type != none_MediaType); |
4340 | findLinkDownload_Media(constMedia_GmDocument(doc), run->linkId) : 0; | 4384 | iGmMediaInfo info; |
4341 | iAssert(imageId || audioId || downloadId); | 4385 | info_Media(constMedia_GmDocument(doc), linkMedia, &info); |
4342 | if (imageId) { | 4386 | switch (linkMedia.type) { |
4343 | iAssert(!isEmpty_Rect(run->bounds)); | 4387 | case image_MediaType: { |
4344 | iGmMediaInfo info; | 4388 | iAssert(!isEmpty_Rect(run->bounds)); |
4345 | imageInfo_Media(constMedia_GmDocument(doc), imageId, &info); | 4389 | const iInt2 imgSize = imageSize_Media(constMedia_GmDocument(doc), linkMedia); |
4346 | const iInt2 imgSize = imageSize_Media(constMedia_GmDocument(doc), imageId); | 4390 | format_String(&text, "%s \u2014 %d x %d \u2014 %.1f%s", |
4347 | format_String(&text, "%s \u2014 %d x %d \u2014 %.1f%s", | 4391 | info.type, imgSize.x, imgSize.y, info.numBytes / 1.0e6f, |
4348 | info.type, imgSize.x, imgSize.y, info.numBytes / 1.0e6f, | 4392 | cstr_Lang("mb")); |
4349 | cstr_Lang("mb")); | 4393 | break; |
4350 | } | 4394 | } |
4351 | else if (audioId) { | 4395 | case audio_MediaType: |
4352 | iGmMediaInfo info; | 4396 | format_String(&text, "%s", info.type); |
4353 | audioInfo_Media(constMedia_GmDocument(doc), audioId, &info); | 4397 | break; |
4354 | format_String(&text, "%s", info.type); | 4398 | case download_MediaType: |
4355 | } | 4399 | format_String(&text, "%s", info.type); |
4356 | else if (downloadId) { | 4400 | break; |
4357 | iGmMediaInfo info; | 4401 | default: |
4358 | downloadInfo_Media(constMedia_GmDocument(doc), downloadId, &info); | 4402 | break; |
4359 | format_String(&text, "%s", info.type); | ||
4360 | } | 4403 | } |
4361 | if (findMediaRequest_DocumentWidget_(d->widget, run->linkId)) { | 4404 | if (linkMedia.type != download_MediaType && /* can't cancel downloads currently */ |
4405 | findMediaRequest_DocumentWidget_(d->widget, run->linkId)) { | ||
4362 | appendFormat_String( | 4406 | appendFormat_String( |
4363 | &text, " %s" close_Icon, isHover ? escape_Color(tmLinkText_ColorId) : ""); | 4407 | &text, " %s" close_Icon, isHover ? escape_Color(tmLinkText_ColorId) : ""); |
4364 | } | 4408 | } |
@@ -4589,18 +4633,25 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) { | |||
4589 | static void drawMedia_DocumentWidget_(const iDocumentWidget *d, iPaint *p) { | 4633 | static void drawMedia_DocumentWidget_(const iDocumentWidget *d, iPaint *p) { |
4590 | iConstForEach(PtrArray, i, &d->visibleMedia) { | 4634 | iConstForEach(PtrArray, i, &d->visibleMedia) { |
4591 | const iGmRun * run = i.ptr; | 4635 | const iGmRun * run = i.ptr; |
4592 | if (run->mediaType == audio_GmRunMediaType) { | 4636 | if (run->mediaType == audio_MediaType) { |
4593 | iPlayerUI ui; | 4637 | iPlayerUI ui; |
4594 | init_PlayerUI(&ui, | 4638 | init_PlayerUI(&ui, |
4595 | audioPlayer_Media(media_GmDocument(d->doc), run->mediaId), | 4639 | audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)), |
4596 | runRect_DocumentWidget_(d, run)); | 4640 | runRect_DocumentWidget_(d, run)); |
4597 | draw_PlayerUI(&ui, p); | 4641 | draw_PlayerUI(&ui, p); |
4598 | } | 4642 | } |
4599 | else if (run->mediaType == download_GmRunMediaType) { | 4643 | else if (run->mediaType == download_MediaType) { |
4600 | iDownloadUI ui; | 4644 | iDownloadUI ui; |
4601 | init_DownloadUI(&ui, d, run->mediaId, runRect_DocumentWidget_(d, run)); | 4645 | init_DownloadUI(&ui, constMedia_GmDocument(d->doc), run->mediaId, |
4646 | runRect_DocumentWidget_(d, run)); | ||
4602 | draw_DownloadUI(&ui, p); | 4647 | draw_DownloadUI(&ui, p); |
4603 | } | 4648 | } |
4649 | else if (run->mediaType == fontpack_MediaType) { | ||
4650 | iFontpackUI ui; | ||
4651 | init_FontpackUI(&ui, constMedia_GmDocument(d->doc), run->mediaId, | ||
4652 | runRect_DocumentWidget_(d, run)); | ||
4653 | draw_FontpackUI(&ui, p); | ||
4654 | } | ||
4604 | } | 4655 | } |
4605 | } | 4656 | } |
4606 | 4657 | ||