diff options
-rw-r--r-- | src/gmdocument.c | 9 | ||||
-rw-r--r-- | src/media.c | 89 | ||||
-rw-r--r-- | src/media.h | 20 | ||||
-rw-r--r-- | src/ui/documentwidget.c | 15 |
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) | |||
37 | struct Impl_GmMediaProps { | 37 | struct 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 | ||
43 | static void init_GmMediaProps_(iGmMediaProps *d) { | 44 | static 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 | ||
49 | static void deinit_GmMediaProps_(iGmMediaProps *d) { | 51 | static 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 | ||
131 | iDeclareType(GmDownload) | ||
132 | |||
133 | struct Impl_GmDownload { | ||
134 | iGmMediaProps props; | ||
135 | /* TODO: Speed statistics. */ | ||
136 | }; | ||
137 | |||
138 | void init_GmDownload(iGmDownload *d) { | ||
139 | init_GmMediaProps_(&d->props); | ||
140 | } | ||
141 | |||
142 | void deinit_GmDownload(iGmDownload *d) { | ||
143 | deinit_GmMediaProps_(&d->props); | ||
144 | } | ||
145 | |||
146 | iDefineTypeConstruction(GmDownload) | ||
147 | |||
148 | /*----------------------------------------------------------------------------------------------*/ | ||
149 | |||
128 | struct Impl_Media { | 150 | struct Impl_Media { |
129 | iPtrArray images; | 151 | iPtrArray images; |
130 | iPtrArray audio; | 152 | iPtrArray audio; |
153 | iPtrArray downloads; | ||
131 | }; | 154 | }; |
132 | 155 | ||
133 | iDefineTypeConstruction(Media) | 156 | iDefineTypeConstruction(Media) |
@@ -135,10 +158,12 @@ iDefineTypeConstruction(Media) | |||
135 | void init_Media(iMedia *d) { | 158 | void 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 | ||
140 | void deinit_Media(iMedia *d) { | 164 | void 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 | |||
186 | iBool 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 | ||
157 | iBool setData_Media(iMedia *d, iGmLinkId linkId, const iString *mime, const iBlock *data, | 205 | iBool 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 | ||
316 | iMediaId 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 | |||
326 | iInt2 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 | |||
256 | SDL_Texture *imageTexture_Media(const iMedia *d, uint16_t imageId) { | 334 | SDL_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 | ||
264 | iBool imageInfo_Media(const iMedia *d, iMediaId imageId, iGmImageInfo *info_out) { | 342 | iBool 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 | ||
285 | iBool audioInfo_Media(const iMedia *d, iMediaId audioId, iGmAudioInfo *info_out) { | 362 | iBool 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. */ | |||
30 | typedef uint16_t iMediaId; | 30 | typedef uint16_t iMediaId; |
31 | 31 | ||
32 | iDeclareType(Player) | 32 | iDeclareType(Player) |
33 | iDeclareType(GmImageInfo) | 33 | iDeclareType(GmMediaInfo) |
34 | iDeclareType(GmAudioInfo) | ||
35 | 34 | ||
36 | struct Impl_GmImageInfo { | 35 | struct 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 | |||
43 | struct 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 | ||
56 | void clear_Media (iMedia *); | 49 | void clear_Media (iMedia *); |
50 | iBool setUrl_Media (iMedia *, uint16_t linkId, const iString *url); | ||
57 | iBool setData_Media (iMedia *, uint16_t linkId, const iString *mime, const iBlock *data, int flags); | 51 | iBool setData_Media (iMedia *, uint16_t linkId, const iString *mime, const iBlock *data, int flags); |
58 | 52 | ||
59 | iMediaId findLinkImage_Media (const iMedia *, uint16_t linkId); | 53 | iMediaId findLinkImage_Media (const iMedia *, uint16_t linkId); |
60 | iBool imageInfo_Media (const iMedia *, iMediaId imageId, iGmImageInfo *info_out); | 54 | iBool imageInfo_Media (const iMedia *, iMediaId imageId, iGmMediaInfo *info_out); |
55 | iInt2 imageSize_Media (const iMedia *, iMediaId imageId); | ||
61 | SDL_Texture * imageTexture_Media (const iMedia *, iMediaId imageId); | 56 | SDL_Texture * imageTexture_Media (const iMedia *, iMediaId imageId); |
62 | 57 | ||
63 | size_t numAudio_Media (const iMedia *); | 58 | size_t numAudio_Media (const iMedia *); |
64 | iMediaId findLinkAudio_Media (const iMedia *, uint16_t linkId); | 59 | iMediaId findLinkAudio_Media (const iMedia *, uint16_t linkId); |
65 | iBool audioInfo_Media (const iMedia *, iMediaId audioId, iGmAudioInfo *info_out); | 60 | iBool audioInfo_Media (const iMedia *, iMediaId audioId, iGmMediaInfo *info_out); |
66 | iPlayer * audioPlayer_Media (const iMedia *, iMediaId audioId); | 61 | iPlayer * audioPlayer_Media (const iMedia *, iMediaId audioId); |
67 | 62 | ||
63 | iMediaId 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( |