From 960df03c17091aca37f53eaab8fc27c669d26a5e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jaakko=20Kera=CC=88nen?= jaakko.keranen@iki.fi
Date: Mon, 11 Oct 2021 12:22:54 +0300
Subject: [PATCH 1/1] Media refactoring; working on FontPack management
Media still needs more work to get rid of redundancies and make lookups faster.
FontPacks are manipulated as Media items (not unlike images) so they can be previewed on page, and installed via a click.
FontPack management is not trivial as it includes such details as versioning and whether individual packs are enabled or disabled.
res/arabic.fontpack/fontpack.ini | 2 +
res/cjk.fontpack/fontpack.ini | 2 +
res/default.fontpack/fontpack.ini | 2 +
res/firasans.fontpack/fontpack.ini | 2 +
res/literata.fontpack/fontpack.ini | 2 +
res/nunito.fontpack/fontpack.ini | 2 +
res/tinos.fontpack/fontpack.ini | 2 +
src/app.c | 1 +
src/defs.h | 4 +-
src/fontpack.c | 299 ++++++++++++++------
src/fontpack.h | 65 ++++-
src/gempub.c | 2 +-
src/gmdocument.c | 159 +++++------
src/gmdocument.h | 16 +-
src/gmrequest.c | 9 +-
src/gmutil.c | 8 +-
src/media.c | 349 +++++++++++++++--------
src/media.h | 92 +++++--
src/ui/documentwidget.c | 429 ++++++++++++++++-------------
src/ui/documentwidget.h | 2 +
src/ui/mediaui.c | 88 +++++-
src/ui/mediaui.h | 20 +-
22 files changed, 1030 insertions(+), 527 deletions(-)
diff --git a/res/arabic.fontpack/fontpack.ini b/res/arabic.fontpack/fontpack.ini
index 00ad5241..305878ce 100644
--- a/res/arabic.fontpack/fontpack.ini
+++ b/res/arabic.fontpack/fontpack.ini
@@ -1,3 +1,5 @@
+version = 1
[arabic]
name = "Noto Sans Arabic UI"
auxiliary = true
diff --git a/res/cjk.fontpack/fontpack.ini b/res/cjk.fontpack/fontpack.ini
index fbac54be..6e0d274c 100644
--- a/res/cjk.fontpack/fontpack.ini
+++ b/res/cjk.fontpack/fontpack.ini
@@ -1,3 +1,5 @@
+version = 1
[notosansjp]
name = "Noto Sans JP"
auxiliary = true
diff --git a/res/default.fontpack/fontpack.ini b/res/default.fontpack/fontpack.ini
index 68316ef6..f8ef31ce 100644
--- a/res/default.fontpack/fontpack.ini
+++ b/res/default.fontpack/fontpack.ini
@@ -17,6 +17,8 @@
glyphscale
and voffset
can also be specified separately for the UI andui.
or doc.
to the key.+version = 1
[default]
name = "Source Sans"
regular = "SourceSans3-Regular.ttf"
diff --git a/res/firasans.fontpack/fontpack.ini b/res/firasans.fontpack/fontpack.ini
index 4378a757..c6eb4c77 100644
--- a/res/firasans.fontpack/fontpack.ini
+++ b/res/firasans.fontpack/fontpack.ini
@@ -1,3 +1,5 @@
+version = 1
[firasans]
name = "Fira Sans"
glyphscale = 0.85
diff --git a/res/literata.fontpack/fontpack.ini b/res/literata.fontpack/fontpack.ini
index e4e49bcb..7c29491d 100644
--- a/res/literata.fontpack/fontpack.ini
+++ b/res/literata.fontpack/fontpack.ini
@@ -1,3 +1,5 @@
+version = 1
[literata]
name = "Literata"
regular = "Literata-Regular-opsz=14.ttf"
diff --git a/res/nunito.fontpack/fontpack.ini b/res/nunito.fontpack/fontpack.ini
index ea4a12b8..2f2471e1 100644
--- a/res/nunito.fontpack/fontpack.ini
+++ b/res/nunito.fontpack/fontpack.ini
@@ -1,3 +1,5 @@
+version = 1
[nunito]
name = "Nunito"
tweaks = 0x1 # some hardcoded kerning changes (Th
, etc.)
diff --git a/res/tinos.fontpack/fontpack.ini b/res/tinos.fontpack/fontpack.ini
index 8759b752..a2cf811e 100644
--- a/res/tinos.fontpack/fontpack.ini
+++ b/res/tinos.fontpack/fontpack.ini
@@ -1,3 +1,5 @@
+version = 1
[tinos]
name = "Tinos"
glyphscale = 0.850
diff --git a/src/app.c b/src/app.c
index b317e7b3..cb5479e8 100644
--- a/src/app.c
+++ b/src/app.c
@@ -2669,6 +2669,7 @@ iBool handleCommand_App(const char *cmd) {
const iBool isSplit = numRoots_Window(get_Window()) > 1;
if (tabCount_Widget(tabs) > 1 || isSplit) {
iWidget *closed = removeTabPage_Widget(tabs, index);
cancelAllRequests_DocumentWidget((iDocumentWidget *) closed);
destroy_Widget(closed); /* released later */
if (index == tabCount_Widget(tabs)) {
index--;
diff --git a/src/defs.h b/src/defs.h
index 65096389..f5479cf3 100644
--- a/src/defs.h
+++ b/src/defs.h
@@ -125,11 +125,13 @@ iLocalDef int acceptKeyMod_ReturnKeyBehavior(int behavior) {
#define delete_Icon "\u232b"
#define copy_Icon "\u2398" //"\u2bba"
#define check_Icon "\u2714"
-#define ballotCheck_Icon "\U0001f5f9"
+#define ballotChecked_Icon "\U0001f5f9"
+#define ballotUnchecked_Icon "\U0001f5f9"
#define inbox_Icon "\U0001f4e5"
#define book_Icon "\U0001f56e"
#define bookmark_Icon "\U0001f516"
#define folder_Icon "\U0001f4c1"
+#define file_Icon "\U0001f5ce"
#define openTab_Icon "\u2750"
#define openTabBg_Icon "\u2b1a"
#define openExt_Icon "\u27a0"
diff --git a/src/fontpack.c b/src/fontpack.c
index ca1d1582..fb1c98ee 100644
--- a/src/fontpack.c
+++ b/src/fontpack.c
@@ -33,7 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#include <the_Foundation/string.h>
#include <the_Foundation/toml.h>
-/* TODO: Clean up and/or reorder this file, it's a bit unorganized. */
+const char *mimeType_FontPack = "application/lagrange-fontpack+zip";
float scale_FontSize(enum iFontSize size) {
static const float sizes[max_FontSize] = {
@@ -57,38 +57,9 @@ float scale_FontSize(enum iFontSize size) {
return sizes[size];
}
-iDeclareType(Fonts)
-struct Impl_Fonts {
-};
-static iFonts fonts_;
-static void unloadFiles_Fonts_(iFonts *d) {
delete_FontFile(i.ptr);
-}
-static iFontFile *findFile_Fonts_(iFonts *d, const iString *id) {
iFontFile *ff = i.ptr;
if (equal_String(&ff->id, id)) {
return ff;
}
-}
/----------------------------------------------------------------------------------------------/
-iDefineTypeConstruction(FontFile)
+iDefineObjectConstruction(FontFile)
void init_FontFile(iFontFile *d) {
init_String(&d->id);
@@ -114,7 +85,7 @@ static void load_FontFile_(iFontFile *d, const iBlock *data) {
HB_MEMORY_MODE_READONLY, NULL, NULL);
d->hbFace = hb_face_create(d->hbBlob, 0);
d->hbFont = hb_font_create(d->hbFace);
-#endif
+#endif
}
static void unload_FontFile_(iFontFile *d) {
@@ -126,12 +97,13 @@ static void unload_FontFile_(iFontFile *d) {
d->hbFont = NULL;
d->hbFace = NULL;
d->hbBlob = NULL;
-#endif
+#endif
clear_Block(&d->sourceData);
iZap(d->stbInfo);
}
void deinit_FontFile(iFontFile *d) {
unload_FontFile_(d);
deinit_Block(&d->sourceData);
deinit_String(&d->id);
@@ -156,12 +128,12 @@ void measureGlyph_FontFile(const iFontFile *d, uint32_t glyphIndex,
/----------------------------------------------------------------------------------------------/
iDefineTypeConstruction(FontSpec)
void init_FontSpec(iFontSpec *d) {
init_String(&d->id);
init_String(&d->name);
d->flags = 0;
d->priority = 0;
for (int i = 0; i < 2; ++i) {
@@ -173,52 +145,119 @@ void init_FontSpec(iFontSpec *d) {
}
void deinit_FontSpec(iFontSpec *d) {
iRelease(d->styles[i]);
deinit_String(&d->name);
deinit_String(&d->id);
}
/----------------------------------------------------------------------------------------------/
-iDeclareType(FontPack)
-iDeclareTypeConstruction(FontPack)
+iDeclareType(Fonts)
+struct Impl_Fonts {
+};
+static iFonts fonts_;
+static void unloadFiles_Fonts_(iFonts *d) {
+}
+static iFontFile *findFile_Fonts_(iFonts *d, const iString *id) {
iFontFile *ff = i.object;
if (equal_String(&ff->id, id)) {
return ff;
}
+}
+static void releaseUnusedFiles_Fonts_(iFonts *d) {
iFontFile *ff = i.object;
if (ff->object.refCount == 1) {
/* No specs use this. */
//printf("[Fonts] releasing unused font file: %p {%s}\n", ff, cstr_String(&ff->id));
remove_ObjectListIterator(&i);
}
+}
+/----------------------------------------------------------------------------------------------/
struct Impl_FontPack {
iArray fonts; /* array of FontSpecs */
iString * loadPath;
iFontSpec * loadSpec;
};
+iDefineTypeConstruction(FontPack)
void init_FontPack(iFontPack *d) {
init_Array(&d->fonts, sizeof(iFontSpec));
d->loadSpec = NULL;
d->loadPath = NULL;
}
void deinit_FontPack(iFontPack *d) {
delete_String(d->loadPath);
iForEach(Array, i, &d->fonts) {
deinit_FontSpec(i.value);
}
deinit_Array(&d->fonts);
}
-iDefineTypeConstruction(FontPack)
+iFontPackId id_FontPack(const iFontPack *d) {
+}
+const iPtrArray *listSpecs_FontPack(const iFontPack *d) {
pushBack_PtrArray(list, i.value);
+}
void handleIniTable_FontPack_(void *context, const iString *table, iBool isStart) {
iFontPack *d = context;
if (isStart) {
iAssert(!d->loadSpec);
/* Each font ID must be unique. */
if (!findSpec_Fonts(cstr_String(table))) {
/* Each font ID must be unique in the non-standalone packs. */
if (d->isStandalone || !findSpec_Fonts(cstr_String(table))) {
d->loadSpec = new_FontSpec();
set_String(&d->loadSpec->id, table);
if (d->loadPath) {
set_String(&d->loadSpec->sourcePath, d->loadPath);
}
}
}
/* Set fallback font files. */ {
const iFontFile **styles = d->loadSpec->styles;
if (!styles[regular_FontStyle]) {
@@ -229,11 +268,11 @@ void handleIniTable_FontPack_(void *context, const iString *table, iBool isStart
return;
}
if (!styles[semiBold_FontStyle]) {
styles[semiBold_FontStyle] = styles[bold_FontStyle];
styles[semiBold_FontStyle] = ref_Object(styles[bold_FontStyle]);
}
for (size_t s = 0; s < max_FontStyle; s++) {
if (!styles[s]) {
styles[s] = styles[regular_FontStyle];
styles[s] = ref_Object(styles[regular_FontStyle]);
}
}
}
@@ -263,7 +302,15 @@ static iBlock *readFile_FontPack_(const iFontPack *d, const iString *path) {
void handleIniKeyValue_FontPack_(void *context, const iString *table, const iString *key,
const iTomlValue *value) {
iFontPack *d = context;
if (!cmp_String(key, "version")) {
d->version = number_TomlValue(value);
}
return;
return;
iUnused(table);
if (!cmp_String(key, "name") && value->type == string_TomlType) {
set_String(&d->loadSpec->name, value->value.string);
@@ -322,12 +369,13 @@ void handleIniKeyValue_FontPack_(void *context, const iString *table, const iStr
ff = new_FontFile();
set_String(&ff->id, fontFileId);
load_FontFile_(ff, data);
pushBack_PtrArray(&fonts_.files, ff); /* centralized ownership */
pushBack_ObjectList(fonts_.files, ff); /* centralized ownership */
iRelease(ff);
delete_Block(data);
// printf("[FontPack] loaded file: %s\n", cstr_String(fontFileId));
}
}
d->loadSpec->styles[i] = ff;
d->loadSpec->styles[i] = ref_Object(ff);
delete_String(fontFileId);
break;
}
@@ -348,29 +396,6 @@ static iBool load_FontPack_(iFontPack *d, const iString *ini) {
return ok;
}
-#if 0
-iBool loadIniFile_FontPack(iFontPack *d, const iString *iniPath) {
d->loadPath = collect_String(newRange_String(dirName_Path(iniPath)));
iString *src = collect_String(readString_File(f));
iTomlParser *ini = collect_TomlParser(new_TomlParser());
setHandlers_TomlParser(ini, handleIniTable_FontPack_, handleIniKeyValue_FontPack_, d);
if (!parse_TomlParser(ini, src)) {
fprintf(stderr, "[FontPack] error parsing %s\n", cstr_String(iniPath));
}
iAssert(d->loadSpec == NULL);
d->loadPath = NULL;
ok = iTrue;
-}
-#endif
iBool loadArchive_FontPack(iFontPack *d, const iArchive *zip) {
d->archive = zip;
iBool ok = iFalse;
@@ -387,6 +412,28 @@ iBool loadArchive_FontPack(iFontPack *d, const iArchive *zip) {
return ok;
}
+void setLoadPath_FontPack(iFontPack *d, const iString *path) {
d->loadPath = new_String();
+}
+void setStandalone_FontPack(iFontPack *d, iBool standalone) {
+}
+void setReadOnly_FontPack(iFontPack *d, iBool readOnly) {
+}
+iBool isReadOnly_FontPack(const iFontPack *d) {
+}
/----------------------------------------------------------------------------------------------/
static void unloadFonts_Fonts_(iFonts *d) {
@@ -397,14 +444,23 @@ static void unloadFonts_Fonts_(iFonts *d) {
clear_PtrArray(&d->packs);
}
+static int cmpName_FontSpecPtr_(const void *a, const void *b) {
+}
static int cmpPriority_FontSpecPtr_(const void *a, const void *b) {
const iFontSpec **p1 = (const iFontSpec **) a, **p2 = (const iFontSpec **) b;
}
-static int cmpName_FontSpecPtr_(const void *a, const void *b) {
+static int cmpSourceAndPriority_FontSpecPtr_(const void *a, const void *b) {
const iFontSpec **p1 = (const iFontSpec **) a, **p2 = (const iFontSpec **) b;
}
static void sortSpecs_Fonts_(iFonts *d) {
@@ -422,11 +478,13 @@ void init_Fonts(const char *userDir) {
iFonts *d = &fonts_;
initCStr_String(&d->userDir, userDir);
init_PtrArray(&d->packs);
init_PtrArray(&d->specOrder);
/* Load the required fonts. */ {
iFontPack *pack = new_FontPack();
setCStr_String(&pack->id, "default");
iArchive *arch = new_Archive();
setReadOnly_FontPack(pack, iTrue);
openData_Archive(arch, &fontpackDefault_Embedded);
loadArchive_FontPack(pack, arch); /* should never fail if we've made it this far */
iRelease(arch);
@@ -436,7 +494,7 @@ void init_Fonts(const char *userDir) {
const char *locations[] = {
".",
"./fonts",
"../share/lagrange",
"../share/lagrange", /* Note: These must match CMakeLists.txt install destination */
"../../share/lagrange",
concatPath_CStr(userDir, "fonts"),
userDir,
@@ -450,7 +508,13 @@ void init_Fonts(const char *userDir) {
iArchive *arch = new_Archive();
if (openFile_Archive(arch, entryPath)) {
iFontPack *pack = new_FontPack();
pack->loadPath = copy_String(entryPath);
setLoadPath_FontPack(pack, entryPath);
setReadOnly_FontPack(pack, !isWritable_FileInfo(entry.value));
+#if defined (iPlatformApple)
if (startsWith_String(pack->loadPath, cstr_String(execDir))) {
setReadOnly_FontPack(pack, iTrue);
}
+#endif
if (loadArchive_FontPack(pack, arch)) {
pushBack_PtrArray(&d->packs, pack);
}
@@ -491,13 +555,18 @@ void init_Fonts(const char *userDir) {
void deinit_Fonts(void) {
iFonts *d = &fonts_;
unloadFonts_Fonts_(d);
deinit_PtrArray(&d->specOrder);
deinit_PtrArray(&d->packs);
deinit_String(&d->userDir);
}
+const iPtrArray *listPacks_Fonts(void) {
+}
const iFontSpec *findSpec_Fonts(const char *fontId) {
iFonts *d = &fonts_;
iConstForEach(PtrArray, i, &d->specOrder) {
@@ -524,3 +593,73 @@ const iPtrArray *listSpecs_Fonts(iBool (*filterFunc)(const iFontSpec *)) {
const iPtrArray *listSpecsByPriority_Fonts(void) {
return &fonts_.specOrder;
}
+const iString *infoPage_Fonts(void) {
const iFontSpec *spec = i.ptr;
if (isEmpty_String(&spec->sourcePath)) {
continue; /* built-in font */
}
if (!equal_String(&spec->sourcePath, currentSourcePath)) {
appendFormat_String(str, "=> %s %s%s\n",
cstrCollect_String(makeFileUrl_String(&spec->sourcePath)),
endsWithCase_String(&spec->sourcePath, ".fontpack") ? "\U0001f520 " : "",
cstr_Rangecc(baseName_Path(&spec->sourcePath)));
set_String(currentSourcePath, &spec->sourcePath);
}
+}
+const iFontPack *findPack_Fonts(const iString *path) {
const iFontPack *pack = i.ptr;
if (pack->loadPath && equal_String(pack->loadPath, path)) {
return pack;
}
+}
+iBool preloadLocalFontpackForPreview_Fonts(iGmDocument *doc) {
const iString *linkUrl = linkUrl_GmDocument(doc, linkId);
if (!linkUrl) {
break; /* ran out of links */
}
const int linkFlags = linkFlags_GmDocument(doc, linkId);
if (linkFlags & fontpackFileExtension_GmLinkFlag &&
scheme_GmLinkFlag(linkFlags) == file_GmLinkScheme) {
iMediaId linkMedia = findMediaForLink_Media(media_GmDocument(doc), linkId, fontpack_MediaType);
if (linkMedia.type) {
continue; /* got this one already */
}
iString *filePath = localFilePathFromUrl_String(linkUrl);
iFile *f = new_File(filePath);
if (open_File(f, readOnly_FileMode)) {
iBlock *fontPackArchiveData = readAll_File(f);
setUrl_Media(media_GmDocument(doc), linkId, fontpack_MediaType, linkUrl);
setData_Media(media_GmDocument(doc),
linkId,
collectNewCStr_String(mimeType_FontPack),
fontPackArchiveData,
0);
delete_Block(fontPackArchiveData);
wasLoaded = iTrue;
}
iRelease(f);
}
+}
+iDefineClass(FontFile)
diff --git a/src/fontpack.h b/src/fontpack.h
index e59154e3..429afb5d 100644
--- a/src/fontpack.h
+++ b/src/fontpack.h
@@ -30,6 +30,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#endif
+extern const char *mimeType_FontPack;
/* Fontpacks are ZIP archives that contain a configuration file and one of more font
files. The fontpack format is used instead of plain TTF/OTF because the text renderer
uses additional metadata about each font.
@@ -68,21 +70,13 @@ enum iFontStyle {
float scale_FontSize (enum iFontSize size);
-iDeclareType(FontSpec)
-iDeclareTypeConstruction(FontSpec)
+/----------------------------------------------------------------------------------------------/
-enum iFontSpecFlags {
-};
-iDeclareType(FontFile)
-iDeclareTypeConstruction(FontFile)
+iDeclareClass(FontFile)
+iDeclareObjectConstruction(FontFile)
struct Impl_FontFile {
iString id; /* for detecting when the same file is used in many places */
enum iFontStyle style;
iBlock sourceData;
@@ -107,9 +101,26 @@ uint8_t * rasterizeGlyph_FontFile(const iFontFile *, float xScale, float yScal
void measureGlyph_FontFile (const iFontFile *, uint32_t glyphIndex,
float xScale, float yScale, float xShift,
int *x0, int *y0, int *x1, int *y1);
+/----------------------------------------------------------------------------------------------/
+/* FontSpec describes a typeface, combining multiple fonts into a group.
+iDeclareType(FontSpec)
+iDeclareTypeConstruction(FontSpec)
+enum iFontSpecFlags {
+};
struct Impl_FontSpec {
iString id; /* unique ID */
iString name; /* human-readable label */
int flags;
int priority;
float heightScale[2]; /* overall height scaling; ui, document */
@@ -121,10 +132,38 @@ struct Impl_FontSpec {
iLocalDef int scaleType_FontSpec(enum iFontSize sizeId) {
return sizeId / contentRegular_FontSize;
}
+/----------------------------------------------------------------------------------------------/
+iDeclareType(FontPack)
+iDeclareTypeConstruction(FontPack)
+iDeclareType(FontPackId)
+struct Impl_FontPackId {
+};
+void setReadOnly_FontPack (iFontPack *, iBool readOnly);
+void setStandalone_FontPack (iFontPack *, iBool standalone);
+void setLoadPath_FontPack (iFontPack *, const iString *path);
+iBool loadArchive_FontPack (iFontPack *, const iArchive *zip);
+iFontPackId id_FontPack (const iFontPack *);
+const iPtrArray * listSpecs_FontPack (const iFontPack *);
+iBool isReadOnly_FontPack (const iFontPack *);
+iDeclareType(GmDocument)
void init_Fonts (const char *userDir);
void deinit_Fonts (void);
+const iFontPack * findPack_Fonts (const iString *path);
const iFontSpec * findSpec_Fonts (const char *fontId);
+const iPtrArray * listPacks_Fonts (void);
const iPtrArray * listSpecs_Fonts (iBool (*filterFunc)(const iFontSpec *));
const iPtrArray * listSpecsByPriority_Fonts (void);
+const iString * infoPage_Fonts (void);
+iBool preloadLocalFontpackForPreview_Fonts (iGmDocument *doc);
diff --git a/src/gempub.c b/src/gempub.c
index 23846414..952d72a1 100644
--- a/src/gempub.c
+++ b/src/gempub.c
@@ -337,7 +337,7 @@ iBool preloadCoverImage_Gempub(const iGempub *d, iGmDocument *doc) {
for (size_t linkId = 1; ; linkId++) {
const iString *linkUrl = linkUrl_GmDocument(doc, linkId);
if (!linkUrl) break;
if (findLinkImage_Media(media_GmDocument(doc), linkId)) {
if (findLinkImage_Media(media_GmDocument(doc), linkId).type) {
continue; /* got this already */
}
if (linkFlags_GmDocument(doc, linkId) & imageFileExtension_GmLinkFlag) {
diff --git a/src/gmdocument.c b/src/gmdocument.c
index c98b0bb8..c271ad94 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -27,6 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#include "ui/color.h"
#include "ui/text.h"
#include "ui/metrics.h"
+#include "ui/mediaui.h"
#include "ui/window.h"
#include "visited.h"
#include "bookmarks.h"
@@ -224,7 +225,7 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li
setScheme_GmLink_(link, finger_GmLinkScheme);
}
else if (equalCase_Rangecc(parts.scheme, "file")) {
setScheme_GmLink_(link, file_GmLinkScheme);
setScheme_GmLink_(link, file_GmLinkScheme);
}
else if (equalCase_Rangecc(parts.scheme, "data")) {
setScheme_GmLink_(link, data_GmLinkScheme);
@@ -251,6 +252,9 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li
endsWithCase_String(path, ".mid") || endsWithCase_String(path, ".ogg")) {
link->flags |= audioFileExtension_GmLinkFlag;
}
else if (endsWithCase_String(path, ".fontpack")) {
link->flags |= fontpackFileExtension_GmLinkFlag;
}
delete_String(path);
}
/* Check if visited. */
@@ -503,7 +507,7 @@ static void doLayout_GmDocument_(iGmDocument *d) {
static const char *arrow = rightArrowhead_Icon;
static const char *envelope = envelope_Icon;
static const char *bullet = "\u2022";
static const char *globe = globe_Icon;
static const char *quote = "\u201c";
static const char *magnifyingGlass = "\U0001f50d";
@@ -900,77 +904,77 @@ static void doLayout_GmDocument_(iGmDocument *d) {
((iGmRun *) back_Array(&d->layout))->flags |= endOfLine_GmRunFlag;
/* Image or audio content. */
if (type == link_GmLineType) {
const iMediaId imageId = findLinkImage_Media(d->media, run.linkId);
const iMediaId audioId = !imageId ? findLinkAudio_Media(d->media, run.linkId) : 0;
const iMediaId downloadId = !imageId && !audioId ? findLinkDownload_Media(d->media, run.linkId) : 0;
if (imageId) {
iGmMediaInfo img;
imageInfo_Media(d->media, imageId, &img);
const iInt2 imgSize = imageSize_Media(d->media, imageId);
linkContentWasLaidOut_GmDocument_(d, &img, run.linkId);
const int margin = lineHeight_Text(paragraph_FontId) / 2;
/* TODO: Cleanup here? Move to a function of its own. */
+// enum iMediaType mediaType = none_MediaType;
const iMediaId media = findMediaForLink_Media(d->media, run.linkId, none_MediaType);
iGmMediaInfo info;
info_Media(d->media, media, &info);
run.mediaType = media.type;
run.mediaId = media.id;
run.text = iNullRange;
run.font = uiLabel_FontId;
run.color = 0;
const int margin = lineHeight_Text(paragraph_FontId) / 2;
if (media.type) {
pos.y += margin;
run.bounds.pos = pos;
run.bounds.size.x = d->size.x;
const float aspect = (float) imgSize.y / (float) imgSize.x;
run.bounds.size.y = d->size.x * aspect;
/* Extend the image to full width, including outside margin, if the viewport
is narrow enough. */
if (isFullWidthImages) {
run.bounds.size.x += d->outsideMargin * 2;
run.bounds.size.y += d->outsideMargin * 2 * aspect;
run.bounds.pos.x -= d->outsideMargin;
}
run.visBounds = run.bounds;
const iInt2 maxSize = mulf_I2(imgSize, get_Window()->pixelRatio);
if (width_Rect(run.visBounds) > maxSize.x) {
/* Don't scale the image up. */
run.visBounds.size.y =
run.visBounds.size.y * maxSize.x / width_Rect(run.visBounds);
run.visBounds.size.x = maxSize.x;
run.visBounds.pos.x = run.bounds.size.x / 2 - width_Rect(run.visBounds) / 2;
run.bounds.size.y = run.visBounds.size.y;
}
run.text = iNullRange;
run.font = 0;
run.color = 0;
run.mediaType = image_GmRunMediaType;
run.mediaId = imageId;
pushBack_Array(&d->layout, &run);
pos.y += run.bounds.size.y + margin;
}
else if (audioId) {
iGmMediaInfo info;
audioInfo_Media(d->media, audioId, &info);
run.bounds.size.y = 0;
linkContentWasLaidOut_GmDocument_(d, &info, run.linkId);
const int margin = lineHeight_Text(paragraph_FontId) / 2;
pos.y += margin;
run.bounds.pos = pos;
run.bounds.size.x = d->size.x;
run.bounds.size.y = lineHeight_Text(uiContent_FontId) + 3 * gap_UI;
run.visBounds = run.bounds;
run.text = iNullRange;
run.color = 0;
run.mediaType = audio_GmRunMediaType;
run.mediaId = audioId;
pushBack_Array(&d->layout, &run);
pos.y += run.bounds.size.y + margin;
}
else if (downloadId) {
iGmMediaInfo info;
downloadInfo_Media(d->media, downloadId, &info);
linkContentWasLaidOut_GmDocument_(d, &info, run.linkId);
const int margin = lineHeight_Text(paragraph_FontId) / 2;
pos.y += margin;
run.bounds.pos = pos;
run.bounds.size.x = d->size.x;
run.bounds.size.y = 2 * lineHeight_Text(uiContent_FontId) + 4 * gap_UI;
run.visBounds = run.bounds;
run.text = iNullRange;
run.color = 0;
run.mediaType = download_GmRunMediaType;
run.mediaId = downloadId;
pushBack_Array(&d->layout, &run);
switch (media.type) {
case image_MediaType: {
const iInt2 imgSize = imageSize_Media(d->media, media);
run.bounds.pos = pos;
run.bounds.size.x = d->size.x;
const float aspect = (float) imgSize.y / (float) imgSize.x;
run.bounds.size.y = d->size.x * aspect;
/* Extend the image to full width, including outside margin, if the viewport
is narrow enough. */
if (isFullWidthImages) {
run.bounds.size.x += d->outsideMargin * 2;
run.bounds.size.y += d->outsideMargin * 2 * aspect;
run.bounds.pos.x -= d->outsideMargin;
}
run.visBounds = run.bounds;
const iInt2 maxSize = mulf_I2(imgSize, get_Window()->pixelRatio);
if (width_Rect(run.visBounds) > maxSize.x) {
/* Don't scale the image up. */
run.visBounds.size.y =
run.visBounds.size.y * maxSize.x / width_Rect(run.visBounds);
run.visBounds.size.x = maxSize.x;
run.visBounds.pos.x = run.bounds.size.x / 2 - width_Rect(run.visBounds) / 2;
run.bounds.size.y = run.visBounds.size.y;
}
pushBack_Array(&d->layout, &run);
break;
}
case audio_MediaType: {
run.bounds.pos = pos;
run.bounds.size.x = d->size.x;
run.bounds.size.y = lineHeight_Text(uiContent_FontId) + 3 * gap_UI;
run.visBounds = run.bounds;
pushBack_Array(&d->layout, &run);
break;
}
case download_MediaType: {
run.bounds.pos = pos;
run.bounds.size.x = d->size.x;
run.bounds.size.y = 2 * lineHeight_Text(uiContent_FontId) + 4 * gap_UI;
run.visBounds = run.bounds;
pushBack_Array(&d->layout, &run);
break;
}
case fontpack_MediaType: {
run.bounds.pos = pos;
run.bounds.size.x = d->size.x;
run.bounds.size.y = height_FontpackUI(d->media, media.id, d->size.x);
run.visBounds = run.bounds;
pushBack_Array(&d->layout, &run);
break;
}
default:
break;
}
if (media.type && run.bounds.size.y) {
pos.y += run.bounds.size.y + margin;
}
}
@@ -1057,21 +1061,6 @@ const iString *url_GmDocument(const iGmDocument *d) {
return &d->url;
}
-#if 0
-void reset_GmDocument(iGmDocument *d) {
-}
-#endif
static void setDerivedThemeColors_(enum iGmDocumentTheme theme) {
set_Color(tmQuoteIcon_ColorId,
mix_Color(get_Color(tmQuote_ColorId), get_Color(tmBackground_ColorId), 0.55f));
diff --git a/src/gmdocument.h b/src/gmdocument.h
index b2c6d9b7..20bc9890 100644
--- a/src/gmdocument.h
+++ b/src/gmdocument.h
@@ -90,6 +90,7 @@ enum iGmLinkFlag {
query_GmLinkFlag = iBit(14), /* Gopher query link */
iconFromLabel_GmLinkFlag = iBit(15), /* use an Emoji/special character from label */
isOpen_GmLinkFlag = iBit(16), /* currently open in a tab */
};
iLocalDef enum iGmLinkScheme scheme_GmLinkFlag(int flags) {
@@ -126,13 +127,6 @@ enum iGmRunFlags {
altText_GmRunFlag = iBit(8),
};
-enum iGmRunMediaType {
-};
/* This structure is tightly packed because GmDocuments are mostly composed of
a large number of GmRuns. */
struct Impl_GmRun {
@@ -146,12 +140,16 @@ struct Impl_GmRun {
uint32_t color : 7; /* see max_ColorId */
uint32_t font : 10;
uint32_t mediaType : 2;
uint32_t mediaId : 10; /* zero if not an image */
uint32_t mediaType : 3;
uint32_t mediaId : 9; /* zero if not an image */
uint32_t preId : 10; /* preformatted block ID (sequential); merge with mediaId? */
};
};
+iLocalDef iMediaId mediaId_GmRun(const iGmRun *d) {
+}
iDeclareType(GmRunRange)
struct Impl_GmRunRange {
diff --git a/src/gmrequest.c b/src/gmrequest.c
index 1a9e83a9..f7a22e0a 100644
--- a/src/gmrequest.c
+++ b/src/gmrequest.c
@@ -361,6 +361,9 @@ static const iBlock *aboutPageSource_(iRangecc path, iRangecc query) {
if (equalCase_Rangecc(path, "debug")) {
return utf8_String(debugInfo_App());
}
return utf8_String(infoPage_Fonts());
if (equalCase_Rangecc(path, "feeds")) {
return utf8_String(entryListPage_Feeds());
}
@@ -710,8 +713,9 @@ void submit_GmRequest(iGmRequest *d) {
sort_Array(sortedInfo, (int (*)(const void *, const void *)) cmp_FileInfoPtr_);
iForEach(PtrArray, s, sortedInfo) {
const iFileInfo *entry = s.ptr;
appendFormat_String(page, "=> %s %s%s\n",
appendFormat_String(page, "=> %s %s%s%s\n",
cstrCollect_String(makeFileUrl_String(path_FileInfo(entry))),
isDirectory_FileInfo(entry) ? folder_Icon " " : "",
cstr_Rangecc(baseName_Path(path_FileInfo(entry))),
isDirectory_FileInfo(entry) ? iPathSeparator : "");
iRelease(entry);
@@ -808,9 +812,10 @@ void submit_GmRequest(iGmRequest *d) {
const iString *subPath = e.value;
iRangecc relSub = range_String(subPath);
relSub.start += size_String(entryPath);
appendFormat_String(page, "=> %s/%s %s\n",
appendFormat_String(page, "=> %s/%s %s%s\n",
cstr_String(&d->url),
cstr_String(withSpacesEncoded_String(collectNewRange_String(relSub))),
endsWith_Rangecc(relSub, "/") ? folder_Icon " " : "",
cstr_Rangecc(relSub));
}
resp->statusCode = success_GmStatusCode;
diff --git a/src/gmutil.c b/src/gmutil.c
index 971747d4..5be7e198 100644
--- a/src/gmutil.c
+++ b/src/gmutil.c
@@ -21,6 +21,7 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#include "gmutil.h"
+#include "fontpack.h"
#include <the_Foundation/file.h>
#include <the_Foundation/fileinfo.h>
@@ -511,7 +512,8 @@ const iString *findContainerArchive_Path(const iString *path) {
while (!isEmpty_String(path) && cmp_String(path, ".")) {
iString *dir = newRange_String(dirName_Path(path));
if (endsWithCase_String(dir, ".zip") ||
endsWithCase_String(dir, ".gpub")) {
endsWithCase_String(dir, ".gpub") ||
endsWithCase_String(dir, ".fontpack")) {
iEndCollect();
return collect_String(dir);
}
@@ -534,6 +536,9 @@ const char *mediaTypeFromFileExtension_String(const iString *d) {
else if (endsWithCase_String(d, ".gpub")) {
return "application/gpub+zip";
}
return mimeType_FontPack;
else if (endsWithCase_String(d, ".xml")) {
return "text/xml";
}
@@ -562,6 +567,7 @@ const char *mediaTypeFromFileExtension_String(const iString *d) {
return "audio/midi";
}
else if (endsWithCase_String(d, ".txt") ||
endsWithCase_String(d, ".ini") ||
endsWithCase_String(d, ".md") ||
endsWithCase_String(d, ".c") ||
endsWithCase_String(d, ".h") ||
diff --git a/src/media.c b/src/media.c
index 26f0af4b..0ce2ac5c 100644
--- a/src/media.c
+++ b/src/media.c
@@ -36,6 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#include <the_Foundation/file.h>
#include <the_Foundation/ptrarray.h>
+#include <the_Foundation/stringlist.h>
#include <SDL_hints.h>
#include <SDL_render.h>
#include <SDL_timer.h>
@@ -287,45 +288,112 @@ iDefineTypeConstruction(GmDownload)
/----------------------------------------------------------------------------------------------/
+iDeclareType(GmFontpack)
+struct Impl_GmFontpack {
+};
+void init_GmFontpack(iGmFontpack *d) {
+}
+void deinit_GmFontpack(iGmFontpack *d) {
+}
+static void loadData_GmFontpack_(iGmFontpack *d, const iBlock *data) {
/* Let's load it now temporarily and see what's inside. */
iArchive *zip = new_Archive();
if (openData_Archive(zip, data)) {
iFontPack *fp = collect_FontPack(new_FontPack());
setLoadPath_FontPack(fp, loadPath);
setStandalone_FontPack(fp, iTrue);
if (loadArchive_FontPack(fp, zip)) {
d->info.isValid = iTrue;
pack = fp;
}
}
iRelease(zip);
set_String(&d->packId, id_FontPack(pack).id);
d->info.packId.id = &d->packId; /* we own this String */
d->info.packId.version = id_FontPack(pack).version;
d->info.isReadOnly = isReadOnly_FontPack(pack);
const iFontSpec *spec = i.ptr;
pushBack_StringList(d->info.names, &spec->name);
iForIndices(j, spec->styles) {
insert_PtrSet(unique, spec->styles[j]);
}
d->info.sizeInBytes += size_Block(&((const iFontFile *) *j.value)->sourceData);
+}
+iDefineTypeConstruction(GmFontpack)
+/----------------------------------------------------------------------------------------------/
struct Impl_Media {
};
iDefineTypeConstruction(Media)
void init_Media(iMedia *d) {
init_PtrArray(&d->items[i]);
}
void deinit_Media(iMedia *d) {
clear_Media(d);
deinit_PtrArray(&d->items[i]);
}
void clear_Media(iMedia *d) {
deinit_GmImage(i.ptr);
}
deinit_GmAudio(a.ptr);
}
deinit_GmDownload(n.ptr);
}
deinit_GmFontpack(f.ptr);
clear_PtrArray(&d->items[type]);
}
size_t memorySize_Media(const iMedia *d) {
size_t memSize = 0;
const iGmImage *img = i.ptr;
if (img->texture) {
const iInt2 texSize = size_SDLTexture(img->texture);
@@ -335,34 +403,49 @@ size_t memorySize_Media(const iMedia *d) {
memSize += size_Block(&img->partialData);
}
}
const iGmAudio *audio = a.ptr;
if (audio->player) {
memSize += sourceDataSize_Player(audio->player);
}
}
const iGmDownload *down = n.ptr;
memSize += down->numBytes;
}
return memSize;
}
-iBool setDownloadUrl_Media(iMedia *d, iGmLinkId linkId, const iString *url) {
isNew = iTrue;
dl = new_GmDownload();
dl->props.linkId = linkId;
dl->props.isPermanent = iTrue;
set_String(&dl->props.url, url);
pushBack_PtrArray(&d->downloads, dl);
+iBool setUrl_Media(iMedia *d, iGmLinkId linkId, enum iMediaType mediaType, const iString *url) {
iGmDownload *dl = NULL;
if (isNew) {
dl = new_GmDownload();
pushBack_PtrArray(&d->items[download_MediaType], dl);
}
else {
dl = at_PtrArray(&d->items[download_MediaType], index_MediaId(existing));
}
props = &dl->props;
}
iGmDownload *dl = at_PtrArray(&d->downloads, existing - 1);
set_String(&dl->props.url, url);
iGmFontpack *fp = NULL;
if (isNew) {
fp = new_GmFontpack();
pushBack_PtrArray(&d->items[fontpack_MediaType], fp);
}
else {
fp = at_PtrArray(&d->items[fontpack_MediaType], index_MediaId(existing));
}
props = &fp->props;
props->linkId = linkId;
props->isPermanent = iTrue;
set_String(&props->url, url);
}
return isNew;
}
@@ -372,16 +455,17 @@ iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo
const iBool isPartial = (flags & partialData_MediaFlag) != 0;
const iBool allowHide = (flags & allowHide_MediaFlag) != 0;
const iBool isDeleting = (!mime || !data);
iBool isNew = iFalse;
iGmImage *img;
if (isDeleting) {
take_PtrArray(&d->images, existing - 1, (void **) &img);
take_PtrArray(&d->items[image_MediaType], existingIndex, (void **) &img);
delete_GmImage(img);
}
else {
img = at_PtrArray(&d->images, existing - 1);
img = at_PtrArray(&d->items[image_MediaType], existingIndex);
iAssert(equal_String(&img->props.mime, mime)); /* MIME cannot change */
set_Block(&img->partialData, data);
if (!isPartial) {
@@ -389,14 +473,14 @@ iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo
}
}
}
iGmAudio *audio;
if (isDeleting) {
take_PtrArray(&d->audio, existing - 1, (void **) &audio);
take_PtrArray(&d->items[audio_MediaType], existingIndex, (void **) &audio);
delete_GmAudio(audio);
}
else {
audio = at_PtrArray(&d->audio, existing - 1);
audio = at_PtrArray(&d->items[audio_MediaType], existingIndex);
iAssert(equal_String(&audio->props.mime, mime)); /* MIME cannot change */
updateSourceData_Player(audio->player, mime, data, append_PlayerUpdate);
if (!isPartial) {
@@ -408,14 +492,14 @@ iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo
}
}
}
iGmDownload *dl;
if (isDeleting) {
take_PtrArray(&d->downloads, existing - 1, (void **) &dl);
take_PtrArray(&d->items[download_MediaType], existingIndex, (void **) &dl);
delete_GmDownload(dl);
}
else {
dl = at_PtrArray(&d->downloads, existing - 1);
dl = at_PtrArray(&d->items[download_MediaType], existingIndex);
if (isEmpty_String(&dl->props.mime)) {
set_String(&dl->props.mime, mime);
}
@@ -428,6 +512,21 @@ iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo
}
}
}
iGmFontpack *fp;
if (isDeleting) {
take_PtrArray(&d->items[fontpack_MediaType], existingIndex, (void **) &fp);
delete_GmFontpack(fp);
}
else {
iAssert(!isPartial);
fp = at_PtrArray(&d->items[fontpack_MediaType], existingIndex);
if (isEmpty_String(&fp->props.mime)) {
set_String(&fp->props.mime, mime);
}
loadData_GmFontpack_(fp, data);
}
else if (!isDeleting) {
if (startsWith_String(mime, "image/")) {
/* Copy the image to a texture. */
@@ -435,7 +534,7 @@ iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo
img->props.linkId = linkId; /* TODO: use a hash? */
img->props.isPermanent = !allowHide;
set_String(&img->props.mime, mime);
pushBack_PtrArray(&d->images, img);
pushBack_PtrArray(&d->items[image_MediaType], img);
if (!isPartial) {
makeTexture_GmImage(img);
}
@@ -450,7 +549,7 @@ iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo
if (!isPartial) {
updateSourceData_Player(audio->player, NULL, NULL, complete_PlayerUpdate);
}
pushBack_PtrArray(&d->audio, audio);
pushBack_PtrArray(&d->items[audio_MediaType], audio);
/* Start playing right away. */
start_Player(audio->player);
postCommandf_App("media.player.started player:%p", audio->player);
@@ -460,125 +559,135 @@ iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo
return isNew;
}
-iMediaId findLinkImage_Media(const iMedia *d, iGmLinkId linkId) {
const iGmImage *img = i.ptr;
if (img->props.linkId == linkId) {
return index_PtrArrayConstIterator(&i) + 1;
+static iMediaId findMediaPtr_Media_(const iPtrArray *items, enum iMediaType mediaType, iGmLinkId linkId) {
const iGmMediaProps *props = i.ptr;
if (props->linkId == linkId) {
return (iMediaId){
.type = mediaType,
.id = index_PtrArrayConstIterator(&i) + 1
};
}
}
-}
-size_t numAudio_Media(const iMedia *d) {
}
-iMediaId findLinkAudio_Media(const iMedia *d, iGmLinkId linkId) {
const iGmAudio *audio = i.ptr;
if (audio->props.linkId == linkId) {
return index_PtrArrayConstIterator(&i) + 1;
+iMediaId findMediaForLink_Media(const iMedia *d, iGmLinkId linkId, enum iMediaType mediaType) {
if (mediaType == i || !mediaType) {
mid = findMediaPtr_Media_(&d->items[i], i, linkId);
if (mid.type) {
return mid;
}
}
}
}
-iMediaId findLinkDownload_Media(const iMedia *d, uint16_t linkId) {
const iGmDownload *dl = i.ptr;
if (dl->props.linkId == linkId) {
return index_PtrArrayConstIterator(&i) + 1;
}
+size_t numAudio_Media(const iMedia *d) {
}
iInt2 imageSize_Media(const iMedia *d, iMediaId imageId) {
const iGmImage *img = constAt_PtrArray(&d->images, imageId - 1);
const iGmImage *img = constAt_PtrArray(&d->items[image_MediaType], index);
return img->size;
}
return zero_I2();
}
-SDL_Texture *imageTexture_Media(const iMedia *d, uint16_t imageId) {
const iGmImage *img = constAt_PtrArray(&d->images, imageId - 1);
+SDL_Texture *imageTexture_Media(const iMedia *d, iMediaId imageId) {
const iGmImage *img = constAt_PtrArray(&d->items[image_MediaType], index);
return img->texture;
}
return NULL;
}
-iBool imageInfo_Media(const iMedia *d, iMediaId imageId, iGmMediaInfo *info_out) {
const iGmImage *img = constAt_PtrArray(&d->images, imageId - 1);
info_out->numBytes = img->numBytes;
info_out->type = cstr_String(&img->props.mime);
info_out->isPermanent = img->props.isPermanent;
return iTrue;
+iBool info_Media(const iMedia *d, iMediaId mediaId, iGmMediaInfo *info_out) {
case image_MediaType:
if (index < size_PtrArray(&d->items[image_MediaType])) {
const iGmImage *img = constAt_PtrArray(&d->items[image_MediaType], index);
info_out->numBytes = img->numBytes;
info_out->type = cstr_String(&img->props.mime);
info_out->isPermanent = img->props.isPermanent;
return iTrue;
}
break;
case audio_MediaType:
if (index < size_PtrArray(&d->items[audio_MediaType])) {
const iGmAudio *audio = constAt_PtrArray(&d->items[audio_MediaType], index);
info_out->type = cstr_String(&audio->props.mime);
info_out->isPermanent = audio->props.isPermanent;
return iTrue;
}
break;
case download_MediaType:
if (index < size_PtrArray(&d->items[download_MediaType])) {
const iGmDownload *dl = constAt_PtrArray(&d->items[download_MediaType], index);
info_out->type = cstr_String(&dl->props.mime);
info_out->isPermanent = dl->props.isPermanent;
info_out->numBytes = dl->numBytes;
return iTrue;
}
break;
case fontpack_MediaType:
/* TODO */
break;
default:
break;
}
iZap(*info_out);
return iFalse;
}
iPlayer *audioData_Media(const iMedia *d, iMediaId audioId) {
const iGmAudio *audio = constAt_PtrArray(&d->audio, audioId - 1);
const iGmAudio *audio = constAt_PtrArray(&d->items[audio_MediaType], index);
return audio->player;
}
return NULL;
}
-iBool audioInfo_Media(const iMedia *d, iMediaId audioId, iGmMediaInfo *info_out) {
const iGmAudio *audio = constAt_PtrArray(&d->audio, audioId - 1);
info_out->type = cstr_String(&audio->props.mime);
info_out->isPermanent = audio->props.isPermanent;
return iTrue;
-}
iPlayer *audioPlayer_Media(const iMedia *d, iMediaId audioId) {
const iGmAudio *audio = constAt_PtrArray(&d->audio, audioId - 1);
const iGmAudio *audio = constAt_PtrArray(&d->items[audio_MediaType], index);
return audio->player;
}
return NULL;
}
void pauseAllPlayers_Media(const iMedia *d, iBool setPaused) {
const iGmAudio *audio = constAt_PtrArray(&d->audio, i);
const iGmAudio *audio = constAt_PtrArray(&d->items[audio_MediaType], i);
if (audio->player) {
setPaused_Player(audio->player, setPaused);
}
}
}
-iBool downloadInfo_Media(const iMedia *d, iMediaId downloadId, iGmMediaInfo *info_out) {
const iGmDownload *dl = constAt_PtrArray(&d->downloads, downloadId - 1);
info_out->type = cstr_String(&dl->props.mime);
info_out->isPermanent = dl->props.isPermanent;
info_out->numBytes = dl->numBytes;
return iTrue;
-}
void downloadStats_Media(const iMedia *d, iMediaId downloadId, const iString **path_out,
float *bytesPerSecond_out, iBool *isFinished_out) {
*bytesPerSecond_out = 0.0f;
const iGmDownload *dl = constAt_PtrArray(&d->downloads, downloadId - 1);
const iGmDownload *dl = constAt_PtrArray(&d->items[download_MediaType], index);
if (dl->path) {
*path_out = dl->path;
}
@@ -587,6 +696,16 @@ void downloadStats_Media(const iMedia *d, iMediaId downloadId, const iString **p
}
}
+void fontpackInfo_Media(const iMedia *d, iMediaId fontpackId, iFontpackMediaInfo *info_out) {
const iGmFontpack *fp = constAt_PtrArray(&d->items[fontpack_MediaType], index);
*info_out = fp->info;
+}
/----------------------------------------------------------------------------------------------/
static void updated_MediaRequest_(iAnyObject *obj) {
diff --git a/src/media.h b/src/media.h
index f7ad6efd..47a4da93 100644
--- a/src/media.h
+++ b/src/media.h
@@ -22,13 +22,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#pragma once
+#include "fontpack.h"
#include <the_Foundation/block.h>
#include <the_Foundation/string.h>
#include <the_Foundation/vec2.h>
#include <SDL_render.h>
-typedef uint16_t iMediaId;
iDeclareType(Player)
iDeclareType(GmMediaInfo)
@@ -38,6 +38,7 @@ struct Impl_GmMediaInfo {
iBool isPermanent;
};
+iDeclareType(MediaId)
iDeclareType(Media)
iDeclareTypeConstruction(Media)
@@ -46,28 +47,81 @@ enum iMediaFlags {
partialData_MediaFlag = iBit(2),
};
-void clear_Media (iMedia *);
-iBool setDownloadUrl_Media (iMedia *, uint16_t linkId, const iString *url);
-iBool setData_Media (iMedia *, uint16_t linkId, const iString *mime, const iBlock *data, int flags);
-size_t memorySize_Media (const iMedia *);
+enum iMediaType { /* Note: There is a limited number of bits for these; see GmRun below. */
+};
-iMediaId findLinkImage_Media (const iMedia *, uint16_t linkId);
-iBool imageInfo_Media (const iMedia *, iMediaId imageId, iGmMediaInfo *info_out);
-iInt2 imageSize_Media (const iMedia *, iMediaId imageId);
-SDL_Texture * imageTexture_Media (const iMedia *, iMediaId imageId);
+struct Impl_MediaId {
+};
-size_t numAudio_Media (const iMedia *);
-iMediaId findLinkAudio_Media (const iMedia *, uint16_t linkId);
-iBool audioInfo_Media (const iMedia *, iMediaId audioId, iGmMediaInfo *info_out);
-iPlayer * audioPlayer_Media (const iMedia *, iMediaId audioId);
-void pauseAllPlayers_Media(const iMedia *, iBool setPaused);
+iLocalDef size_t index_MediaId(const iMediaId mediaId) {
+}
+#define iInvalidMediaId (iMediaId){ none_MediaType, 0 }
+void clear_Media (iMedia *);
+iBool setUrl_Media (iMedia *, uint16_t linkId, enum iMediaType mediaType, const iString *url);
+iBool setData_Media (iMedia *, uint16_t linkId, const iString *mime, const iBlock *data, int flags);
+size_t memorySize_Media (const iMedia *);
+iMediaId findMediaForLink_Media (const iMedia *, uint16_t linkId, enum iMediaType mediaType);
+iMediaId id_Media (const iMedia *, uint16_t linkId, enum iMediaType type);
+iBool info_Media (const iMedia *, iMediaId mediaId, iGmMediaInfo *info_out);
+iLocalDef iMediaId findLinkImage_Media(const iMedia *d, uint16_t linkId) {
+}
+iLocalDef iMediaId findLinkAudio_Media (const iMedia *d, uint16_t linkId) {
+}
+iLocalDef iMediaId findLinkDownload_Media(const iMedia *d, uint16_t linkId) {
+}
+iLocalDef iBool imageInfo_Media(const iMedia *d, uint16_t mediaId, iGmMediaInfo *info_out) {
+}
+iLocalDef iBool audioInfo_Media(const iMedia *d, uint16_t mediaId, iGmMediaInfo *info_out) {
+}
+iLocalDef iBool downloadInfo_Media(const iMedia *d, uint16_t mediaId, iGmMediaInfo *info_out) {
+}
+iInt2 imageSize_Media (const iMedia *, iMediaId imageId);
+SDL_Texture * imageTexture_Media (const iMedia *, iMediaId imageId);
+size_t numAudio_Media (const iMedia *);
+iPlayer * audioPlayer_Media (const iMedia *, iMediaId audioId);
+void pauseAllPlayers_Media (const iMedia *, iBool setPaused);
-iMediaId findLinkDownload_Media (const iMedia *, uint16_t linkId);
-iBool downloadInfo_Media (const iMedia *, iMediaId downloadId, iGmMediaInfo *info_out);
void downloadStats_Media (const iMedia *, iMediaId downloadId, const iString **path_out,
float *bytesPerSecond_out, iBool *isFinished_out);
+iDeclareType(FontpackMediaInfo)
+struct Impl_FontpackMediaInfo {
+};
+void fontpackInfo_Media (const iMedia *, iMediaId fontpackId,
iFontpackMediaInfo *info_out);
/----------------------------------------------------------------------------------------------/
iDeclareType(GmRequest)
@@ -78,7 +132,7 @@ iDeclareClass(MediaRequest)
struct Impl_MediaRequest {
iObject object;
iDocumentWidget *doc;
iGmRequest * req;
};
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) {
addAction_Widget(w, navigateRoot_KeyShortcut, "navigate.root");
}
+void cancelAllRequests_DocumentWidget(iDocumentWidget *d) {
iMediaRequest *mr = i.object;
cancel_GmRequest(mr->req);
cancel_GmRequest(d->request);
+}
void deinit_DocumentWidget(iDocumentWidget *d) {
pauseAllPlayers_Media(media_GmDocument(d->doc), iTrue);
removeTicker_App(animate_DocumentWidget_, d);
removeTicker_App(prerender_DocumentWidget_, d);
@@ -564,7 +575,8 @@ static void addVisible_DocumentWidget_(void *context, const iGmRun *run) {
pushBack_PtrArray(&d->visibleWideRuns, run);
}
}
iAssert(run->mediaId);
pushBack_PtrArray(&d->visibleMedia, run);
}
@@ -758,14 +770,14 @@ static uint32_t mediaUpdateInterval_DocumentWidget_(const iDocumentWidget *d) {
uint32_t interval = invalidInterval_;
iConstForEach(PtrArray, i, &d->visibleMedia) {
const iGmRun *run = i.ptr;
if (run->mediaType == audio_GmRunMediaType) {
iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), run->mediaId);
if (run->mediaType == audio_MediaType) {
iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run));
if (flags_Player(plr) & adjustingVolume_PlayerFlag ||
(isStarted_Player(plr) && !isPaused_Player(plr))) {
interval = iMin(interval, 1000 / 15);
}
}
else if (run->mediaType == download_GmRunMediaType) {
else if (run->mediaType == download_MediaType) {
interval = iMin(interval, 1000);
}
}
@@ -784,8 +796,8 @@ static void updateMedia_DocumentWidget_(iDocumentWidget *d) {
refresh_Widget(d);
iConstForEach(PtrArray, i, &d->visibleMedia) {
const iGmRun *run = i.ptr;
if (run->mediaType == audio_GmRunMediaType) {
iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), run->mediaId);
if (run->mediaType == audio_MediaType) {
iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run));
if (idleTimeMs_Player(plr) > 3000 && ~flags_Player(plr) & volumeGrabbed_PlayerFlag &&
flags_Player(plr) & adjustingVolume_PlayerFlag) {
setFlags_Player(plr, adjustingVolume_PlayerFlag, iFalse);
@@ -1244,6 +1256,9 @@ static const char *zipPageHeading_(const iRangecc mime) {
if (equalCase_Rangecc(mime, "application/gpub+zip")) {
return book_Icon " Gempub";
}
return "\U0001f520 Fontpack";
iRangecc type = iNullRange;
nextSplit_Rangecc(mime, "/", &type); /* skip the part before the slash */
nextSplit_Rangecc(mime, "/", &type);
@@ -1258,165 +1273,175 @@ static const char *zipPageHeading_(const iRangecc mime) {
static void postProcessRequestContent_DocumentWidget_(iDocumentWidget *d, iBool isCached) {
iWidget *w = as_Widget(d);
!cmpCase_String(&d->sourceMime, mimeType_Gempub) ||
endsWithCase_String(d->mod.url, ".gpub")) {
iGempub *gempub = new_Gempub();
if (open_Gempub(gempub, &d->sourceContent)) {
setBaseUrl_Gempub(gempub, d->mod.url);
setSource_DocumentWidget(d, collect_String(coverPageSource_Gempub(gempub)));
setCStr_String(&d->sourceMime, mimeType_Gempub);
d->sourceGempub = gempub;
}
else {
delete_Gempub(gempub);
}
const iString *localPath = collect_String(localFilePathFromUrl_String(d->mod.url));
iBool isInside = iFalse;
if (localPath && !fileExists_FileInfo(localPath)) {
/* This URL may refer to a file inside the archive. */
localPath = findContainerArchive_Path(localPath);
isInside = iTrue;
}
if (localPath && equal_CStr(mediaType_Path(localPath), "application/gpub+zip")) {
/* TODO: move this to gempub.c */
delete_Gempub(d->sourceGempub);
d->sourceGempub = NULL;
if (!cmpCase_String(&d->sourceMime, "application/octet-stream") ||
!cmpCase_String(&d->sourceMime, mimeType_Gempub) ||
endsWithCase_String(d->mod.url, ".gpub")) {
iGempub *gempub = new_Gempub();
if (openFile_Gempub(gempub, localPath)) {
setBaseUrl_Gempub(gempub, collect_String(makeFileUrl_String(localPath)));
if (!isInside) {
setSource_DocumentWidget(d, collect_String(coverPageSource_Gempub(gempub)));
setCStr_String(&d->sourceMime, mimeType_Gempub);
}
if (open_Gempub(gempub, &d->sourceContent)) {
setBaseUrl_Gempub(gempub, d->mod.url);
setSource_DocumentWidget(d, collect_String(coverPageSource_Gempub(gempub)));
setCStr_String(&d->sourceMime, mimeType_Gempub);
d->sourceGempub = gempub;
}
else {
delete_Gempub(gempub);
}
}
if (equal_String(d->mod.url, coverPageUrl_Gempub(d->sourceGempub))) {
if (!isRemote_Gempub(d->sourceGempub)) {
iArray *items = collectNew_Array(sizeof(iMenuItem));
pushBack_Array(
items,
&(iMenuItem){ book_Icon " ${gempub.cover.view}",
0,
0,
format_CStr("!open url:%s",
cstr_String(indexPageUrl_Gempub(d->sourceGempub))) });
if (navSize_Gempub(d->sourceGempub) > 0) {
pushBack_Array(
items,
&(iMenuItem){
format_CStr(forwardArrow_Icon " %s",
cstr_String(navLinkLabel_Gempub(d->sourceGempub, 0))),
SDLK_RIGHT,
0,
format_CStr("!open url:%s",
cstr_String(navLinkUrl_Gempub(d->sourceGempub, 0))) });
}
makeFooterButtons_DocumentWidget_(d, constData_Array(items), size_Array(items));
if (!d->sourceGempub) {
const iString *localPath = collect_String(localFilePathFromUrl_String(d->mod.url));
iBool isInside = iFalse;
if (localPath && !fileExists_FileInfo(localPath)) {
/* This URL may refer to a file inside the archive. */
localPath = findContainerArchive_Path(localPath);
isInside = iTrue;
}
else {
makeFooterButtons_DocumentWidget_(
d,
(iMenuItem[]){ { book_Icon " ${menu.save.downloads.open}",
SDLK_s,
KMOD_PRIMARY | KMOD_SHIFT,
"document.save open:1" },
{ download_Icon " " saveToDownloads_Label,
SDLK_s,
KMOD_PRIMARY,
"document.save" } },
2);
}
if (preloadCoverImage_Gempub(d->sourceGempub, d->doc)) {
redoLayout_GmDocument(d->doc);
updateVisible_DocumentWidget_(d);
invalidate_DocumentWidget_(d);
if (localPath && equal_CStr(mediaType_Path(localPath), mimeType_Gempub)) {
iGempub *gempub = new_Gempub();
if (openFile_Gempub(gempub, localPath)) {
setBaseUrl_Gempub(gempub, collect_String(makeFileUrl_String(localPath)));
if (!isInside) {
setSource_DocumentWidget(d, collect_String(coverPageSource_Gempub(gempub)));
setCStr_String(&d->sourceMime, mimeType_Gempub);
}
d->sourceGempub = gempub;
}
else {
delete_Gempub(gempub);
}
}
}
else if (equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) {
makeFooterButtons_DocumentWidget_(
d,
(iMenuItem[]){ { format_CStr(book_Icon " %s",
cstr_String(property_Gempub(d->sourceGempub,
title_GempubProperty))),
SDLK_LEFT,
0,
format_CStr("!open url:%s",
cstr_String(coverPageUrl_Gempub(d->sourceGempub))) } },
1);
}
else {
/* Navigation buttons. */
iArray *items = collectNew_Array(sizeof(iMenuItem));
const size_t navIndex = navIndex_Gempub(d->sourceGempub, d->mod.url);
if (navIndex != iInvalidPos) {
if (navIndex < navSize_Gempub(d->sourceGempub) - 1) {
if (d->sourceGempub) {
if (equal_String(d->mod.url, coverPageUrl_Gempub(d->sourceGempub))) {
if (!isRemote_Gempub(d->sourceGempub)) {
iArray *items = collectNew_Array(sizeof(iMenuItem));
pushBack_Array(
items,
&(iMenuItem){
format_CStr(forwardArrow_Icon " %s",
cstr_String(navLinkLabel_Gempub(d->sourceGempub, navIndex + 1))),
SDLK_RIGHT,
0,
format_CStr("!open url:%s",
cstr_String(navLinkUrl_Gempub(d->sourceGempub, navIndex + 1))) });
&(iMenuItem){ book_Icon " ${gempub.cover.view}",
0,
0,
format_CStr("!open url:%s",
cstr_String(indexPageUrl_Gempub(d->sourceGempub))) });
if (navSize_Gempub(d->sourceGempub) > 0) {
pushBack_Array(
items,
&(iMenuItem){
format_CStr(forwardArrow_Icon " %s",
cstr_String(navLinkLabel_Gempub(d->sourceGempub, 0))),
SDLK_RIGHT,
0,
format_CStr("!open url:%s",
cstr_String(navLinkUrl_Gempub(d->sourceGempub, 0))) });
}
makeFooterButtons_DocumentWidget_(d, constData_Array(items), size_Array(items));
}
if (navIndex > 0) {
pushBack_Array(
items,
&(iMenuItem){
format_CStr(backArrow_Icon " %s",
cstr_String(navLinkLabel_Gempub(d->sourceGempub, navIndex - 1))),
SDLK_LEFT,
0,
format_CStr("!open url:%s",
cstr_String(navLinkUrl_Gempub(d->sourceGempub, navIndex - 1))) });
else {
makeFooterButtons_DocumentWidget_(
d,
(iMenuItem[]){ { book_Icon " ${menu.save.downloads.open}",
SDLK_s,
KMOD_PRIMARY | KMOD_SHIFT,
"document.save open:1" },
{ download_Icon " " saveToDownloads_Label,
SDLK_s,
KMOD_PRIMARY,
"document.save" } },
2);
}
else if (!equalCase_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) {
pushBack_Array(
items,
&(iMenuItem){
format_CStr(book_Icon " %s",
cstr_String(property_Gempub(d->sourceGempub, title_GempubProperty))),
SDLK_LEFT,
0,
format_CStr("!open url:%s",
cstr_String(coverPageUrl_Gempub(d->sourceGempub))) });
if (preloadCoverImage_Gempub(d->sourceGempub, d->doc)) {
redoLayout_GmDocument(d->doc);
updateVisible_DocumentWidget_(d);
invalidate_DocumentWidget_(d);
}
}
if (!isEmpty_Array(items)) {
makeFooterButtons_DocumentWidget_(d, constData_Array(items), size_Array(items));
else if (equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) {
makeFooterButtons_DocumentWidget_(
d,
(iMenuItem[]){ { format_CStr(book_Icon " %s",
cstr_String(property_Gempub(d->sourceGempub,
title_GempubProperty))),
SDLK_LEFT,
0,
format_CStr("!open url:%s",
cstr_String(coverPageUrl_Gempub(d->sourceGempub))) } },
1);
}
}
if (!isCached && prefs_App()->pinSplit &&
equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) {
const iString *navStart = navStartLinkUrl_Gempub(d->sourceGempub);
if (navStart) {
iWindow *win = get_Window();
/* Auto-split to show index and the first navigation link. */
if (numRoots_Window(win) == 2) {
/* This document is showing the index page. */
iRoot *other = otherRoot_Window(win, w->root);
postCommandf_Root(other, "open url:%s", cstr_String(navStart));
if (prefs_App()->pinSplit == 1 && w->root == win->roots[1]) {
/* On the wrong side. */
postCommand_App("ui.split swap:1");
else {
/* Navigation buttons. */
iArray *items = collectNew_Array(sizeof(iMenuItem));
const size_t navIndex = navIndex_Gempub(d->sourceGempub, d->mod.url);
if (navIndex != iInvalidPos) {
if (navIndex < navSize_Gempub(d->sourceGempub) - 1) {
pushBack_Array(
items,
&(iMenuItem){
format_CStr(forwardArrow_Icon " %s",
cstr_String(navLinkLabel_Gempub(d->sourceGempub, navIndex + 1))),
SDLK_RIGHT,
0,
format_CStr("!open url:%s",
cstr_String(navLinkUrl_Gempub(d->sourceGempub, navIndex + 1))) });
}
if (navIndex > 0) {
pushBack_Array(
items,
&(iMenuItem){
format_CStr(backArrow_Icon " %s",
cstr_String(navLinkLabel_Gempub(d->sourceGempub, navIndex - 1))),
SDLK_LEFT,
0,
format_CStr("!open url:%s",
cstr_String(navLinkUrl_Gempub(d->sourceGempub, navIndex - 1))) });
}
else if (!equalCase_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) {
pushBack_Array(
items,
&(iMenuItem){
format_CStr(book_Icon " %s",
cstr_String(property_Gempub(d->sourceGempub, title_GempubProperty))),
SDLK_LEFT,
0,
format_CStr("!open url:%s",
cstr_String(coverPageUrl_Gempub(d->sourceGempub))) });
}
}
else {
postCommandf_App(
"open newtab:%d url:%s", otherRoot_OpenTabFlag, cstr_String(navStart));
if (!isEmpty_Array(items)) {
makeFooterButtons_DocumentWidget_(d, constData_Array(items), size_Array(items));
}
}
if (!isCached && prefs_App()->pinSplit &&
equal_String(d->mod.url, indexPageUrl_Gempub(d->sourceGempub))) {
const iString *navStart = navStartLinkUrl_Gempub(d->sourceGempub);
if (navStart) {
iWindow *win = get_Window();
/* Auto-split to show index and the first navigation link. */
if (numRoots_Window(win) == 2) {
/* This document is showing the index page. */
iRoot *other = otherRoot_Window(win, w->root);
postCommandf_Root(other, "open url:%s", cstr_String(navStart));
if (prefs_App()->pinSplit == 1 && w->root == win->roots[1]) {
/* On the wrong side. */
postCommand_App("ui.split swap:1");
}
}
else {
postCommandf_App(
"open newtab:%d url:%s", otherRoot_OpenTabFlag, cstr_String(navStart));
}
}
}
}
}
documentRunsInvalidated_DocumentWidget_(d);
redoLayout_GmDocument(d->doc);
updateVisible_DocumentWidget_(d);
invalidate_DocumentWidget_(d);
}
static void updateDocument_DocumentWidget_(iDocumentWidget *d,
@@ -1484,7 +1509,7 @@ static void updateDocument_DocumentWidget_(iDocumentWidget *d,
}
delete_String(localPath);
if (equalCase_Rangecc(urlScheme_String(d->mod.url), "file")) {
appendFormat_String(&str, "=> %s/ ${doc.archive.view}\n",
appendFormat_String(&str, "=> %s/ " folder_Icon " ${doc.archive.view}\n",
cstr_String(withSpacesEncoded_String(d->mod.url)));
}
translate_Lang(&str);
@@ -2089,7 +2114,7 @@ static iBool requestMedia_DocumentWidget_(iDocumentWidget *d, iGmLinkId linkId,
}
static iBool isDownloadRequest_DocumentWidget(const iDocumentWidget *d, const iMediaRequest *req) {
}
static iBool handleMediaCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd) {
@@ -2174,7 +2199,7 @@ static void allocVisBuffer_DocumentWidget_(const iDocumentWidget *d) {
static iBool fetchNextUnfetchedImage_DocumentWidget_(iDocumentWidget *d) {
iConstForEach(PtrArray, i, &d->visibleLinks) {
const iGmRun *run = i.ptr;
if (run->linkId && run->mediaType == none_GmRunMediaType &&
if (run->linkId && run->mediaType == none_MediaType &&
~run->flags & decoration_GmRunFlag) {
const int linkFlags = linkFlags_GmDocument(d->doc, run->linkId);
if (isMediaLink_GmDocument(d->doc, run->linkId) &&
@@ -2763,8 +2788,10 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
else if (equalWidget_Command(cmd, w, "document.downloadlink")) {
if (d->contextLink) {
const iGmLinkId linkId = d->contextLink->linkId;
setDownloadUrl_Media(
media_GmDocument(d->doc), linkId, linkUrl_GmDocument(d->doc, linkId));
setUrl_Media(media_GmDocument(d->doc),
linkId,
download_MediaType,
linkUrl_GmDocument(d->doc, linkId));
requestMedia_DocumentWidget_(d, linkId, iFalse /* no filters */);
redoLayout_GmDocument(d->doc); /* inline downloader becomes visible */
updateVisible_DocumentWidget_(d);
@@ -2874,7 +2901,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
const iMedia * media = media_GmDocument(d->doc);
const size_t num = numAudio_Media(media);
for (size_t id = 1; id <= num; id++) {
iPlayer *plr = audioPlayer_Media(media, id);
iPlayer *plr = audioPlayer_Media(media, (iMediaId){ audio_MediaType, id });
if (plr != startedPlr) {
setPaused_Player(plr, iTrue);
}
@@ -3212,8 +3239,8 @@ static iRect runRect_DocumentWidget_(const iDocumentWidget *d, const iGmRun *run
}
static void setGrabbedPlayer_DocumentWidget_(iDocumentWidget *d, const iGmRun *run) {
iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), run->mediaId);
iPlayer *plr = audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run));
setFlags_Player(plr, volumeGrabbed_PlayerFlag, iTrue);
d->grabbedStartVolume = volume_Player(plr);
d->grabbedPlayer = run;
@@ -3221,7 +3248,7 @@ static void setGrabbedPlayer_DocumentWidget_(iDocumentWidget *d, const iGmRun *r
}
else if (d->grabbedPlayer) {
setFlags_Player(
audioPlayer_Media(media_GmDocument(d->doc), d->grabbedPlayer->mediaId),
audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(d->grabbedPlayer)),
volumeGrabbed_PlayerFlag,
iFalse);
d->grabbedPlayer = NULL;
@@ -3249,11 +3276,21 @@ static iBool processMediaEvents_DocumentWidget_(iDocumentWidget *d, const SDL_Ev
const iInt2 mouse = init_I2(ev->button.x, ev->button.y);
iConstForEach(PtrArray, i, &d->visibleMedia) {
const iGmRun *run = i.ptr;
if (run->mediaType != audio_GmRunMediaType) {
if (run->mediaType == fontpack_MediaType) {
iFontpackUI ui;
init_FontpackUI(&ui, media_GmDocument(d->doc), run->mediaId,
runRect_DocumentWidget_(d, run));
if (processEvent_FontpackUI(&ui, ev)) {
refresh_Widget(d);
return iTrue;
}
}
if (run->mediaType != audio_MediaType) {
continue;
}
/* TODO: move this to mediaui.c */
const iRect rect = runRect_DocumentWidget_(d, run);
iPlayer * plr = audioPlayer_Media(media_GmDocument(d->doc), run->mediaId);
iPlayer * plr = audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run));
if (contains_Rect(rect, mouse)) {
iPlayerUI ui;
init_PlayerUI(&ui, plr, rect);
@@ -3633,7 +3670,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
cstr_String(linkUrl)) },
},
3);
if (isNative && d->contextLink->mediaType != download_GmRunMediaType) {
if (isNative && d->contextLink->mediaType != download_MediaType) {
pushBackN_Array(&items, (iMenuItem[]){
{ "---" },
{ download_Icon " ${link.download}", 0, 0, "document.downloadlink" },
@@ -3641,7 +3678,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
}
iMediaRequest *mediaReq;
if ((mediaReq = findMediaRequest_DocumentWidget_(d, d->contextLink->linkId)) != NULL &&
d->contextLink->mediaType != download_GmRunMediaType) {
d->contextLink->mediaType != download_MediaType) {
if (isFinished_GmRequest(mediaReq->req)) {
pushBack_Array(&items,
&(iMenuItem){ download_Icon " " saveToDownloads_Label,
@@ -3763,7 +3800,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
case drag_ClickResult: {
if (d->grabbedPlayer) {
iPlayer *plr =
audioPlayer_Media(media_GmDocument(d->doc), d->grabbedPlayer->mediaId);
audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(d->grabbedPlayer));
iPlayerUI ui;
init_PlayerUI(&ui, plr, runRect_DocumentWidget_(d, d->grabbedPlayer));
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
}
if (d->hoverLink) {
/* TODO: Move this to a method. */
const iGmLinkId linkId = d->hoverLink->linkId;
const int linkFlags = linkFlags_GmDocument(d->doc, linkId);
const iGmLinkId linkId = d->hoverLink->linkId;
const iMediaId linkMedia = mediaId_GmRun(d->hoverLink);
const int linkFlags = linkFlags_GmDocument(d->doc, linkId);
iAssert(linkId);
/* Media links are opened inline by default. */
if (isMediaLink_GmDocument(d->doc, linkId)) {
@@ -3937,6 +3975,12 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
}
refresh_Widget(w);
}
else if (linkMedia.type == download_MediaType ||
findMediaRequest_DocumentWidget_(d, linkId)) {
/* TODO: What should be done when clicking on an inline download?
Maybe dismiss if finished? */
return iTrue;
}
else if (linkFlags & supportedScheme_GmLinkFlag) {
int tabMode = openTabMode_Sym(modState_Keys());
if (isPinned_DocumentWidget_(d)) {
@@ -4071,7 +4115,7 @@ static void fillRange_DrawContext_(iDrawContext *d, const iGmRun *run, enum iCol
static void drawMark_DrawContext_(void *context, const iGmRun *run) {
iDrawContext *d = context;
fillRange_DrawContext_(d, run, uiMatching_ColorId, d->widget->foundMark, &d->inFoundMark);
fillRange_DrawContext_(d, run, uiMarked_ColorId, d->widget->selectMark, &d->inSelectMark);
}
@@ -4178,8 +4222,8 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
d->runsDrawn.end = run;
}
}
SDL_Texture *tex = imageTexture_Media(media_GmDocument(d->widget->doc), run->mediaId);
SDL_Texture *tex = imageTexture_Media(media_GmDocument(d->widget->doc), mediaId_GmRun(run));
const iRect dst = moved_Rect(run->visBounds, origin);
if (tex) {
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) {
fg = linkColor_GmDocument(doc, run->linkId, textHover_GmLinkPart);
iString text;
init_String(&text);
iMediaId imageId = linkImage_GmDocument(doc, run->linkId);
iMediaId audioId = !imageId ? linkAudio_GmDocument(doc, run->linkId) : 0;
iMediaId downloadId = !imageId && !audioId ?
findLinkDownload_Media(constMedia_GmDocument(doc), run->linkId) : 0;
iAssert(imageId || audioId || downloadId);
if (imageId) {
iAssert(!isEmpty_Rect(run->bounds));
iGmMediaInfo info;
imageInfo_Media(constMedia_GmDocument(doc), imageId, &info);
const iInt2 imgSize = imageSize_Media(constMedia_GmDocument(doc), imageId);
format_String(&text, "%s \u2014 %d x %d \u2014 %.1f%s",
info.type, imgSize.x, imgSize.y, info.numBytes / 1.0e6f,
cstr_Lang("mb"));
}
else if (audioId) {
iGmMediaInfo info;
audioInfo_Media(constMedia_GmDocument(doc), audioId, &info);
format_String(&text, "%s", info.type);
}
else if (downloadId) {
iGmMediaInfo info;
downloadInfo_Media(constMedia_GmDocument(doc), downloadId, &info);
format_String(&text, "%s", info.type);
const iMediaId linkMedia = findMediaForLink_Media(constMedia_GmDocument(doc),
run->linkId, none_MediaType);
iAssert(linkMedia.type != none_MediaType);
iGmMediaInfo info;
info_Media(constMedia_GmDocument(doc), linkMedia, &info);
switch (linkMedia.type) {
case image_MediaType: {
iAssert(!isEmpty_Rect(run->bounds));
const iInt2 imgSize = imageSize_Media(constMedia_GmDocument(doc), linkMedia);
format_String(&text, "%s \u2014 %d x %d \u2014 %.1f%s",
info.type, imgSize.x, imgSize.y, info.numBytes / 1.0e6f,
cstr_Lang("mb"));
break;
}
case audio_MediaType:
format_String(&text, "%s", info.type);
break;
case download_MediaType:
format_String(&text, "%s", info.type);
break;
default:
break;
}
if (findMediaRequest_DocumentWidget_(d->widget, run->linkId)) {
if (linkMedia.type != download_MediaType && /* can't cancel downloads currently */
findMediaRequest_DocumentWidget_(d->widget, run->linkId)) {
appendFormat_String(
&text, " %s" close_Icon, isHover ? escape_Color(tmLinkText_ColorId) : "");
}
@@ -4589,18 +4633,25 @@ static void drawSideElements_DocumentWidget_(const iDocumentWidget *d) {
static void drawMedia_DocumentWidget_(const iDocumentWidget *d, iPaint *p) {
iConstForEach(PtrArray, i, &d->visibleMedia) {
const iGmRun * run = i.ptr;
if (run->mediaType == audio_GmRunMediaType) {
if (run->mediaType == audio_MediaType) {
iPlayerUI ui;
init_PlayerUI(&ui,
audioPlayer_Media(media_GmDocument(d->doc), run->mediaId),
audioPlayer_Media(media_GmDocument(d->doc), mediaId_GmRun(run)),
runRect_DocumentWidget_(d, run));
draw_PlayerUI(&ui, p);
}
else if (run->mediaType == download_GmRunMediaType) {
else if (run->mediaType == download_MediaType) {
iDownloadUI ui;
init_DownloadUI(&ui, d, run->mediaId, runRect_DocumentWidget_(d, run));
init_DownloadUI(&ui, constMedia_GmDocument(d->doc), run->mediaId,
runRect_DocumentWidget_(d, run));
draw_DownloadUI(&ui, p);
}
else if (run->mediaType == fontpack_MediaType) {
iFontpackUI ui;
init_FontpackUI(&ui, constMedia_GmDocument(d->doc), run->mediaId,
runRect_DocumentWidget_(d, run));
draw_FontpackUI(&ui, p);
}
}
}
diff --git a/src/ui/documentwidget.h b/src/ui/documentwidget.h
index cc09c72d..1f2ecfc0 100644
--- a/src/ui/documentwidget.h
+++ b/src/ui/documentwidget.h
@@ -32,6 +32,8 @@ iDeclareType(History)
iDeclareWidgetClass(DocumentWidget)
iDeclareObjectConstruction(DocumentWidget)
+void cancelAllRequests_DocumentWidget(iDocumentWidget *);
void serializeState_DocumentWidget (const iDocumentWidget *, iStream *outs);
void deserializeState_DocumentWidget (iDocumentWidget *, iStream *ins);
diff --git a/src/ui/mediaui.c b/src/ui/mediaui.c
index 22552027..aa45d73a 100644
--- a/src/ui/mediaui.c
+++ b/src/ui/mediaui.c
@@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#include "lang.h"
#include <the_Foundation/path.h>
+#include <the_Foundation/stringlist.h>
static const char *volumeChar_(float volume) {
if (volume <= 0) {
@@ -61,7 +62,7 @@ void init_PlayerUI(iPlayerUI *d, const iPlayer *player, iRect bounds) {
}
}
-static void drawPlayerButton_(iPaint *p, iRect rect, const char *label, int font) {
+static void drawInlineButton_(iPaint *p, iRect rect, const char *label, int font) {
const iInt2 mouse = mouseCoord_Window(get_Window(), 0);
const iBool isHover = contains_Rect(rect, mouse);
const iBool isPressed = isHover && (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON_LEFT) != 0;
@@ -113,14 +114,14 @@ void draw_PlayerUI(iPlayerUI *d, iPaint *p) {
const iBool isAdjusting = (flags_Player(d->player) & adjustingVolume_PlayerFlag) != 0;
fillRect_Paint(p, d->bounds, playerBackground_ColorId);
drawRect_Paint(p, d->bounds, playerFrame_ColorId);
d->playPauseRect,
isPaused_Player(d->player) ? "\U0001f782" : "\u23f8",
uiContent_FontId);
if (!isAdjusting) {
drawPlayerButton_(
drawInlineButton_(
p, d->volumeRect, volumeChar_(volume_Player(d->player)), uiContentSymbols_FontId);
}
const int hgt = lineHeight_Text(uiLabelBig_FontId);
@@ -228,24 +229,26 @@ static void drawSevenSegmentBytes_(iInt2 pos, int color, size_t numBytes) {
deinit_String(&digits);
}
-void init_DownloadUI(iDownloadUI *d, const iDocumentWidget *doc, uint16_t mediaId, iRect bounds) {
+void init_DownloadUI(iDownloadUI *d, const iMedia *media, uint16_t mediaId, iRect bounds) {
d->mediaId = mediaId;
d->bounds = bounds;
}
+/----------------------------------------------------------------------------------------------/
iBool processEvent_DownloadUI(iDownloadUI *d, const SDL_Event *ev) {
return iFalse;
}
void draw_DownloadUI(const iDownloadUI *d, iPaint *p) {
iGmMediaInfo info;
float bytesPerSecond;
const iString *path;
iBool isFinished;
&path, &bytesPerSecond, &isFinished);
fillRect_Paint(p, d->bounds, uiBackground_ColorId);
drawRect_Paint(p, d->bounds, uiSeparator_ColorId);
iRect rect = d->bounds;
@@ -275,3 +278,68 @@ void draw_DownloadUI(const iDownloadUI *d, iPaint *p) {
translateCStr_Lang("\u2014 ${mb.per.sec}"));
}
}
+/----------------------------------------------------------------------------------------------/
+void init_FontpackUI(iFontpackUI *d, const iMedia *media, uint16_t mediaId, iRect bounds) {
muli_I2(gap2_UI, 3));
+}
+iBool processEvent_FontpackUI(iFontpackUI *d, const SDL_Event *ev) {
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP: {
const iInt2 pos = init_I2(ev->button.x, ev->button.y);
if (contains_Rect(d->installRect, pos)) {
return iTrue;
}
break;
}
case SDL_MOUSEMOTION:
if (contains_Rect(d->bounds, init_I2(ev->motion.x, ev->motion.y))) {
return iTrue;
}
break;
+}
+int height_FontpackUI(const iMedia *media, uint16_t mediaId, int width) {
lineHeight_Text(uiLabel_FontId) * (1 + size_StringList(info.names));
+}
+void draw_FontpackUI(const iFontpackUI *d, iPaint *p) {
cstr_String(info.packId.id), info.packId.version);
info.sizeInBytes / 1.0e6, size_StringList(info.names),
+// checks[info.isValid], info.isValid ? "No errors" : "Errors detected",
checks[info.isInstalled], info.isInstalled ? "Installed" : "Not installed",
info.isReadOnly ? "Read-Only" : "");
drawRange_Text(uiLabel_FontId, pos, uiText_ColorId, range_String(i.value));
pos.y += lineHeight_Text(uiLabel_FontId);
"${media.fontpack.install}", uiLabel_FontId);
+}
diff --git a/src/ui/mediaui.h b/src/ui/mediaui.h
index e79dedc0..73de1994 100644
--- a/src/ui/mediaui.h
+++ b/src/ui/mediaui.h
@@ -51,11 +51,27 @@ iDeclareType(Media)
iDeclareType(DownloadUI)
struct Impl_DownloadUI {
uint16_t mediaId;
iRect bounds;
};
-void init_DownloadUI (iDownloadUI *, const iDocumentWidget *doc, uint16_t mediaId, iRect bounds);
+void init_DownloadUI (iDownloadUI *, const iMedia *media, uint16_t mediaId, iRect bounds);
iBool processEvent_DownloadUI (iDownloadUI *, const SDL_Event *ev);
void draw_DownloadUI (const iDownloadUI *, iPaint *p);
+/----------------------------------------------------------------------------------------------/
+iDeclareType(FontpackUI)
+struct Impl_FontpackUI {
+};
+void init_FontpackUI (iFontpackUI *, const iMedia *media, uint16_t mediaId, iRect bounds);
+int height_FontpackUI (const iMedia *media, uint16_t mediaId, int width);
+iBool processEvent_FontpackUI (iFontpackUI *, const SDL_Event *ev);
+void draw_FontpackUI (const iFontpackUI *, iPaint *p);
--
2.25.1
text/plain
This content has been proxied by September (ba2dc).