summaryrefslogtreecommitdiff
path: root/appl/spree/lib/commandline.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/spree/lib/commandline.b')
-rw-r--r--appl/spree/lib/commandline.b191
1 files changed, 191 insertions, 0 deletions
diff --git a/appl/spree/lib/commandline.b b/appl/spree/lib/commandline.b
new file mode 100644
index 00000000..8b60ab01
--- /dev/null
+++ b/appl/spree/lib/commandline.b
@@ -0,0 +1,191 @@
+implement Commandline;
+
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "tk.m";
+ tk: Tk;
+include "tkclient.m";
+ tkclient: Tkclient;
+include "commandline.m";
+
+Debug: con 0;
+
+nomodule(modpath: string)
+{
+ sys->fprint(stderr(), "fibs: couldn't load %s: %r\n", modpath);
+ raise "fail:bad module";
+}
+
+init()
+{ sys = load Sys Sys->PATH;
+
+ tk = load Tk Tk->PATH;
+ if (tk == nil) nomodule(Tk->PATH);
+
+ tkclient = load Tkclient Tkclient->PATH;
+ if (tkclient == nil) nomodule(Tkclient->PATH);
+ tkclient->init();
+}
+
+Cmdline.new(top: ref Tk->Toplevel, w, textopts: string): (ref Cmdline, chan of string)
+{
+ window_cfg := array[] of {
+ "frame " + w,
+ "scrollbar " + w + ".scroll -command {" + w + ".t yview}",
+ "text " + w + ".t -yscrollcommand {" + w + ".scroll set} " + textopts,
+ "pack " + w + ".scroll -side left -fill y",
+ "pack " + w + ".t -fill both -expand 1",
+
+ "bind " + w + ".t <Key> {send evch k {%A}}",
+ "bind " + w + ".t <Control-d> {send evch k {%A}}",
+ "bind " + w + ".t <Control-u> {send evch k {%A}}",
+ "bind " + w + ".t <Control-w> {send evch k {%A}}",
+ "bind " + w + ".t <Control-h> {send evch k {%A}}",
+ # treat button 2 and button 3 the same so we're alright with a 2-button mouse
+ "bind " + w + ".t <ButtonPress-2> {send evch b %x %y}",
+ "bind " + w + ".t <ButtonPress-3> {send evch b %x %y}",
+ w + ".t mark set outpoint end",
+ w + ".t mark gravity outpoint left",
+ w + ".t mark set inpoint end",
+ w + ".t mark gravity inpoint left",
+ };
+ evch := chan of string;
+ tk->namechan(top, evch, "evch");
+
+ for (i := 0; i < len window_cfg; i++) {
+ e := cmd(top, window_cfg[i]);
+ if (e != nil && e[0] == '!')
+ break;
+ }
+
+ err := tk->cmd(top, "variable lasterror");
+ if (err != nil) {
+ sys->fprint(stderr(), "error in commandline config: %s\n", err);
+ raise "fail:commandline config error";
+ }
+ cmd(top, w + ".t mark set insert end;" + w + ".t see insert");
+ return (ref Cmdline(w, top), evch);
+}
+
+Cmdline.focus(cmdl: self ref Cmdline)
+{
+ cmd(cmdl.top, "focus " + cmdl.w + ".t");
+}
+
+Cmdline.event(cmdl: self ref Cmdline, e: string): list of string
+{
+ case e[0] {
+ 'k' =>
+ return handle_key(cmdl, e[2:]);
+ 'b' =>
+ ;
+ }
+ return nil;
+}
+
+BS: con 8; # ^h backspace character
+BSW: con 23; # ^w bacspace word
+BSL: con 21; # ^u backspace line
+
+handle_key(cmdl: ref Cmdline, c: string): list of string
+{
+ (w, top) := (cmdl.w, cmdl.top);
+ # don't allow editing of the text before the inpoint.
+ if (int cmd(top, w + ".t compare insert < inpoint"))
+ return nil;
+ lines: list of string;
+ char := c[1];
+ if (char == '\\')
+ char = c[2];
+ case char {
+ * =>
+ cmd(top, w + ".t insert insert "+c+" {}");
+ '\n' =>
+ cmd(top, w + ".t insert insert "+c+" {}");
+ lines = sendinput(cmdl);
+ BSL or BSW or BS =>
+ delpoint: string;
+ case char {
+ BSL => delpoint = "{insert linestart}";
+ BSW => delpoint = "{insert -1char wordstart}"; # wordstart isn't ideal
+ BS => delpoint = "{insert-1char}";
+ }
+ if (int cmd(top, w + ".t compare inpoint < " + delpoint))
+ cmd(top, w + ".t delete "+delpoint+" insert");
+ else
+ cmd(top, w + ".t delete inpoint insert");
+ }
+ cmd(top, w + ".t see insert;update");
+ return lines;
+}
+
+sendinput(cmdl: ref Cmdline): list of string
+{
+ (w, top) := (cmdl.w, cmdl.top);
+ # loop through all the lines that have been entered,
+ # processing each one in turn.
+ nl, lines: list of string;
+ for (;;) {
+ input: string;
+ input = cmd(top, w + ".t get inpoint end");
+ if (len input == 0)
+ break;
+ for (i := 0; i < len input; i++)
+ if (input[i] == '\n')
+ break;
+ if (i >= len input)
+ break;
+ cmd(top, w + ".t mark set outpoint inpoint+"+string (i+1)+"chars");
+ cmd(top, w + ".t mark set inpoint outpoint");
+ lines = input[0:i+1] :: lines;
+ }
+ for (; lines != nil; lines = tl lines)
+ nl = hd lines :: nl;
+ return nl;
+}
+
+add(cmdl: ref Cmdline, t: string, n: int)
+{
+ (w, top) := (cmdl.w, cmdl.top);
+ cmd(top, w + ".t insert outpoint " + t);
+ cmd(top, w + ".t mark set outpoint outpoint+"+string n+"chars");
+ cmd(top, w + ".t mark set inpoint outpoint");
+ cmd(top, w + ".t see insert");
+}
+
+Cmdline.tagaddtext(cmdl: self ref Cmdline, t: list of (string, string))
+{
+ txt := "";
+ n := 0;
+ for (; t != nil; t = tl t) {
+ (tags, s) := hd t;
+ txt += " " + tk->quote(s) + " {" + tags + "}";
+ n += len s;
+ }
+ add(cmdl, txt, n);
+}
+
+Cmdline.addtext(cmdl: self ref Cmdline, txt: string)
+{
+ if (Debug) sys->print("%s", txt);
+ add(cmdl, tk->quote(txt) + " {}" , len txt);
+}
+
+Cmdline.maketag(cmdl: self ref Cmdline, name, options: string)
+{
+ cmd(cmdl.top, cmdl.w + ".t tag configure " + name + " " + options);
+}
+
+stderr(): ref Sys->FD
+{
+ return sys->fildes(2);
+}
+
+cmd(top: ref Tk->Toplevel, s: string): string
+{
+ e := tk->cmd(top, s);
+ if (e != nil && e[0] == '!')
+ sys->fprint(stderr(), "cmd error on '%s': %s\n", s, e);
+ return e;
+}