summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--po/en.po69
-rw-r--r--res/lang/de.binbin26398 -> 27035 bytes
-rw-r--r--res/lang/en.binbin23836 -> 24559 bytes
-rw-r--r--res/lang/eo.binbin22690 -> 23327 bytes
-rw-r--r--res/lang/es.binbin26782 -> 27419 bytes
-rw-r--r--res/lang/es_MX.binbin24750 -> 25387 bytes
-rw-r--r--res/lang/fi.binbin26694 -> 27331 bytes
-rw-r--r--res/lang/fr.binbin27604 -> 28241 bytes
-rw-r--r--res/lang/gl.binbin26058 -> 26695 bytes
-rw-r--r--res/lang/ia.binbin25723 -> 26360 bytes
-rw-r--r--res/lang/ie.binbin25928 -> 26565 bytes
-rw-r--r--res/lang/isv.binbin22626 -> 23263 bytes
-rw-r--r--res/lang/pl.binbin26999 -> 27636 bytes
-rw-r--r--res/lang/ru.binbin39338 -> 39975 bytes
-rw-r--r--res/lang/sk.binbin22959 -> 23596 bytes
-rw-r--r--res/lang/sr.binbin39243 -> 39880 bytes
-rw-r--r--res/lang/tok.binbin24173 -> 24810 bytes
-rw-r--r--res/lang/zh_Hans.binbin22675 -> 23312 bytes
-rw-r--r--res/lang/zh_Hant.binbin22816 -> 23453 bytes
-rw-r--r--src/app.c50
-rw-r--r--src/defs.h5
-rw-r--r--src/fontpack.c197
-rw-r--r--src/fontpack.h13
-rw-r--r--src/gmdocument.c37
-rw-r--r--src/gmutil.c3
-rw-r--r--src/lang.c9
-rw-r--r--src/lang.h1
-rw-r--r--src/media.c129
-rw-r--r--src/media.h17
-rw-r--r--src/ui/documentwidget.c75
-rw-r--r--src/ui/mediaui.c82
-rw-r--r--src/ui/mediaui.h17
32 files changed, 341 insertions, 363 deletions
diff --git a/po/en.po b/po/en.po
index 526a723d..51bd66b2 100644
--- a/po/en.po
+++ b/po/en.po
@@ -448,6 +448,16 @@ msgid_plural "num.bytes.n"
448msgstr[0] "%zu byte" 448msgstr[0] "%zu byte"
449msgstr[1] "%zu bytes" 449msgstr[1] "%zu bytes"
450 450
451msgid "num.files"
452msgid_plural "num.files.n"
453msgstr[0] "%zu file"
454msgstr[1] "%zu files"
455
456msgid "num.fonts"
457msgid_plural "num.fonts.n"
458msgstr[0] "%zu font"
459msgstr[1] "%zu fonts"
460
451# strftime() formatted, split on two lines 461# strftime() formatted, split on two lines
452#, c-format 462#, c-format
453msgid "page.timestamp" 463msgid "page.timestamp"
@@ -1869,4 +1879,61 @@ msgid "gempub.meta.lang"
1869msgstr "Language" 1879msgstr "Language"
1870 1880
1871msgid "gempub.meta.license" 1881msgid "gempub.meta.license"
1872msgstr "License" \ No newline at end of file 1882msgstr "License"
1883
1884msgid "heading.fontpack.meta"
1885msgstr "Fonts"
1886
1887msgid "heading.fontpack.meta.enabled"
1888msgstr "Enabled fontpacks"
1889
1890msgid "heading.fontpack.meta.disabled"
1891msgstr "Disabled fontpacks"
1892
1893# Action label
1894msgid "fontpack.meta.viewfile"
1895msgstr "View file"
1896
1897#, c-format
1898msgid "fontpack.meta.version"
1899msgstr "Version %d"
1900
1901msgid "fontpack.meta.installed"
1902msgstr "Installed"
1903
1904msgid "fontpack.meta.notinstalled"
1905msgstr "Not installed"
1906
1907msgid "fontpack.meta.disabled"
1908msgstr ", disabled"
1909
1910#, c-format
1911msgid "fontpack.enable"
1912msgstr "Enable \"%s\""
1913
1914#, c-format
1915msgid "fontpack.disable"
1916msgstr "Disable \"%s\""
1917
1918#, c-format
1919msgid "fontpack.install"
1920msgstr "Install \"%s\""
1921
1922#, c-format
1923msgid "fontpack.upgrade"
1924msgstr "Upgrade \"%s\" to version %d"
1925
1926#, c-format
1927msgid "fontpack.delete"
1928msgstr "Permanently delete \"%s\""
1929
1930msgid "heading.fontpack.delete"
1931msgstr "DELETE FONTPACK"
1932
1933#, c-format
1934msgid "dlg.fontpack.delete.confirm"
1935msgstr "Do you really want to permanently delete\nthe fontpack \"%s\"?"
1936
1937msgid "dlg.fontpack.delete"
1938msgstr "Delete Fontpack"
1939
diff --git a/res/lang/de.bin b/res/lang/de.bin
index 17a2d191..332da0e3 100644
--- a/res/lang/de.bin
+++ b/res/lang/de.bin
Binary files differ
diff --git a/res/lang/en.bin b/res/lang/en.bin
index 51b9a10d..429b639f 100644
--- a/res/lang/en.bin
+++ b/res/lang/en.bin
Binary files differ
diff --git a/res/lang/eo.bin b/res/lang/eo.bin
index 7b83b77f..2c4b98fc 100644
--- a/res/lang/eo.bin
+++ b/res/lang/eo.bin
Binary files differ
diff --git a/res/lang/es.bin b/res/lang/es.bin
index 91121500..bc3e23fc 100644
--- a/res/lang/es.bin
+++ b/res/lang/es.bin
Binary files differ
diff --git a/res/lang/es_MX.bin b/res/lang/es_MX.bin
index df129a8a..d158cc92 100644
--- a/res/lang/es_MX.bin
+++ b/res/lang/es_MX.bin
Binary files differ
diff --git a/res/lang/fi.bin b/res/lang/fi.bin
index fad0413b..4c21d7f9 100644
--- a/res/lang/fi.bin
+++ b/res/lang/fi.bin
Binary files differ
diff --git a/res/lang/fr.bin b/res/lang/fr.bin
index 20fd19ef..42b96be1 100644
--- a/res/lang/fr.bin
+++ b/res/lang/fr.bin
Binary files differ
diff --git a/res/lang/gl.bin b/res/lang/gl.bin
index 1cac09b9..8c0010b6 100644
--- a/res/lang/gl.bin
+++ b/res/lang/gl.bin
Binary files differ
diff --git a/res/lang/ia.bin b/res/lang/ia.bin
index 64ebfe03..2ee2884f 100644
--- a/res/lang/ia.bin
+++ b/res/lang/ia.bin
Binary files differ
diff --git a/res/lang/ie.bin b/res/lang/ie.bin
index 87064cef..3524454f 100644
--- a/res/lang/ie.bin
+++ b/res/lang/ie.bin
Binary files differ
diff --git a/res/lang/isv.bin b/res/lang/isv.bin
index 726441ce..cbe694c0 100644
--- a/res/lang/isv.bin
+++ b/res/lang/isv.bin
Binary files differ
diff --git a/res/lang/pl.bin b/res/lang/pl.bin
index a857c547..207f588d 100644
--- a/res/lang/pl.bin
+++ b/res/lang/pl.bin
Binary files differ
diff --git a/res/lang/ru.bin b/res/lang/ru.bin
index c1884651..5f8a3aa5 100644
--- a/res/lang/ru.bin
+++ b/res/lang/ru.bin
Binary files differ
diff --git a/res/lang/sk.bin b/res/lang/sk.bin
index 981ab7e1..766a7d1c 100644
--- a/res/lang/sk.bin
+++ b/res/lang/sk.bin
Binary files differ
diff --git a/res/lang/sr.bin b/res/lang/sr.bin
index 384665cc..d9b85067 100644
--- a/res/lang/sr.bin
+++ b/res/lang/sr.bin
Binary files differ
diff --git a/res/lang/tok.bin b/res/lang/tok.bin
index 51080283..9b80ccba 100644
--- a/res/lang/tok.bin
+++ b/res/lang/tok.bin
Binary files differ
diff --git a/res/lang/zh_Hans.bin b/res/lang/zh_Hans.bin
index 0cf56935..07f65240 100644
--- a/res/lang/zh_Hans.bin
+++ b/res/lang/zh_Hans.bin
Binary files differ
diff --git a/res/lang/zh_Hant.bin b/res/lang/zh_Hant.bin
index 28e7cedf..d0846df8 100644
--- a/res/lang/zh_Hant.bin
+++ b/res/lang/zh_Hant.bin
Binary files differ
diff --git a/src/app.c b/src/app.c
index 70b611c2..c7e803d4 100644
--- a/src/app.c
+++ b/src/app.c
@@ -2566,12 +2566,23 @@ iBool handleCommand_App(const char *cmd) {
2566 const iBool fromSidebar = argLabel_Command(cmd, "fromsidebar") != 0; 2566 const iBool fromSidebar = argLabel_Command(cmd, "fromsidebar") != 0;
2567 iUrl parts; 2567 iUrl parts;
2568 init_Url(&parts, url); 2568 init_Url(&parts, url);
2569 if (equal_Rangecc(parts.scheme, "about") && equal_Rangecc(parts.path, "command") &&
2570 !isEmpty_Range(&parts.query)) {
2571 /* NOTE: Careful here! `about:command` allows issuing UI events via links on the page.
2572 There is a special set of pages where these are allowed (e.g., "about:fonts").
2573 On every other page, `about:command` links will not be clickable. */
2574 iString *query = collectNewRange_String((iRangecc){
2575 parts.query.start + 1, parts.query.end
2576 });
2577 replace_String(query, "%20", " ");
2578 postCommandString_Root(NULL, query);
2579 return iTrue;
2580 }
2569 if (equalCase_Rangecc(parts.scheme, "titan")) { 2581 if (equalCase_Rangecc(parts.scheme, "titan")) {
2570 iUploadWidget *upload = new_UploadWidget(); 2582 iUploadWidget *upload = new_UploadWidget();
2571 setUrl_UploadWidget(upload, url); 2583 setUrl_UploadWidget(upload, url);
2572 setResponseViewer_UploadWidget(upload, document_App()); 2584 setResponseViewer_UploadWidget(upload, document_App());
2573 addChild_Widget(get_Root()->widget, iClob(upload)); 2585 addChild_Widget(get_Root()->widget, iClob(upload));
2574// finalizeSheet_Mobile(as_Widget(upload));
2575 setupSheetTransition_Mobile(as_Widget(upload), iTrue); 2586 setupSheetTransition_Mobile(as_Widget(upload), iTrue);
2576 postRefresh_App(); 2587 postRefresh_App();
2577 return iTrue; 2588 return iTrue;
@@ -3043,8 +3054,8 @@ iBool handleCommand_App(const char *cmd) {
3043 } 3054 }
3044 return iFalse; 3055 return iFalse;
3045 } 3056 }
3046 else if (equal_Command(cmd, "media.fontpack.enable")) { 3057 else if (equal_Command(cmd, "fontpack.enable")) {
3047 const iString *packId = collect_String(suffix_Command(cmd, "packid")); 3058 const iString *packId = collect_String(suffix_Command(cmd, "id"));
3048 if (arg_Command(cmd)) { 3059 if (arg_Command(cmd)) {
3049 remove_StringSet(d->prefs.disabledFontPacks, packId); 3060 remove_StringSet(d->prefs.disabledFontPacks, packId);
3050 } 3061 }
@@ -3052,10 +3063,35 @@ iBool handleCommand_App(const char *cmd) {
3052 insert_StringSet(d->prefs.disabledFontPacks, packId); 3063 insert_StringSet(d->prefs.disabledFontPacks, packId);
3053 } 3064 }
3054 resetFonts_App(); 3065 resetFonts_App();
3055 const iMedia *media = pointerLabel_Command(cmd, "media"); 3066 postCommand_App("navigate.reload");
3056 if (media) { 3067 return iTrue;
3057 postCommandf_App("media.fontpack.updated id:%u media:%p", 3068 }
3058 argU32Label_Command(cmd, "mediaid"), media); 3069 else if (equal_Command(cmd, "fontpack.delete")) {
3070 const iString *packId = collect_String(suffix_Command(cmd, "id"));
3071 if (isEmpty_String(packId)) {
3072 return iTrue;
3073 }
3074 const iFontPack *pack = pack_Fonts(cstr_String(packId));
3075 if (pack && loadPath_FontPack(pack)) {
3076 if (argLabel_Command(cmd, "confirmed")) {
3077 remove_StringSet(d->prefs.disabledFontPacks, packId);
3078 remove(cstr_String(loadPath_FontPack(pack)));
3079 reload_Fonts();
3080 postCommand_App("navigate.reload");
3081 }
3082 else {
3083 makeQuestion_Widget(
3084 uiTextCaution_ColorEscape "${heading.fontpack.delete}",
3085 format_Lang("${dlg.fontpack.delete.confirm}",
3086 cstr_String(packId)),
3087 (iMenuItem[]){ { "${cancel}" },
3088 { uiTextCaution_ColorEscape " ${dlg.fontpack.delete}",
3089 0,
3090 0,
3091 format_CStr("!fontpack.delete confirmed:1 id:%s",
3092 cstr_String(packId)) } },
3093 2);
3094 }
3059 } 3095 }
3060 return iTrue; 3096 return iTrue;
3061 } 3097 }
diff --git a/src/defs.h b/src/defs.h
index 40e134c9..9a466674 100644
--- a/src/defs.h
+++ b/src/defs.h
@@ -126,8 +126,8 @@ iLocalDef int acceptKeyMod_ReturnKeyBehavior(int behavior) {
126#define delete_Icon "\u232b" 126#define delete_Icon "\u232b"
127#define copy_Icon "\u2398" //"\u2bba" 127#define copy_Icon "\u2398" //"\u2bba"
128#define check_Icon "\u2714" 128#define check_Icon "\u2714"
129#define ballotChecked_Icon "\U0001f5f9" 129#define ballotChecked_Icon "\u2611"
130#define ballotUnchecked_Icon "\U0001f5f9" 130#define ballotUnchecked_Icon "\u2610"
131#define inbox_Icon "\U0001f4e5" 131#define inbox_Icon "\U0001f4e5"
132#define book_Icon "\U0001f56e" 132#define book_Icon "\U0001f56e"
133#define bookmark_Icon "\U0001f516" 133#define bookmark_Icon "\U0001f516"
@@ -162,6 +162,7 @@ iLocalDef int acceptKeyMod_ReturnKeyBehavior(int behavior) {
162#define select_Icon "\u2b1a" 162#define select_Icon "\u2b1a"
163#define downAngle_Icon "\ufe40" 163#define downAngle_Icon "\ufe40"
164#define photo_Icon "\U0001f5bc" 164#define photo_Icon "\U0001f5bc"
165#define fontpack_Icon "\U0001f520"
165 166
166#if defined (iPlatformApple) 167#if defined (iPlatformApple)
167# define shift_Icon "\u21e7" 168# define shift_Icon "\u21e7"
diff --git a/src/fontpack.c b/src/fontpack.c
index 9603bded..9b165051 100644
--- a/src/fontpack.c
+++ b/src/fontpack.c
@@ -32,6 +32,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32#include <the_Foundation/path.h> 32#include <the_Foundation/path.h>
33#include <the_Foundation/ptrarray.h> 33#include <the_Foundation/ptrarray.h>
34#include <the_Foundation/string.h> 34#include <the_Foundation/string.h>
35#include <the_Foundation/stringlist.h>
35#include <the_Foundation/toml.h> 36#include <the_Foundation/toml.h>
36 37
37const char *mimeType_FontPack = "application/lagrange-fontpack+zip"; 38const char *mimeType_FontPack = "application/lagrange-fontpack+zip";
@@ -236,6 +237,10 @@ iFontPackId id_FontPack(const iFontPack *d) {
236 return (iFontPackId){ &d->id, d->version }; 237 return (iFontPackId){ &d->id, d->version };
237} 238}
238 239
240const iString *loadPath_FontPack(const iFontPack *d) {
241 return d->loadPath;
242}
243
239const iPtrArray *listSpecs_FontPack(const iFontPack *d) { 244const iPtrArray *listSpecs_FontPack(const iFontPack *d) {
240 if (!d) return NULL; 245 if (!d) return NULL;
241 iPtrArray *list = collectNew_PtrArray(); 246 iPtrArray *list = collectNew_PtrArray();
@@ -441,6 +446,20 @@ void setLoadPath_FontPack(iFontPack *d, const iString *path) {
441 setRange_String(&d->id, withoutExtension_Path(&d->id)); 446 setRange_String(&d->id, withoutExtension_Path(&d->id));
442} 447}
443 448
449const iString *idFromUrl_FontPack(const iString *url) {
450 iString *id = new_String();
451 iUrl parts;
452 init_Url(&parts, url);
453 setRange_String(id, baseName_Path(collectNewRange_String(parts.path)));
454 setRange_String(id, withoutExtension_Path(id));
455 return collect_String(id);
456}
457
458void setUrl_FontPack(iFontPack *d, const iString *url) {
459 /* TODO: Should we remember the URL as well? */
460 set_String(&d->id, idFromUrl_FontPack(url));
461}
462
444void setStandalone_FontPack(iFontPack *d, iBool standalone) { 463void setStandalone_FontPack(iFontPack *d, iBool standalone) {
445 d->isStandalone = standalone; 464 d->isStandalone = standalone;
446} 465}
@@ -486,7 +505,7 @@ static void sortSpecs_Fonts_(iFonts *d) {
486 clear_PtrArray(&d->specOrder); 505 clear_PtrArray(&d->specOrder);
487 iConstForEach(PtrArray, p, &d->packs) { 506 iConstForEach(PtrArray, p, &d->packs) {
488 const iFontPack *pack = p.ptr; 507 const iFontPack *pack = p.ptr;
489 if (!contains_StringSet(prefs_App()->disabledFontPacks, &pack->id)) { 508 if (!isDisabled_FontPack(pack)) {
490 iConstForEach(Array, i, &pack->fonts) { 509 iConstForEach(Array, i, &pack->fonts) {
491 pushBack_PtrArray(&d->specOrder, i.value); 510 pushBack_PtrArray(&d->specOrder, i.value);
492 } 511 }
@@ -620,24 +639,131 @@ const iPtrArray *listSpecsByPriority_Fonts(void) {
620 return &fonts_.specOrder; 639 return &fonts_.specOrder;
621} 640}
622 641
642iString *infoText_FontPack(const iFontPack *d) {
643 const iFontPack *installed = pack_Fonts(cstr_String(&d->id));
644 const iBool isInstalled = (installed != NULL);
645 const int installedVersion = installed ? installed->version : 0;
646 const iBool isDisabled = isDisabled_FontPack(d);
647 iString *str = new_String();
648 size_t sizeInBytes = 0;
649 iPtrSet *uniqueFiles = new_PtrSet();
650 iStringList *names = new_StringList();
651 iConstForEach(PtrArray, i, listSpecs_FontPack(d)) {
652 const iFontSpec *spec = i.ptr;
653 pushBack_StringList(names, &spec->name);
654 iForIndices(j, spec->styles) {
655 insert_PtrSet(uniqueFiles, spec->styles[j]);
656 }
657 }
658 iConstForEach(PtrSet, j, uniqueFiles) {
659 sizeInBytes += size_Block(&((const iFontFile *) *j.value)->sourceData);
660 }
661 appendFormat_String(str, "%.1f ${mb} (%s)\n%s: %s\n",
662 sizeInBytes / 1.0e6,
663 formatCStrs_Lang("num.files.n", size_PtrSet(uniqueFiles)),
664 formatCStrs_Lang("num.fonts.n", size_StringList(names)),
665 cstrCollect_String(joinCStr_StringList(names, ", ")),
666 d->version);
667 if (isInstalled && installedVersion != d->version) {
668 appendCStr_String(str, format_Lang("${fontpack.meta.version}\n", d->version));
669 }
670 if (!isEmpty_String(&d->id)) {
671 appendFormat_String(str, "%s %s%s\n",
672 isInstalled ? ballotChecked_Icon : ballotUnchecked_Icon,
673 isInstalled ? (installedVersion == d->version ? "${fontpack.meta.installed}"
674 : format_CStr("${fontpack.meta.installed} (%s)",
675 format_Lang("${fontpack.meta.version}",
676 installedVersion)))
677 : "${fontpack.meta.notinstalled}",
678 isDisabled ? "${fontpack.meta.disabled}" : "");
679 }
680 iRelease(names);
681 delete_PtrSet(uniqueFiles);
682 return str;
683}
684
685const iArray *actions_FontPack(const iFontPack *d) {
686 iArray *items = new_Array(sizeof(iMenuItem));
687 const iFontPackId fp = id_FontPack(d);
688 const char *fpId = cstr_String(fp.id);
689 const iFontPack *installed = pack_Fonts(fpId);
690 const iBool isEnabled = !isDisabled_FontPack(d);
691 if (isInstalled_Fonts(fpId)) {
692 if (d->version > installed->version) {
693 pushBack_Array(items, &(iMenuItem){
694 format_Lang(add_Icon " ${fontpack.upgrade}", fpId, d->version),
695 SDLK_RETURN, 0, "fontpack.install"
696 });
697 }
698 pushBack_Array(items, &(iMenuItem){
699 format_Lang(isEnabled ? close_Icon " ${fontpack.disable}"
700 : leftArrowhead_Icon " ${fontpack.enable}", fpId), 0, 0,
701 format_CStr("fontpack.enable arg:%d id:%s", !isEnabled, fpId) });
702 if (!d->isReadOnly && installed->loadPath && d->loadPath &&
703 !cmpString_String(installed->loadPath, d->loadPath)) {
704 pushBack_Array(items, &(iMenuItem){
705 format_Lang(delete_Icon " ${fontpack.delete}", fpId), 0, 0,
706 format_CStr("fontpack.delete id:%s", fpId) });
707 }
708 }
709 else if (d->isStandalone) {
710 pushBack_Array(items, &(iMenuItem){
711 format_Lang(add_Icon " ${fontpack.install}", fpId),
712 SDLK_RETURN, 0, "fontpack.install"
713 });
714 }
715 return collect_Array(items);
716}
717
718iBool isDisabled_FontPack(const iFontPack *d) {
719 return contains_StringSet(prefs_App()->disabledFontPacks, &d->id);
720}
721
623const iString *infoPage_Fonts(void) { 722const iString *infoPage_Fonts(void) {
624 iFonts *d = &fonts_; 723 iFonts *d = &fonts_;
625 iString *str = collectNewCStr_String("# Fonts\n"); 724 iString *str = collectNewCStr_String("# ${heading.fontpack.meta}\n"
725 "=> gemini://skyjake.fi/fonts Download more fonts\n"
726 "=> about:command?!open%20newtab:1%20gotoheading:1%20url:about:help Using fonts in Lagrange\n"
727 "=> about:command?!open%20newtab:1%20gotoheading:1%20url:about:help How to create a fontpack\n");
626 iPtrArray *specsByPack = collectNew_PtrArray(); 728 iPtrArray *specsByPack = collectNew_PtrArray();
627 setCopy_PtrArray(specsByPack, &d->specOrder); 729 setCopy_PtrArray(specsByPack, &d->specOrder);
628 sort_Array(specsByPack, cmpSourceAndPriority_FontSpecPtr_); 730 sort_Array(specsByPack, cmpSourceAndPriority_FontSpecPtr_);
629 iString *currentSourcePath = collectNew_String(); 731 iString *currentSourcePath = collectNew_String();
630 iConstForEach(PtrArray, i, specsByPack) { 732 for (int group = 0; group < 2; group++) {
631 const iFontSpec *spec = i.ptr; 733 iBool isFirst = iTrue;
632 if (isEmpty_String(&spec->sourcePath)) { 734 iConstForEach(PtrArray, i, specsByPack) {
633 continue; /* built-in font */ 735 const iFontSpec *spec = i.ptr;
634 } 736 if (isEmpty_String(&spec->sourcePath)) {
635 if (!equal_String(&spec->sourcePath, currentSourcePath)) { 737 continue; /* built-in font */
636 appendFormat_String(str, "=> %s %s%s\n", 738 }
637 cstrCollect_String(makeFileUrl_String(&spec->sourcePath)), 739 if (!equal_String(&spec->sourcePath, currentSourcePath)) {
638 endsWithCase_String(&spec->sourcePath, ".fontpack") ? "\U0001f520 " : "", 740 set_String(currentSourcePath, &spec->sourcePath);
639 cstr_Rangecc(baseName_Path(&spec->sourcePath))); 741 /* Print some information about this page. */
640 set_String(currentSourcePath, &spec->sourcePath); 742 const iFontPack *pack = packByPath_Fonts(currentSourcePath);
743 if (pack) {
744 if (!isDisabled_FontPack(pack) ^ group) {
745 if (isFirst) {
746 appendCStr_String(str, "\n## ");
747 appendCStr_String(str, group == 0 ? "${heading.fontpack.meta.enabled}"
748 : "${heading.fontpack.meta.disabled}");
749 appendCStr_String(str, "\n\n");
750 isFirst = iFalse;
751 }
752 const iString *packId = id_FontPack(pack).id;
753 appendFormat_String(str, "### %s\n=> %s ${fontpack.meta.viewfile}\n",
754 isEmpty_String(packId) ? "fonts.ini" :
755 cstr_String(packId),
756 cstrCollect_String(makeFileUrl_String(&spec->sourcePath)));
757 append_String(str, collect_String(infoText_FontPack(pack)));
758 iConstForEach(Array, a, actions_FontPack(pack)) {
759 const iMenuItem *item = a.value;
760 appendFormat_String(str, "=> about:command?%s %s\n",
761 cstr_String(withSpacesEncoded_String(collectNewCStr_String(item->command))),
762 item->label);
763 }
764 }
765 }
766 }
641 } 767 }
642 } 768 }
643 return str; 769 return str;
@@ -645,6 +771,9 @@ const iString *infoPage_Fonts(void) {
645 771
646const iFontPack *pack_Fonts(const char *packId) { 772const iFontPack *pack_Fonts(const char *packId) {
647 iFonts *d = &fonts_; 773 iFonts *d = &fonts_;
774 if (!*packId) {
775 return NULL;
776 }
648 iConstForEach(PtrArray, i, &d->packs) { 777 iConstForEach(PtrArray, i, &d->packs) {
649 const iFontPack *pack = i.ptr; 778 const iFontPack *pack = i.ptr;
650 if (!cmp_String(&pack->id, packId)) { 779 if (!cmp_String(&pack->id, packId)) {
@@ -675,10 +804,15 @@ void reload_Fonts(void) {
675 delete_String(userDir); 804 delete_String(userDir);
676} 805}
677 806
678void install_Fonts(const iString *fontId, const iBlock *data) { 807void install_Fonts(const iString *packId, const iBlock *data) {
808 if (!detect_FontPack(data)) {
809 return;
810 }
811 /* Newly installed packs will never be disabled. */
812 remove_StringSet(prefs_App()->disabledFontPacks, packId);
679 iFonts *d = &fonts_; 813 iFonts *d = &fonts_;
680 iFile *f = new_File(collect_String(concatCStr_Path( 814 iFile *f = new_File(collect_String(concatCStr_Path(
681 userFontsDirectory_Fonts_(d), format_CStr("%s.fontpack", cstr_String(fontId))))); 815 userFontsDirectory_Fonts_(d), format_CStr("%s.fontpack", cstr_String(packId)))));
682 if (open_File(f, writeOnly_FileMode)) { 816 if (open_File(f, writeOnly_FileMode)) {
683 write_File(f, data); 817 write_File(f, data);
684 } 818 }
@@ -687,38 +821,5 @@ void install_Fonts(const iString *fontId, const iBlock *data) {
687 reload_Fonts(); 821 reload_Fonts();
688} 822}
689 823
690iBool preloadLocalFontpackForPreview_Fonts(iGmDocument *doc) {
691 iBool wasLoaded = iFalse;
692 for (size_t linkId = 1; ; linkId++) {
693 const iString *linkUrl = linkUrl_GmDocument(doc, linkId);
694 if (!linkUrl) {
695 break; /* ran out of links */
696 }
697 const int linkFlags = linkFlags_GmDocument(doc, linkId);
698 if (linkFlags & fontpackFileExtension_GmLinkFlag &&
699 scheme_GmLinkFlag(linkFlags) == file_GmLinkScheme) {
700 iMediaId linkMedia = findMediaForLink_Media(media_GmDocument(doc), linkId, fontpack_MediaType);
701 if (linkMedia.type) {
702 continue; /* got this one already */
703 }
704 iString *filePath = localFilePathFromUrl_String(linkUrl);
705 iFile *f = new_File(filePath);
706 if (open_File(f, readOnly_FileMode)) {
707 iBlock *fontPackArchiveData = readAll_File(f);
708 setUrl_Media(media_GmDocument(doc), linkId, fontpack_MediaType, linkUrl);
709 setData_Media(media_GmDocument(doc),
710 linkId,
711 collectNewCStr_String(mimeType_FontPack),
712 fontPackArchiveData,
713 0);
714 delete_Block(fontPackArchiveData);
715 wasLoaded = iTrue;
716 }
717 iRelease(f);
718 }
719 }
720 return wasLoaded;
721}
722
723iDefineClass(FontFile) 824iDefineClass(FontFile)
724 825
diff --git a/src/fontpack.h b/src/fontpack.h
index 2fdfa9ab..fb8d757e 100644
--- a/src/fontpack.h
+++ b/src/fontpack.h
@@ -148,12 +148,21 @@ struct Impl_FontPackId {
148void setReadOnly_FontPack (iFontPack *, iBool readOnly); 148void setReadOnly_FontPack (iFontPack *, iBool readOnly);
149void setStandalone_FontPack (iFontPack *, iBool standalone); 149void setStandalone_FontPack (iFontPack *, iBool standalone);
150void setLoadPath_FontPack (iFontPack *, const iString *path); 150void setLoadPath_FontPack (iFontPack *, const iString *path);
151void setUrl_FontPack (iFontPack *, const iString *url);
151iBool loadArchive_FontPack (iFontPack *, const iArchive *zip); 152iBool loadArchive_FontPack (iFontPack *, const iArchive *zip);
152iBool detect_FontPack (const iBlock *data); 153iBool detect_FontPack (const iBlock *data);
153 154
154iFontPackId id_FontPack (const iFontPack *); 155iFontPackId id_FontPack (const iFontPack *);
155const iPtrArray * listSpecs_FontPack (const iFontPack *); 156const iString * loadPath_FontPack (const iFontPack *); /* may return NULL */
157iBool isDisabled_FontPack (const iFontPack *);
156iBool isReadOnly_FontPack (const iFontPack *); 158iBool isReadOnly_FontPack (const iFontPack *);
159const iPtrArray * listSpecs_FontPack (const iFontPack *);
160iString * infoText_FontPack (const iFontPack *);
161const iArray * actions_FontPack (const iFontPack *);
162
163const iString * idFromUrl_FontPack (const iString *url);
164
165/*----------------------------------------------------------------------------------------------*/
157 166
158iDeclareType(GmDocument) 167iDeclareType(GmDocument)
159 168
@@ -170,8 +179,6 @@ const iString * infoPage_Fonts (void);
170void install_Fonts (const iString *fontId, const iBlock *data); 179void install_Fonts (const iString *fontId, const iBlock *data);
171void reload_Fonts (void); 180void reload_Fonts (void);
172 181
173iBool preloadLocalFontpackForPreview_Fonts (iGmDocument *doc);
174
175iLocalDef iBool isInstalled_Fonts(const char *packId) { 182iLocalDef iBool isInstalled_Fonts(const char *packId) {
176 return pack_Fonts(packId) != NULL; 183 return pack_Fonts(packId) != NULL;
177} 184}
diff --git a/src/gmdocument.c b/src/gmdocument.c
index 0adf5243..1f66e978 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -94,6 +94,7 @@ struct Impl_GmDocument {
94 iString localHost; 94 iString localHost;
95 iInt2 size; 95 iInt2 size;
96 int outsideMargin; 96 int outsideMargin;
97 iBool enableCommandLinks; /* `about:command?` only allowed on selected pages */
97 iArray layout; /* contents of source, laid out in document space */ 98 iArray layout; /* contents of source, laid out in document space */
98 iPtrArray links; 99 iPtrArray links;
99 enum iGmDocumentBanner bannerType; 100 enum iGmDocumentBanner bannerType;
@@ -256,6 +257,14 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li
256 link->urlRange = capturedRange_RegExpMatch(&m, 1); 257 link->urlRange = capturedRange_RegExpMatch(&m, 1);
257 setRange_String(&link->url, link->urlRange); 258 setRange_String(&link->url, link->urlRange);
258 set_String(&link->url, canonicalUrl_String(absoluteUrl_String(&d->url, &link->url))); 259 set_String(&link->url, canonicalUrl_String(absoluteUrl_String(&d->url, &link->url)));
260 if (startsWithCase_String(&link->url, "about:command")) {
261 /* This is a special internal page that allows submitting UI events. */
262 if (!d->enableCommandLinks) {
263 delete_GmLink(link);
264 *linkId = 0;
265 return line;
266 }
267 }
259 /* Check the URL. */ { 268 /* Check the URL. */ {
260 iUrl parts; 269 iUrl parts;
261 init_Url(&parts, &link->url); 270 init_Url(&parts, &link->url);
@@ -336,7 +345,7 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li
336 /* Check for a custom icon. */ 345 /* Check for a custom icon. */
337 enum iGmLinkScheme scheme = scheme_GmLinkFlag(link->flags); 346 enum iGmLinkScheme scheme = scheme_GmLinkFlag(link->flags);
338 if ((scheme == gemini_GmLinkScheme && ~link->flags & remote_GmLinkFlag) || 347 if ((scheme == gemini_GmLinkScheme && ~link->flags & remote_GmLinkFlag) ||
339 scheme == file_GmLinkScheme || 348 scheme == about_GmLinkScheme || scheme == file_GmLinkScheme ||
340 scheme == mailto_GmLinkScheme) { 349 scheme == mailto_GmLinkScheme) {
341 iChar icon = 0; 350 iChar icon = 0;
342 int len = 0; 351 int len = 0;
@@ -826,6 +835,7 @@ static void doLayout_GmDocument_(iGmDocument *d) {
826 : scheme == mailto_GmLinkScheme ? envelope 835 : scheme == mailto_GmLinkScheme ? envelope
827 : link->flags & remote_GmLinkFlag ? globe 836 : link->flags & remote_GmLinkFlag ? globe
828 : link->flags & imageFileExtension_GmLinkFlag ? image 837 : link->flags & imageFileExtension_GmLinkFlag ? image
838 : link->flags & fontpackFileExtension_GmLinkFlag ? fontpack_Icon
829 : scheme == file_GmLinkScheme ? folder 839 : scheme == file_GmLinkScheme ? folder
830 : arrow); 840 : arrow);
831 /* Custom link icon is shown on local Gemini links only. */ 841 /* Custom link icon is shown on local Gemini links only. */
@@ -1012,14 +1022,6 @@ static void doLayout_GmDocument_(iGmDocument *d) {
1012 pushBack_Array(&d->layout, &run); 1022 pushBack_Array(&d->layout, &run);
1013 break; 1023 break;
1014 } 1024 }
1015 case fontpack_MediaType: {
1016 run.bounds.pos = pos;
1017 run.bounds.size.x = d->size.x;
1018 run.bounds.size.y = height_FontpackUI(d->media, media.id, d->size.x);
1019 run.visBounds = run.bounds;
1020 pushBack_Array(&d->layout, &run);
1021 break;
1022 }
1023 default: 1025 default:
1024 break; 1026 break;
1025 } 1027 }
@@ -1069,6 +1071,7 @@ void init_GmDocument(iGmDocument *d) {
1069 d->bannerType = siteDomain_GmDocumentBanner; 1071 d->bannerType = siteDomain_GmDocumentBanner;
1070 d->outsideMargin = 0; 1072 d->outsideMargin = 0;
1071 d->size = zero_I2(); 1073 d->size = zero_I2();
1074 d->enableCommandLinks = iFalse;
1072 init_Array(&d->layout, sizeof(iGmRun)); 1075 init_Array(&d->layout, sizeof(iGmRun));
1073 init_PtrArray(&d->links); 1076 init_PtrArray(&d->links);
1074 init_String(&d->bannerText); 1077 init_String(&d->bannerText);
@@ -1760,6 +1763,10 @@ void setUrl_GmDocument(iGmDocument *d, const iString *url) {
1760 init_Url(&parts, url); 1763 init_Url(&parts, url);
1761 setRange_String(&d->localHost, parts.host); 1764 setRange_String(&d->localHost, parts.host);
1762 updateIconBasedOnUrl_GmDocument_(d); 1765 updateIconBasedOnUrl_GmDocument_(d);
1766 if (!cmp_String(url, "about:fonts")) {
1767 /* This is an interactive internal page. */
1768 d->enableCommandLinks = iTrue;
1769 }
1763} 1770}
1764 1771
1765static int replaceRegExp_String(iString *d, const iRegExp *regexp, const char *replacement, 1772static int replaceRegExp_String(iString *d, const iRegExp *regexp, const char *replacement,
@@ -1973,9 +1980,15 @@ void setSource_GmDocument(iGmDocument *d, const iString *source, int width, int
1973 d->theme.ansiEscapesEnabled = prefs_App()->gemtextAnsiEscapes; 1980 d->theme.ansiEscapesEnabled = prefs_App()->gemtextAnsiEscapes;
1974 } 1981 }
1975 else if (d->format == markdown_SourceFormat) { 1982 else if (d->format == markdown_SourceFormat) {
1976 convertMarkdownToGemtext_GmDocument_(d); 1983 /* Attempt a conversion to Gemtext when viewing local Markdown files. */
1977 set_String(&d->unormSource, &d->source); /* use the converted source from now on */ 1984 if (equalCase_Rangecc(urlScheme_String(&d->url), "file")) {
1978 d->theme.ansiEscapesEnabled = iTrue; /* escapes are used for styling */ 1985 convertMarkdownToGemtext_GmDocument_(d);
1986 set_String(&d->unormSource, &d->source); /* use the converted source from now on */
1987 d->theme.ansiEscapesEnabled = iTrue; /* escapes are used for styling */
1988 }
1989 else {
1990 d->format = plainText_SourceFormat; /* just show as plain text */
1991 }
1979 } 1992 }
1980 else { 1993 else {
1981 d->theme.ansiEscapesEnabled = iTrue; 1994 d->theme.ansiEscapesEnabled = iTrue;
diff --git a/src/gmutil.c b/src/gmutil.c
index 692c1cb9..e44971d0 100644
--- a/src/gmutil.c
+++ b/src/gmutil.c
@@ -539,6 +539,9 @@ const char *mediaTypeFromFileExtension_String(const iString *d) {
539 else if (endsWithCase_String(d, ".fontpack")) { 539 else if (endsWithCase_String(d, ".fontpack")) {
540 return mimeType_FontPack; 540 return mimeType_FontPack;
541 } 541 }
542 else if (endsWithCase_String(d, ".ttf")) {
543 return "font/ttf";
544 }
542 else if (endsWithCase_String(d, ".xml")) { 545 else if (endsWithCase_String(d, ".xml")) {
543 return "text/xml"; 546 return "text/xml";
544 } 547 }
diff --git a/src/lang.c b/src/lang.c
index a867f911..1dac4201 100644
--- a/src/lang.c
+++ b/src/lang.c
@@ -247,3 +247,12 @@ const char *formatCStr_Lang(const char *formatMsgId, int count) {
247const char *formatCStrs_Lang(const char *formatMsgId, size_t count) { 247const char *formatCStrs_Lang(const char *formatMsgId, size_t count) {
248 return format_CStr(cstrCount_Lang(formatMsgId, (int) count), count); 248 return format_CStr(cstrCount_Lang(formatMsgId, (int) count), count);
249} 249}
250
251const char *format_Lang(const char *formatTextWithIds, ...) {
252 iBlock *msg = new_Block(0);
253 va_list args;
254 va_start(args, formatTextWithIds);
255 vprintf_Block(msg, translateCStr_Lang(formatTextWithIds), args);
256 va_end(args);
257 return cstr_Block(collect_Block(msg));
258}
diff --git a/src/lang.h b/src/lang.h
index 23161bb2..e3e6c433 100644
--- a/src/lang.h
+++ b/src/lang.h
@@ -39,3 +39,4 @@ const char * translateCStr_Lang (const char *textWithIds);
39const char * cstrCount_Lang (const char *msgId, int count); 39const char * cstrCount_Lang (const char *msgId, int count);
40const char * formatCStr_Lang (const char *formatMsgId, int count); 40const char * formatCStr_Lang (const char *formatMsgId, int count);
41const char * formatCStrs_Lang (const char *formatMsgId, size_t count); 41const char * formatCStrs_Lang (const char *formatMsgId, size_t count);
42const char * format_Lang (const char *formatTextWithIds, ...);
diff --git a/src/media.c b/src/media.c
index 412205a7..a3f381ec 100644
--- a/src/media.c
+++ b/src/media.c
@@ -288,76 +288,6 @@ iDefineTypeConstruction(GmDownload)
288 288
289/*----------------------------------------------------------------------------------------------*/ 289/*----------------------------------------------------------------------------------------------*/
290 290
291iDeclareType(GmFontpack)
292
293struct Impl_GmFontpack {
294 iGmMediaProps props;
295 iString packId;
296 iFontpackMediaInfo info;
297 /* TODO: Font preview images? */
298};
299
300void init_GmFontpack(iGmFontpack *d) {
301 init_GmMediaProps_(&d->props);
302 init_String(&d->packId);
303 iZap(d->info);
304 d->info.names = new_StringList();
305}
306
307void deinit_GmFontpack(iGmFontpack *d) {
308 iRelease(d->info.names);
309 deinit_String(&d->packId);
310 deinit_GmMediaProps_(&d->props);
311}
312
313static void loadData_GmFontpack_(iGmFontpack *d, const iBlock *data) {
314 const iString *loadPath = collect_String(localFilePathFromUrl_String(&d->props.url));
315 const iFontPack *pack = packByPath_Fonts(loadPath);
316 d->info.isValid = d->info.isInstalled = pack != NULL;
317 d->info.isReadOnly = iFalse;
318 d->info.isDisabled = iFalse;
319 clear_StringList(d->info.names);
320 clear_String(&d->packId);
321 if (!pack) {
322 /* Let's load it now temporarily and see what's inside. */
323 iArchive *zip = new_Archive();
324 if (openData_Archive(zip, data)) {
325 iFontPack *fp = collect_FontPack(new_FontPack());
326 setLoadPath_FontPack(fp, loadPath);
327 /* TODO: No need to load all the font files here, just the metadata. */
328 setStandalone_FontPack(fp, iTrue);
329 if (loadArchive_FontPack(fp, zip)) {
330 d->info.isValid = iTrue;
331 pack = fp;
332 }
333 }
334 iRelease(zip);
335 }
336 if (pack) {
337 set_String(&d->packId, id_FontPack(pack).id);
338 d->info.packId.id = &d->packId; /* we own this String */
339 d->info.packId.version = id_FontPack(pack).version;
340 d->info.isReadOnly = isReadOnly_FontPack(pack);
341 d->info.isDisabled = contains_StringSet(prefs_App()->disabledFontPacks, &d->packId);
342 }
343 iPtrSet *unique = new_PtrSet();
344 iConstForEach(PtrArray, i, listSpecs_FontPack(pack)) {
345 const iFontSpec *spec = i.ptr;
346 pushBack_StringList(d->info.names, &spec->name);
347 iForIndices(j, spec->styles) {
348 insert_PtrSet(unique, spec->styles[j]);
349 }
350 }
351 iConstForEach(PtrSet, j, unique) {
352 d->info.sizeInBytes += size_Block(&((const iFontFile *) *j.value)->sourceData);
353 }
354 delete_PtrSet(unique);
355}
356
357iDefineTypeConstruction(GmFontpack)
358
359/*----------------------------------------------------------------------------------------------*/
360
361struct Impl_Media { 291struct Impl_Media {
362 iPtrArray items[max_MediaType]; 292 iPtrArray items[max_MediaType];
363 /* TODO: Add a hash to quickly look up a link's media. */ 293 /* TODO: Add a hash to quickly look up a link's media. */
@@ -388,9 +318,6 @@ void clear_Media(iMedia *d) {
388 iForEach(PtrArray, n, &d->items[download_MediaType]) { 318 iForEach(PtrArray, n, &d->items[download_MediaType]) {
389 deinit_GmDownload(n.ptr); 319 deinit_GmDownload(n.ptr);
390 } 320 }
391 iForEach(PtrArray, f, &d->items[fontpack_MediaType]) {
392 deinit_GmFontpack(f.ptr);
393 }
394 iForIndices(type, d->items) { 321 iForIndices(type, d->items) {
395 clear_PtrArray(&d->items[type]); 322 clear_PtrArray(&d->items[type]);
396 } 323 }
@@ -436,17 +363,6 @@ iBool setUrl_Media(iMedia *d, iGmLinkId linkId, enum iMediaType mediaType, const
436 } 363 }
437 props = &dl->props; 364 props = &dl->props;
438 } 365 }
439 else if (mediaType == fontpack_MediaType) {
440 iGmFontpack *fp = NULL;
441 if (isNew) {
442 fp = new_GmFontpack();
443 pushBack_PtrArray(&d->items[fontpack_MediaType], fp);
444 }
445 else {
446 fp = at_PtrArray(&d->items[fontpack_MediaType], index_MediaId(existing));
447 }
448 props = &fp->props;
449 }
450 if (props) { 366 if (props) {
451 props->linkId = linkId; 367 props->linkId = linkId;
452 props->isPermanent = iTrue; 368 props->isPermanent = iTrue;
@@ -517,21 +433,6 @@ iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo
517 } 433 }
518 } 434 }
519 } 435 }
520 else if (existing.type == fontpack_MediaType) {
521 iGmFontpack *fp;
522 if (isDeleting) {
523 take_PtrArray(&d->items[fontpack_MediaType], existingIndex, (void **) &fp);
524 delete_GmFontpack(fp);
525 }
526 else {
527 iAssert(!isPartial);
528 fp = at_PtrArray(&d->items[fontpack_MediaType], existingIndex);
529 if (isEmpty_String(&fp->props.mime)) {
530 set_String(&fp->props.mime, mime);
531 }
532 loadData_GmFontpack_(fp, data);
533 }
534 }
535 else if (!isDeleting) { 436 else if (!isDeleting) {
536 if (startsWith_String(mime, "image/")) { 437 if (startsWith_String(mime, "image/")) {
537 /* Copy the image to a texture. */ 438 /* Copy the image to a texture. */
@@ -591,23 +492,6 @@ iMediaId findMediaForLink_Media(const iMedia *d, iGmLinkId linkId, enum iMediaTy
591 return mid; 492 return mid;
592} 493}
593 494
594#if 0
595iMediaId findUrl_Media(const iMedia *d, const iString *url) {
596 iMediaId mid = iInvalidMediaId;
597 for (int i = 0; i < max_MediaType; i++) {
598 for (int j = 0; j < size_PtrArray(&d->items[i]); j++) {
599 const iGmMediaProps *props = constAt_PtrArray(&d->items[i], j);
600 if (equal_String(&props->url, url)) {
601 mid.type = i;
602 mid.id = j + 1;
603 break;
604 }
605 }
606 }
607 return mid;
608}
609#endif
610
611size_t numAudio_Media(const iMedia *d) { 495size_t numAudio_Media(const iMedia *d) {
612 return size_PtrArray(&d->items[audio_MediaType]); 496 return size_PtrArray(&d->items[audio_MediaType]);
613} 497}
@@ -662,9 +546,6 @@ iBool info_Media(const iMedia *d, iMediaId mediaId, iGmMediaInfo *info_out) {
662 return iTrue; 546 return iTrue;
663 } 547 }
664 break; 548 break;
665 case fontpack_MediaType:
666 /* TODO */
667 break;
668 default: 549 default:
669 break; 550 break;
670 } 551 }
@@ -718,16 +599,6 @@ void downloadStats_Media(const iMedia *d, iMediaId downloadId, const iString **p
718 } 599 }
719} 600}
720 601
721void fontpackInfo_Media(const iMedia *d, iMediaId fontpackId, iFontpackMediaInfo *info_out) {
722 iAssert(fontpackId.type == fontpack_MediaType);
723 iZap(*info_out);
724 const size_t index = index_MediaId(fontpackId);
725 if (index < size_PtrArray(&d->items[fontpack_MediaType])) {
726 const iGmFontpack *fp = constAt_PtrArray(&d->items[fontpack_MediaType], index);
727 *info_out = fp->info;
728 }
729}
730
731/*----------------------------------------------------------------------------------------------*/ 602/*----------------------------------------------------------------------------------------------*/
732 603
733static void updated_MediaRequest_(iAnyObject *obj) { 604static void updated_MediaRequest_(iAnyObject *obj) {
diff --git a/src/media.h b/src/media.h
index 92a46cd3..3b329716 100644
--- a/src/media.h
+++ b/src/media.h
@@ -53,7 +53,6 @@ enum iMediaType { /* Note: There is a limited number of bits for these; see GmRu
53 //animatedImage_MediaType, /* TODO */ 53 //animatedImage_MediaType, /* TODO */
54 audio_MediaType, 54 audio_MediaType,
55 download_MediaType, 55 download_MediaType,
56 fontpack_MediaType,
57 max_MediaType 56 max_MediaType
58}; 57};
59 58
@@ -74,7 +73,6 @@ iBool setData_Media (iMedia *, uint16_t linkId, const iStrin
74 73
75size_t memorySize_Media (const iMedia *); 74size_t memorySize_Media (const iMedia *);
76iMediaId findMediaForLink_Media (const iMedia *, uint16_t linkId, enum iMediaType mediaType); 75iMediaId findMediaForLink_Media (const iMedia *, uint16_t linkId, enum iMediaType mediaType);
77//iMediaId findUrl_Media (const iMedia *, const iString *url);
78 76
79iMediaId id_Media (const iMedia *, uint16_t linkId, enum iMediaType type); 77iMediaId id_Media (const iMedia *, uint16_t linkId, enum iMediaType type);
80iBool info_Media (const iMedia *, iMediaId mediaId, iGmMediaInfo *info_out); 78iBool info_Media (const iMedia *, iMediaId mediaId, iGmMediaInfo *info_out);
@@ -109,21 +107,6 @@ void pauseAllPlayers_Media (const iMedia *, iBool setPaused);
109void downloadStats_Media (const iMedia *, iMediaId downloadId, const iString **path_out, 107void downloadStats_Media (const iMedia *, iMediaId downloadId, const iString **path_out,
110 float *bytesPerSecond_out, iBool *isFinished_out); 108 float *bytesPerSecond_out, iBool *isFinished_out);
111 109
112iDeclareType(FontpackMediaInfo)
113
114struct Impl_FontpackMediaInfo {
115 iFontPackId packId;
116 iBool isValid;
117 iBool isInstalled;
118 iBool isDisabled;
119 iBool isReadOnly;
120 size_t sizeInBytes;
121 iStringList *names;
122};
123
124void fontpackInfo_Media (const iMedia *, iMediaId fontpackId,
125 iFontpackMediaInfo *info_out);
126
127/*----------------------------------------------------------------------------------------------*/ 110/*----------------------------------------------------------------------------------------------*/
128 111
129iDeclareType(GmRequest) 112iDeclareType(GmRequest)
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 1aca895c..a1992967 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -1257,7 +1257,7 @@ static const char *zipPageHeading_(const iRangecc mime) {
1257 return book_Icon " Gempub"; 1257 return book_Icon " Gempub";
1258 } 1258 }
1259 else if (equalCase_Rangecc(mime, mimeType_FontPack)) { 1259 else if (equalCase_Rangecc(mime, mimeType_FontPack)) {
1260 return "\U0001f520 Fontpack"; 1260 return fontpack_Icon " Fontpack";
1261 } 1261 }
1262 iRangecc type = iNullRange; 1262 iRangecc type = iNullRange;
1263 nextSplit_Rangecc(mime, "/", &type); /* skip the part before the slash */ 1263 nextSplit_Rangecc(mime, "/", &type); /* skip the part before the slash */
@@ -1435,13 +1435,6 @@ static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool
1435 } 1435 }
1436 } 1436 }
1437 } 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 }
1445} 1438}
1446 1439
1447static void updateDocument_DocumentWidget_(iDocumentWidget *d, 1440static void updateDocument_DocumentWidget_(iDocumentWidget *d,
@@ -1503,6 +1496,25 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d,
1503 appendFormat_String(&str, 1496 appendFormat_String(&str,
1504 cstr_Lang("doc.archive"), 1497 cstr_Lang("doc.archive"),
1505 cstr_Rangecc(baseName_Path(d->mod.url))); 1498 cstr_Rangecc(baseName_Path(d->mod.url)));
1499 if (isRequestFinished) {
1500 if (equal_Rangecc(param, mimeType_FontPack)) {
1501 /* Show some information about fontpacks, and set up footer actions. */
1502 iArchive *zip = iClob(new_Archive());
1503 if (openData_Archive(zip, &d->sourceContent)) {
1504 iFontPack *fp = new_FontPack();
1505 setUrl_FontPack(fp, d->mod.url);
1506 setStandalone_FontPack(fp, iTrue);
1507 if (loadArchive_FontPack(fp, zip)) {
1508 appendFormat_String(&str, "\n\n%s",
1509 cstrCollect_String(infoText_FontPack(fp)));
1510 }
1511 const iArray *actions = actions_FontPack(fp);
1512 makeFooterButtons_DocumentWidget_(d, constData_Array(actions),
1513 size_Array(actions));
1514 delete_FontPack(fp);
1515 }
1516 }
1517 }
1506 appendCStr_String(&str, "\n\n"); 1518 appendCStr_String(&str, "\n\n");
1507 iString *localPath = localFilePathFromUrl_String(d->mod.url); 1519 iString *localPath = localFilePathFromUrl_String(d->mod.url);
1508 if (!localPath) { 1520 if (!localPath) {
@@ -2916,27 +2928,6 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
2916 updateMedia_DocumentWidget_(d); 2928 updateMedia_DocumentWidget_(d);
2917 return iFalse; 2929 return iFalse;
2918 } 2930 }
2919 else if (equal_Command(cmd, "media.fontpack.updated")) {
2920 iMedia *media = pointerLabel_Command(cmd, "media");
2921 if (media == media_GmDocument(d->doc)) {
2922 /*iGmMediaInfo info;
2923 if (info_Media(media,
2924 (iMediaId){ fontpack_MediaType, argU32Label_Command(cmd, "id")},
2925 &info)) {
2926
2927 }*/
2928
2929// findCachedContent_App(<#const iString *url#>, <#iString *mime_out#>, <#iBlock *data_out#>)
2930// setData_Media(media,
2931 }
2932 return iFalse;
2933 }
2934 else if (equal_Command(cmd, "media.fontpack.install")) {
2935 if (pointerLabel_Command(cmd, "media") == media_GmDocument(d->doc)) {
2936 /* TODO: This is ours, we may have a MediaRequest with the data in memory. */
2937 }
2938 return iFalse;
2939 }
2940 else if (equal_Command(cmd, "document.stop") && document_App() == d) { 2931 else if (equal_Command(cmd, "document.stop") && document_App() == d) {
2941 if (cancelRequest_DocumentWidget_(d, iTrue /* navigate back */)) { 2932 if (cancelRequest_DocumentWidget_(d, iTrue /* navigate back */)) {
2942 return iTrue; 2933 return iTrue;
@@ -3253,7 +3244,16 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
3253 return handleSwipe_DocumentWidget_(d, cmd); 3244 return handleSwipe_DocumentWidget_(d, cmd);
3254 } 3245 }
3255 else if (equal_Command(cmd, "document.setmediatype") && document_App() == d) { 3246 else if (equal_Command(cmd, "document.setmediatype") && document_App() == d) {
3256 setUrlAndSource_DocumentWidget(d, d->mod.url, string_Command(cmd, "mime"), &d->sourceContent); 3247 if (!isRequestOngoing_DocumentWidget(d)) {
3248 setUrlAndSource_DocumentWidget(d, d->mod.url, string_Command(cmd, "mime"),
3249 &d->sourceContent);
3250 }
3251 return iTrue;
3252 }
3253 else if (equalWidget_Command(cmd, w, "fontpack.install")) {
3254 const iString *id = idFromUrl_FontPack(d->mod.url);
3255 install_Fonts(id, &d->sourceContent);
3256 postCommandf_App("open gotoheading:%s url:about:fonts", cstr_String(id));
3257 return iTrue; 3257 return iTrue;
3258 } 3258 }
3259 return iFalse; 3259 return iFalse;
@@ -3302,15 +3302,6 @@ static iBool processMediaEvents_DocumentWidget_(iDocumentWidget *d, const SDL_Ev
3302 const iInt2 mouse = init_I2(ev->button.x, ev->button.y); 3302 const iInt2 mouse = init_I2(ev->button.x, ev->button.y);
3303 iConstForEach(PtrArray, i, &d->visibleMedia) { 3303 iConstForEach(PtrArray, i, &d->visibleMedia) {
3304 const iGmRun *run = i.ptr; 3304 const iGmRun *run = i.ptr;
3305 if (run->mediaType == fontpack_MediaType) {
3306 iFontpackUI ui;
3307 init_FontpackUI(&ui, media_GmDocument(d->doc), run->mediaId,
3308 runRect_DocumentWidget_(d, run));
3309 if (processEvent_FontpackUI(&ui, ev)) {
3310 refresh_Widget(d);
3311 return iTrue;
3312 }
3313 }
3314 if (run->mediaType != audio_MediaType) { 3305 if (run->mediaType != audio_MediaType) {
3315 continue; 3306 continue;
3316 } 3307 }
@@ -4685,12 +4676,6 @@ static void drawMedia_DocumentWidget_(const iDocumentWidget *d, iPaint *p) {
4685 runRect_DocumentWidget_(d, run)); 4676 runRect_DocumentWidget_(d, run));
4686 draw_DownloadUI(&ui, p); 4677 draw_DownloadUI(&ui, p);
4687 } 4678 }
4688 else if (run->mediaType == fontpack_MediaType) {
4689 iFontpackUI ui;
4690 init_FontpackUI(&ui, constMedia_GmDocument(d->doc), run->mediaId,
4691 runRect_DocumentWidget_(d, run));
4692 draw_FontpackUI(&ui, p);
4693 }
4694 } 4679 }
4695} 4680}
4696 4681
diff --git a/src/ui/mediaui.c b/src/ui/mediaui.c
index ac9475dd..d85d0df9 100644
--- a/src/ui/mediaui.c
+++ b/src/ui/mediaui.c
@@ -279,85 +279,3 @@ void draw_DownloadUI(const iDownloadUI *d, iPaint *p) {
279 translateCStr_Lang("\u2014 ${mb.per.sec}")); 279 translateCStr_Lang("\u2014 ${mb.per.sec}"));
280 } 280 }
281} 281}
282
283/*----------------------------------------------------------------------------------------------*/
284
285static iMenuItem action_FontpackUI_(const iFontpackUI *d) {
286 if (d->info.isInstalled) {
287 return (iMenuItem){ d->info.isDisabled ? "${media.fontpack.enable}"
288 : close_Icon " ${media.fontpack.disable}",
289 0, 0, format_CStr("media.fontpack.enable arg:%d", d->info.isDisabled) };
290 }
291 return (iMenuItem){ d->info.isInstalled ? close_Icon " ${media.fontpack.disable}"
292 : "${media.fontpack.install}",
293 0, 0,
294 d->info.isInstalled ? "media.fontpack.enable arg:0" : "media.fontpack.install" };
295}
296
297void init_FontpackUI(iFontpackUI *d, const iMedia *media, uint16_t mediaId, iRect bounds) {
298 d->media = media;
299 d->mediaId = mediaId;
300 fontpackInfo_Media(d->media, (iMediaId){ fontpack_MediaType, d->mediaId }, &d->info);
301 d->bounds = bounds;
302 iMenuItem action = action_FontpackUI_(d);
303 d->buttonRect.size = add_I2(measure_Text(uiLabel_FontId, action.label).bounds.size,
304 muli_I2(gap2_UI, 3));
305 d->buttonRect.pos.x = right_Rect(d->bounds) - gap_UI - d->buttonRect.size.x;
306 d->buttonRect.pos.y = mid_Rect(d->bounds).y - d->buttonRect.size.y / 2;
307}
308
309iBool processEvent_FontpackUI(iFontpackUI *d, const SDL_Event *ev) {
310 switch (ev->type) {
311 case SDL_MOUSEBUTTONDOWN:
312 case SDL_MOUSEBUTTONUP: {
313 const iInt2 pos = init_I2(ev->button.x, ev->button.y);
314 if (contains_Rect(d->buttonRect, pos)) {
315 if (ev->type == SDL_MOUSEBUTTONUP) {
316 postCommandf_App("%s media:%p mediaid:%u packid:%s",
317 action_FontpackUI_(d).command,
318 d->media, d->mediaId, cstr_String(d->info.packId.id));
319 }
320 return iTrue;
321 }
322 break;
323 }
324 case SDL_MOUSEMOTION:
325 if (contains_Rect(d->bounds, init_I2(ev->motion.x, ev->motion.y))) {
326 return iTrue;
327 }
328 break;
329 }
330 return iFalse;
331}
332
333int height_FontpackUI(const iMedia *media, uint16_t mediaId, int width) {
334 const iStringList *names;
335 iFontpackMediaInfo info;
336 fontpackInfo_Media(media, (iMediaId){ fontpack_MediaType, mediaId }, &info);
337 return lineHeight_Text(uiContent_FontId) +
338 lineHeight_Text(uiLabel_FontId) * (1 + size_StringList(info.names));
339}
340
341void draw_FontpackUI(const iFontpackUI *d, iPaint *p) {
342 /* Draw a background box. */
343 fillRect_Paint(p, d->bounds, uiBackground_ColorId);
344 drawRect_Paint(p, d->bounds, uiSeparator_ColorId);
345 iInt2 pos = topLeft_Rect(d->bounds);
346 const char *checks[] = { "\u2610", "\u2611" };
347 draw_Text(uiContentBold_FontId, pos,
348 d->info.isDisabled ? uiText_ColorId : uiHeading_ColorId, "\"%s\" v%d",
349 cstr_String(d->info.packId.id), d->info.packId.version);
350 pos.y += lineHeight_Text(uiContentBold_FontId);
351 draw_Text(uiLabelBold_FontId, pos, uiText_ColorId, "%.1f MB, %d fonts %s %s %s",
352 d->info.sizeInBytes / 1.0e6, size_StringList(d->info.names),
353// checks[info.isValid], info.isValid ? "No errors" : "Errors detected",
354 checks[d->info.isInstalled], d->info.isInstalled ? "Installed" : "Not installed",
355 d->info.isReadOnly ? "Read-Only" : "");
356 pos.y += lineHeight_Text(uiLabelBold_FontId);
357 iConstForEach(StringList, i, d->info.names) {
358 drawRange_Text(uiLabel_FontId, pos, uiText_ColorId, range_String(i.value));
359 pos.y += lineHeight_Text(uiLabel_FontId);
360 }
361 /* Buttons. */
362 drawInlineButton_(p, d->buttonRect, action_FontpackUI_(d).label, uiLabel_FontId);
363}
diff --git a/src/ui/mediaui.h b/src/ui/mediaui.h
index 03ea0afc..3d51e4c9 100644
--- a/src/ui/mediaui.h
+++ b/src/ui/mediaui.h
@@ -59,20 +59,3 @@ struct Impl_DownloadUI {
59void init_DownloadUI (iDownloadUI *, const iMedia *media, uint16_t mediaId, iRect bounds); 59void init_DownloadUI (iDownloadUI *, const iMedia *media, uint16_t mediaId, iRect bounds);
60iBool processEvent_DownloadUI (iDownloadUI *, const SDL_Event *ev); 60iBool processEvent_DownloadUI (iDownloadUI *, const SDL_Event *ev);
61void draw_DownloadUI (const iDownloadUI *, iPaint *p); 61void draw_DownloadUI (const iDownloadUI *, iPaint *p);
62
63/*----------------------------------------------------------------------------------------------*/
64
65iDeclareType(FontpackUI)
66
67struct Impl_FontpackUI {
68 const iMedia *media;
69 uint16_t mediaId;
70 iFontpackMediaInfo info;
71 iRect bounds;
72 iRect buttonRect;
73};
74
75void init_FontpackUI (iFontpackUI *, const iMedia *media, uint16_t mediaId, iRect bounds);
76int height_FontpackUI (const iMedia *media, uint16_t mediaId, int width);
77iBool processEvent_FontpackUI (iFontpackUI *, const SDL_Event *ev);
78void draw_FontpackUI (const iFontpackUI *, iPaint *p);