]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libcontrol/text.c
plumber: open rule files as OCEXEC, to avoid leaking them to sub commands
[plan9front.git] / sys / src / libcontrol / text.c
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
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
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
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
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
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
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
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*
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 }