summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/gmdocument.c9
-rw-r--r--src/media.c89
-rw-r--r--src/media.h20
-rw-r--r--src/ui/documentwidget.c15
4 files changed, 104 insertions, 29 deletions
diff --git a/src/gmdocument.c b/src/gmdocument.c
index 5e762dff..72bd0f33 100644
--- a/src/gmdocument.c
+++ b/src/gmdocument.c
@@ -559,8 +559,9 @@ static void doLayout_GmDocument_(iGmDocument *d) {
559 const iMediaId imageId = findLinkImage_Media(d->media, run.linkId); 559 const iMediaId imageId = findLinkImage_Media(d->media, run.linkId);
560 const iMediaId audioId = !imageId ? findLinkAudio_Media(d->media, run.linkId) : 0; 560 const iMediaId audioId = !imageId ? findLinkAudio_Media(d->media, run.linkId) : 0;
561 if (imageId) { 561 if (imageId) {
562 iGmImageInfo img; 562 iGmMediaInfo img;
563 imageInfo_Media(d->media, imageId, &img); 563 imageInfo_Media(d->media, imageId, &img);
564 const iInt2 imgSize = imageSize_Media(d->media, imageId);
564 /* Mark the link as having content. */ { 565 /* Mark the link as having content. */ {
565 iGmLink *link = at_PtrArray(&d->links, run.linkId - 1); 566 iGmLink *link = at_PtrArray(&d->links, run.linkId - 1);
566 link->flags |= content_GmLinkFlag; 567 link->flags |= content_GmLinkFlag;
@@ -572,10 +573,10 @@ static void doLayout_GmDocument_(iGmDocument *d) {
572 pos.y += margin; 573 pos.y += margin;
573 run.bounds.pos = pos; 574 run.bounds.pos = pos;
574 run.bounds.size.x = d->size.x; 575 run.bounds.size.x = d->size.x;
575 const float aspect = (float) img.size.y / (float) img.size.x; 576 const float aspect = (float) imgSize.y / (float) imgSize.x;
576 run.bounds.size.y = d->size.x * aspect; 577 run.bounds.size.y = d->size.x * aspect;
577 run.visBounds = run.bounds; 578 run.visBounds = run.bounds;
578 const iInt2 maxSize = mulf_I2(img.size, get_Window()->pixelRatio); 579 const iInt2 maxSize = mulf_I2(imgSize, get_Window()->pixelRatio);
579 if (width_Rect(run.visBounds) > maxSize.x) { 580 if (width_Rect(run.visBounds) > maxSize.x) {
580 /* Don't scale the image up. */ 581 /* Don't scale the image up. */
581 run.visBounds.size.y = 582 run.visBounds.size.y =
@@ -593,7 +594,7 @@ static void doLayout_GmDocument_(iGmDocument *d) {
593 pos.y += run.bounds.size.y + margin; 594 pos.y += run.bounds.size.y + margin;
594 } 595 }
595 else if (audioId) { 596 else if (audioId) {
596 iGmAudioInfo info; 597 iGmMediaInfo info;
597 audioInfo_Media(d->media, audioId, &info); 598 audioInfo_Media(d->media, audioId, &info);
598 /* Mark the link as having content. */ { 599 /* Mark the link as having content. */ {
599 iGmLink *link = at_PtrArray(&d->links, run.linkId - 1); 600 iGmLink *link = at_PtrArray(&d->links, run.linkId - 1);
diff --git a/src/media.c b/src/media.c
index 8bd635a5..65454756 100644
--- a/src/media.c
+++ b/src/media.c
@@ -37,16 +37,19 @@ iDeclareType(GmMediaProps)
37struct Impl_GmMediaProps { 37struct Impl_GmMediaProps {
38 iGmLinkId linkId; 38 iGmLinkId linkId;
39 iString mime; 39 iString mime;
40 iString url;
40 iBool isPermanent; 41 iBool isPermanent;
41}; 42};
42 43
43static void init_GmMediaProps_(iGmMediaProps *d) { 44static void init_GmMediaProps_(iGmMediaProps *d) {
44 d->linkId = 0; 45 d->linkId = 0;
45 init_String(&d->mime); 46 init_String(&d->mime);
47 init_String(&d->url);
46 d->isPermanent = iFalse; 48 d->isPermanent = iFalse;
47} 49}
48 50
49static void deinit_GmMediaProps_(iGmMediaProps *d) { 51static void deinit_GmMediaProps_(iGmMediaProps *d) {
52 deinit_String(&d->url);
50 deinit_String(&d->mime); 53 deinit_String(&d->mime);
51} 54}
52 55
@@ -125,9 +128,29 @@ iDefineTypeConstruction(GmAudio)
125 128
126/*----------------------------------------------------------------------------------------------*/ 129/*----------------------------------------------------------------------------------------------*/
127 130
131iDeclareType(GmDownload)
132
133struct Impl_GmDownload {
134 iGmMediaProps props;
135 /* TODO: Speed statistics. */
136};
137
138void init_GmDownload(iGmDownload *d) {
139 init_GmMediaProps_(&d->props);
140}
141
142void deinit_GmDownload(iGmDownload *d) {
143 deinit_GmMediaProps_(&d->props);
144}
145
146iDefineTypeConstruction(GmDownload)
147
148/*----------------------------------------------------------------------------------------------*/
149
128struct Impl_Media { 150struct Impl_Media {
129 iPtrArray images; 151 iPtrArray images;
130 iPtrArray audio; 152 iPtrArray audio;
153 iPtrArray downloads;
131}; 154};
132 155
133iDefineTypeConstruction(Media) 156iDefineTypeConstruction(Media)
@@ -135,10 +158,12 @@ iDefineTypeConstruction(Media)
135void init_Media(iMedia *d) { 158void init_Media(iMedia *d) {
136 init_PtrArray(&d->images); 159 init_PtrArray(&d->images);
137 init_PtrArray(&d->audio); 160 init_PtrArray(&d->audio);
161 init_PtrArray(&d->downloads);
138} 162}
139 163
140void deinit_Media(iMedia *d) { 164void deinit_Media(iMedia *d) {
141 clear_Media(d); 165 clear_Media(d);
166 deinit_PtrArray(&d->downloads);
142 deinit_PtrArray(&d->audio); 167 deinit_PtrArray(&d->audio);
143 deinit_PtrArray(&d->images); 168 deinit_PtrArray(&d->images);
144} 169}
@@ -152,6 +177,29 @@ void clear_Media(iMedia *d) {
152 deinit_GmAudio(a.ptr); 177 deinit_GmAudio(a.ptr);
153 } 178 }
154 clear_PtrArray(&d->audio); 179 clear_PtrArray(&d->audio);
180 iForEach(PtrArray, n, &d->downloads) {
181 deinit_GmDownload(n.ptr);
182 }
183 clear_PtrArray(&d->downloads);
184}
185
186iBool setUrl_Media(iMedia *d, iGmLinkId linkId, const iString *url) {
187 iGmDownload *dl = NULL;
188 iMediaId existing = findLinkDownload_Media(d, linkId);
189 iBool isNew = iFalse;
190 if (!existing) {
191 isNew = iTrue;
192 dl = new_GmDownload();
193 dl->props.linkId = linkId;
194 dl->props.isPermanent = iTrue;
195 set_String(&dl->props.url, url);
196 pushBack_PtrArray(&d->downloads, dl);
197 }
198 else {
199 iGmDownload *dl = at_PtrArray(&d->downloads, existing - 1);
200 set_String(&dl->props.url, url);
201 }
202 return isNew;
155} 203}
156 204
157iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlock *data, 205iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlock *data,
@@ -195,6 +243,18 @@ iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlo
195 } 243 }
196 } 244 }
197 } 245 }
246 else if ((existing = findLinkDownload_Media(d, linkId)) != 0) {
247 iGmDownload *dl;
248 if (isDeleting) {
249 take_PtrArray(&d->downloads, existing - 1, (void **) &dl);
250 delete_GmDownload(dl);
251 }
252 else {
253 dl = at_PtrArray(&d->downloads, existing - 1);
254 iAssert(equal_String(&dl->props.mime, mime)); /* MIME cannot change */
255 /* TODO: Write data chunk to file. */
256 }
257 }
198 else if (!isDeleting) { 258 else if (!isDeleting) {
199 if (startsWith_String(mime, "image/")) { 259 if (startsWith_String(mime, "image/")) {
200 /* Copy the image to a texture. */ 260 /* Copy the image to a texture. */
@@ -253,6 +313,24 @@ iMediaId findLinkAudio_Media(const iMedia *d, iGmLinkId linkId) {
253 return 0; 313 return 0;
254} 314}
255 315
316iMediaId findLinkDownload_Media(const iMedia *d, uint16_t linkId) {
317 iConstForEach(PtrArray, i, &d->downloads) {
318 const iGmDownload *dl = i.ptr;
319 if (dl->props.linkId == linkId) {
320 return index_PtrArrayConstIterator(&i) + 1;
321 }
322 }
323 return 0;
324}
325
326iInt2 imageSize_Media(const iMedia *d, iMediaId imageId) {
327 if (imageId > 0 && imageId <= size_PtrArray(&d->images)) {
328 const iGmImage *img = constAt_PtrArray(&d->images, imageId - 1);
329 return img->size;
330 }
331 return zero_I2();
332}
333
256SDL_Texture *imageTexture_Media(const iMedia *d, uint16_t imageId) { 334SDL_Texture *imageTexture_Media(const iMedia *d, uint16_t imageId) {
257 if (imageId > 0 && imageId <= size_PtrArray(&d->images)) { 335 if (imageId > 0 && imageId <= size_PtrArray(&d->images)) {
258 const iGmImage *img = constAt_PtrArray(&d->images, imageId - 1); 336 const iGmImage *img = constAt_PtrArray(&d->images, imageId - 1);
@@ -261,12 +339,11 @@ SDL_Texture *imageTexture_Media(const iMedia *d, uint16_t imageId) {
261 return NULL; 339 return NULL;
262} 340}
263 341
264iBool imageInfo_Media(const iMedia *d, iMediaId imageId, iGmImageInfo *info_out) { 342iBool imageInfo_Media(const iMedia *d, iMediaId imageId, iGmMediaInfo *info_out) {
265 if (imageId > 0 && imageId <= size_PtrArray(&d->images)) { 343 if (imageId > 0 && imageId <= size_PtrArray(&d->images)) {
266 const iGmImage *img = constAt_PtrArray(&d->images, imageId - 1); 344 const iGmImage *img = constAt_PtrArray(&d->images, imageId - 1);
267 info_out->size = img->size;
268 info_out->numBytes = img->numBytes; 345 info_out->numBytes = img->numBytes;
269 info_out->mime = cstr_String(&img->props.mime); 346 info_out->type = cstr_String(&img->props.mime);
270 info_out->isPermanent = img->props.isPermanent; 347 info_out->isPermanent = img->props.isPermanent;
271 return iTrue; 348 return iTrue;
272 } 349 }
@@ -282,10 +359,10 @@ iPlayer *audioData_Media(const iMedia *d, iMediaId audioId) {
282 return NULL; 359 return NULL;
283} 360}
284 361
285iBool audioInfo_Media(const iMedia *d, iMediaId audioId, iGmAudioInfo *info_out) { 362iBool audioInfo_Media(const iMedia *d, iMediaId audioId, iGmMediaInfo *info_out) {
286 if (audioId > 0 && audioId <= size_PtrArray(&d->audio)) { 363 if (audioId > 0 && audioId <= size_PtrArray(&d->audio)) {
287 const iGmAudio *audio = constAt_PtrArray(&d->audio, audioId - 1); 364 const iGmAudio *audio = constAt_PtrArray(&d->audio, audioId - 1);
288 info_out->mime = cstr_String(&audio->props.mime); 365 info_out->type = cstr_String(&audio->props.mime);
289 info_out->isPermanent = audio->props.isPermanent; 366 info_out->isPermanent = audio->props.isPermanent;
290 return iTrue; 367 return iTrue;
291 } 368 }
diff --git a/src/media.h b/src/media.h
index ddaa2d3c..ebead352 100644
--- a/src/media.h
+++ b/src/media.h
@@ -30,18 +30,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
30typedef uint16_t iMediaId; 30typedef uint16_t iMediaId;
31 31
32iDeclareType(Player) 32iDeclareType(Player)
33iDeclareType(GmImageInfo) 33iDeclareType(GmMediaInfo)
34iDeclareType(GmAudioInfo)
35 34
36struct Impl_GmImageInfo { 35struct Impl_GmMediaInfo {
37 iInt2 size; 36 const char *type; /* MIME */
38 size_t numBytes; 37 size_t numBytes;
39 const char *mime;
40 iBool isPermanent;
41};
42
43struct Impl_GmAudioInfo {
44 const char *mime;
45 iBool isPermanent; 38 iBool isPermanent;
46}; 39};
47 40
@@ -54,17 +47,20 @@ enum iMediaFlags {
54}; 47};
55 48
56void clear_Media (iMedia *); 49void clear_Media (iMedia *);
50iBool setUrl_Media (iMedia *, uint16_t linkId, const iString *url);
57iBool setData_Media (iMedia *, uint16_t linkId, const iString *mime, const iBlock *data, int flags); 51iBool setData_Media (iMedia *, uint16_t linkId, const iString *mime, const iBlock *data, int flags);
58 52
59iMediaId findLinkImage_Media (const iMedia *, uint16_t linkId); 53iMediaId findLinkImage_Media (const iMedia *, uint16_t linkId);
60iBool imageInfo_Media (const iMedia *, iMediaId imageId, iGmImageInfo *info_out); 54iBool imageInfo_Media (const iMedia *, iMediaId imageId, iGmMediaInfo *info_out);
55iInt2 imageSize_Media (const iMedia *, iMediaId imageId);
61SDL_Texture * imageTexture_Media (const iMedia *, iMediaId imageId); 56SDL_Texture * imageTexture_Media (const iMedia *, iMediaId imageId);
62 57
63size_t numAudio_Media (const iMedia *); 58size_t numAudio_Media (const iMedia *);
64iMediaId findLinkAudio_Media (const iMedia *, uint16_t linkId); 59iMediaId findLinkAudio_Media (const iMedia *, uint16_t linkId);
65iBool audioInfo_Media (const iMedia *, iMediaId audioId, iGmAudioInfo *info_out); 60iBool audioInfo_Media (const iMedia *, iMediaId audioId, iGmMediaInfo *info_out);
66iPlayer * audioPlayer_Media (const iMedia *, iMediaId audioId); 61iPlayer * audioPlayer_Media (const iMedia *, iMediaId audioId);
67 62
63iMediaId findLinkDownload_Media (const iMedia *, uint16_t linkId);
68 64
69/*----------------------------------------------------------------------------------------------*/ 65/*----------------------------------------------------------------------------------------------*/
70 66
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 6b023349..87b8d7ad 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -1058,7 +1058,7 @@ static void updateTrust_DocumentWidget_(iDocumentWidget *d, const iGmResponse *r
1058 else if (d->certFlags & trusted_GmCertFlag) { 1058 else if (d->certFlags & trusted_GmCertFlag) {
1059 updateTextCStr_LabelWidget(lock, green_ColorEscape closedLock_CStr); 1059 updateTextCStr_LabelWidget(lock, green_ColorEscape closedLock_CStr);
1060 } 1060 }
1061 else { 1061 else {
1062 updateTextCStr_LabelWidget(lock, isDarkMode ? orange_ColorEscape "\u26a0" 1062 updateTextCStr_LabelWidget(lock, isDarkMode ? orange_ColorEscape "\u26a0"
1063 : black_ColorEscape "\u26a0"); 1063 : black_ColorEscape "\u26a0");
1064 } 1064 }
@@ -1755,7 +1755,7 @@ static iBool handleCommand_DocumentWidget_(iDocumentWidget *d, const char *cmd)
1755 const iRangecc host = urlHost_String(d->mod.url); 1755 const iRangecc host = urlHost_String(d->mod.url);
1756 if (!isEmpty_Block(d->certFingerprint) && !isEmpty_Range(&host)) { 1756 if (!isEmpty_Block(d->certFingerprint) && !isEmpty_Range(&host)) {
1757 setTrusted_GmCerts(certs_App(), host, d->certFingerprint, &d->certExpiry); 1757 setTrusted_GmCerts(certs_App(), host, d->certFingerprint, &d->certExpiry);
1758 d->certFlags |= trusted_GmCertFlag; 1758 d->certFlags |= trusted_GmCertFlag;
1759 postCommand_App("document.info"); 1759 postCommand_App("document.info");
1760 updateTrust_DocumentWidget_(d, NULL); 1760 updateTrust_DocumentWidget_(d, NULL);
1761 redoLayout_GmDocument(d->doc); 1761 redoLayout_GmDocument(d->doc);
@@ -2667,7 +2667,7 @@ static iBool processEvent_DocumentWidget_(iDocumentWidget *d, const SDL_Event *e
2667 const int linkFlags = linkFlags_GmDocument(d->doc, linkId); 2667 const int linkFlags = linkFlags_GmDocument(d->doc, linkId);
2668 iAssert(linkId); 2668 iAssert(linkId);
2669 /* Media links are opened inline by default. */ 2669 /* Media links are opened inline by default. */
2670 if (isMediaLink_GmDocument(d->doc, linkId)) { 2670 if (isMediaLink_GmDocument(d->doc, linkId)) {
2671 if (linkFlags & content_GmLinkFlag && linkFlags & permanent_GmLinkFlag) { 2671 if (linkFlags & content_GmLinkFlag && linkFlags & permanent_GmLinkFlag) {
2672 /* We have the content and it cannot be dismissed, so nothing 2672 /* We have the content and it cannot be dismissed, so nothing
2673 further to do. */ 2673 further to do. */
@@ -3005,15 +3005,16 @@ static void drawRun_DrawContext_(void *context, const iGmRun *run) {
3005 iAssert(imageId || audioId); 3005 iAssert(imageId || audioId);
3006 if (imageId) { 3006 if (imageId) {
3007 iAssert(!isEmpty_Rect(run->bounds)); 3007 iAssert(!isEmpty_Rect(run->bounds));
3008 iGmImageInfo info; 3008 iGmMediaInfo info;
3009 imageInfo_Media(constMedia_GmDocument(doc), imageId, &info); 3009 imageInfo_Media(constMedia_GmDocument(doc), imageId, &info);
3010 const iInt2 imgSize = imageSize_Media(constMedia_GmDocument(doc), imageId);
3010 format_String(&text, "%s \u2014 %d x %d \u2014 %.1fMB", 3011 format_String(&text, "%s \u2014 %d x %d \u2014 %.1fMB",
3011 info.mime, info.size.x, info.size.y, info.numBytes / 1.0e6f); 3012 info.type, imgSize.x, imgSize.y, info.numBytes / 1.0e6f);
3012 } 3013 }
3013 else if (audioId) { 3014 else if (audioId) {
3014 iGmAudioInfo info; 3015 iGmMediaInfo info;
3015 audioInfo_Media(constMedia_GmDocument(doc), audioId, &info); 3016 audioInfo_Media(constMedia_GmDocument(doc), audioId, &info);
3016 format_String(&text, "%s", info.mime); 3017 format_String(&text, "%s", info.type);
3017 } 3018 }
3018 if (findMediaRequest_DocumentWidget_(d->widget, run->linkId)) { 3019 if (findMediaRequest_DocumentWidget_(d->widget, run->linkId)) {
3019 appendFormat_String( 3020 appendFormat_String(