1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <mouse.h>
6 #include <keyboard.h>
7 #include <control.h>
8
9 static int debug = 0;
10
11 typedef struct Text Text;
12
13 struct Text
14 {
15 Control;
16 int border;
17 int topline;
18 int scroll;
19 int nvis;
20 int lastbut;
21 CFont *font;
22 CImage *image;
23 CImage *textcolor;
24 CImage *bordercolor;
25 CImage *selectcolor;
26 CImage *selectingcolor;
27 Rune **line;
28 int selectmode; // Selsingle, Selmulti
29 int selectstyle; // Seldown, Selup (use Selup only with Selsingle)
30 uchar *selected;
31 int nline;
32 int warp;
33 int align;
34 int sel; // line nr of selection made by last button down
35 int but; // last button down (still being hold)
36 int offsel; // we are on selection
37 };
38
39 enum
40 {
41 Selsingle,
42 Selmulti,
43 Seldown,
44 Selup,
45 };
46
47 enum{
48 EAccumulate,
49 EAdd,
50 EAlign,
51 EBorder,
52 EBordercolor,
53 EClear,
54 EDelete,
55 EFocus,
56 EFont,
57 EHide,
58 EImage,
59 ERect,
60 EReplace,
61 EReveal,
62 EScroll,
63 ESelect,
64 ESelectcolor,
65 ESelectingcolor,
66 ESelectmode,
67 ESelectstyle,
68 EShow,
69 ESize,
70 ETextcolor,
71 ETopline,
72 EValue,
73 EWarp,
74 };
75
76 static char *cmds[] = {
77 [EAccumulate] = "accumulate",
78 [EAdd] = "add",
79 [EAlign] = "align",
80 [EBorder] = "border",
81 [EBordercolor] = "bordercolor",
82 [EClear] = "clear",
83 [EDelete] = "delete",
84 [EFocus] = "focus",
85 [EFont] = "font",
86 [EHide] = "hide",
87 [EImage] = "image",
88 [ERect] = "rect",
89 [EReplace] = "replace",
90 [EReveal] = "reveal",
91 [EScroll] = "scroll",
92 [ESelect] = "select",
93 [ESelectcolor] = "selectcolor",
94 [ESelectingcolor] = "selectingcolor",
95 [ESelectmode] = "selectmode",
96 [ESelectstyle] = "selectstyle",
97 [EShow] = "show",
98 [ESize] = "size",
99 [ETextcolor] = "textcolor",
100 [ETopline] = "topline",
101 [EValue] = "value",
102 [EWarp] = "warp",
103 nil
104 };
105
106 static void textshow(Text*);
107 static void texttogglei(Text*, int);
108 static int textline(Text*, Point);
109 static int texttoggle(Text*, Point);
110
111 static void
textmouse(Control * c,Mouse * m)112 textmouse(Control *c, Mouse *m)
113 {
114 Text *t;
115 int sel;
116
117 t = (Text*)c;
118 if (debug) fprint(2, "textmouse %s t->lastbut %d; m->buttons %d\n", t->name, t->lastbut, m->buttons);
119 if (t->warp >= 0)
120 return;
121 if ((t->selectstyle == Selup) && (m->buttons&7)) {
122 sel = textline(t, m->xy);
123 if (t->sel >= 0) {
124 // if (debug) fprint(2, "textmouse Selup %q sel=%d t->sel=%d t->but=%d\n",
125 // t->name, sel, t->sel, t->but);
126 t->offsel = (sel == t->sel) ? 0 : 1;
127 if ((sel == t->sel &&
128 ((t->selected[t->sel] && !t->but) ||
129 ((!t->selected[t->sel]) && t->but))) ||
130 (sel != t->sel &&
131 ((t->selected[t->sel] && t->but) ||
132 ((!t->selected[t->sel]) && (!t->but))))) {
133 texttogglei(t, t->sel);
134 }
135 }
136 }
137 if(t->lastbut != (m->buttons&7)){
138 if(m->buttons & 7){
139 sel = texttoggle(t, m->xy);
140 if(sel >= 0) {
141 if (t->selectstyle == Seldown) {
142 chanprint(t->event, "%q: select %d %d",
143 t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0);
144 if (debug) fprint(2, "textmouse Seldown event %q: select %d %d\n",
145 t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0);
146 } else {
147 if (debug) fprint(2, "textmouse Selup no event yet %q: select %d %d\n",
148 t->name, sel, t->selected[sel] ? (m->buttons & 7) : 0);
149 t->sel = sel;
150 t->but = t->selected[sel] ? (m->buttons & 7) : 0;
151 }
152 }
153 } else if (t->selectstyle == Selup) {
154 sel = textline(t, m->xy);
155 t->offsel = 0;
156 if ((sel >= 0) && (sel == t->sel)) {
157 chanprint(t->event, "%q: select %d %d",
158 t->name, sel, t->but);
159 if (debug) fprint(2, "textmouse Selup event %q: select %d %d\n",
160 t->name, sel, t->but);
161 } else if (sel != t->sel) {
162 if ((t->selected[t->sel] && t->but) ||
163 ((!t->selected[t->sel]) && (!t->but))) {
164 texttogglei(t, t->sel);
165 } else {
166 textshow(t);
167 }
168 if (debug) fprint(2, "textmouse Selup cancel %q: select %d %d\n",
169 t->name, sel, t->but);
170 }
171 t->sel = -1;
172 t->but = 0;
173 }
174 t->lastbut = m->buttons & 7;
175 }
176 }
177
178 static void
textfree(Control * c)179 textfree(Control *c)
180 {
181 int i;
182 Text *t;
183
184 t = (Text*)c;
185 _putctlfont(t->font);
186 _putctlimage(t->image);
187 _putctlimage(t->textcolor);
188 _putctlimage(t->bordercolor);
189 _putctlimage(t->selectcolor);
190 _putctlimage(t->selectingcolor);
191 for(i=0; i<t->nline; i++)
192 free(t->line[i]);
193 free(t->line);
194 free(t->selected);
195 }
196
197 static void
textshow(Text * t)198 textshow(Text *t)
199 {
200 Rectangle r, tr;
201 Point p;
202 int i, ntext;
203 Font *f;
204 Rune *text;
205
206 if (t->hidden)
207 return;
208 r = t->rect;
209 f = t->font->font;
210 draw(t->screen, r, t->image->image, nil, t->image->image->r.min);
211 if(t->border > 0){
212 border(t->screen, r, t->border, t->bordercolor->image, t->bordercolor->image->r.min);
213 r = insetrect(r, t->border);
214 }
215 tr = r;
216 t->nvis = Dy(r)/f->height;
217 for(i=t->topline; i<t->nline && i<t->topline+t->nvis; i++){
218 text = t->line[i];
219 ntext = runestrlen(text);
220 r.max.y = r.min.y+f->height;
221 if(t->sel == i && t->offsel)
222 draw(t->screen, r, t->selectingcolor->image, nil, ZP);
223 else if(t->selected[i])
224 draw(t->screen, r, t->selectcolor->image, nil, ZP);
225 p = _ctlalignpoint(r,
226 runestringnwidth(f, text, ntext),
227 f->height, t->align);
228 if(t->warp == i) {
229 Point p2;
230 p2.x = p.x + 0.5*runestringnwidth(f, text, ntext);
231 p2.y = p.y + 0.5*f->height;
232 moveto(t->controlset->mousectl, p2);
233 t->warp = -1;
234 }
235 _string(t->screen, p, t->textcolor->image,
236 ZP, f, nil, text, ntext, tr,
237 nil, ZP, SoverD);
238 r.min.y += f->height;
239 }
240 flushimage(display, 1);
241 }
242
243 static void
textctl(Control * c,CParse * cp)244 textctl(Control *c, CParse *cp)
245 {
246 int cmd, i, n;
247 Rectangle r;
248 Text *t;
249 Rune *rp;
250
251 t = (Text*)c;
252 cmd = _ctllookup(cp->args[0], cmds, nelem(cmds));
253 switch(cmd){
254 default:
255 ctlerror("%q: unrecognized message '%s'", t->name, cp->str);
256 break;
257 case EAlign:
258 _ctlargcount(t, cp, 2);
259 t->align = _ctlalignment(cp->args[1]);
260 break;
261 case EBorder:
262 _ctlargcount(t, cp, 2);
263 if(cp->iargs[1] < 0)
264 ctlerror("%q: bad border: %c", t->name, cp->str);
265 t->border = cp->iargs[1];
266 break;
267 case EBordercolor:
268 _ctlargcount(t, cp, 2);
269 _setctlimage(t, &t->bordercolor, cp->args[1]);
270 break;
271 case EClear:
272 _ctlargcount(t, cp, 1);
273 for(i=0; i<t->nline; i++)
274 free(t->line[i]);
275 free(t->line);
276 free(t->selected);
277 t->line = ctlmalloc(sizeof(Rune*));
278 t->selected = ctlmalloc(1);
279 t->nline = 0;
280 textshow(t);
281 break;
282 case EDelete:
283 _ctlargcount(t, cp, 2);
284 i = cp->iargs[1];
285 if(i<0 || i>=t->nline)
286 ctlerror("%q: line number out of range: %s", t->name, cp->str);
287 free(t->line[i]);
288 memmove(t->line+i, t->line+i+1, (t->nline-(i+1))*sizeof(Rune*));
289 memmove(t->selected+i, t->selected+i+1, t->nline-(i+1));
290 t->nline--;
291 textshow(t);
292 break;
293 case EFocus:
294 break;
295 case EFont:
296 _ctlargcount(t, cp, 2);
297 _setctlfont(t, &t->font, cp->args[1]);
298 break;
299 case EHide:
300 _ctlargcount(t, cp, 1);
301 t->hidden = 1;
302 break;
303 case EImage:
304 _ctlargcount(t, cp, 2);
305 _setctlimage(t, &t->image, cp->args[1]);
306 break;
307 case ERect:
308 _ctlargcount(t, cp, 5);
309 r.min.x = cp->iargs[1];
310 r.min.y = cp->iargs[2];
311 r.max.x = cp->iargs[3];
312 r.max.y = cp->iargs[4];
313 if(Dx(r)<=0 || Dy(r)<=0)
314 ctlerror("%q: bad rectangle: %s", t->name, cp->str);
315 t->rect = r;
316 t->nvis = (Dy(r)-2*t->border)/t->font->font->height;
317 break;
318 case EReplace:
319 _ctlargcount(t, cp, 3);
320 i = cp->iargs[1];
321 if(i<0 || i>=t->nline)
322 ctlerror("%q: line number out of range: %s", t->name, cp->str);
323 free(t->line[i]);
324 t->line[i] = _ctlrunestr(cp->args[2]);
325 textshow(t);
326 break;
327 case EReveal:
328 _ctlargcount(t, cp, 1);
329 t->hidden = 0;
330 textshow(t);
331 break;
332 case EScroll:
333 _ctlargcount(t, cp, 2);
334 t->scroll = cp->iargs[1];
335 break;
336 case ESelect:
337 if(cp->nargs!=2 && cp->nargs!=3)
338 badselect:
339 ctlerror("%q: bad select message: %s", t->name, cp->str);
340 if(cp->nargs == 2){
341 if(strcmp(cp->args[1], "all") == 0){
342 memset(t->selected, 1, t->nline);
343 break;
344 }
345 if(strcmp(cp->args[1], "none") == 0){
346 memset(t->selected, 0, t->nline);
347 break;
348 }
349 if(cp->args[1][0]<'0' && '9'<cp->args[1][0])
350 goto badselect;
351 texttogglei(t, cp->iargs[1]);
352 break;
353 }
354 if(cp->iargs[1]<0 || cp->iargs[1]>=t->nline)
355 ctlerror("%q: selection index out of range (nline %d): %s", t->name, t->nline, cp->str);
356 if(t->selected[cp->iargs[1]] != (cp->iargs[2]!=0))
357 texttogglei(t, cp->iargs[1]);
358 break;
359 case ESelectcolor:
360 _ctlargcount(t, cp, 2);
361 _setctlimage(t, &t->selectcolor, cp->args[1]);
362 break;
363 case ESelectmode:
364 _ctlargcount(t, cp, 2);
365 if(strcmp(cp->args[1], "single") == 0)
366 t->selectmode = Selsingle;
367 else if(strncmp(cp->args[1], "multi", 5) == 0)
368 t->selectmode = Selmulti;
369 break;
370 case ESelectstyle:
371 _ctlargcount(t, cp, 2);
372 if(strcmp(cp->args[1], "down") == 0)
373 t->selectstyle = Seldown;
374 else if(strcmp(cp->args[1], "up") == 0)
375 t->selectstyle = Selup;
376 break;
377 case EShow:
378 _ctlargcount(t, cp, 1);
379 textshow(t);
380 break;
381 case ESize:
382 if (cp->nargs == 3)
383 r.max = Pt(10000, 10000);
384 else{
385 _ctlargcount(t, cp, 5);
386 r.max.x = cp->iargs[3];
387 r.max.y = cp->iargs[4];
388 }
389 r.min.x = cp->iargs[1];
390 r.min.y = cp->iargs[2];
391 if(r.min.x<=0 || r.min.y<=0 || r.max.x<=0 || r.max.y<=0 || r.max.x < r.min.x || r.max.y < r.min.y)
392 ctlerror("%q: bad sizes: %s", t->name, cp->str);
393 t->size.min = r.min;
394 t->size.max = r.max;
395 break;
396 case ETextcolor:
397 _ctlargcount(t, cp, 2);
398 _setctlimage(t, &t->textcolor, cp->args[1]);
399 break;
400 case ETopline:
401 _ctlargcount(t, cp, 2);
402 i = cp->iargs[1];
403 if(i < 0)
404 i = 0;
405 if(i > t->nline)
406 i = t->nline;
407 if(t->topline != i){
408 t->topline = i;
409 textshow(t);
410 }
411 break;
412 case EValue:
413 /* set contents to single line */
414 /* free existing text and fall through to add */
415 for(i=0; i<t->nline; i++){
416 free(t->line[i]);
417 t->line[i] = nil;
418 }
419 t->nline = 0;
420 t->topline = 0;
421 /* fall through */
422 case EAccumulate:
423 case EAdd:
424 switch (cp->nargs) {
425 default:
426 ctlerror("%q: wrong argument count in '%s'", t->name, cp->str);
427 case 2:
428 n = t->nline;
429 break;
430 case 3:
431 n = cp->iargs[1];
432 if(n<0 || n>t->nline)
433 ctlerror("%q: line number out of range: %s", t->name, cp->str);
434 break;
435 }
436 rp = _ctlrunestr(cp->args[cp->nargs-1]);
437 t->line = ctlrealloc(t->line, (t->nline+1)*sizeof(Rune*));
438 memmove(t->line+n+1, t->line+n, (t->nline-n)*sizeof(Rune*));
439 t->line[n] = rp;
440 t->selected = ctlrealloc(t->selected, t->nline+1);
441 memmove(t->selected+n+1, t->selected+n, t->nline-n);
442 t->selected[n] = (t->selectmode==Selmulti && cmd!=EAccumulate);
443 t->nline++;
444 if(t->scroll) {
445 if(n > t->topline + (t->nvis - 1)){
446 t->topline = n - (t->nvis - 1);
447 if(t->topline < 0)
448 t->topline = 0;
449 }
450 if(n < t->topline)
451 t->topline = n;
452 }
453 if(cmd != EAccumulate)
454 if(t->scroll || t->nline<=t->topline+t->nvis)
455 textshow(t);
456 break;
457 case EWarp:
458 _ctlargcount(t, cp, 2);
459 i = cp->iargs[1];
460 if(i <0 || i>=t->nline)
461 ctlerror("%q: selection index out of range (nline %d): %s", t->name, t->nline, cp->str);
462 if(i < t->topline || i >= t->topline+t->nvis){
463 t->topline = i;
464 }
465 t->warp = cp->iargs[1];
466 textshow(t);
467 t->warp = -1;
468 break;
469 }
470 }
471
472 static void
texttogglei(Text * t,int i)473 texttogglei(Text *t, int i)
474 {
475 int prev;
476
477 if(t->selectmode == Selsingle){
478 /* clear the others */
479 prev = t->selected[i];
480 memset(t->selected, 0, t->nline);
481 t->selected[i] = prev;
482 }
483 t->selected[i] ^= 1;
484 textshow(t);
485 }
486
487 static int
textline(Text * t,Point p)488 textline(Text *t, Point p)
489 {
490 Rectangle r;
491 int i;
492
493 r = t->rect;
494 if(t->border > 0)
495 r = insetrect(r, t->border);
496 if(!ptinrect(p, r))
497 return -1;
498 i = (p.y-r.min.y)/t->font->font->height;
499 i += t->topline;
500 if(i >= t->nline)
501 return -1;
502 return i;
503 }
504
505 static int
texttoggle(Text * t,Point p)506 texttoggle(Text *t, Point p)
507 {
508 int i;
509
510 i = textline(t, p);
511 if (i >= 0)
512 texttogglei(t, i);
513 return i;
514 }
515
516 Control*
createtext(Controlset * cs,char * name)517 createtext(Controlset *cs, char *name)
518 {
519 Text *t;
520
521 t = (Text*)_createctl(cs, "text", sizeof(Text), name);
522 t->line = ctlmalloc(sizeof(Rune*));
523 t->selected = ctlmalloc(1);
524 t->nline = 0;
525 t->image = _getctlimage("white");
526 t->textcolor = _getctlimage("black");
527 t->bordercolor = _getctlimage("black");
528 t->selectcolor = _getctlimage("yellow");
529 t->selectingcolor = _getctlimage("paleyellow");
530 t->font = _getctlfont("font");
531 t->selectmode = Selsingle;
532 t->selectstyle = Selup; // Seldown;
533 t->lastbut = 0;
534 t->mouse = textmouse;
535 t->ctl = textctl;
536 t->exit = textfree;
537 t->warp = -1;
538 t->sel = -1;
539 t->offsel = 0;
540 t->but = 0;
541 return (Control *)t;
542 }
543