twind.c - plan9port - [fork] Plan 9 from user space | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
twind.c (34335B) | |
--- | |
1 #include <u.h> | |
2 #include <libc.h> | |
3 #include <draw.h> | |
4 #include <thread.h> | |
5 #include <cursor.h> | |
6 #include <mouse.h> | |
7 #include <keyboard.h> | |
8 #include <frame.h> | |
9 #include <fcall.h> | |
10 #include <9pclient.h> | |
11 #include <plumb.h> | |
12 #include <complete.h> | |
13 #include "dat.h" | |
14 #include "fns.h" | |
15 | |
16 #define MOVEIT if(0) | |
17 | |
18 enum | |
19 { | |
20 HiWater = 64000000, /* max size of history */ | |
21 LoWater = 400000, /* min size of history after max… | |
22 MinWater = 20000 /* room to leave available when r… | |
23 }; | |
24 | |
25 static int topped; | |
26 static int id; | |
27 | |
28 static Image *cols[NCOL]; | |
29 static Image *grey; | |
30 static Image *darkgrey; | |
31 static Cursor *lastcursor; | |
32 static Image *titlecol; | |
33 static Image *lighttitlecol; | |
34 static Image *holdcol; | |
35 static Image *lightholdcol; | |
36 static Image *paleholdcol; | |
37 | |
38 static int | |
39 wscale(Window *w, int n) | |
40 { | |
41 if(w == nil || w->i == nil) | |
42 return n; | |
43 return scalesize(w->i->display, n); | |
44 } | |
45 | |
46 Window* | |
47 wmk(Image *i, Mousectl *mc, Channel *ck, Channel *cctl, int scrolling) | |
48 { | |
49 Window *w; | |
50 Rectangle r; | |
51 | |
52 if(cols[0] == nil){ | |
53 /* greys are multiples of 0x11111100+0xFF, 14* being pal… | |
54 grey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, 0xEE… | |
55 darkgrey = allocimage(display, Rect(0,0,1,1), CMAP8, 1, … | |
56 cols[BACK] = display->white; | |
57 cols[HIGH] = allocimage(display, Rect(0,0,1,1), CMAP8, 1… | |
58 cols[BORD] = allocimage(display, Rect(0,0,1,1), CMAP8, 1… | |
59 cols[TEXT] = display->black; | |
60 cols[HTEXT] = display->black; | |
61 titlecol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, … | |
62 lighttitlecol = allocimage(display, Rect(0,0,1,1), CMAP8… | |
63 holdcol = allocimage(display, Rect(0,0,1,1), CMAP8, 1, D… | |
64 lightholdcol = allocimage(display, Rect(0,0,1,1), CMAP8,… | |
65 paleholdcol = allocimage(display, Rect(0,0,1,1), CMAP8, … | |
66 } | |
67 w = emalloc(sizeof(Window)); | |
68 w->screenr = i->r; | |
69 r = insetrect(i->r, wscale(w, Selborder)+wscale(w, 1)); | |
70 w->i = i; | |
71 w->mc = *mc; | |
72 w->ck = ck; | |
73 w->cctl = cctl; | |
74 w->cursorp = nil; | |
75 w->conswrite = chancreate(sizeof(Conswritemesg), 0); | |
76 w->consread = chancreate(sizeof(Consreadmesg), 0); | |
77 w->mouseread = chancreate(sizeof(Mousereadmesg), 0); | |
78 w->wctlread = chancreate(sizeof(Consreadmesg), 0); | |
79 w->scrollr = r; | |
80 w->scrollr.max.x = r.min.x+wscale(w, Scrollwid); | |
81 w->lastsr = ZR; | |
82 r.min.x += wscale(w, Scrollwid)+wscale(w, Scrollgap); | |
83 frinit(&w->f, r, font, i, cols); | |
84 w->f.maxtab = maxtab*stringwidth(font, "0"); | |
85 w->topped = ++topped; | |
86 w->id = ++id; | |
87 w->notefd = -1; | |
88 w->scrolling = scrolling; | |
89 w->dir = estrdup(startdir); | |
90 w->label = estrdup("<unnamed>"); | |
91 r = insetrect(w->i->r, wscale(w, Selborder)); | |
92 draw(w->i, r, cols[BACK], nil, w->f.entire.min); | |
93 wborder(w, wscale(w, Selborder)); | |
94 wscrdraw(w); | |
95 incref(&w->ref); /* ref will be removed after mounting; a… | |
96 return w; | |
97 } | |
98 | |
99 void | |
100 wsetname(Window *w) | |
101 { | |
102 int i, n; | |
103 char err[ERRMAX]; | |
104 | |
105 n = sprint(w->name, "window.%d.%d", w->id, w->namecount++); | |
106 for(i='A'; i<='Z'; i++){ | |
107 if(nameimage(w->i, w->name, 1) > 0) | |
108 return; | |
109 errstr(err, sizeof err); | |
110 if(strcmp(err, "image name in use") != 0) | |
111 break; | |
112 w->name[n] = i; | |
113 w->name[n+1] = 0; | |
114 } | |
115 w->name[0] = 0; | |
116 fprint(2, "rio: setname failed: %s\n", err); | |
117 } | |
118 | |
119 void | |
120 wresize(Window *w, Image *i, int move) | |
121 { | |
122 Rectangle r, or; | |
123 | |
124 or = w->i->r; | |
125 if(move || (Dx(or)==Dx(i->r) && Dy(or)==Dy(i->r))) | |
126 draw(i, i->r, w->i, nil, w->i->r.min); | |
127 if(w->i != i){ | |
128 fprint(2, "res %p %p\n", w->i, i); | |
129 freeimage(w->i); | |
130 w->i = i; | |
131 } | |
132 /* wsetname(w); */ | |
133 /*XXX w->mc.image = i; */ | |
134 r = insetrect(i->r, wscale(w, Selborder)+wscale(w, 1)); | |
135 w->scrollr = r; | |
136 w->scrollr.max.x = r.min.x+wscale(w, Scrollwid); | |
137 w->lastsr = ZR; | |
138 r.min.x += wscale(w, Scrollwid)+wscale(w, Scrollgap); | |
139 if(move) | |
140 frsetrects(&w->f, r, w->i); | |
141 else{ | |
142 frclear(&w->f, FALSE); | |
143 frinit(&w->f, r, w->f.font, w->i, cols); | |
144 wsetcols(w); | |
145 w->f.maxtab = maxtab*stringwidth(w->f.font, "0"); | |
146 r = insetrect(w->i->r, wscale(w, Selborder)); | |
147 draw(w->i, r, cols[BACK], nil, w->f.entire.min); | |
148 wfill(w); | |
149 wsetselect(w, w->q0, w->q1); | |
150 wscrdraw(w); | |
151 } | |
152 wborder(w, wscale(w, Selborder)); | |
153 w->topped = ++topped; | |
154 w->resized = TRUE; | |
155 w->mouse.counter++; | |
156 } | |
157 | |
158 void | |
159 wrefresh(Window *w, Rectangle r) | |
160 { | |
161 /* USED(r); */ | |
162 | |
163 /* BUG: rectangle is ignored */ | |
164 if(w == input) | |
165 wborder(w, wscale(w, Selborder)); | |
166 else | |
167 wborder(w, wscale(w, Unselborder)); | |
168 if(w->mouseopen) | |
169 return; | |
170 draw(w->i, insetrect(w->i->r, Borderwidth), w->f.cols[BACK], nil… | |
171 w->f.ticked = 0; | |
172 if(w->f.p0 > 0) | |
173 frdrawsel(&w->f, frptofchar(&w->f, 0), 0, w->f.p0, 0); | |
174 if(w->f.p1 < w->f.nchars) | |
175 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, w-… | |
176 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w->f.p1, 1… | |
177 w->lastsr = ZR; | |
178 wscrdraw(w); | |
179 } | |
180 | |
181 int | |
182 wclose(Window *w) | |
183 { | |
184 int i; | |
185 | |
186 i = decref(&w->ref); | |
187 if(i > 0) | |
188 return 0; | |
189 if(i < 0) | |
190 error("negative ref count"); | |
191 if(!w->deleted) | |
192 wclosewin(w); | |
193 wsendctlmesg(w, Exited, ZR, nil); | |
194 return 1; | |
195 } | |
196 | |
197 | |
198 void | |
199 winctl(void *arg) | |
200 { | |
201 Rune *rp, *bp, *up, *kbdr; | |
202 uint qh; | |
203 int nr, nb, c, wid, i, npart, initial, lastb, scrolling; | |
204 char *s, *t, part[UTFmax]; | |
205 Window *w; | |
206 Mousestate *mp, m; | |
207 enum { WKey, WMouse, WMouseread, WCtl, WCwrite, WCread, WWread, … | |
208 Alt alts[NWALT+1]; | |
209 Mousereadmesg mrm; | |
210 Conswritemesg cwm; | |
211 Consreadmesg crm; | |
212 Consreadmesg cwrm; | |
213 Stringpair pair; | |
214 Wctlmesg wcm; | |
215 char buf[4*12+1]; | |
216 | |
217 w = arg; | |
218 snprint(buf, sizeof buf, "winctl-id%d", w->id); | |
219 threadsetname(buf); | |
220 | |
221 mrm.cm = chancreate(sizeof(Mouse), 0); | |
222 cwm.cw = chancreate(sizeof(Stringpair), 0); | |
223 crm.c1 = chancreate(sizeof(Stringpair), 0); | |
224 crm.c2 = chancreate(sizeof(Stringpair), 0); | |
225 cwrm.c1 = chancreate(sizeof(Stringpair), 0); | |
226 cwrm.c2 = chancreate(sizeof(Stringpair), 0); | |
227 | |
228 | |
229 alts[WKey].c = w->ck; | |
230 alts[WKey].v = &kbdr; | |
231 alts[WKey].op = CHANRCV; | |
232 alts[WMouse].c = w->mc.c; | |
233 alts[WMouse].v = &w->mc.m; | |
234 alts[WMouse].op = CHANRCV; | |
235 alts[WMouseread].c = w->mouseread; | |
236 alts[WMouseread].v = &mrm; | |
237 alts[WMouseread].op = CHANSND; | |
238 alts[WCtl].c = w->cctl; | |
239 alts[WCtl].v = &wcm; | |
240 alts[WCtl].op = CHANRCV; | |
241 alts[WCwrite].c = w->conswrite; | |
242 alts[WCwrite].v = &cwm; | |
243 alts[WCwrite].op = CHANSND; | |
244 alts[WCread].c = w->consread; | |
245 alts[WCread].v = &crm; | |
246 alts[WCread].op = CHANSND; | |
247 alts[WWread].c = w->wctlread; | |
248 alts[WWread].v = &cwrm; | |
249 alts[WWread].op = CHANSND; | |
250 alts[NWALT].op = CHANEND; | |
251 | |
252 npart = 0; | |
253 lastb = -1; | |
254 for(;;){ | |
255 if(w->mouseopen && w->mouse.counter != w->mouse.lastcoun… | |
256 alts[WMouseread].op = CHANSND; | |
257 else | |
258 alts[WMouseread].op = CHANNOP; | |
259 // if(!w->scrolling && !w->mouseopen && w->qh>w->… | |
260 // alts[WCwrite].op = CHANNOP; | |
261 // else | |
262 alts[WCwrite].op = CHANSND; | |
263 if(w->deleted || !w->wctlready) | |
264 alts[WWread].op = CHANNOP; | |
265 else | |
266 alts[WWread].op = CHANSND; | |
267 /* this code depends on NL and EOT fitting in a single b… | |
268 /* kind of expensive for each loop; worth precomputing? … | |
269 if(w->holding) | |
270 alts[WCread].op = CHANNOP; | |
271 else if(npart || (w->rawing && w->nraw>0)) | |
272 alts[WCread].op = CHANSND; | |
273 else{ | |
274 alts[WCread].op = CHANNOP; | |
275 for(i=w->qh; i<w->nr; i++){ | |
276 c = w->r[i]; | |
277 if(c=='\n' || c=='\004'){ | |
278 alts[WCread].op = CHANSND; | |
279 break; | |
280 } | |
281 } | |
282 } | |
283 switch(alt(alts)){ | |
284 case WKey: | |
285 for(i=0; kbdr[i]!=L'\0'; i++) | |
286 wkeyctl(w, kbdr[i]); | |
287 /* wkeyctl(w, r); */ | |
288 /* while(nbrecv(w->ck, &r)) */ | |
289 /* wkeyctl(w, r); */ | |
290 break; | |
291 case WMouse: | |
292 if(w->mouseopen) { | |
293 w->mouse.counter++; | |
294 | |
295 /* queue click events */ | |
296 if(!w->mouse.qfull && lastb != w->mc.m.b… | |
297 mp = &w->mouse.queue[w->mouse.wi… | |
298 if(++w->mouse.wi == nelem(w->mou… | |
299 w->mouse.wi = 0; | |
300 if(w->mouse.wi == w->mouse.ri) | |
301 w->mouse.qfull = TRUE; | |
302 mp->m = w->mc.m; | |
303 mp->counter = w->mouse.counter; | |
304 lastb = w->mc.m.buttons; | |
305 } | |
306 } else | |
307 wmousectl(w); | |
308 break; | |
309 case WMouseread: | |
310 /* send a queued event or, if the queue is empty… | |
311 /* if the queue has filled, we discard all the e… | |
312 /* the intent is to discard frantic clicking by … | |
313 w->mouse.qfull = FALSE; | |
314 if(w->mouse.wi != w->mouse.ri) { | |
315 m = w->mouse.queue[w->mouse.ri]; | |
316 if(++w->mouse.ri == nelem(w->mouse.queue… | |
317 w->mouse.ri = 0; | |
318 } else { | |
319 m.m = w->mc.m; | |
320 m.counter = w->mouse.counter; | |
321 } | |
322 w->mouse.lastcounter = m.counter; | |
323 send(mrm.cm, &m.m); | |
324 continue; | |
325 case WCtl: | |
326 if(wctlmesg(w, wcm.type, wcm.r, wcm.image) == Ex… | |
327 chanfree(crm.c1); | |
328 chanfree(crm.c2); | |
329 chanfree(mrm.cm); | |
330 chanfree(cwm.cw); | |
331 chanfree(cwrm.c1); | |
332 chanfree(cwrm.c2); | |
333 threadexits(nil); | |
334 } | |
335 continue; | |
336 case WCwrite: | |
337 recv(cwm.cw, &pair); | |
338 rp = pair.s; | |
339 nr = pair.ns; | |
340 bp = rp; | |
341 up = rp; | |
342 initial = 0; | |
343 for(i=0; i<nr; i++){ | |
344 switch(*bp){ | |
345 case 0: | |
346 break; | |
347 case '\b': | |
348 if(up == rp) | |
349 initial++; | |
350 else | |
351 --up; | |
352 break; | |
353 case '\r': | |
354 while(i<nr-1 && *(bp+1) == '\r'){ | |
355 bp++; | |
356 i++; | |
357 } | |
358 if(i<nr-1 && *(bp+1) != '\n'){ | |
359 while(up > rp && *(up-1)… | |
360 up--; | |
361 if(up == rp) | |
362 initial = wbswid… | |
363 }else if(i == nr-1) | |
364 *up++ = '\n'; | |
365 break; | |
366 default: | |
367 *up++ = *bp; | |
368 break; | |
369 } | |
370 bp++; | |
371 } | |
372 if(initial){ | |
373 if(initial > w->qh) | |
374 initial = w->qh; | |
375 qh = w->qh - initial; | |
376 wdelete(w, qh, qh+initial); | |
377 w->qh = qh; | |
378 } | |
379 nr = up - rp; | |
380 scrolling = w->scrolling && w->org <= w->qh && w… | |
381 w->qh = winsert(w, rp, nr, w->qh)+nr; | |
382 if(scrolling) | |
383 wshow(w, w->qh); | |
384 wsetselect(w, w->q0, w->q1); | |
385 wscrdraw(w); | |
386 free(rp); | |
387 break; | |
388 case WCread: | |
389 recv(crm.c1, &pair); | |
390 t = pair.s; | |
391 nb = pair.ns; | |
392 i = npart; | |
393 npart = 0; | |
394 if(i) | |
395 memmove(t, part, i); | |
396 while(i<nb && (w->qh<w->nr || w->nraw>0)){ | |
397 if(w->qh == w->nr){ | |
398 wid = runetochar(t+i, &w->raw[0]… | |
399 w->nraw--; | |
400 runemove(w->raw, w->raw+1, w->nr… | |
401 }else | |
402 wid = runetochar(t+i, &w->r[w->q… | |
403 c = t[i]; /* knows break characte… | |
404 i += wid; | |
405 if(!w->rawing && (c == '\n' || c=='\004'… | |
406 /* if(c == '\004') */ | |
407 /* i--; */ | |
408 break; | |
409 } | |
410 } | |
411 /* if(i==nb && w->qh<w->nr && w->r[w->qh]=='\004'… | |
412 /* w->qh++; */ | |
413 if(i > nb){ | |
414 npart = i-nb; | |
415 memmove(part, t+nb, npart); | |
416 i = nb; | |
417 } | |
418 pair.s = t; | |
419 pair.ns = i; | |
420 send(crm.c2, &pair); | |
421 continue; | |
422 case WWread: | |
423 w->wctlready = 0; | |
424 recv(cwrm.c1, &pair); | |
425 if(w->deleted || w->i==nil) | |
426 pair.ns = sprint(pair.s, ""); | |
427 else{ | |
428 s = "visible"; | |
429 for(i=0; i<nhidden; i++) | |
430 if(hidden[i] == w){ | |
431 s = "hidden"; | |
432 break; | |
433 } | |
434 t = "notcurrent"; | |
435 if(w == input) | |
436 t = "current"; | |
437 pair.ns = snprint(pair.s, pair.ns, "%11d… | |
438 w->i->r.min.x, w->i->r.min.y, w-… | |
439 } | |
440 send(cwrm.c2, &pair); | |
441 continue; | |
442 } | |
443 if(!w->deleted) | |
444 flushimage(display, 1); | |
445 } | |
446 } | |
447 | |
448 void | |
449 waddraw(Window *w, Rune *r, int nr) | |
450 { | |
451 w->raw = runerealloc(w->raw, w->nraw+nr); | |
452 runemove(w->raw+w->nraw, r, nr); | |
453 w->nraw += nr; | |
454 } | |
455 | |
456 /* | |
457 * Need to do this in a separate proc because if process we're interrupt… | |
458 * is dying and trying to print tombstone, kernel is blocked holding p->… | |
459 */ | |
460 void | |
461 interruptproc(void *v) | |
462 { | |
463 int *notefd; | |
464 | |
465 notefd = v; | |
466 write(*notefd, "interrupt", 9); | |
467 free(notefd); | |
468 } | |
469 | |
470 int | |
471 windfilewidth(Window *w, uint q0, int oneelement) | |
472 { | |
473 uint q; | |
474 Rune r; | |
475 | |
476 q = q0; | |
477 while(q > 0){ | |
478 r = w->r[q-1]; | |
479 if(r<=' ') | |
480 break; | |
481 if(oneelement && r=='/') | |
482 break; | |
483 --q; | |
484 } | |
485 return q0-q; | |
486 } | |
487 | |
488 void | |
489 showcandidates(Window *w, Completion *c) | |
490 { | |
491 int i; | |
492 Fmt f; | |
493 Rune *rp; | |
494 uint nr, qline, q0; | |
495 char *s; | |
496 | |
497 runefmtstrinit(&f); | |
498 if (c->nmatch == 0) | |
499 s = "[no matches in "; | |
500 else | |
501 s = "["; | |
502 if(c->nfile > 32) | |
503 fmtprint(&f, "%s%d files]\n", s, c->nfile); | |
504 else{ | |
505 fmtprint(&f, "%s", s); | |
506 for(i=0; i<c->nfile; i++){ | |
507 if(i > 0) | |
508 fmtprint(&f, " "); | |
509 fmtprint(&f, "%s", c->filename[i]); | |
510 } | |
511 fmtprint(&f, "]\n"); | |
512 } | |
513 /* place text at beginning of line before host point */ | |
514 qline = w->qh; | |
515 while(qline>0 && w->r[qline-1] != '\n') | |
516 qline--; | |
517 | |
518 rp = runefmtstrflush(&f); | |
519 nr = runestrlen(rp); | |
520 | |
521 q0 = w->q0; | |
522 q0 += winsert(w, rp, runestrlen(rp), qline) - qline; | |
523 free(rp); | |
524 wsetselect(w, q0+nr, q0+nr); | |
525 } | |
526 | |
527 Rune* | |
528 namecomplete(Window *w) | |
529 { | |
530 int nstr, npath; | |
531 Rune *rp, *path, *str; | |
532 Completion *c; | |
533 char *s, *dir, *root; | |
534 | |
535 /* control-f: filename completion; works back to white space or … | |
536 if(w->q0<w->nr && w->r[w->q0]>' ') /* must be at end of w… | |
537 return nil; | |
538 nstr = windfilewidth(w, w->q0, TRUE); | |
539 str = runemalloc(nstr); | |
540 runemove(str, w->r+(w->q0-nstr), nstr); | |
541 npath = windfilewidth(w, w->q0-nstr, FALSE); | |
542 path = runemalloc(npath); | |
543 runemove(path, w->r+(w->q0-nstr-npath), npath); | |
544 rp = nil; | |
545 | |
546 /* is path rooted? if not, we need to make it relative to window… | |
547 if(npath>0 && path[0]=='/'){ | |
548 dir = malloc(UTFmax*npath+1); | |
549 sprint(dir, "%.*S", npath, path); | |
550 }else{ | |
551 if(strcmp(w->dir, "") == 0) | |
552 root = "."; | |
553 else | |
554 root = w->dir; | |
555 dir = malloc(strlen(root)+1+UTFmax*npath+1); | |
556 sprint(dir, "%s/%.*S", root, npath, path); | |
557 } | |
558 dir = cleanname(dir); | |
559 | |
560 s = smprint("%.*S", nstr, str); | |
561 c = complete(dir, s); | |
562 free(s); | |
563 if(c == nil) | |
564 goto Return; | |
565 | |
566 if(!c->advance) | |
567 showcandidates(w, c); | |
568 | |
569 if(c->advance) | |
570 rp = runesmprint("%s", c->string); | |
571 | |
572 Return: | |
573 freecompletion(c); | |
574 free(dir); | |
575 free(path); | |
576 free(str); | |
577 return rp; | |
578 } | |
579 | |
580 void | |
581 wkeyctl(Window *w, Rune r) | |
582 { | |
583 uint q0 ,q1; | |
584 int n, nb, nr; | |
585 Rune *rp; | |
586 | |
587 if(r == 0) | |
588 return; | |
589 if(w->deleted) | |
590 return; | |
591 w->rawing = rawon(); | |
592 /* navigation keys work only when mouse is not open */ | |
593 if(!w->mouseopen) | |
594 switch(r){ | |
595 case Kdown: | |
596 n = w->f.maxlines/3; | |
597 goto case_Down; | |
598 case Kscrollonedown: | |
599 n = mousescrollsize(w->f.maxlines); | |
600 if(n <= 0) | |
601 n = 1; | |
602 goto case_Down; | |
603 case Kpgdown: | |
604 n = 2*w->f.maxlines/3; | |
605 case_Down: | |
606 q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w… | |
607 wsetorigin(w, q0, TRUE); | |
608 return; | |
609 case Kup: | |
610 n = w->f.maxlines/3; | |
611 goto case_Up; | |
612 case Kscrolloneup: | |
613 n = mousescrollsize(w->f.maxlines); | |
614 if(n <= 0) | |
615 n = 1; | |
616 goto case_Up; | |
617 case Kpgup: | |
618 n = 2*w->f.maxlines/3; | |
619 case_Up: | |
620 q0 = wbacknl(w, w->org, n); | |
621 wsetorigin(w, q0, TRUE); | |
622 return; | |
623 case Kleft: | |
624 if(w->q0 > 0){ | |
625 q0 = w->q0-1; | |
626 wsetselect(w, q0, q0); | |
627 wshow(w, q0); | |
628 } | |
629 return; | |
630 case Kright: | |
631 if(w->q1 < w->nr){ | |
632 q1 = w->q1+1; | |
633 wsetselect(w, q1, q1); | |
634 wshow(w, q1); | |
635 } | |
636 return; | |
637 case Khome: | |
638 if(w->org > w->iq1) { | |
639 q0 = wbacknl(w, w->iq1, 1); | |
640 wsetorigin(w, q0, TRUE); | |
641 } else | |
642 wshow(w, 0); | |
643 return; | |
644 case Kend: | |
645 if(w->iq1 > w->org+w->f.nchars) { | |
646 q0 = wbacknl(w, w->iq1, 1); | |
647 wsetorigin(w, q0, TRUE); | |
648 } else | |
649 wshow(w, w->nr); | |
650 return; | |
651 case 0x01: /* ^A: beginning of line */ | |
652 if(w->q0==0 || w->q0==w->qh || w->r[w->q0-1]=='\… | |
653 return; | |
654 nb = wbswidth(w, 0x15 /* ^U */); | |
655 wsetselect(w, w->q0-nb, w->q0-nb); | |
656 wshow(w, w->q0); | |
657 return; | |
658 case 0x05: /* ^E: end of line */ | |
659 q0 = w->q0; | |
660 while(q0 < w->nr && w->r[q0]!='\n') | |
661 q0++; | |
662 wsetselect(w, q0, q0); | |
663 wshow(w, w->q0); | |
664 return; | |
665 } | |
666 /* | |
667 * This if used to be below the if(w->rawing ...), | |
668 * but let's try putting it here. This will allow ESC-processing | |
669 * to toggle hold mode even in remote SSH connections. | |
670 * The drawback is that vi-style processing gets harder. | |
671 * If you find yourself in some weird readline mode, good | |
672 * luck getting out without ESC. Let's see who complains. | |
673 */ | |
674 if(r==0x1B || (w->holding && r==0x7F)){ /* toggle hold */ | |
675 if(w->holding) | |
676 --w->holding; | |
677 else | |
678 w->holding++; | |
679 wrepaint(w); | |
680 if(r == 0x1B) | |
681 return; | |
682 } | |
683 if(!w->holding && w->rawing && (w->q0==w->nr || w->mouseopen)){ | |
684 waddraw(w, &r, 1); | |
685 return; | |
686 } | |
687 if(r == Kcmd+'x'){ | |
688 wsnarf(w); | |
689 wcut(w); | |
690 wscrdraw(w); | |
691 return; | |
692 } | |
693 if(r == Kcmd+'c'){ | |
694 wsnarf(w); | |
695 return; | |
696 } | |
697 if(r == Kcmd+'v'){ | |
698 riogetsnarf(); | |
699 wpaste(w); | |
700 wscrdraw(w); | |
701 return; | |
702 } | |
703 if(r != 0x7F){ | |
704 wsnarf(w); | |
705 wcut(w); | |
706 } | |
707 switch(r){ | |
708 case 0x03: /* maybe send interrupt */ | |
709 /* since ^C is so commonly used as interrupt, special ca… | |
710 if (intrc() != 0x03) | |
711 break; | |
712 /* fall through */ | |
713 case 0x7F: /* send interrupt */ | |
714 w->qh = w->nr; | |
715 wshow(w, w->qh); | |
716 winterrupt(w); | |
717 w->iq1 = w->q0; | |
718 return; | |
719 case 0x06: /* ^F: file name completion */ | |
720 case Kins: /* Insert: file name completion */ | |
721 rp = namecomplete(w); | |
722 if(rp == nil) | |
723 return; | |
724 nr = runestrlen(rp); | |
725 q0 = w->q0; | |
726 q0 = winsert(w, rp, nr, q0); | |
727 wshow(w, q0+nr); | |
728 w->iq1 = w->q0; | |
729 free(rp); | |
730 return; | |
731 case 0x08: /* ^H: erase character */ | |
732 case 0x15: /* ^U: erase line */ | |
733 case 0x17: /* ^W: erase word */ | |
734 if(w->q0==0 || w->q0==w->qh) | |
735 return; | |
736 nb = wbswidth(w, r); | |
737 q1 = w->q0; | |
738 q0 = q1-nb; | |
739 if(q0 < w->org){ | |
740 q0 = w->org; | |
741 nb = q1-q0; | |
742 } | |
743 if(nb > 0){ | |
744 wdelete(w, q0, q0+nb); | |
745 wsetselect(w, q0, q0); | |
746 } | |
747 w->iq1 = w->q0; | |
748 return; | |
749 } | |
750 /* otherwise ordinary character; just insert */ | |
751 q0 = w->q0; | |
752 q0 = winsert(w, &r, 1, q0); | |
753 wshow(w, q0+1); | |
754 w->iq1 = w->q0; | |
755 } | |
756 | |
757 void | |
758 wsetcols(Window *w) | |
759 { | |
760 if(w->holding) | |
761 if(w == input) | |
762 w->f.cols[TEXT] = w->f.cols[HTEXT] = holdcol; | |
763 else | |
764 w->f.cols[TEXT] = w->f.cols[HTEXT] = lightholdco… | |
765 else | |
766 if(w == input) | |
767 w->f.cols[TEXT] = w->f.cols[HTEXT] = display->bl… | |
768 else | |
769 w->f.cols[TEXT] = w->f.cols[HTEXT] = darkgrey; | |
770 } | |
771 | |
772 void | |
773 wrepaint(Window *w) | |
774 { | |
775 wsetcols(w); | |
776 if(!w->mouseopen){ | |
777 frredraw(&w->f); | |
778 } | |
779 if(w == input){ | |
780 wborder(w, wscale(w, Selborder)); | |
781 wsetcursor(w, 0); | |
782 }else | |
783 wborder(w, wscale(w, Unselborder)); | |
784 } | |
785 | |
786 int | |
787 wbswidth(Window *w, Rune c) | |
788 { | |
789 uint q, eq, stop; | |
790 Rune r; | |
791 int skipping; | |
792 | |
793 /* there is known to be at least one character to erase */ | |
794 if(c == 0x08) /* ^H: erase character */ | |
795 return 1; | |
796 q = w->q0; | |
797 stop = 0; | |
798 if(q > w->qh) | |
799 stop = w->qh; | |
800 skipping = TRUE; | |
801 while(q > stop){ | |
802 r = w->r[q-1]; | |
803 if(r == '\n'){ /* eat at most one more ch… | |
804 if(q == w->q0 && c != '\r') /* eat the ne… | |
805 --q; | |
806 break; | |
807 } | |
808 if(c == 0x17){ | |
809 eq = isalnum(r); | |
810 if(eq && skipping) /* found one; stop ski… | |
811 skipping = FALSE; | |
812 else if(!eq && !skipping) | |
813 break; | |
814 } | |
815 --q; | |
816 } | |
817 return w->q0-q; | |
818 } | |
819 | |
820 void | |
821 wsnarf(Window *w) | |
822 { | |
823 if(w->q1 == w->q0) | |
824 return; | |
825 nsnarf = w->q1-w->q0; | |
826 snarf = runerealloc(snarf, nsnarf); | |
827 snarfversion++; /* maybe modified by parent */ | |
828 runemove(snarf, w->r+w->q0, nsnarf); | |
829 rioputsnarf(); | |
830 } | |
831 | |
832 void | |
833 wcut(Window *w) | |
834 { | |
835 if(w->q1 == w->q0) | |
836 return; | |
837 wdelete(w, w->q0, w->q1); | |
838 wsetselect(w, w->q0, w->q0); | |
839 } | |
840 | |
841 void | |
842 wpaste(Window *w) | |
843 { | |
844 uint q0; | |
845 | |
846 if(nsnarf == 0) | |
847 return; | |
848 wcut(w); | |
849 q0 = w->q0; | |
850 if(w->rawing && !w->holding && q0==w->nr){ | |
851 waddraw(w, snarf, nsnarf); | |
852 wsetselect(w, q0, q0); | |
853 }else{ | |
854 q0 = winsert(w, snarf, nsnarf, w->q0); | |
855 wsetselect(w, q0, q0+nsnarf); | |
856 } | |
857 } | |
858 | |
859 void | |
860 wplumb(Window *w) | |
861 { | |
862 Plumbmsg *m; | |
863 static CFid *fd; | |
864 char buf[32]; | |
865 uint p0, p1; | |
866 Cursor *c; | |
867 | |
868 if(fd == nil) | |
869 fd = plumbopenfid("send", OWRITE); | |
870 if(fd == nil) | |
871 return; | |
872 m = emalloc(sizeof(Plumbmsg)); | |
873 m->src = estrdup("rio"); | |
874 m->dst = nil; | |
875 m->wdir = estrdup(w->dir); | |
876 m->type = estrdup("text"); | |
877 p0 = w->q0; | |
878 p1 = w->q1; | |
879 if(w->q1 > w->q0) | |
880 m->attr = nil; | |
881 else{ | |
882 while(p0>0 && w->r[p0-1]!=' ' && w->r[p0-1]!='\t' && w->… | |
883 p0--; | |
884 while(p1<w->nr && w->r[p1]!=' ' && w->r[p1]!='\t' && w->… | |
885 p1++; | |
886 sprint(buf, "click=%d", w->q0-p0); | |
887 m->attr = plumbunpackattr(buf); | |
888 } | |
889 if(p1-p0 > messagesize-1024){ | |
890 plumbfree(m); | |
891 return; /* too large for 9P */ | |
892 } | |
893 m->data = runetobyte(w->r+p0, p1-p0, &m->ndata); | |
894 if(plumbsendtofid(fd, m) < 0){ | |
895 c = lastcursor; | |
896 riosetcursor(&query, 1); | |
897 sleep(300); | |
898 riosetcursor(c, 1); | |
899 } | |
900 plumbfree(m); | |
901 } | |
902 | |
903 int | |
904 winborder(Window *w, Point xy) | |
905 { | |
906 return ptinrect(xy, w->screenr) && !ptinrect(xy, insetrect(w->sc… | |
907 } | |
908 | |
909 void | |
910 wlook(Window *w) | |
911 { | |
912 int i, n, e; | |
913 | |
914 i = w->q1; | |
915 n = i - w->q0; | |
916 e = w->nr - n; | |
917 if(n <= 0 || e < n) | |
918 return; | |
919 | |
920 if(i > e) | |
921 i = 0; | |
922 | |
923 while(runestrncmp(w->r+w->q0, w->r+i, n) != 0){ | |
924 if(i < e) | |
925 i++; | |
926 else | |
927 i = 0; | |
928 } | |
929 | |
930 wsetselect(w, i, i+n); | |
931 wshow(w, i); | |
932 } | |
933 | |
934 void | |
935 wmousectl(Window *w) | |
936 { | |
937 int but; | |
938 | |
939 if(w->mc.m.buttons == 1) | |
940 but = 1; | |
941 else if(w->mc.m.buttons == 2) | |
942 but = 2; | |
943 else if(w->mc.m.buttons == 4) | |
944 but = 3; | |
945 else{ | |
946 if(w->mc.m.buttons == 8) | |
947 wkeyctl(w, Kscrolloneup); | |
948 if(w->mc.m.buttons == 16) | |
949 wkeyctl(w, Kscrollonedown); | |
950 return; | |
951 } | |
952 | |
953 incref(&w->ref); /* hold up window while we track… | |
954 if(w->deleted) | |
955 goto Return; | |
956 if(ptinrect(w->mc.m.xy, w->scrollr)){ | |
957 if(but) | |
958 wscroll(w, but); | |
959 goto Return; | |
960 } | |
961 if(but == 1) | |
962 wselect(w); | |
963 /* else all is handled by main process */ | |
964 Return: | |
965 wclose(w); | |
966 } | |
967 | |
968 void | |
969 wdelete(Window *w, uint q0, uint q1) | |
970 { | |
971 uint n, p0, p1; | |
972 | |
973 n = q1-q0; | |
974 if(n == 0) | |
975 return; | |
976 runemove(w->r+q0, w->r+q1, w->nr-q1); | |
977 w->nr -= n; | |
978 if(q0 < w->iq1) | |
979 w->iq1 -= min(n, w->iq1-q0); | |
980 if(q0 < w->q0) | |
981 w->q0 -= min(n, w->q0-q0); | |
982 if(q0 < w->q1) | |
983 w->q1 -= min(n, w->q1-q0); | |
984 if(q1 < w->qh) | |
985 w->qh -= n; | |
986 else if(q0 < w->qh) | |
987 w->qh = q0; | |
988 if(q1 <= w->org) | |
989 w->org -= n; | |
990 else if(q0 < w->org+w->f.nchars){ | |
991 p1 = q1 - w->org; | |
992 if(p1 > w->f.nchars) | |
993 p1 = w->f.nchars; | |
994 if(q0 < w->org){ | |
995 w->org = q0; | |
996 p0 = 0; | |
997 }else | |
998 p0 = q0 - w->org; | |
999 frdelete(&w->f, p0, p1); | |
1000 wfill(w); | |
1001 } | |
1002 } | |
1003 | |
1004 | |
1005 static Window *clickwin; | |
1006 static uint clickmsec; | |
1007 static Window *selectwin; | |
1008 static uint selectq; | |
1009 | |
1010 /* | |
1011 * called from frame library | |
1012 */ | |
1013 void | |
1014 framescroll(Frame *f, int dl) | |
1015 { | |
1016 if(f != &selectwin->f) | |
1017 error("frameselect not right frame"); | |
1018 wframescroll(selectwin, dl); | |
1019 } | |
1020 | |
1021 void | |
1022 wframescroll(Window *w, int dl) | |
1023 { | |
1024 uint q0; | |
1025 | |
1026 if(dl == 0){ | |
1027 wscrsleep(w, 100); | |
1028 return; | |
1029 } | |
1030 if(dl < 0){ | |
1031 q0 = wbacknl(w, w->org, -dl); | |
1032 if(selectq > w->org+w->f.p0) | |
1033 wsetselect(w, w->org+w->f.p0, selectq); | |
1034 else | |
1035 wsetselect(w, selectq, w->org+w->f.p0); | |
1036 }else{ | |
1037 if(w->org+w->f.nchars == w->nr) | |
1038 return; | |
1039 q0 = w->org+frcharofpt(&w->f, Pt(w->f.r.min.x, w->f.r.mi… | |
1040 if(selectq >= w->org+w->f.p1) | |
1041 wsetselect(w, w->org+w->f.p1, selectq); | |
1042 else | |
1043 wsetselect(w, selectq, w->org+w->f.p1); | |
1044 } | |
1045 wsetorigin(w, q0, TRUE); | |
1046 } | |
1047 | |
1048 void | |
1049 wselect(Window *w) | |
1050 { | |
1051 uint q0, q1; | |
1052 int b, x, y, first; | |
1053 | |
1054 first = 1; | |
1055 selectwin = w; | |
1056 /* | |
1057 * Double-click immediately if it might make sense. | |
1058 */ | |
1059 b = w->mc.m.buttons; | |
1060 q0 = w->q0; | |
1061 q1 = w->q1; | |
1062 selectq = w->org+frcharofpt(&w->f, w->mc.m.xy); | |
1063 if(clickwin==w && w->mc.m.msec-clickmsec<500) | |
1064 if(q0==q1 && selectq==w->q0){ | |
1065 wdoubleclick(w, &q0, &q1); | |
1066 wsetselect(w, q0, q1); | |
1067 flushimage(display, 1); | |
1068 x = w->mc.m.xy.x; | |
1069 y = w->mc.m.xy.y; | |
1070 /* stay here until something interesting happens */ | |
1071 do | |
1072 readmouse(&w->mc); | |
1073 while(w->mc.m.buttons==b && abs(w->mc.m.xy.x-x)<3 && abs… | |
1074 w->mc.m.xy.x = x; /* in case we're calling frsele… | |
1075 w->mc.m.xy.y = y; | |
1076 q0 = w->q0; /* may have changed */ | |
1077 q1 = w->q1; | |
1078 selectq = q0; | |
1079 } | |
1080 if(w->mc.m.buttons == b){ | |
1081 w->f.scroll = framescroll; | |
1082 frselect(&w->f, &w->mc); | |
1083 /* horrible botch: while asleep, may have lost selection… | |
1084 if(selectq > w->nr) | |
1085 selectq = w->org + w->f.p0; | |
1086 w->f.scroll = nil; | |
1087 if(selectq < w->org) | |
1088 q0 = selectq; | |
1089 else | |
1090 q0 = w->org + w->f.p0; | |
1091 if(selectq > w->org+w->f.nchars) | |
1092 q1 = selectq; | |
1093 else | |
1094 q1 = w->org+w->f.p1; | |
1095 } | |
1096 if(q0 == q1){ | |
1097 if(q0==w->q0 && clickwin==w && w->mc.m.msec-clickmsec<50… | |
1098 wdoubleclick(w, &q0, &q1); | |
1099 clickwin = nil; | |
1100 }else{ | |
1101 clickwin = w; | |
1102 clickmsec = w->mc.m.msec; | |
1103 } | |
1104 }else | |
1105 clickwin = nil; | |
1106 wsetselect(w, q0, q1); | |
1107 flushimage(display, 1); | |
1108 while(w->mc.m.buttons){ | |
1109 w->mc.m.msec = 0; | |
1110 b = w->mc.m.buttons; | |
1111 if(b & 6){ | |
1112 if(b & 2){ | |
1113 wsnarf(w); | |
1114 wcut(w); | |
1115 }else{ | |
1116 if(first){ | |
1117 first = 0; | |
1118 riogetsnarf(); | |
1119 } | |
1120 wpaste(w); | |
1121 } | |
1122 } | |
1123 wscrdraw(w); | |
1124 flushimage(display, 1); | |
1125 while(w->mc.m.buttons == b) | |
1126 readmouse(&w->mc); | |
1127 clickwin = nil; | |
1128 } | |
1129 } | |
1130 | |
1131 void | |
1132 wsendctlmesg(Window *w, int type, Rectangle r, Image *image) | |
1133 { | |
1134 Wctlmesg wcm; | |
1135 | |
1136 wcm.type = type; | |
1137 wcm.r = r; | |
1138 wcm.image = image; | |
1139 send(w->cctl, &wcm); | |
1140 } | |
1141 | |
1142 int | |
1143 wctlmesg(Window *w, int m, Rectangle r, Image *i) | |
1144 { | |
1145 char buf[64]; | |
1146 | |
1147 switch(m){ | |
1148 default: | |
1149 error("unknown control message"); | |
1150 break; | |
1151 case Wakeup: | |
1152 break; | |
1153 case Moved: | |
1154 case Reshaped: | |
1155 if(w->deleted){ | |
1156 freeimage(i); | |
1157 break; | |
1158 } | |
1159 w->screenr = r; | |
1160 strcpy(buf, w->name); | |
1161 wresize(w, i, m==Moved); | |
1162 w->wctlready = 1; | |
1163 if(Dx(r) > 0){ | |
1164 if(w != input) | |
1165 wcurrent(w); | |
1166 }else if(w == input) | |
1167 wcurrent(nil); | |
1168 flushimage(display, 1); | |
1169 break; | |
1170 case Refresh: | |
1171 if(w->deleted || Dx(w->screenr)<=0 || !rectclip(&r, w->i… | |
1172 break; | |
1173 if(!w->mouseopen) | |
1174 wrefresh(w, r); | |
1175 flushimage(display, 1); | |
1176 break; | |
1177 case Movemouse: | |
1178 if(sweeping || !ptinrect(r.min, w->i->r)) | |
1179 break; | |
1180 wmovemouse(w, r.min); | |
1181 case Rawon: | |
1182 break; | |
1183 case Rawoff: | |
1184 if(w->deleted) | |
1185 break; | |
1186 while(w->nraw > 0){ | |
1187 wkeyctl(w, w->raw[0]); | |
1188 --w->nraw; | |
1189 runemove(w->raw, w->raw+1, w->nraw); | |
1190 } | |
1191 break; | |
1192 case Holdon: | |
1193 case Holdoff: | |
1194 if(w->deleted) | |
1195 break; | |
1196 wrepaint(w); | |
1197 flushimage(display, 1); | |
1198 break; | |
1199 case Deleted: | |
1200 if(w->deleted) | |
1201 break; | |
1202 write(w->notefd, "hangup", 6); | |
1203 wclosewin(w); | |
1204 break; | |
1205 case Exited: | |
1206 frclear(&w->f, TRUE); | |
1207 close(w->notefd); | |
1208 chanfree(w->mc.c); | |
1209 chanfree(w->ck); | |
1210 chanfree(w->cctl); | |
1211 chanfree(w->conswrite); | |
1212 chanfree(w->consread); | |
1213 chanfree(w->mouseread); | |
1214 chanfree(w->wctlread); | |
1215 free(w->raw); | |
1216 free(w->r); | |
1217 free(w->dir); | |
1218 free(w->label); | |
1219 free(w); | |
1220 break; | |
1221 } | |
1222 return m; | |
1223 } | |
1224 | |
1225 /* | |
1226 * Convert back to physical coordinates | |
1227 */ | |
1228 void | |
1229 wmovemouse(Window *w, Point p) | |
1230 { | |
1231 p.x += w->screenr.min.x-w->i->r.min.x; | |
1232 p.y += w->screenr.min.y-w->i->r.min.y; | |
1233 moveto(mousectl, p); | |
1234 } | |
1235 | |
1236 void | |
1237 wcurrent(Window *w) | |
1238 { | |
1239 Window *oi; | |
1240 | |
1241 if(wkeyboard!=nil && w==wkeyboard) | |
1242 return; | |
1243 oi = input; | |
1244 input = w; | |
1245 if(oi!=w && oi!=nil) | |
1246 wrepaint(oi); | |
1247 if(w !=nil){ | |
1248 wrepaint(w); | |
1249 wsetcursor(w, 0); | |
1250 } | |
1251 if(w != oi){ | |
1252 if(oi){ | |
1253 oi->wctlready = 1; | |
1254 wsendctlmesg(oi, Wakeup, ZR, nil); | |
1255 } | |
1256 if(w){ | |
1257 w->wctlready = 1; | |
1258 wsendctlmesg(w, Wakeup, ZR, nil); | |
1259 } | |
1260 } | |
1261 } | |
1262 | |
1263 void | |
1264 wsetcursor(Window *w, int force) | |
1265 { | |
1266 Cursor *p; | |
1267 | |
1268 if(w==nil || /*w!=input || */ w->i==nil || Dx(w->screenr)<=0) | |
1269 p = nil; | |
1270 else if(wpointto(mouse->xy) == w){ | |
1271 p = w->cursorp; | |
1272 if(p==nil && w->holding) | |
1273 p = &whitearrow; | |
1274 }else | |
1275 p = nil; | |
1276 if(!menuing) | |
1277 riosetcursor(p, force && !menuing); | |
1278 } | |
1279 | |
1280 void | |
1281 riosetcursor(Cursor *p, int force) | |
1282 { | |
1283 if(!force && p==lastcursor) | |
1284 return; | |
1285 setcursor(mousectl, p); | |
1286 lastcursor = p; | |
1287 } | |
1288 | |
1289 Window* | |
1290 wtop(Point pt) | |
1291 { | |
1292 Window *w; | |
1293 | |
1294 w = wpointto(pt); | |
1295 if(w){ | |
1296 if(w->topped == topped) | |
1297 return nil; | |
1298 topwindow(w->i); | |
1299 wcurrent(w); | |
1300 flushimage(display, 1); | |
1301 w->topped = ++topped; | |
1302 } | |
1303 return w; | |
1304 } | |
1305 | |
1306 void | |
1307 wtopme(Window *w) | |
1308 { | |
1309 if(w!=nil && w->i!=nil && !w->deleted && w->topped!=topped){ | |
1310 topwindow(w->i); | |
1311 flushimage(display, 1); | |
1312 w->topped = ++ topped; | |
1313 } | |
1314 } | |
1315 | |
1316 void | |
1317 wbottomme(Window *w) | |
1318 { | |
1319 if(w!=nil && w->i!=nil && !w->deleted){ | |
1320 bottomwindow(w->i); | |
1321 flushimage(display, 1); | |
1322 w->topped = 0; | |
1323 } | |
1324 } | |
1325 | |
1326 Window* | |
1327 wlookid(int id) | |
1328 { | |
1329 int i; | |
1330 | |
1331 for(i=0; i<nwindow; i++) | |
1332 if(window[i]->id == id) | |
1333 return window[i]; | |
1334 return nil; | |
1335 } | |
1336 | |
1337 void | |
1338 wclosewin(Window *w) | |
1339 { | |
1340 Rectangle r; | |
1341 int i; | |
1342 | |
1343 w->deleted = TRUE; | |
1344 if(w == input){ | |
1345 input = nil; | |
1346 wsetcursor(w, 0); | |
1347 } | |
1348 if(w == wkeyboard) | |
1349 wkeyboard = nil; | |
1350 for(i=0; i<nhidden; i++) | |
1351 if(hidden[i] == w){ | |
1352 --nhidden; | |
1353 memmove(hidden+i, hidden+i+1, (nhidden-i)*sizeof… | |
1354 break; | |
1355 } | |
1356 for(i=0; i<nwindow; i++) | |
1357 if(window[i] == w){ | |
1358 --nwindow; | |
1359 memmove(window+i, window+i+1, (nwindow-i)*sizeof… | |
1360 w->deleted = TRUE; | |
1361 r = w->i->r; | |
1362 /* move it off-screen to hide it, in case client… | |
1363 MOVEIT originwindow(w->i, r.min, view->r.max); | |
1364 freeimage(w->i); | |
1365 w->i = nil; | |
1366 return; | |
1367 } | |
1368 error("unknown window in closewin"); | |
1369 } | |
1370 | |
1371 void | |
1372 wsetpid(Window *w, int pid, int dolabel) | |
1373 { | |
1374 char buf[128]; | |
1375 | |
1376 w->pid = pid; | |
1377 if(dolabel){ | |
1378 sprint(buf, "rc %d", pid); | |
1379 free(w->label); | |
1380 w->label = estrdup(buf); | |
1381 drawsetlabel(w->label); | |
1382 } | |
1383 } | |
1384 | |
1385 static Rune left1[] = { | |
1386 '{', '[', '(', '<', 0xAB, | |
1387 0x207d, 0x2329, 0x27e6, 0x27e8, 0x27ea, | |
1388 0xfe59, 0xfe5b, 0xfe5d, 0xff08, 0xff3b, 0xff5b, | |
1389 0 | |
1390 }; | |
1391 static Rune right1[] = { | |
1392 '}', ']', ')', '>', 0xBB, | |
1393 0x207e, 0x232a, 0x27e7, 0x27e9, 0x27eb, | |
1394 0xfe5a, 0xfe5c, 0xfe5e, 0xff09, 0xff3d, 0xff5d, | |
1395 0 | |
1396 }; | |
1397 static Rune left2[] = { '\n', 0 }; | |
1398 static Rune left3[] = { '\'', '"', '`', 0 }; | |
1399 | |
1400 Rune *left[] = { | |
1401 left1, | |
1402 left2, | |
1403 left3, | |
1404 nil | |
1405 }; | |
1406 Rune *right[] = { | |
1407 right1, | |
1408 left2, | |
1409 left3, | |
1410 nil | |
1411 }; | |
1412 | |
1413 void | |
1414 wdoubleclick(Window *w, uint *q0, uint *q1) | |
1415 { | |
1416 int c, i; | |
1417 Rune *r, *l, *p; | |
1418 uint q; | |
1419 | |
1420 for(i=0; left[i]!=nil; i++){ | |
1421 q = *q0; | |
1422 l = left[i]; | |
1423 r = right[i]; | |
1424 /* try matching character to left, looking right */ | |
1425 if(q == 0) | |
1426 c = '\n'; | |
1427 else | |
1428 c = w->r[q-1]; | |
1429 p = strrune(l, c); | |
1430 if(p != nil){ | |
1431 if(wclickmatch(w, c, r[p-l], 1, &q)) | |
1432 *q1 = q-(c!='\n'); | |
1433 return; | |
1434 } | |
1435 /* try matching character to right, looking left */ | |
1436 if(q == w->nr) | |
1437 c = '\n'; | |
1438 else | |
1439 c = w->r[q]; | |
1440 p = strrune(r, c); | |
1441 if(p != nil){ | |
1442 if(wclickmatch(w, c, l[p-r], -1, &q)){ | |
1443 *q1 = *q0+(*q0<w->nr && c=='\n'); | |
1444 *q0 = q; | |
1445 if(c!='\n' || q!=0 || w->r[0]=='\n') | |
1446 (*q0)++; | |
1447 } | |
1448 return; | |
1449 } | |
1450 } | |
1451 /* try filling out word to right */ | |
1452 while(*q1<w->nr && isalnum(w->r[*q1])) | |
1453 (*q1)++; | |
1454 /* try filling out word to left */ | |
1455 while(*q0>0 && isalnum(w->r[*q0-1])) | |
1456 (*q0)--; | |
1457 } | |
1458 | |
1459 int | |
1460 wclickmatch(Window *w, int cl, int cr, int dir, uint *q) | |
1461 { | |
1462 Rune c; | |
1463 int nest; | |
1464 | |
1465 nest = 1; | |
1466 for(;;){ | |
1467 if(dir > 0){ | |
1468 if(*q == w->nr) | |
1469 break; | |
1470 c = w->r[*q]; | |
1471 (*q)++; | |
1472 }else{ | |
1473 if(*q == 0) | |
1474 break; | |
1475 (*q)--; | |
1476 c = w->r[*q]; | |
1477 } | |
1478 if(c == cr){ | |
1479 if(--nest==0) | |
1480 return 1; | |
1481 }else if(c == cl) | |
1482 nest++; | |
1483 } | |
1484 return cl=='\n' && nest==1; | |
1485 } | |
1486 | |
1487 | |
1488 uint | |
1489 wbacknl(Window *w, uint p, uint n) | |
1490 { | |
1491 int i, j; | |
1492 | |
1493 /* look for start of this line if n==0 */ | |
1494 if(n==0 && p>0 && w->r[p-1]!='\n') | |
1495 n = 1; | |
1496 i = n; | |
1497 while(i-->0 && p>0){ | |
1498 --p; /* it's at a newline now; back over it */ | |
1499 if(p == 0) | |
1500 break; | |
1501 /* at 128 chars, call it a line anyway */ | |
1502 for(j=128; --j>0 && p>0; p--) | |
1503 if(w->r[p-1]=='\n') | |
1504 break; | |
1505 } | |
1506 return p; | |
1507 } | |
1508 | |
1509 void | |
1510 wshow(Window *w, uint q0) | |
1511 { | |
1512 int qe; | |
1513 int nl; | |
1514 uint q; | |
1515 | |
1516 qe = w->org+w->f.nchars; | |
1517 if(w->org<=q0 && (q0<qe || (q0==qe && qe==w->nr))) | |
1518 wscrdraw(w); | |
1519 else{ | |
1520 nl = 4*w->f.maxlines/5; | |
1521 q = wbacknl(w, q0, nl); | |
1522 /* avoid going backwards if trying to go forwards - long… | |
1523 if(!(q0>w->org && q<w->org)) | |
1524 wsetorigin(w, q, TRUE); | |
1525 while(q0 > w->org+w->f.nchars) | |
1526 wsetorigin(w, w->org+1, FALSE); | |
1527 } | |
1528 } | |
1529 | |
1530 void | |
1531 wsetorigin(Window *w, uint org, int exact) | |
1532 { | |
1533 int i, a, fixup; | |
1534 Rune *r; | |
1535 uint n; | |
1536 | |
1537 if(org>0 && !exact){ | |
1538 /* org is an estimate of the char posn; find a newline */ | |
1539 /* don't try harder than 256 chars */ | |
1540 for(i=0; i<256 && org<w->nr; i++){ | |
1541 if(w->r[org] == '\n'){ | |
1542 org++; | |
1543 break; | |
1544 } | |
1545 org++; | |
1546 } | |
1547 } | |
1548 a = org-w->org; | |
1549 fixup = 0; | |
1550 if(a>=0 && a<w->f.nchars){ | |
1551 frdelete(&w->f, 0, a); | |
1552 fixup = 1; /* frdelete can leave end of last line… | |
1553 }else if(a<0 && -a<w->f.nchars){ | |
1554 n = w->org - org; | |
1555 r = runemalloc(n); | |
1556 runemove(r, w->r+org, n); | |
1557 frinsert(&w->f, r, r+n, 0); | |
1558 free(r); | |
1559 }else | |
1560 frdelete(&w->f, 0, w->f.nchars); | |
1561 w->org = org; | |
1562 wfill(w); | |
1563 wscrdraw(w); | |
1564 wsetselect(w, w->q0, w->q1); | |
1565 if(fixup && w->f.p1 > w->f.p0) | |
1566 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1-1), w->f.p1-1… | |
1567 } | |
1568 | |
1569 void | |
1570 wsetselect(Window *w, uint q0, uint q1) | |
1571 { | |
1572 int p0, p1; | |
1573 | |
1574 /* w->f.p0 and w->f.p1 are always right; w->q0 and w->q1 may be … | |
1575 w->q0 = q0; | |
1576 w->q1 = q1; | |
1577 /* compute desired p0,p1 from q0,q1 */ | |
1578 p0 = q0-w->org; | |
1579 p1 = q1-w->org; | |
1580 if(p0 < 0) | |
1581 p0 = 0; | |
1582 if(p1 < 0) | |
1583 p1 = 0; | |
1584 if(p0 > w->f.nchars) | |
1585 p0 = w->f.nchars; | |
1586 if(p1 > w->f.nchars) | |
1587 p1 = w->f.nchars; | |
1588 if(p0==w->f.p0 && p1==w->f.p1) | |
1589 return; | |
1590 /* screen disagrees with desired selection */ | |
1591 if(w->f.p1<=p0 || p1<=w->f.p0 || p0==p1 || w->f.p1==w->f.p0){ | |
1592 /* no overlap or too easy to bother trying */ | |
1593 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, w-… | |
1594 frdrawsel(&w->f, frptofchar(&w->f, p0), p0, p1, 1); | |
1595 goto Return; | |
1596 } | |
1597 /* overlap; avoid unnecessary painting */ | |
1598 if(p0 < w->f.p0){ | |
1599 /* extend selection backwards */ | |
1600 frdrawsel(&w->f, frptofchar(&w->f, p0), p0, w->f.p0, 1); | |
1601 }else if(p0 > w->f.p0){ | |
1602 /* trim first part of selection */ | |
1603 frdrawsel(&w->f, frptofchar(&w->f, w->f.p0), w->f.p0, p0… | |
1604 } | |
1605 if(p1 > w->f.p1){ | |
1606 /* extend selection forwards */ | |
1607 frdrawsel(&w->f, frptofchar(&w->f, w->f.p1), w->f.p1, p1… | |
1608 }else if(p1 < w->f.p1){ | |
1609 /* trim last part of selection */ | |
1610 frdrawsel(&w->f, frptofchar(&w->f, p1), p1, w->f.p1, 0); | |
1611 } | |
1612 | |
1613 Return: | |
1614 w->f.p0 = p0; | |
1615 w->f.p1 = p1; | |
1616 } | |
1617 | |
1618 uint | |
1619 winsert(Window *w, Rune *r, int n, uint q0) | |
1620 { | |
1621 uint m; | |
1622 | |
1623 if(n == 0) | |
1624 return q0; | |
1625 if(w->nr+n>HiWater && q0>=w->org && q0>=w->qh){ | |
1626 m = min(HiWater-LoWater, min(w->org, w->qh)); | |
1627 w->org -= m; | |
1628 w->qh -= m; | |
1629 if(w->q0 > m) | |
1630 w->q0 -= m; | |
1631 else | |
1632 w->q0 = 0; | |
1633 if(w->q1 > m) | |
1634 w->q1 -= m; | |
1635 else | |
1636 w->q1 = 0; | |
1637 w->nr -= m; | |
1638 runemove(w->r, w->r+m, w->nr); | |
1639 q0 -= m; | |
1640 } | |
1641 if(w->nr+n > w->maxr){ | |
1642 /* | |
1643 * Minimize realloc breakage: | |
1644 * Allocate at least MinWater | |
1645 * Double allocation size each time | |
1646 * But don't go much above HiWater | |
1647 */ | |
1648 m = max(min(2*(w->nr+n), HiWater), w->nr+n)+MinWater; | |
1649 if(m > HiWater) | |
1650 m = max(HiWater+MinWater, w->nr+n); | |
1651 if(m > w->maxr){ | |
1652 w->r = runerealloc(w->r, m); | |
1653 w->maxr = m; | |
1654 } | |
1655 } | |
1656 runemove(w->r+q0+n, w->r+q0, w->nr-q0); | |
1657 runemove(w->r+q0, r, n); | |
1658 w->nr += n; | |
1659 /* if output touches, advance selection, not qh; works best for … | |
1660 if(q0 <= w->q1) | |
1661 w->q1 += n; | |
1662 if(q0 <= w->q0) | |
1663 w->q0 += n; | |
1664 if(q0 < w->qh) | |
1665 w->qh += n; | |
1666 if(q0 < w->iq1) | |
1667 w->iq1 += n; | |
1668 if(q0 < w->org) | |
1669 w->org += n; | |
1670 else if(q0 <= w->org+w->f.nchars) | |
1671 frinsert(&w->f, r, r+n, q0-w->org); | |
1672 return q0; | |
1673 } | |
1674 | |
1675 void | |
1676 wfill(Window *w) | |
1677 { | |
1678 Rune *rp; | |
1679 int i, n, m, nl; | |
1680 | |
1681 if(w->f.lastlinefull) | |
1682 return; | |
1683 rp = malloc(messagesize); | |
1684 do{ | |
1685 n = w->nr-(w->org+w->f.nchars); | |
1686 if(n == 0) | |
1687 break; | |
1688 if(n > 2000) /* educated guess at reasonable amou… | |
1689 n = 2000; | |
1690 runemove(rp, w->r+(w->org+w->f.nchars), n); | |
1691 /* | |
1692 * it's expensive to frinsert more than we need, so | |
1693 * count newlines. | |
1694 */ | |
1695 nl = w->f.maxlines-w->f.nlines; | |
1696 m = 0; | |
1697 for(i=0; i<n; ){ | |
1698 if(rp[i++] == '\n'){ | |
1699 m++; | |
1700 if(m >= nl) | |
1701 break; | |
1702 } | |
1703 } | |
1704 frinsert(&w->f, rp, rp+i, w->f.nchars); | |
1705 }while(w->f.lastlinefull == FALSE); | |
1706 free(rp); | |
1707 } | |
1708 | |
1709 char* | |
1710 wcontents(Window *w, int *ip) | |
1711 { | |
1712 return runetobyte(w->r, w->nr, ip); | |
1713 } |