swk.c (11178B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <stdio.h> 3 #include <string.h> 4 #include <stdlib.h> 5 #include "swk.h" 6 #include "config.h" 7 8 #define BORDERCOLOR ((e->win->box==e->box)?ColorHI:ColorFG) 9 static void setscrollbox(int delta); 10 static SwkWindow *w = NULL; 11 static int running = 0; 12 static __thread int rendering = 0; 13 14 int 15 swk_use(SwkWindow *win) { 16 w = win = win->_e.win = win; 17 w->colpos = COLSPLIT; 18 if(win->box == NULL) 19 swk_focus_first(w); 20 if(w->r.w == 0 || w->r.h == 0) { 21 w->r.w = WINWIDTH; 22 w->r.h = WINHEIGHT; 23 } 24 if(!running && !swk_gi_init(w)) 25 return 0; 26 running = 1; 27 swk_update(); 28 return 1; 29 } 30 31 // TODO: merge with clean() 32 void 33 drawcol(SwkWindow *w) { 34 Rect r = {0}; 35 r.h = 1; 36 if (!w->boxes[1]) { 37 r.x = 0; 38 r.y = 0; 39 r.w = w->r.w+2; 40 r.h = w->r.h+2; 41 } else 42 if (!w->col) { 43 r.x = 0; 44 r.y = 0; 45 r.w = w->colpos; 46 r.h = w->r.h+2; 47 } else { 48 r.x = w->colpos; 49 r.y = 0; 50 r.w = 100; 51 r.h = w->r.h+2; 52 } 53 swk_gi_fill(r, ColorCC, 0); 54 } 55 56 void 57 swk_update() { 58 char text[8]; 59 int roy, oy, scroll = 0; 60 int col = (w->boxes[1])?((w->colpos>w->r.w)?w->r.w:w->colpos):w->r.w; 61 if(rendering) 62 return; 63 // TODO: Handle scrollup by widget focus 64 //if(w->box->r.y > w->r.h) 65 //setscrollbox(-2); 66 rendering = 1; 67 w->_e.type = EExpose; 68 if(swk_gi_update(w)) { 69 int count = 2; 70 int orw = w->r.w; 71 SwkBox *b = w->boxes[0]; 72 73 swk_fit(w); 74 swk_gi_clear(); 75 //if(!w->colpos) { 76 if(w->colpos<2) { 77 b = w->boxes[1]; 78 count--; 79 col = w->r.w; 80 } 81 drawcol(w); 82 for(w->r.w=col; ; b = w->boxes[1]) { 83 swk_fit(w); 84 roy = oy = 0; 85 if(b) 86 for(;b->cb; b++) { 87 w->_e.box = b; 88 if(IS_SCROLLBOX(b)) 89 roy = oy+1; 90 if(b->scroll) 91 scroll = b->scroll; 92 if(roy && b->r.y < roy) { 93 Rect r = w->r; 94 r.x = col-1; 95 r.y = roy; 96 r.w = 3; 97 sprintf(text, "(%d)", scroll); 98 swk_gi_text(r, text); 99 //swk_gi_line(--r.x, roy, 2, 0, ColorHI); 100 swk_gi_line((b==w->boxes[0])?w->colpos:0, roy, w->r.w, 0, ColorHI); 101 } else b->cb(&w->_e); 102 oy = b->r.h; 103 } 104 if(!w->boxes[1] || !--count) 105 break; 106 col = orw-w->col; 107 } 108 swk_gi_flip(); 109 w->r.w = orw; 110 } 111 rendering = 0; 112 } 113 114 // TODO: enqueue events here instead of use a global variable? 115 void 116 swk_exit() { 117 running = 0; 118 } 119 120 void 121 swk_loop() { 122 SwkEvent *e; 123 do { 124 if((e = swk_next_event(w))) 125 swk_handle_event(e); 126 } while(!e || e->type != EQuit); 127 } 128 129 void 130 swk_fontsize_increase() { 131 swk_gi_fontsize(1); 132 w->colpos--; 133 swk_update(w); 134 } 135 136 void 137 swk_fontsize_decrease() { 138 swk_gi_fontsize(-1); 139 w->colpos++; 140 swk_update(w); 141 } 142 143 void 144 swk_column_move_left() { 145 if(w->colpos>0) 146 w->colpos--; 147 swk_update(w); 148 } 149 150 void 151 swk_column_move_right() { 152 if(w->colpos<w->r.w) 153 w->colpos++; 154 swk_update(w); 155 } 156 157 static void 158 setscrollbox(int delta) { 159 SwkBox *r = NULL; 160 SwkBox *b = w->boxes[w->col]; 161 for(; b->cb; b++) { 162 if(IS_SCROLLBOX(b)) 163 r = b; 164 if(w->box==b && r) 165 break; 166 } 167 if(r) r->scroll += delta; 168 } 169 170 void 171 swk_scroll_up() { 172 w->box = w->boxes[w->col]; 173 setscrollbox(SCROLLSPEED); 174 } 175 176 void 177 swk_scroll_down() { 178 w->box = w->boxes[w->col]; 179 setscrollbox(-SCROLLSPEED); 180 } 181 182 static void 183 swk_fit_row(SwkBox *a, SwkBox *b, int col, int y) { 184 int x = (col)?w->colpos:0, count = b-a; 185 if(count) { 186 int winc = w->r.w / count; 187 SwkBox *btmp = a; 188 for(; btmp<b; btmp++) { 189 btmp->r.x = x; 190 btmp->r.y = y; 191 btmp->r.w = winc; 192 if(!btmp->r.h) 193 btmp->r.h = 1; 194 x += winc; 195 } 196 } 197 } 198 199 static int 200 countrows(SwkBox *b) { 201 int row = 17; // XXX hacky value to center widgets 202 for(; b->cb; b++) 203 if(IS_SCROLLBOX(b)) 204 row += (int)(size_t)b->data; 205 return row; 206 } 207 208 void 209 swk_fit() { 210 int i, x, y, skip, tskip; 211 SwkBox *b, *b2; 212 for(i=0;i<2;i++) { 213 skip = tskip = y = 0; 214 for(b=b2=w->boxes[i]; b->cb; b++) { 215 if(b->r.w==-1 && b->r.h==-1) { 216 x = (int)(size_t)b->data; 217 swk_fit_row(b2, b, i, y); 218 y += x-skip+tskip; 219 // vertical align // 220 tskip = 0; 221 if(x<0) y += w->r.h-countrows(b2); 222 b2 = b+1; 223 } else if(b->r.h>1) 224 tskip = b->r.h; 225 y += b->scroll; 226 } 227 swk_fit_row(b2, b, i, y); 228 if(!w->boxes[1]) 229 break; 230 } 231 } 232 233 void 234 swk_focus_activate() { 235 w->_e.box = w->box; 236 w->_e.type = EClick; 237 } 238 239 SwkEvent * 240 swk_next_event() { 241 if(running) 242 return swk_gi_event(w, 1); 243 w->_e.type = EQuit; 244 w->_e.win = w; 245 return &w->_e; 246 } 247 248 void 249 swk_handle_event(SwkEvent *e) { 250 int i; 251 SwkBox *b; 252 if(e->win->cb) 253 e->win->cb(e); 254 switch(e->type) { 255 case EKey: 256 for(i=0; keys[i].cb; i++) { 257 if(e->data.key.modmask == keys[i].modmask 258 && e->data.key.keycode == keys[i].keycode) { 259 keys[i].cb(e->win); 260 break; 261 } 262 } 263 e->box = e->win->box; 264 if(e->win->box) 265 e->win->box->cb(e); 266 swk_update(); 267 break; 268 case EMotion: 269 w->col = w->boxes[1]?((e->data.motion.x > w->colpos)?1:0):0; 270 for(b=e->win->boxes[w->col]; b->cb; b++) { 271 if(SWK_HIT(b->r, e->data.motion)) { 272 e->win->box = e->box = b; 273 b->cb(e); 274 // swk_update(); 275 break; 276 } 277 } 278 break; 279 case EClick: 280 // TODO: move click events in config.h 281 switch(e->data.click.button) { 282 case 4: 283 swk_scroll_up(e->win); 284 break; 285 case 5: 286 swk_scroll_down(e->win); 287 break; 288 default: 289 for(b=e->win->boxes[w->col]; b->cb; b++) { 290 if(SWK_HIT(b->r, e->data.click.point)) { 291 e->box = e->win->box = b; 292 e->box->cb(e); 293 } 294 } 295 } 296 swk_update(); 297 break; 298 case EExpose: 299 swk_update(); 300 break; 301 case EQuit: 302 swk_gi_exit(); 303 break; 304 default: 305 break; 306 } 307 } 308 309 void 310 swk_focus_first() { 311 w->box = w->boxes[w->col]; 312 while(w->box->cb == swk_filler) 313 w->box++; 314 if(w->box->cb == NULL) 315 w->box = w->boxes[w->col]; 316 } 317 318 void 319 swk_focus_next() { 320 w->box++; 321 if(w->box->cb == NULL) 322 w->box = w->boxes[w->col]; 323 while(w->box->cb == swk_filler) 324 w->box++; 325 if(w->box->cb == NULL) 326 swk_focus_first(w); 327 } 328 329 void 330 swk_focus_prev() { 331 if(w->box == w->boxes[w->col]) { 332 while(w->box->cb) 333 w->box++; 334 w->box--; 335 } else { 336 w->box--; 337 while(w->box->cb == swk_filler) { 338 w->box--; 339 if(w->box < w->boxes[w->col]) { 340 w->box = w->boxes[w->col]; 341 swk_focus_prev(w); 342 return; 343 } 344 } 345 } 346 } 347 348 /* -- widgets -- */ 349 350 void 351 swk_label(SwkEvent *e) { 352 char text[128]; // XXX 353 int cut, len; 354 Rect r; 355 switch(e->type) { 356 case EExpose: 357 r = e->box->r; 358 r.w += 4; 359 cut = r.w*2; 360 strncpy(text, e->box->text, sizeof(text)-1); 361 len = strlen(text); 362 if (len>cut) 363 text[cut]=0; 364 swk_gi_text(r, text); 365 r.w -= 4; 366 if(e->win->box == e->box) 367 swk_gi_line(r.x, r.y+1, r.w, 0, ColorHI); 368 break; 369 default: 370 break; 371 } 372 } 373 374 void 375 swk_password(SwkEvent *e) { 376 char *str, *ptr; 377 int len; 378 Rect r; 379 switch(e->type) { 380 case EExpose: 381 r = e->box->r; 382 swk_gi_fill(r, ColorBG, 1); 383 if(e->win->box == e->box) 384 swk_gi_line(r.x, r.y+1, r.w, 0, ColorHI); 385 len = strlen(e->box->text); 386 if(len>0) { 387 ptr = str = malloc(len+1); 388 for(;len--;ptr++) 389 *ptr='*'; 390 *ptr='\0'; 391 swk_gi_text(r, str); 392 free(str); 393 } 394 break; 395 case EClick: 396 printf("password: %s\n", e->box->text); 397 default: 398 swk_entry(e); 399 } 400 } 401 402 void 403 swk_entry(SwkEvent *e) { 404 int len, key; 405 char *ptr; 406 switch(e->type) { 407 case EKey: 408 if (e->data.key.modmask&Ctrl) 409 return; 410 key = e->data.key.keycode; 411 if(key == 8) { 412 ptr = (char*)malloc(strlen(e->box->text)+2); 413 strcpy(ptr, e->box->text); 414 if(e->box->data) 415 free(e->box->text); 416 if((len = strlen (ptr))>0) 417 ptr[len-1] = '\0'; 418 e->box->text = e->box->data = ptr; 419 } else { 420 if(key>=' '&&key<='~') { 421 ptr = (char*)malloc(strlen(e->box->text)+2); 422 sprintf(ptr, "%s%c", e->box->text, e->data.key.keycode); 423 if(e->box->data) 424 free(e->box->text); 425 e->box->text = e->box->data = ptr; 426 } 427 } 428 break; 429 case EExpose: 430 // XXX: add support for cursor (handle arrow keys) 431 swk_gi_fill(e->box->r, ColorBG, 1); 432 swk_label(e); 433 /* cursor */ { 434 int cut = e->box->r.w*2; 435 #ifdef USE_SDL 436 len = 4*e->box->r.x; 437 #else 438 len = 3*e->box->r.x; 439 #endif 440 if (strlen(e->box->text)>cut) 441 len += cut; 442 #ifdef USE_SDL 443 else len += 2*strlen(e->box->text)+1; 444 #else 445 else len += strlen(e->box->text)+1; 446 #endif 447 Rect r = { len, e->box->r.y, 1, 1 }; 448 swk_gi_fill(r, ColorFG, 2); 449 } 450 break; 451 default: 452 swk_label(e); 453 break; 454 } 455 } 456 457 void 458 swk_button(SwkEvent *e) { 459 Rect r; 460 switch(e->type) { 461 case EExpose: 462 r = e->box->r; 463 r.w--; 464 swk_gi_fill(r, ColorBG, 0); 465 swk_gi_rect(r, BORDERCOLOR); 466 r = e->box->r; 467 r.x++; 468 swk_gi_text(r, e->box->text); 469 break; 470 default: 471 break; 472 } 473 } 474 475 void 476 swk_bigbutton(SwkEvent *e) { 477 Rect r; 478 switch(e->type) { 479 case EExpose: 480 e->box->r.h = 3; 481 r = e->box->r; 482 r.x--; 483 r.y--; 484 r.w--; 485 r = e->box->r; 486 swk_gi_fill(r, ColorBG, 0); 487 swk_gi_rect(r, BORDERCOLOR); 488 r = e->box->r; 489 r.x += 2; 490 r.y++; 491 swk_gi_text(r, e->box->text); 492 break; 493 default: 494 break; 495 } 496 } 497 498 void 499 swk_filler(SwkEvent *e) { 500 /* empty widget */ 501 } 502 503 void 504 swk_option(SwkEvent *e) { 505 Rect r; 506 SwkBox **b = (SwkBox**)e->box->data; 507 switch(e->type) { 508 case EClick: 509 if(b==(void*)0) e->box->data = (void*)1; 510 else if(b==(void*)1) e->box->data = (void*)0; 511 else *b = (e->box==*b)?NULL:e->box; 512 break; 513 case EExpose: 514 r = e->box->r; 515 if(e->win->box == e->box) 516 swk_gi_line(r.x, r.y+1, r.w, 0, ColorHI); 517 //r.w = r.h = 1; 518 r.w = 1; 519 if(b==(void*)1) swk_gi_fill(r, ColorHI, 1); 520 else if(b==(void*)0) swk_gi_fill(r, ColorFG, 1); 521 else if(e->box==*b) swk_gi_fill(r, ColorHI, 1); 522 else swk_gi_fill(r, ColorFG, 1); 523 r = e->box->r; 524 r.x += 2; 525 swk_gi_text(r, e->box->text); 526 break; 527 default: 528 break; 529 } 530 } 531 532 void 533 swk_separator(SwkEvent *e) { 534 Rect r; 535 switch(e->type) { 536 case EExpose: 537 r = e->box->r; 538 swk_gi_line(r.x, r.y+1, r.w, 0, BORDERCOLOR); 539 break; 540 default: 541 break; 542 } 543 } 544 545 void 546 swk_progress(SwkEvent *e) { 547 int pc, len; 548 Rect r; 549 switch(e->type) { 550 case EExpose: 551 r = e->box->r; 552 swk_gi_fill(r, ColorBG, 0); 553 r.x+=1; 554 swk_gi_text(r, e->box->text); 555 r.x-=1; 556 swk_gi_rect(r, ColorFG); 557 len = strlen(e->box->text)+2; 558 r.x += len*0.8; 559 r.w -= len*0.6; 560 pc = atoi(e->box->text); 561 if(pc<0) pc = 0; else if(pc>100) pc = 100; 562 r.w = (int)((float)r.w*((float)pc/100)); 563 if(r.w>0) 564 swk_gi_fill(r, ColorFG, 1); 565 break; 566 default: 567 break; 568 } 569 } 570 571 /* -- */ 572 void 573 swk_image_free(SwkBox *b) { 574 swk_gi_img_free(b->data); 575 b->data = NULL; 576 } 577 578 void 579 swk_image(SwkEvent *e) { 580 if(e->box->data == NULL) { 581 e->box->data = swk_gi_img_load(e->box->text); 582 if(!e->box->data) 583 fprintf(stderr, "Cannot open image %s\n", e->box->text); 584 } 585 switch(e->type) { 586 case EExpose: 587 swk_gi_img(e->box->r, e->box->data); 588 //swk_gi_rect(e->box->r, ColorFG); 589 if(e->win->box == e->box) { 590 Rect r = e->box->r; 591 swk_gi_line(r.x, r.y+1, r.w, 0, ColorHI); 592 } 593 break; 594 default: 595 break; 596 } 597 } 598 599 void 600 swk_sketch(SwkEvent *e) { 601 int x, y; 602 if(e->box->data == NULL) 603 e->box->data = swk_gi_img_new(e->box->r.w, e->box->r.h, ColorHI); 604 switch(e->type) { 605 case EClick: 606 // TODO: implement low-level primitives for pixel-level rendering 607 for(x=y=5;x<10;x=++y) 608 swk_gi_img_set(e->box->data, x, y, ColorFG); 609 for(x=y=5;x<10;x++,y--) 610 swk_gi_img_set(e->box->data, x, y, ColorFG); 611 printf("CLICKED %p %d %d\n", e->box->data, e->data.click.point.x, e->data.click.point.y); 612 break; 613 case EExpose: 614 swk_gi_rect(e->box->r, ColorHI); // border 615 swk_gi_img(e->box->r, e->box->data); 616 break; 617 default: 618 break; 619 } 620 }