]> git.neil.brown.name Git - edlib.git/commitdiff
Add render-imageview
authorNeilBrown <neil@brown.name>
Fri, 22 Sep 2023 23:48:35 +0000 (09:48 +1000)
committerNeilBrown <neil@brown.name>
Fri, 29 Sep 2023 02:18:12 +0000 (12:18 +1000)
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 <neil@brown.name>
Makefile
data/modules.ini
doc-email.c
python/module-notmuch.py
render-imageview.c [new file with mode: 0644]

index bda901a72a285b50424138eb07e793a50aefff7b..f258410f90d1787bb86e15478a07ab0611a48dd1 100644 (file)
--- 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 \
index 3cb81a369d27d941ed44a92cc5854b7b619e5b3c..dbe6e3c5ccf8c0e77a743e40ca670a512576bb55 100644 (file)
@@ -167,3 +167,4 @@ lib-menubar =
        attach-menubar
 
 lib-rangetrack = rangetrack:new
+render-imageview = attach-render-imageview
index 6579a1adde16e52d501a6ee61c2121b0a93fef06..58d44cbb914fb817e1c2ecd6cc48035f94daeecb 100644 (file)
@@ -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;
 }
 
index f2890f5c80d0254884bbdc2aea5ee0bc51e72077..f0e7b627fddbc977c19e3400e3dac8399efd6a7f 100644 (file)
@@ -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 (file)
index 0000000..ae84cbb
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright Neil Brown ©2023 <neil@brown.name>
+ * May be distributed under terms of GPLv2 - see file:COPYING
+ *
+ * Display an image and allow it to be scaled and panned.
+ */
+
+#include <stdio.h>
+#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);
+}