]> git.neil.brown.name Git - edlib.git/commitdiff
Add pygtk display.
authorNeilBrown <neil@brown.name>
Sat, 26 Dec 2015 01:18:51 +0000 (12:18 +1100)
committerNeilBrown <neil@brown.name>
Sun, 27 Dec 2015 05:42:20 +0000 (16:42 +1100)
This is an alternate display, using pygtk.

It also provides an alternate event loop, and the alternate loop
must be used.  This is wart which will be resolved eventually.

Only a constant-width font is used.  Obviously that must change.

Signed-off-by: NeilBrown <neil@brown.name>
edlib.c
python/display-pygtk.py [new file with mode: 0644]

diff --git a/edlib.c b/edlib.c
index f8fdcb4fdd8a45bff6f91d8653cd39bc32aa6067..a5b52835d621c6cc2b4520f00ed096bf84528a66 100644 (file)
--- a/edlib.c
+++ b/edlib.c
@@ -57,6 +57,10 @@ int main(int argc, char *argv[])
        struct call_return cr;
        struct editor *ed;
        struct pane *vroot = editor_new();
+       int gtk = 0;
+
+       if (argc > 1 && strcmp(argv[1], "-g") == 0)
+               gtk = 1;
 
        ed = pane2ed(vroot);
        setlocale(LC_ALL, "");
@@ -64,17 +68,27 @@ int main(int argc, char *argv[])
 
        editor_load_module(ed, "lib-line-count");
        editor_load_module(ed, "lib-search");
-       editor_load_module(ed, "lib-libevent");
-       editor_load_module(ed, "display-ncurses");
-       call3("libevent:activate", vroot, 0, NULL);
+       editor_load_module(ed, "lang-python");
+       if (gtk) {
+               call5("python-load", vroot, 0, NULL, "python/display-pygtk.py", 0);
+               call3("pygtkevent:activate", vroot, 0, NULL);
+               ci.key = "display-pygtk";
+       } else {
+               editor_load_module(ed, "lib-libevent");
+               editor_load_module(ed, "display-ncurses");
+               call3("libevent:activate", vroot, 0, NULL);
+               ci.key = "display-ncurses";
+       }
        ci.home = ci.focus = vroot;
-       ci.key = "display-ncurses";
        cr.c = take_pane;
        cr.p = NULL;
        ci.comm2 = &cr.c;
        if (key_handle(&ci) <= 0)
                exit(1);
-       root = cr.p;
+       if (gtk)
+               root = pane_attach(cr.p, "input", NULL, NULL);
+       else
+               root = cr.p;
        global = pane_attach(root, "messageline", NULL, NULL);
        global = pane_attach(global, "global-keymap", NULL, NULL);
 
