summaryrefslogtreecommitdiff
path: root/libtk/twind.c
diff options
context:
space:
mode:
Diffstat (limited to 'libtk/twind.c')
-rw-r--r--libtk/twind.c396
1 files changed, 396 insertions, 0 deletions
diff --git a/libtk/twind.c b/libtk/twind.c
new file mode 100644
index 00000000..49b9c94b
--- /dev/null
+++ b/libtk/twind.c
@@ -0,0 +1,396 @@
+#include "lib9.h"
+#include "draw.h"
+#include "tk.h"
+#include "textw.h"
+
+#define istring u.string
+#define iwin u.win
+#define imark u.mark
+#define iline u.line
+
+#define O(t, e) ((long)(&((t*)0)->e))
+
+static char* tktwincget(Tk*, char*, char**);
+static char* tktwinconfigure(Tk*, char*, char**);
+static char* tktwincreate(Tk*, char*, char**);
+static char* tktwinnames(Tk*, char*, char**);
+static int winowned(Tk *tk, Tk *sub);
+
+static
+TkStab tkalign[] =
+{
+ "top", Tktop,
+ "bottom", Tkbottom,
+ "center", Tkcenter,
+ "baseline", Tkbaseline,
+ nil
+};
+
+static
+TkOption twinopts[] =
+{
+ "align", OPTstab, O(TkTwind, align), tkalign,
+ "create", OPTtext, O(TkTwind, create), nil,
+ "padx", OPTnndist, O(TkTwind, padx), nil,
+ "pady", OPTnndist, O(TkTwind, pady), nil,
+ "stretch", OPTstab, O(TkTwind, stretch), tkbool,
+ "window", OPTwinp, O(TkTwind, sub), nil,
+ "ascent", OPTdist, O(TkTwind, ascent), nil,
+ nil
+};
+
+TkCmdtab
+tktwincmd[] =
+{
+ "cget", tktwincget,
+ "configure", tktwinconfigure,
+ "create", tktwincreate,
+ "names", tktwinnames,
+ nil
+};
+
+int
+tktfindsubitem(Tk *sub, TkTindex *ix)
+{
+ Tk *tk, *isub;
+ TkText *tkt;
+
+ tk = sub->parent;
+ if(tk != nil) {
+ tkt = TKobj(TkText, tk);
+ tktstartind(tkt, ix);
+ do {
+ if(ix->item->kind == TkTwin) {
+ isub = ix->item->iwin->sub;
+ if(isub != nil &&
+ isub->name != nil &&
+ strcmp(isub->name->name, sub->name->name) == 0)
+ return 1;
+ }
+ } while(tktadjustind(tkt, TkTbyitem, ix));
+ }
+ return 0;
+}
+
+static void
+tktwindsize(Tk *tk, TkTindex *ix)
+{
+ Tk *s;
+ TkTitem *i;
+ TkTwind *w;
+
+
+ i = ix->item;
+ /* assert(i->kind == TkTwin); */
+
+ w = i->iwin;
+ s = w->sub;
+ if(s == nil)
+ return;
+
+ if(w->width != s->act.width || w->height != s->act.height) {
+ s->act.width = w->width;
+ s->act.height = w->height;
+ if(s->slave) {
+ tkpackqit(s);
+ tkrunpack(tk->env->top);
+ }
+ }
+
+ tktfixgeom(tk, tktprevwrapline(tk, ix->line), ix->line, 0);
+ tktextsize(tk, 1);
+}
+
+/*
+ * check that w->focus is a window packed under tk.
+ * XXX couldn't this be done more simply by traversing
+ * directly upwards from w->focus and seeing whether
+ * it hits tk? (same applies to tkcvschkwfocus in cwind.c)
+ */
+static int
+tktchkwfocus(TkTwind *w, Tk *tk)
+{
+ if(w->focus == tk)
+ return 1;
+ for(tk = tk->slave; tk; tk = tk->next)
+ if(tktchkwfocus(w, tk))
+ return 1;
+ return 0;
+}
+
+static void
+tktwingeom(Tk *sub, int x, int y, int w, int h)
+{
+ TkTindex ix;
+ Tk *tk;
+ TkTwind *win;
+
+ USED(x);
+ USED(y);
+
+ tk = sub->parent;
+ if(!tktfindsubitem(sub, &ix)) {
+ print("tktwingeom: %s not found\n", sub->name->name);
+ return;
+ }
+
+ win = ix.item->iwin;
+
+ if(win->focus != nil) {
+ if(tktchkwfocus(win, sub) == 0)
+ win->focus = nil;
+ }
+
+ win->width = w;
+ win->height = h;
+
+ sub->req.width = w;
+ sub->req.height = h;
+ tktwindsize(tk, &ix);
+}
+
+static void
+tktdestroyed(Tk *sub)
+{
+ TkTindex ix;
+ Tk *tk;
+
+ if(tktfindsubitem(sub, &ix)) {
+ ix.item->iwin->sub = nil;
+ ix.item->iwin->focus = nil;
+ if((tk = sub->parent) != nil) {
+ tktfixgeom(tk, tktprevwrapline(tk, ix.line), ix.line, 0);
+ tktextsize(tk, 1);
+ sub->parent = nil;
+ }
+ }
+}
+
+void
+tktdirty(Tk *sub)
+{
+ Tk *tk, *parent, *isub;
+ TkText *tkt;
+ TkTindex ix;
+
+ parent = nil;
+ for(tk = sub; tk && parent == nil; tk = tk->master)
+ parent = tk->parent;
+ if(tk == nil)
+ return;
+
+ tkt = TKobj(TkText, parent);
+ tktstartind(tkt, &ix);
+ do {
+ if(ix.item->kind == TkTwin) {
+ isub = ix.item->iwin->sub;
+ if(isub != nil) {
+ tktfixgeom(parent, tktprevwrapline(parent, ix.line), ix.line, 0);
+ if (sub->flag & Tktransparent)
+ parent->flag |= Tkrefresh; /* XXX could be more efficient, by drawing the background locally? */
+ return;
+ }
+ }
+ } while(tktadjustind(tkt, TkTbyitem, &ix));
+ tktextsize(parent, 1);
+}
+
+static char*
+tktwinchk(Tk *tk, TkTwind *w, Tk *oldsub)
+{
+ Tk *sub;
+
+ sub = w->sub;
+ if (sub != oldsub) {
+ w->sub = oldsub;
+ if(sub == nil)
+ return nil;
+
+ if(sub->flag & Tkwindow)
+ return TkIstop;
+
+ if(sub->master != nil || sub->parent != nil)
+ return TkWpack;
+
+ if (oldsub != nil) {
+ oldsub->parent = nil;
+ oldsub->geom = nil;
+ oldsub->destroyed = nil;
+ }
+ w->sub = sub;
+ w->focus = nil;
+
+ sub->parent = tk;
+ tksetbits(sub, Tksubsub);
+ sub->geom = tktwingeom;
+ sub->destroyed = tktdestroyed;
+
+ w->width = sub->req.width;
+ w->height = sub->req.height;
+ w->owned = winowned(tk, sub);
+ }
+
+ return nil;
+}
+
+
+/* Text Window Command (+ means implemented)
+ +cget
+ +configure
+ +create
+ +names
+*/
+
+static char*
+tktwincget(Tk *tk, char *arg, char **val)
+{
+ char *e;
+ TkTindex ix;
+ TkOptab tko[2];
+
+ e = tktindparse(tk, &arg, &ix);
+ if(e != nil)
+ return e;
+ if(ix.item->kind != TkTwin)
+ return TkBadwp;
+
+ tko[0].ptr = ix.item->iwin;
+ tko[0].optab = twinopts;
+ tko[1].ptr = nil;
+
+ return tkgencget(tko, arg, val, tk->env->top);
+}
+
+static char*
+tktwinconfigure(Tk *tk, char *arg, char **val)
+{
+ char *e;
+ TkTindex ix;
+ TkOptab tko[2];
+ Tk *oldsub;
+
+ USED(val);
+
+ e = tktindparse(tk, &arg, &ix);
+ if(e != nil)
+ return e;
+ if(ix.item->kind != TkTwin)
+ return TkBadwp;
+
+ oldsub = ix.item->iwin->sub;
+
+ tko[0].ptr = ix.item->iwin;
+ tko[0].optab = twinopts;
+ tko[1].ptr = nil;
+
+ e = tkparse(tk->env->top, arg, tko, nil);
+ if(e != nil)
+ return e;
+
+ e = tktwinchk(tk, ix.item->iwin, oldsub);
+ if(e != nil)
+ return e;
+
+ tktwindsize(tk, &ix);
+ return nil;
+}
+
+/*
+ * return true if tk is an ancestor of sub
+ */
+static int
+winowned(Tk *tk, Tk *sub)
+{
+ int len;
+ if (tk->name == nil || sub->name == nil)
+ return 0;
+ len = strlen(tk->name->name);
+ if (strncmp(tk->name->name, sub->name->name, len) == 0 &&
+ sub->name->name[len] == '.')
+ return 1;
+ return 0;
+}
+
+static char*
+tktwincreate(Tk *tk, char *arg, char **val)
+{
+ char *e;
+ TkTindex ix;
+ TkTitem *i;
+ TkText *tkt;
+ TkOptab tko[2];
+
+ USED(val);
+
+ tkt = TKobj(TkText, tk);
+
+ e = tktindparse(tk, &arg, &ix);
+ if(e != nil)
+ return e;
+
+ e = tktnewitem(TkTwin, 0, &i);
+ if(e != nil)
+ return e;
+
+ i->iwin = malloc(sizeof(TkTwind));
+ if(i->iwin == nil) {
+ tktfreeitems(tkt, i, 1);
+ return TkNomem;
+ }
+
+ memset(i->iwin, 0, sizeof(TkTwind));
+ i->iwin->align = Tkcenter;
+ i->iwin->ascent = -1;
+
+ tko[0].ptr = i->iwin;
+ tko[0].optab = twinopts;
+ tko[1].ptr = nil;
+
+ e = tkparse(tk->env->top, arg, tko, nil);
+ if(e != nil) {
+ err1:
+ tktfreeitems(tkt, i, 1);
+ return e;
+ }
+
+ e = tktwinchk(tk, i->iwin, nil);
+ if(e != nil)
+ goto err1;
+
+ e = tktsplititem(&ix);
+ if(e != nil)
+ goto err1;
+
+ tktiteminsert(tkt, &ix, i);
+ if(e != nil)
+ goto err1;
+
+ tktadjustind(tkt, TkTbyitemback, &ix);
+ tktwindsize(tk, &ix);
+
+ return nil;
+}
+
+static char*
+tktwinnames(Tk *tk, char *arg, char **val)
+{
+ char *e, *fmt;
+ TkTindex ix;
+ TkText *tkt = TKobj(TkText, tk);
+
+ USED(arg);
+
+ tktstartind(tkt, &ix);
+ fmt = "%s";
+ do {
+ if(ix.item->kind == TkTwin
+ && ix.item->iwin->sub != nil
+ && (ix.item->iwin->sub->name != nil)) {
+ e = tkvalue(val, fmt, ix.item->iwin->sub->name->name);
+ if(e != nil)
+ return e;
+ fmt = " %s";
+ }
+ } while(tktadjustind(tkt, TkTbyitem, &ix));
+ return nil;
+}