implement Wpanel; include "mods.m"; mods, debug, win, tree: import dat; Menu: import menus; Cpointer, Tagwid, Taght, Inset, setcursor, drawtag, getfont, readsnarf, cookclick, Arrow, Drag: import gui; # Panel polymorphism support. # Panels are only drawn due to user interaction or updates made. All that # from the o/mero tree. This means that write and update (data/ctl) routines should never draw. # They just set the Pdraw flag. # Once the whole tree is updated, we call draw for those that must # be redrawn. # Also, mouse and keyboard handlers are called from within the tree, after # synchronizing, thus it's safe to operate on the panel. # However: # Containers do not keep the panel busy in the mean time, thus they may call # tree operations but may have races if not done with care. # Atoms keep the panel busy while doing user I/O, thus they may NOT call # tree operations (directly) but should not have races. # Doing this right yet not creating one process per panel is hard. # Perhaps we should have gone for Xfids. panels: list of Pimpl; cc: chan of (ref Panel, string); init(d: Livedat, dir: string) { dat = d; initmods(); fd := open(dir, OREAD); if (fd == nil) error(sprint("can't open %s: %r", dir)); (dirs, n) := readdir->readall(fd, Readdir->NONE); if (n < 0) error(sprint("reading %s: %r", dir)); panels = nil; for (i := 0; i < n; i++){ nm := dirs[i].name; l := len nm; if (l > 7 && nm[0:3] == "owp" && nm[l-4:] == ".dis"){ path := dir + "/" + nm; if (debug['P']) fprint(stderr, "loading %s\n", path); m := load Pimpl path; if (m == nil) fprint(stderr, "%s: %r\n", path); else if ((e := m->init(d)) != nil) fprint(stderr, "%s: %s\n", path, e); else panels = m :: panels; } } if (debug['d'] || debug['P']) fprint(stderr, "%d panels loaded\n", len panels); } screenname(s: string): int { for (i := 0; i < len s; i++) if (s[i] == ':') return 0; return 1; } addchild(fp: ref Panel, p: ref Panel) { n := len fp.child; child := array[n + 1] of ref Panel; child[0:] = fp.child; child[n] = p; fp.child = child; p.parent = fp; } nullpanel: Panel; Panel.new(n: string, fp: ref Panel): ref Panel { pname := n; if (screenname(n)) pname = "row:" + n; for (l := panels; l != nil; l = tl l){ m := hd l; for (prefs := m->prefixes; prefs != nil; prefs = tl prefs){ pl := len hd prefs; if (len pname > pl && pname[0:pl] == hd prefs){ p := ref nullpanel; p.impl = m; p.name = n; if (fp != nil){ addchild(fp, p); p.path = names->rooted(fp.path, n); if(fp.flags&Ptag) p.depth = fp.depth + 1; else p.depth = fp.depth; } p.rowcol = Qatom; p.init(); p.flags |= Predraw; if (debug['P']) fprint(stderr, "o/live: new %s\n", p.path); return p; } } } return nil; } Panel.text(p: self ref Panel): string { flags := "-------------"; if (p.flags&Phide) flags[0] = 'h'; if (p.flags&Playout) flags[1] = 'l'; if (p.flags&Pedit) flags[2] = 'e'; if (p.flags&Ptag) flags[3] = 't'; if (p.flags&Pmore) flags[4] = 'm'; if (p.flags&Pdirties) flags[5] = 'd'; if (p.flags&Pdirty) flags[6] = 'd'; if (p.flags&Pline) flags[7] = '1'; if (p.flags&Ptbl) flags[8] = 't'; if (p.flags&Psync) flags[9] = 'f'; if (p.flags&Predraw) flags[10] = 'r'; if (p.flags&Pdead) flags[11]='!'; if (p.flags&Pbusy) flags[12] = 'b'; s := sprint("%s row=%d, flags %s %d childs %d shown", p.name, p.rowcol, flags, len p.child, p.nshown); return s; } escape(s: string): string { for (i := 0; i < len s -1; i++) if (s[i] == '\n') s[i] = 1; if (s[len s -1] != '\n' ) s[len s] = '\n'; return s; } unescape(s: string): string { for (i := 0; i < len s; i++) if (s[i] == 1) s[i] = '\n'; return s; } pfsctl(p: ref Panel, s: string) { fname := p.path + "/ctl"; fd := open(fname, OWRITE); if (fd != nil){ if (debug['E']) fprint(stderr, "fsctl: %s: %s\n", p.path, s); seek(fd, big 0, 2); data := array of byte escape(s); if (write(fd, data, len data) != len data && debug['E']) fprint(stderr, "fsctl: write: %r\n"); } else if (debug['E']) fprint(stderr, "fsctl: open %s/ctl: %r\n", p.path); } fsctlproc() { for(;;){ (p, s) := <-cc; pfsctl(p, s); } } Panel.fsctl(p: self ref Panel, s: string, async: int) { if (!async){ pfsctl(p, s); return; } if (cc == nil){ cc = chan[16] of (ref Panel, string); spawn fsctlproc(); } cc <-= (p, s); } Panel.init(p: self ref Panel) { if (p.impl == nil) panic("nil panel implementation"); p.impl->pinit(p); p.flags |= Predraw; } Panel.term(p: self ref Panel) { p.flags |= Pdead; # safety if (debug['P']) fprint(stderr, "o/live: gone %s\n", p.path); p.impl->pterm(p); } # Process control operation, during update. # Data is guaranteed to be updated. This is the update for ctl. Panel.ctl(p: self ref Panel, s: string) { p.impl->pctl(p, s); } # update panel data, during update Panel.update(p: self ref Panel, d: array of byte) { p.impl->pupdate(p, d); } # For panels that require incremental changes. # Currently text ins/del events only. Panel.event(p: self ref Panel, s: string) { p.impl->pevent(p, s); } Panel.draw(p: self ref Panel) { if (p.flags&Pshown) p.impl->pdraw(p); } Panel.mouse(p: self ref Panel, m: ref Cpointer, cm: chan of ref Cpointer) { p.impl->pmouse(p, m, cm); } Panel.kbd(p: self ref Panel, r: int) { p.impl->pkbd(p, r); } Panel.sync(p: self ref Panel) { p.impl->psync(p); } intag(p: ref Panel, xy: Point): int { ht := Taght; if (p.flags&Pmore) ht *= 2; r := Rect(p.rect.min, (p.rect.min.x+Tagwid, p.rect.min.y+Taght)); return r.contains(xy); } nth(l: list of string, n: int): string { for (i := 0; l != nil && i < n; i++) l = tl l; if (l != nil) return hd l; return nil; }