From 4382041409c8b5987830e3873895d7a1e36970ce Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sat, 23 Sep 2023 09:48:35 +1000 Subject: [PATCH] Add render-imageview render-imageview displays an image and allows it to be zoomed or panned. This isn't quite the final design, but it is a first step that lets me explore what I want. Signed-off-by: NeilBrown --- Makefile | 1 + data/modules.ini | 1 + doc-email.c | 10 +- python/module-notmuch.py | 17 ++++ render-imageview.c | 209 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 render-imageview.c diff --git a/Makefile b/Makefile index bda901a7..f258410f 100644 --- a/Makefile +++ b/Makefile @@ -77,6 +77,7 @@ SHOBJ = O/doc-text.o O/doc-dir.o O/doc-docs.o \ O/doc-email.o O/doc-multipart.o O/doc-list.o \ O/render-hex.o O/render-lines.o \ O/render-format.o O/render-complete.o \ + O/render-imageview.o \ O/lib-view.o O/lib-tile.o O/lib-popup.o O/lib-linecount.o O/lib-keymap.o \ O/lib-search.o O/lib-messageline.o O/lib-input.o O/lib-libevent.o \ O/lib-history.o O/lib-crop.o O/lib-markup.o O/lib-rfc822header.o \ diff --git a/data/modules.ini b/data/modules.ini index 3cb81a36..dbe6e3c5 100644 --- a/data/modules.ini +++ b/data/modules.ini @@ -167,3 +167,4 @@ lib-menubar = attach-menubar lib-rangetrack = rangetrack:new +render-imageview = attach-render-imageview diff --git a/doc-email.c b/doc-email.c index 6579a1ad..58d44cbb 100644 --- a/doc-email.c +++ b/doc-email.c @@ -24,7 +24,7 @@ * etc). * * The middle part has attributes set on the document which can be - * accessed form the spacer using "multipart-prev:" + * accessed from the spacer using "multipart-prev:" * - email:path identify the part in the nexted multipart struture * e.g. "header", "body", "body,multipart/mixed:0,mulitpart/alternate:1" * - email:actions a ':' separated list of buttons. "hide:save:view" @@ -1109,6 +1109,14 @@ DEF_CMD(email_view_get_attr) return comm_call(ci->comm2, "callback", ci->focus, 0, ci->mark, v, 0, NULL, ci->str); } + if (strcmp(ci->str, "email:image") == 0) { + char im[100]; + + p = get_part(ci->home->parent, ci->mark); + sprintf(im, "comm:doc:multipart-%d-doc:get-bytes", to_orig(p)); + return comm_call(ci->comm2, "cb", ci->focus, 0, ci->mark, + im, 0, NULL, ci->str); + } return Efallthrough; } diff --git a/python/module-notmuch.py b/python/module-notmuch.py index f2890f5c..f0e7b627 100644 --- a/python/module-notmuch.py +++ b/python/module-notmuch.py @@ -3373,6 +3373,23 @@ class notmuch_message_view(edlib.Pane): self.set_vis(focus, mark, s == "none") return 1 + def handle_query(self, key, focus, mark, **a): + "handle:doc:char-?" + s = focus.call("doc:get-attr", mark, "email:content-type", ret='str') + if not s or not s.startswith("image/"): + return 1 + s = focus.call("doc:get-attr", mark, "email:image", ret='str') + if not s: + return 1 + d = focus.call("doc:get-doc", ret='pane') + d['linecount-disable'] = "yes" + p = focus.call("PopupTile", "D4M", ret='pane') + p = focus.call("doc:attach-view", p, "invisible", ret='pane') + p = p.call("attach-view", ret='pane') + p['status-line'] = "image view" + p.call("attach-render-imageview", s) + return 1 + def handle_space(self, key, focus, mark, **a): "handle:doc:char- " if focus.call("K:Next", 1, mark) == 2: diff --git a/render-imageview.c b/render-imageview.c new file mode 100644 index 00000000..ae84cbbe --- /dev/null +++ b/render-imageview.c @@ -0,0 +1,209 @@ +/* + * Copyright Neil Brown ©2023 + * May be distributed under terms of GPLv2 - see file:COPYING + * + * Display an image and allow it to be scaled and panned. + */ + +#include +#define PANE_DATA_TYPE struct imageview_data +#include "core.h" + +struct imageview_data { + char *image; + int w,h; /* size of image */ + int scale; /* 1024 * displayed-size / actual size */ + int cx,cy; /* image co-ordinates at center of pane. + * Kept stable during zoom + */ + short px,py; /* number of pixels in each pane cell */ +}; +#include "core-pane.h" + +DEF_CMD_CLOSED(imageview_close) +{ + struct imageview_data *ivd = ci->home->data; + + free(ivd->image); + ivd->image = NULL; + return 1; +} + +DEF_CMD(imageview_refresh) +{ + struct imageview_data *ivd = ci->home->data; + char *img = ivd->image; + int x,y; + int pw = ci->home->w * ivd->px; + int ph = ci->home->h * ivd->py; + + call("Draw:clear", ci->focus, 0, NULL, "bg:black"); + if (!img) + return 1; + + if (ivd->scale <= 0) { + int xs = pw * 1024 / ivd->w; + int ys = ph * 1024 / ivd->h; + ivd->scale = xs > ys ? ys : xs; + } + + x = (ivd->cx * ivd->scale) - pw * 1024 / 2; + y = (ivd->cy * ivd->scale) - ph * 1024 / 2; + + if (ivd->scale * ivd->w < pw * 1024) + /* Doesn't use full width, so centre */ + x = -(pw * 1024 - ivd->scale * ivd->w) / 2; + else { + /* Does use full width, so avoid margins */ + if (x < 0) + x = 0; + if (x > ivd->w * ivd->scale - pw * 1024) + x = ivd->w * ivd->scale - pw * 1024; + } + if (ivd->scale * ivd->h < ph * 1024) + y = -(ph * 1024 - ivd->scale * ivd->h) / 2; + else { + if (y < 0) + y = 0; + if (y > ivd->h * ivd->scale - ph * 1024) + y = ivd->h * ivd->scale - ph * 1024; + } + + ivd->cx = (pw * 1024 / 2 + x) / ivd->scale; + ivd->cy = (ph * 1024 / 2 + y) / ivd->scale; + + call("Draw:image", ci->focus, ivd->scale, NULL, img, + 0, NULL, NULL, x / 1024, y / 1024); + + return 1; +} + +DEF_CMD(imageview_refresh_size) +{ + struct imageview_data *ivd = ci->home->data; + int pw = ci->home->w * ivd->px; + int ph = ci->home->h * ivd->py; + + if (ivd->scale * ivd->w < pw * 1024 && + ivd->scale * ivd->h < ph * 1024) + /* Scale it too small to make use of space - reset */ + ivd->scale = 0; + pane_damaged(ci->home, DAMAGED_REFRESH); + + return Efallthrough; +} + +DEF_CMD(imageview_zoom) +{ + /* Keep the centre of the pane at the same pixel when + * zooming. + */ + struct imageview_data *ivd = ci->home->data; + + if (strcmp(ci->key, "K-+") == 0) { + /* zoom up */ + ivd->scale += ivd->scale / 10; + } else { + /* zoom down */ + ivd->scale -= ivd->scale / 11; + } + + pane_damaged(ci->home, DAMAGED_REFRESH); + return 1; +} + +DEF_CMD(imageview_pan) +{ + struct imageview_data *ivd = ci->home->data; + int pw = ci->home->w * ivd->px; + int ph = ci->home->h * ivd->py; + + switch (ci->key[2]) { + case 'L': + ivd->cx -= pw * 1024 / ivd->scale / 10; + break; + case 'R': + ivd->cx += pw * 1024 / ivd->scale / 10; + break; + case 'U': + ivd->cy -= ph * 1024 / ivd->scale / 10; + break; + case 'D': + ivd->cy += ph * 1024 / ivd->scale / 10; + break; + } + pane_damaged(ci->home, DAMAGED_REFRESH); + return 1; +} + +DEF_CMD(imageview_reset) +{ + struct imageview_data *ivd = ci->home->data; + + ivd->scale = 0; + + pane_damaged(ci->home, DAMAGED_REFRESH); + return 1; +} + +DEF_CMD(imageview_quit) +{ + call("Window:close", ci->focus); + return 1; +} + +static struct map *iv_map; +DEF_LOOKUP_CMD(iv_handle, iv_map); + +DEF_CMD(imageview_attach) +{ + struct pane *p; + struct imageview_data *ivd; + char *pxl; + + p = pane_register(ci->focus, 0, &iv_handle.c); + if (!p) + return Efail; + ivd = p->data; + if (ci->str) { + struct call_return cr; + cr = call_ret(bytes, ci->str+5, ci->focus); + + ivd->image = strdup(ci->str); + cr = call_ret(all, "Draw:image-size", ci->focus, + 0, NULL, ivd->image); + ivd->w = cr.x; + ivd->h = cr.y; + } + ivd->scale = 0; + pxl = pane_attr_get(p, "Display:pixels"); + if (sscanf(pxl ?: "1x1", "%hdx%hx", &ivd->px, &ivd->py) != 2) + ivd->px = ivd->py = 1; + + pane_damaged(p, DAMAGED_REFRESH); + + return comm_call(ci->comm2, "cb", p); +} + +void edlib_init(struct pane *ed safe) +{ + call_comm("global-set-command", ed, &imageview_attach, 0, NULL, + "attach-render-imageview"); + iv_map = key_alloc(); + key_add(iv_map, "Close", &imageview_close); + key_add(iv_map, "Refresh", &imageview_refresh); + key_add(iv_map, "Refresh:size", &imageview_refresh_size); + + key_add(iv_map, "K-+", &imageview_zoom); + key_add(iv_map, "K--", &imageview_zoom); + + key_add(iv_map, "K:Left", &imageview_pan); + key_add(iv_map, "K:Right", &imageview_pan); + key_add(iv_map, "K:Up", &imageview_pan); + key_add(iv_map, "K:Down", &imageview_pan); + key_add(iv_map, "K:Home", &imageview_reset); + key_add(iv_map, "K-.", &imageview_reset); + + key_add(iv_map, "K:ESC", &imageview_quit); + key_add(iv_map, "K-q", &imageview_quit); +} -- 2.39.5