@@ -85,7 +99,6 @@ int main(int argc, char *argv[])
        if (b)
                p = doc_from_text(b, "*Welcome*", WelcomeText);
        if (p) {
-               editor_load_module(ed, "lang-python");
                memset(&ci, 0, sizeof(ci));
                ci.home = ci.focus = p;
                ci.key = "python-load";
diff --git a/python/display-pygtk.py b/python/display-pygtk.py
new file mode 100644 (file)
index 0000000..b317b2e
--- /dev/null
@@ -0,0 +1,240 @@
+# -*- coding: utf-8 -*-
+# Copyright Neil Brown ©2015 <neil@brown.name>
+# May be distributed under terms of GPLv2 - see file:COPYING
+
+# edlib display module using pygtk to create a window, handle drawing, and
+# receive mouse/keyboard events.
+# provides eventloop function using gtk.main.
+
+import sys
+import os
+import pygtk
+import gtk
+import pango
+import thread
+import gobject
+import glib
+
+class EdDisplay(gtk.Window):
+    def __init__(self, home):
+        gtk.Window.__init__(self)
+        self.pane = edlib.Pane(home, self.handle, None)
+        self.pane.w = 80
+        self.pane.h = 24
+        self.panes = {}
+        self.set_default_size(700, 300)
+        self.set_title("EDLIB")
+        self.connect('destroy', self.close_win)
+        self.create_ui()
+        self.show()
+
+    def handle(self, key, **a):
+        if key == "pane-text":
+            (x,y) = a["xy"]
+            c = a["extra"]
+            f = a["focus"]
+            if "str2" in a:
+                attr = a["str2"]
+            else:
+                attr = ""
+            pm = self.get_pixmap(f);
+            self.draw_char(pm, x, y, c, attr)
+            return True
+        if key == "pane-clear":
+            f = a["focus"]
+            pm = self.get_pixmap(f)
+            self.do_clear(pm)
+            return True
+        if key == "Notify:Close":
+            f = a["focus"]
+            if f and f in self.panes:
+                del self.panes[f]
+            return True
+
+        return None
+
+    def get_pixmap(self, p):
+        if p in self.panes:
+            pm = self.panes[p]
+            (w,h) = pm.get_size()
+            if w == p.w * self.charwidth and h == p.h * self.lineheight:
+                return pm
+            del self.panes[p]
+        else:
+            self.pane.add_notify(p, "Notify:Close")
+
+        self.panes[p] = gtk.gdk.Pixmap(self.window, p.w * self.charwidth, p.h * self.lineheight)
+        return self.panes[p]
+
+    def close_win(self, *a):
+        self.destroy()
+
+    def create_ui(self):
+        text = gtk.DrawingArea()
+        self.text = text
+        self.add(text)
+        text.show()
+        self.fd = pango.FontDescription("mono 12")
+        text.modify_font(self.fd)
+        ctx = text.get_pango_context()
+        metric = ctx.get_metrics(self.fd)
+        self.lineheight = (metric.get_ascent() + metric.get_descent()) / pango.SCALE
+        self.charwidth = metric.get_approximate_char_width() / pango.SCALE
+        self.set_default_size(self.charwidth * 80, self.lineheight * 24)
+        self.gc = None
+        self.bg = None
+
+        self.text.connect("expose-event", self.refresh)
+        self.text.connect("button-press-event", self.press)
+        self.text.connect("key-press-event", self.keystroke)
+        self.text.connect("configure-event", self.reconfigure)
+        self.text.set_events(gtk.gdk.EXPOSURE_MASK|
+                             gtk.gdk.STRUCTURE_MASK|
+                             gtk.gdk.BUTTON_PRESS_MASK|
+                             gtk.gdk.BUTTON_RELEASE_MASK|
+                             gtk.gdk.KEY_PRESS_MASK|
+                             gtk.gdk.KEY_RELEASE_MASK);
+        self.text.set_property("can-focus", True)
+
+
+    def refresh(self, *a):
+        self.pane.refresh()
+        l = self.panes.keys()
+        l.sort(key=lambda pane: pane.abs_z)
+        for p in l:
+            pm = self.panes[p]
+            (rx,ry) = p.abs(0,0)
+            self.text.window.draw_drawable(self.bg, pm, 0, 0,
+                                           rx * self.charwidth, ry * self.lineheight,
+                                           -1, -1)
+
+    def reconfigure(self, w, ev):
+        alloc = w.get_allocation()
+        rows = int(alloc.height / self.lineheight)
+        cols = int(alloc.width / self.charwidth)
+        self.pane.w = cols
+        self.pane.h = rows
+        self.text.queue_draw()
+
+    def press(self, c, event):
+        c.grab_focus()
+        x = int(event.x / self.charwidth)
+        y = int(event.y / self.lineheight)
+        s = "Click-" + ("%d"%event.button)
+        if event.state & gtk.gdk.SHIFT_MASK:
+            s = "S-" + s;
+        if event.state & gtk.gdk.CONTROL_MASK:
+            s = "C-" + s;
+        if event.state & gtk.gdk.MOD1_MASK:
+            s = "M-" + s;
+        self.pane.call_xy("Mouse-event", "Click-1", self.pane, (x,y))
+        self.refresh()
+
+    eventmap = { "Return" : "Return",
+                 "Tab" : "Tab",
+                 "Escape" : "ESC",
+                 "Linefeed" : "LF",
+                 "Down" : "Down",
+                 "Up" : "Up",
+                 "Left" : "Left",
+                 "Right" : "Right",
+                 "Home" : "Home",
+                 "End" : "End",
+                 "BackSpace" : "Backspace",
+                 "Delete" : "Del",
+                 "Insert" : "Ins",
+                 "Page_Up" : "Prior",
+                 "Page_Down" : "Next",
+                 }
+    def keystroke(self, c, event):
+        kv = gtk.gdk.keyval_name(event.keyval)
+        if kv in self.eventmap:
+            s = self.eventmap[kv]
+            if event.state & gtk.gdk.SHIFT_MASK:
+                s = "S-" + s;
+            if event.state & gtk.gdk.CONTROL_MASK:
+                s = "C-" + s;
+        else:
+            s = event.string
+            if len(s) == 0:
+                return
+            if ord(s[0]) < 32:
+                s = "C-Chr-" + chr(ord(s[0])+64)
+            else:
+                s = "Chr-" + s
+        if event.state & gtk.gdk.MOD1_MASK:
+            s = "M-" + s;
+        self.pane.call_focus("Keystroke", self.pane, s)
+        self.pane.refresh()
+
+    def draw_char(self, pm, x, y, c, attr):
+        t = self.text
+        if not self.gc:
+            self.gc = t.window.new_gc()
+            cmap = t.get_colormap()
+            self.gc.set_foreground(cmap.alloc_color(gtk.gdk.color_parse("blue")))
+        if not self.bg:
+            self.bg = t.window.new_gc()
+            cmap = t.get_colormap()
+            self.bg.set_foreground(cmap.alloc_color(gtk.gdk.color_parse("lightyellow")))
+        layout = t.create_pango_layout(unichr(c))
+        bg = self.bg; fg = self.gc
+        if "inverse" in attr:
+            fg,bg = bg,fg
+        pm.draw_rectangle(bg, True, x * self.charwidth, y * self.lineheight, self.charwidth, self.lineheight)
+        pm.draw_layout(fg, x * self.charwidth, y * self.lineheight, layout)
+        t.queue_draw()
+        return False
+
+    def do_clear(self, pm):
+
+        t = self.text
+        if not self.bg:
+            self.bg = t.window.new_gc()
+            cmap = t.get_colormap()
+            self.bg.set_foreground(cmap.alloc_color(gtk.gdk.color_parse("lightyellow")))
+        (w,h) = pm.get_size()
+        pm.draw_rectangle(self.bg, True, 0, 0, w, h)
+
+def new_display(key, home, comm2, **a):
+    disp = EdDisplay(home)
+    comm2('callback', disp.pane)
+    return 1
+
+class events:
+    def __init__(self):
+        self.active = True
+
+    def read(self, key, focus, comm2, numeric, **a):
+        gobject.io_add_watch(numeric, gobject.IO_IN, self.docall, comm2, focus, numeric)
+
+    def docall(self, comm, focus, fd):
+        comm2.call("callback", focus, fd)
+        return 1
+
+    def signal(self, key, focus, comm2, numeric, **a):
+        pass
+
+    def run(self, key, **a):
+        if self.active:
+            gtk.main()
+            return 1
+        else:
+            return 0
+
+    def deactivate(self, key, **a):
+        self.active = False
+        gtk.main_quit()
+        return 1
+
+def events_activate(key, home, focus, **a):
+    ev = events()
+    home.call("global-set-command", focus, "event:read", ev.read)
+    home.call("global-set-command", focus, "event:signal", ev.signal)
+    home.call("global-set-command", focus, "event:run", ev.run)
+    home.call("global-set-command", focus, "event:deactivate", ev.deactivate)
+
+    return 1
+
+editor.call("global-set-command", pane, "display-pygtk", new_display);
+editor.call("global-set-command", pane, "pygtkevent:activate", events_activate);