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