xref: /inferno-os/appl/spree/lib/commandline.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Commandline;
2
3include "sys.m";
4	sys: Sys;
5include "draw.m";
6include "tk.m";
7	tk: Tk;
8include "tkclient.m";
9	tkclient: Tkclient;
10include "commandline.m";
11
12Debug: con 0;
13
14nomodule(modpath: string)
15{
16	sys->fprint(stderr(), "fibs: couldn't load %s: %r\n", modpath);
17	raise "fail:bad module";
18}
19
20init()
21{	sys = load Sys Sys->PATH;
22
23	tk = load Tk Tk->PATH;
24	if (tk == nil) nomodule(Tk->PATH);
25
26	tkclient = load Tkclient Tkclient->PATH;
27	if (tkclient == nil) nomodule(Tkclient->PATH);
28	tkclient->init();
29}
30
31Cmdline.new(top: ref Tk->Toplevel, w, textopts: string): (ref Cmdline, chan of string)
32{
33	window_cfg := array[] of {
34		"frame " + w,
35		"scrollbar " + w + ".scroll -command {" + w + ".t yview}",
36		"text " + w + ".t -yscrollcommand {" + w + ".scroll set} " + textopts,
37		"pack " + w + ".scroll -side left -fill y",
38		"pack " + w + ".t -fill both -expand 1",
39
40		"bind " + w + ".t <Key> {send evch k {%A}}",
41		"bind " + w + ".t <Control-d> {send evch k {%A}}",
42		"bind " + w + ".t <Control-u> {send evch k {%A}}",
43		"bind " + w + ".t <Control-w> {send evch k {%A}}",
44		"bind " + w + ".t <Control-h> {send evch k {%A}}",
45		# treat button 2 and button 3 the same so we're alright with a 2-button mouse
46		"bind " + w + ".t <ButtonPress-2> {send evch b %x %y}",
47		"bind " + w + ".t <ButtonPress-3> {send evch b %x %y}",
48		w + ".t mark set outpoint end",
49		w + ".t mark gravity outpoint left",
50		w + ".t mark set inpoint end",
51		w + ".t mark gravity inpoint left",
52	};
53	evch := chan of string;
54	tk->namechan(top, evch, "evch");
55
56	for (i := 0; i < len window_cfg; i++) {
57		e := cmd(top, window_cfg[i]);
58		if (e != nil && e[0] == '!')
59			break;
60	}
61
62	err := tk->cmd(top, "variable lasterror");
63	if (err != nil) {
64		sys->fprint(stderr(), "error in commandline config: %s\n", err);
65		raise "fail:commandline config error";
66	}
67	cmd(top, w + ".t mark set insert end;" + w + ".t see insert");
68	return (ref Cmdline(w, top), evch);
69}
70
71Cmdline.focus(cmdl: self ref Cmdline)
72{
73	cmd(cmdl.top, "focus " + cmdl.w + ".t");
74}
75
76Cmdline.event(cmdl: self ref Cmdline, e: string): list of string
77{
78	case e[0] {
79	'k' =>
80		return handle_key(cmdl, e[2:]);
81	'b' =>
82		;
83	}
84	return nil;
85}
86
87BS:		con 8;		# ^h backspace character
88BSW:		con 23;		# ^w bacspace word
89BSL:		con 21;		# ^u backspace line
90
91handle_key(cmdl: ref Cmdline, c: string): list of string
92{
93	(w, top) := (cmdl.w, cmdl.top);
94	# don't allow editing of the text before the inpoint.
95	if (int cmd(top, w + ".t compare insert < inpoint"))
96		return nil;
97	lines: list of string;
98	char := c[1];
99	if (char == '\\')
100		char = c[2];
101	case char {
102	* =>
103		cmd(top, w + ".t insert insert "+c+" {}");
104	'\n' =>
105		cmd(top, w + ".t insert insert "+c+" {}");
106		lines = sendinput(cmdl);
107	BSL or BSW or BS =>
108		delpoint: string;
109		case char {
110		BSL =>	delpoint = "{insert linestart}";
111		BSW =>	delpoint = "{insert -1char wordstart}";	# wordstart isn't ideal
112		BS  =>	delpoint = "{insert-1char}";
113		}
114		if (int cmd(top, w + ".t compare inpoint < " + delpoint))
115			cmd(top, w + ".t delete "+delpoint+" insert");
116		else
117			cmd(top, w + ".t delete inpoint insert");
118	}
119	cmd(top, w + ".t see insert;update");
120	return lines;
121}
122
123sendinput(cmdl: ref Cmdline): list of string
124{
125	(w, top) := (cmdl.w, cmdl.top);
126	# loop through all the lines that have been entered,
127	# processing each one in turn.
128	nl, lines: list of string;
129	for (;;) {
130		input: string;
131		input = cmd(top, w + ".t get inpoint end");
132		if (len input == 0)
133			break;
134		for (i := 0; i < len input; i++)
135			if (input[i] == '\n')
136				break;
137		if (i >= len input)
138			break;
139		cmd(top, w + ".t mark set outpoint inpoint+"+string (i+1)+"chars");
140		cmd(top, w + ".t mark set inpoint outpoint");
141		lines = input[0:i+1] :: lines;
142	}
143	for (; lines != nil; lines = tl lines)
144		nl = hd lines :: nl;
145	return nl;
146}
147
148add(cmdl: ref Cmdline, t: string, n: int)
149{
150	(w, top) := (cmdl.w, cmdl.top);
151	cmd(top, w + ".t insert outpoint " + t);
152	cmd(top, w + ".t mark set outpoint outpoint+"+string n+"chars");
153	cmd(top, w + ".t mark set inpoint outpoint");
154	cmd(top, w + ".t see insert");
155}
156
157Cmdline.tagaddtext(cmdl: self ref Cmdline, t: list of (string, string))
158{
159	txt := "";
160	n := 0;
161	for (; t != nil; t = tl t) {
162		(tags, s) := hd t;
163		txt += " " + tk->quote(s) + " {" + tags + "}";
164		n += len s;
165	}
166	add(cmdl, txt, n);
167}
168
169Cmdline.addtext(cmdl: self ref Cmdline, txt: string)
170{
171	if (Debug) sys->print("%s", txt);
172	add(cmdl, tk->quote(txt) + " {}" , len txt);
173}
174
175Cmdline.maketag(cmdl: self ref Cmdline, name, options: string)
176{
177	cmd(cmdl.top, cmdl.w + ".t tag configure " + name + " " + options);
178}
179
180stderr(): ref Sys->FD
181{
182	return sys->fildes(2);
183}
184
185cmd(top: ref Tk->Toplevel, s: string): string
186{
187	e := tk->cmd(top, s);
188	if (e != nil && e[0] == '!')
189		sys->fprint(stderr(), "cmd error on '%s': %s\n", s, e);
190	return e;
191}
192