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 } 543