zori.c 28.7 KB
Newer Older
1 2

#include "zori.h"
3
#include "miao.h"
4
#include <allegro5/allegro_color.h>
5 6
#include "str.h"
#include "draw.h"
7 8 9 10 11 12

/*
* Pardon the pun name, but Zori is the submodule that handles the user
* interface and the menus. 
*/

13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
/* registry functionality */

/** Compare registry entries. */
int zori_registry_entry_compare(const void * v1, const void * v2) {
  const struct zori_registry_entry * entry1 = v1;
  const struct zori_registry_entry * entry2 = v2;
  return (entry2->id - entry1->id);
}

/** Initialize the registry. */
zori_id zori_registry_init(struct zori_registry * registry) {
  miao_init(registry);
  return ZORI_ID_OK;
}


/** Add an entry to the registry. */
zori_id 
zori_registry_add(struct zori_registry * registry, zori_id id, 
struct zori_widget * widget) {
  struct zori_registry_entry entry = { id, widget };
  if (miao_push(registry, entry)) { 
    miao_qsort(registry, zori_registry_entry_compare);
    return ZORI_ID_OK;
  }
  return ZORI_ID_ENOMEM;
}

/** Look up an entry in the registry. */
struct zori_registry_entry *  
zori_registry_lookup_entry(struct zori_registry * registry, zori_id id) {
  struct zori_registry_entry key = { id, NULL };
  return miao_bsearch(registry, zori_registry_entry_compare, &key);
}


/** Look up a widget in the registry. */
struct zori_widget *  
zori_registry_lookup(struct zori_registry * registry, zori_id id) {
  struct zori_widget * result = NULL;
  struct zori_registry_entry * entry = NULL;
  
  entry = zori_registry_lookup_entry(registry, id);
  if (entry) { 
    result = entry->widget;
  }
  return result;
}

/** Remove an entry from the registry. */
zori_id 
zori_registry_remove(struct zori_registry * registry, zori_id id) {
  struct zori_registry_entry * entry = NULL;
  entry = zori_registry_lookup_entry(registry, id);
  if (entry) {
    miao_delete_entry(registry, entry);
  }
  return ZORI_ID_OK;
}



/** Handler functionality */

77 78 79 80 81 82 83 84
int zori_handler_compare(const void * v1, const void * v2) {
  const struct zori_handler * h1 = v1;
  const struct zori_handler * h2 = v2;
  
  if (h1->type < h2->type) return -1;
  if (h1->type > h2->type) return 1;
  return 0;  
}
85

86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
struct zori_handler * zori_handlers_add(struct zori_handlers  * me, zori_event_type type, zori_handler_func * handler, void * data) {
  struct zori_handler * result = NULL;
  
  result = miao_push_ptr(me);
  if (result) {
    result->type    = type;
    result->handler = handler;
    result->data    = data;
  }
  miao_qsort(me, zori_handler_compare);
  return result;    
}


void zori_handlers_done(struct zori_handlers * me) {
  miao_done(me);
}

void zori_handlers_init(struct zori_handlers * me) {  
  miao_init(me);
}


struct zori_handler * zori_handlers_search(struct zori_handlers * me,  zori_event_type type) {    
  struct zori_handler * result;
  struct zori_handler key;
  key.type    = type;
  key.handler = NULL;
  key.data    = NULL;
  if (!me) return NULL;
  result = miao_bsearch(me, zori_handler_compare, &key);
117
  return result; 
118 119 120
}


121 122
int zori_handlers_handle(struct zori_handlers * me, union zori_event * event) {    
  struct zori_handler * handler = zori_handlers_search(me, event->type);
123
  if (!handler) return 0;
124
  event->any.data = handler->data;
125 126 127
  return handler->handler(event);
}

128 129 130 131 132
int zori_widget_raise_system_event
(struct zori_widget * widget, zori_system_event * sysev) {
  union zori_event event;
  event.sys.any.type   = sysev->type;
  event.sys.any.widget = widget;
beoran's avatar
beoran committed
133
  event.sys.ev         = sysev;
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
  return zori_handlers_handle(&widget->handlers, &event);
}

int zori_widget_raise_draw_event(struct zori_widget * widget) {
  union zori_event event;
  event.draw.any.type   = ZORI_EVENT_DRAW;
  event.draw.any.widget = widget; 
  return zori_handlers_handle(&widget->handlers, &event);
}

int zori_widget_raise_done_event(struct zori_widget * widget) {
  union zori_event event;
  event.done.any.type   = ZORI_EVENT_DONE;
  event.done.any.widget = widget; 
  return zori_handlers_handle(&widget->handlers, &event);
}

int zori_widget_raise_free_event(struct zori_widget * widget) {
  union zori_event event;
  event.free.any.type   = ZORI_EVENT_FREE;
  event.free.any.widget = widget; 
  return zori_handlers_handle(&widget->handlers, &event);
}

int zori_widget_raise_update_event
(struct zori_widget * widget, double dt) {
  union zori_event event;
  event.update.any.type   = ZORI_EVENT_DONE;
  event.update.any.widget = widget;
  event.update.dt         = dt; 
  return zori_handlers_handle(&widget->handlers, &event);
}

beoran's avatar
beoran committed
167 168 169 170 171 172 173 174
int zori_widget_raise_action_event
(struct zori_widget * widget) {
  union zori_event event;
  event.action.any.type   = ZORI_EVENT_ACTION;
  event.action.any.widget = widget;
  return zori_handlers_handle(&widget->handlers, &event);
}

175 176 177



178 179 180 181 182 183
static struct zori_root * the_zori_root = NULL;

static struct zori_style * the_default_style = NULL;

static struct zori_registry * the_zori_registry = NULL;

184 185 186 187
struct zori_root * zori_get_root(void) {
  return the_zori_root;
}

188 189 190 191 192 193 194 195

int zori_widget_compare(const void * v1, const void * v2) {
  const struct zori_widget * w1 = v1, * w2 = v2;
  return (w2->id - w1->id);
}


struct zori_widget * zori_get_widget(zori_id id) {
196 197
  if (!the_zori_registry) return NULL;
  return zori_registry_lookup(the_zori_registry, id);
198 199 200 201 202 203 204 205 206 207 208 209 210
}

zori_id zori_get_unused_id(void) {
  zori_id id = -1;
  struct zori_widget * found;
  do {
    id++;
    if (id == INT_MAX) { return ZORI_ID_ERROR; }
    found = zori_get_widget(id);
  } while(found);
  return id;
}

211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
struct zori_handler * zori_widget_add_handler
(struct zori_widget * widget, zori_event_type type, zori_handler_func * handler, void * data) {  
  if ((!widget) || (!handler)) return NULL;
  
  return zori_handlers_add(&widget->handlers, type, handler, data);
}

struct zori_handler * zori_widget_add_handlers
(struct zori_widget * widget, struct zori_handler * handlers, size_t amount) {
  size_t i;
  for (i = 0; i < amount; i++) { 
    struct zori_handler * handler = handlers + i;
    if (!zori_widget_add_handler(widget, handler->type, handler->handler, handler->data)) return NULL;
  }
  return handlers + amount - 1;
}



230 231 232 233 234 235 236 237 238 239 240 241 242
zori_id zori_start(struct zori_style * default_style) {
  if (the_zori_root) return ZORI_ID_OK;
  the_zori_root = calloc(1, sizeof(*the_zori_root));
  if (!the_zori_root) return ZORI_ID_ENOMEM;
  the_default_style = calloc(1, sizeof(*the_default_style));
  if (!the_default_style) return ZORI_ID_ENOMEM;
  the_zori_registry = calloc(1, sizeof(*the_zori_registry));
  if (!the_zori_registry) return ZORI_ID_ENOMEM;
  
 
  the_default_style->text.font      = al_create_builtin_font();  
  the_default_style->text.color     = al_color_name("white");  
  the_default_style->border.color   = al_color_name("white");  
243
  the_default_style->back.color     = al_color_name("green");
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
  
  if (default_style) {
    the_zori_root->widget.style = *default_style;
  } else {
    the_zori_root->widget.style = *the_default_style;
  }
  
  the_zori_root->widget.id = 0;
  zori_registry_add(the_zori_registry, the_zori_root->widget.id, &the_zori_root->widget);
  return the_zori_root->widget.id;  
}

void zori_widget_free(struct zori_widget * widget);

struct zori_widget * zori_widget_done(struct zori_widget * widget) {
259
  size_t index;
260 261 262
  struct zori_widget * aid, * next;
  if (!widget) return widget;
  
263 264 265
  for (index = 0; index < miao_size(&widget->children); index ++) {
    struct zori_widget * child = miao_unsafe_get(&widget->children, index);
    zori_widget_free(child);
266
  }
267 268 269 270
    
  miao_done(&widget->handlers);
  miao_done(&widget->children);
  
271 272 273 274
  return widget;
}

void zori_widget_free(struct zori_widget * widget) {
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
  if (widget) {
    zori_registry_remove(the_zori_registry, widget->id);
  }
  zori_widget_done(widget);
  free(widget);
}

struct zori_widget *  
zori_widget_add_child(struct zori_widget * parent, struct zori_widget * child) {
  if (parent) { 
    miao_push((&parent->children), child);
    child->parent = child;
  }
  return child;
} 

struct zori_widget * zori_widget_init
  ( struct zori_widget * widget, 
293 294 295 296
    zori_id id,
    struct zori_widget * parent, 
    zori_rebox * box, struct zori_style * style) {
    if (!widget) return NULL;  
297 298 299 300 301
    
    miao_init(&widget->children);
    miao_init(&widget->handlers);
    widget->z     = 0;
    widget->flags = 0;
302
    widget->id    = ( (id < 0) ? widget->id : id);
303 304 305 306 307 308 309 310 311 312 313 314 315 316
    
    if (style) {
      widget->style = *style;
      if (!widget->style.text.font) {
        widget->style.text.font = the_default_style->text.font;
      }
    } else {
      widget->style = *the_default_style;
    }
    if (box) {
      widget->box = *box;
    } else if (parent) {
      widget->box = parent->box;
    } else {
beoran's avatar
beoran committed
317
      widget->box = rebox_make(0, 0, ZORI_WIDGET_DEFAULT_W, ZORI_WIDGET_DEFAULT_H);
318 319
    }
    zori_widget_add_child(parent, widget);
320
    zori_registry_add(the_zori_registry, widget->id, widget);
321
    return widget;
322 323
}

324

325 326 327 328 329 330 331
/* Shut down Zori and destroys all widgets. Return 0 on succes or 
 * negative on error.
 */
zori_id zori_shutdown() {
  assert((void *)(&the_zori_root->widget) == (void *)the_zori_root);
  zori_widget_free(&the_zori_root->widget);
  the_zori_root = NULL;
332 333 334 335 336 337 338 339
  free(the_default_style);
  the_default_style = NULL;
  /* clean up registry last so zori_widget fre can unregister it's widgets.*/
  miao_done(the_zori_registry);
  free(the_zori_registry);
  the_zori_registry = NULL;
  
  
340
  return ZORI_ID_OK;
341
}
342

343 344

struct zori_widget * 
345
zori_widget_initall(struct zori_widget * widget, int id,
346 347
struct zori_widget * parent, zori_rebox * box, struct zori_style * style, 
size_t amount, struct zori_handler * handlers) {
348
  if (zori_widget_init(widget, id, parent, box, style)) {
349 350 351 352 353 354
    zori_widget_add_handlers(widget, handlers, amount);
  }
  return widget;
}


355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
/* Creates a new screen widget. Normally this should be the first widget 
 * you create after zori_start.  */
zori_id zori_new_screen(zori_display * display);

/* Creates a new page widget on the given screen. The page is not 
 * made the active page, unless if it is the first one created. */
zori_id zori_new_page(zori_id screen);

/* Activates the page on it's display. All other pages are dectivated and 
 * hidden. */
zori_id zori_activate_page(zori_id page);

/* Creates a new generic widget on the given screen with the given
 * dimensions. */
zori_id zori_new(zori_id screen, zori_rebox * box);

/* Sets the flags of a widget.  */
zori_id zori_set_flags(zori_id widget, enum zori_flag flags);


/* Sets the whole style of a widget. */
zori_id zori_set_style(zori_id id, struct zori_style * style);

/* Sets the background color of the widget. */
zori_id zori_set_background_color(zori_id id, zori_color color);

/* Sets the foreground color of the widget. */
zori_id zori_set_foreground_color(zori_id id, zori_color color);

/* Creates a new frame widget. */
zori_id zori_new_frame_widget(zori_id parent, zori_rebox box);

/* Creates a new (vertical) menu widget. */
zori_id zori_new_menu_widget(zori_id parent, zori_rebox box, char * text);

/* Creates a new button widget. */
zori_id zori_new_button_widget(zori_id parent, zori_rebox box, char * text);

/* Creates a new conversation widget. */
zori_id zori_new_conversation_widget(zori_id parent, zori_rebox box, char * text);

396 397 398 399 400 401 402 403 404 405 406 407
/* Draws the widget and it's children recursively. */
void zori_draw_widget(struct zori_widget * widget) {
  size_t index;
  if (widget && zori_widget_visible(widget)) {
    zori_widget_raise_draw_event(widget);
    for (index = 0; index < miao_size(&widget->children); index++) {
      struct zori_widget * child = miao_unsafe_get(&widget->children, index);
      zori_draw_widget(child);
    }
  }
}

408 409
/* Draws the whole UI and all visible parts. */
void zori_draw_all(void) {
410 411
  zori_draw_widget(&the_zori_root->widget);
}
412

413 414
int zori_widget_visible(struct zori_widget * widget)  {
  return widget && ((widget->flags & ZORI_FLAG_HIDDEN) != ZORI_FLAG_HIDDEN);
415 416
}

417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
int zori_widget_active(struct zori_widget * widget)  {
  return widget && ((widget->flags & ZORI_FLAG_DISABLED) != ZORI_FLAG_DISABLED);
}

int zori_widget_active_(struct zori_widget * widget, int set)  {
  if (set) {  
    widget->flags = widget->flags & (~ZORI_FLAG_DISABLED);
  } else {
    widget->flags = widget->flags | ZORI_FLAG_DISABLED;
  }
  return zori_widget_active(widget);
}

int zori_widget_visible_(struct zori_widget * widget, int set)  {
  if (set) {  
    widget->flags = widget->flags & (~ZORI_FLAG_HIDDEN);
  } else {
    widget->flags = widget->flags | ZORI_FLAG_HIDDEN;
  }
  return zori_widget_active(widget);
}



441 442 443
/* Updates the state of the UI. Pass in the time passed since last update. */
void zori_update(double dt) 
{
444
  zori_widget_raise_update_event(&the_zori_root->widget, dt);
445 446 447 448 449 450 451
}

/* Registers an event handler for a widget. */
zori_id zori_register(zori_id id,  zori_event_type type, zori_handler_func handler, void * extra);



452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934
zori_font * zori_widget_font(struct zori_widget * widget) {
  return widget->style.text.font;
}

zori_color zori_widget_forecolor(struct zori_widget * widget) {
  return widget->style.text.color;
}

zori_color zori_widget_backcolor(struct zori_widget * widget) {
  return widget->style.back.color;
}


int zori_widget_h(struct zori_widget * widget) {
  return widget->box.size.y;
}

int zori_widget_w(struct zori_widget * widget) {
  return widget->box.size.x;
}

int zori_widget_x(struct zori_widget * widget) {
  return widget->box.at.x;
}

int zori_widget_y(struct zori_widget * widget) {
  return widget->box.at.y;
}


/* Helper struct that keeps track of the BYTE positions within 
a c string or USTR where a line or word starts or ends with a given maxwidth. */
struct zori_textinfo {
  int from_char;
  int start_char;
  int stop_char;
  int maxwidth;
};


/* Creates a temporary ustr as per al_ref_ustr but with 
 start and stop as code positions, not byte positions. */
static const USTR * 
ustrinfo_newref(
  USTR_INFO * uinfo, const USTR * ustr, int start, int stop) {
  return ustr_refustr(uinfo, ustr, 
                      ustr_offset(ustr, start),
                      ustr_offset(ustr, stop)
                     );
}



/* Creates a temporary ustr that refers ustr but respecds the bounds of the 
textinfo (start_char and enc_char) */
const USTR * zori_textinfo_refustr(struct zori_textinfo * self, 
                                USTR_INFO  * uinfo,
                                const USTR * ustr) { 
  return ustrinfo_newref(uinfo, ustr, self->start_char, self->stop_char);
}


struct zori_textinfo * 
zori_textinfo_wordfromtext(struct zori_textinfo * self, USTR * ustr, zori_font * font) {
  int found;
  int start_pos;
  int end_pos;
  int now_char;
  int end_char;
  int len;
  int ch;
  if(!self) return NULL;
  now_char         = self->from_char;
  self->start_char = now_char;
  ch = ustr_getnext(ustr, &now_char); 
  while (ch > 0) { 
    switch(ch) { 
      case ' ': /* Found a space, here the word ends, include the space. */
        self->stop_char = now_char;
        return self;
      case '\n': /* A newline ends a word, include the newline. */
        self->stop_char = now_char;
        return self;
      default: /* Other characters mean the word is not finished yet. */
        break;
    }
    /* XXX: Should handle the case for languages that use no spaces, 
    * by checking with al_get_ustr_width but it's not a pressing matter yet.
    */
    ch = ustr_getnext(ustr, &now_char); 
  } 
  // no word found, just set end here and be done. 
  self->stop_char = now_char;
  /* return nULL to signify end */
  return NULL;
}


/** Prints a ustring, since puts or printf print too much some
 times for a refstring.*/
static int ustr_print(USTR * word) {
  size_t index;
    for(index = 0; index < ustr_length(word) ; index++) {
      putchar(ustr_get(word, index));
    }
  return index;
}


/** Gets the positions of the next line of text fort he given Unicode string 
and store them in the given info. If the info's from is set to 0 or less, 
the first line is assumed, otherwise, the line will be started from "from".
Uses the given font to determine the width of the text as it is built.
*/
struct zori_textinfo * 
zori_textinfo_linefromtext(struct zori_textinfo * self, USTR * ustr, zori_font * font) {
  struct zori_textinfo wordinfo;
  USTR_INFO  lineuinfo;
  const USTR     * line;

  USTR_INFO  worduinfo = { 0, 0, 0};
  const USTR     * word;
  int ch;
  int index;
  int width;
  int last_stop;
  self->start_char   = self->from_char;
  wordinfo.from_char = self->from_char;
  
  while(zori_textinfo_wordfromtext(&wordinfo, ustr, font)) {
    word = zori_textinfo_refustr(&wordinfo, &worduinfo, ustr);
    line = ustrinfo_newref(&lineuinfo, ustr, self->start_char, wordinfo.stop_char);
    width = al_get_ustr_width(font, line);
    if (width > self->maxwidth) { 
      /* XXX: handle case of text overflow by bluntly retuning the word as is.
      Should split single word based on length too.
      There is overflow if this is still the first word as see from wordinfo_start_char.
      */
      if (wordinfo.start_char == self->start_char) {
        self->stop_char  = wordinfo.stop_char;
      } else { 
        self->stop_char  = wordinfo.start_char;
      }
      return self;
    }
    // If we get here, the word way still end on a newline character 
    // check this case. XXX: It works like this because 
    // stop_char is a bit wonky... it points at the first character of the 
    // next word in this case...
    ch = ustr_get(ustr, wordinfo.stop_char - 1);
    if (ch == '\n') {
      self->start_char = self->from_char;
      self->stop_char  = wordinfo.stop_char - 1;
      return self;
    }
    wordinfo.from_char = wordinfo.stop_char;
  }
  /* if we get here, the whole string fits. */
  self->start_char = self->from_char;
  self->stop_char  = wordinfo.stop_char;
  /* Return NULL to tell caller text has been completely split up. */
  return NULL;
}



#define ZORI_WIDGET_BORDER 3 

/** Draws a rounded frame as background for a widget. */
void zori_widget_drawroundframe(struct zori_widget * self) {
  if(!self) return;
  draw_roundframe(zori_widget_x(self), zori_widget_y(self), 
                  zori_widget_w(self), zori_widget_h(self),
                  ZORI_WIDGET_BORDER,
                  zori_widget_forecolor(self), zori_widget_backcolor(self));
}


/** Skips the text info to the next word or line of text. Must be called 
when looping over zori_textinfo_linefromtext. */
struct zori_textinfo * zori_textinfo_next(struct zori_textinfo * self) { 
  if(!self) return NULL;
  self->from_char  = self->stop_char + 1;
  self->start_char = self->from_char;
  return self;
}



/* Converts a widget to a console. Only works if the pointer is wrapped correctly,
 by a console. */
struct zori_console * zori_widget_console(struct zori_widget * widget) { 
  if (!widget) return NULL;
  return ZORI_CONTAINER_OF(widget, struct zori_console, widget);
}


/** Sets the console's command function and data. */
void zori_console_command_(struct zori_console * self, zori_console_command * command, void * data) {
  self->command      = command;
  self->command_data = data;
}

/** Let the console perform a command if possible. returns nonzero on error,
zero if OK. */
int zori_console_docommand(struct zori_console * self, const char * text) {
  if(!self) return -1;
  if(!self->command) return -2;
  return self->command(&self->widget, text, self->command_data);
}



/** Adds a line of text to the console. */
int zori_console_addstr(struct zori_console * self, const char * str) {
  if(!self) return -1;
  if(!ustrlist_shiftcstr(&self->text, str)) { 
    return -3;
  }  
  while(ustrlist_size(&self->text) >= self->max) { // remove last node(s)
    ustrlist_droplast(&self->text);
  }
  return ustrlist_size(&self->text);
}

/** Adds a line of text to the console. */
int zori_console_addustr(struct zori_console * self, const USTR * ustr) {
  if(!self) return -1;
  if(!ustrlist_shiftustr(&self->text, ustr)) { 
    return -3;
  }  
  while(ustrlist_size(&self->text) >= self->max) { // remove last node(s)
    ustrlist_droplast(&self->text);
  }
  return ustrlist_size(&self->text);
}


/** Puts a string on the console .*/
int zori_console_puts(struct zori_console * self, const char * str) {
  int index;
  int size     = strlen(str);
  int leftsize = size;
  int lines = 0;
  USTR_INFO uinfo;
  struct zori_textinfo info = { 0, 0, 0, 0};
  info.maxwidth   = zori_widget_w(&self->widget) - 10;
  USTR * ustr;
  const USTR * uline;
  ustr = ustr_new(str);
  while(zori_textinfo_linefromtext(&info, ustr, self->widget.style.text.font)) {
    uline = zori_textinfo_refustr(&info, &uinfo, ustr);
    zori_console_addustr(self, uline);
    // don't forget to skip to next line!!!
    zori_textinfo_next(&info);
  }
  uline = zori_textinfo_refustr(&info, &uinfo, ustr);
  zori_console_addustr(self, uline);
  ustr_free(ustr);
  return lines;
} 

#define BBCONSOLE_VPRINTF_MAX 1024

/** Prints a formatted string on the console, truncaded to 1024 characters.  */
int zori_console_vprintf(struct zori_console * self, const char * format, va_list args) {
  char buffer[BBCONSOLE_VPRINTF_MAX] = { '\0' };
  vsnprintf(buffer, BBCONSOLE_VPRINTF_MAX, format, args);
  return zori_console_puts(self, buffer);
}

/** Prints a formatted string on the console, truncaded to 1024 characters.  */
int zori_console_printf(struct zori_console * self, const char * format, ...) {
  int result;
  va_list args;
  va_start(args, format);
  result = zori_console_vprintf(self, format, args);
  va_end(args);
  return result;
}


/** Draws a console. */
int zori_console_draw(union zori_event * zevent) {
  struct zori_console * self  ;
  zori_font * font       ;
  zori_color color       ;
  USTRListNode * now;
  int high, linehigh, index, x, y, skip;
  int linew;
  struct zori_widget * widget = zevent->any.widget;
  
  if (!zori_widget_visible(widget)) return ZORI_HANDLE_IGNORE;
  
  self  = zori_widget_console(widget);
  font  = zori_widget_font(widget);
  color = zori_widget_forecolor(widget);
  
  zori_widget_drawroundframe(widget);
  high        = zori_widget_h(widget) - 10;
  x           = zori_widget_x(widget) +  5;
  y           = zori_widget_y(widget) -  5;
  linehigh    = zori_font_lineheight(font);
  
  now         = ustrlist_head(&self->text);
  // skip start lines (to allow scrolling backwards) 
  now         = ustrlist_skipnode(&self->text, self->start);
  
  for (index = high-(linehigh*2); index > 0; index -= linehigh) {
    USTR * textstr;
    if(!now) break;
    textstr = ustrlistnode_ustr(now);
    if(textstr) {
      zori_font_drawstr(font, color, x, y + index, 0, textstr);
    }
    now = ustrlistnode_next(now);
  }
  // draw input string
  zori_font_drawstr(font, color, x, y + high - linehigh, 0, self->input);
  // Draw cursor
  linew = al_get_ustr_width(font, self->input);
  al_draw_line(x + linew, y + high - linehigh, x + linew, y + high, color, 1);
  // draw start for debugging
  al_draw_textf(font, color, x, y, 0, "start: %d, size: %d", self->start, 
                ustrlist_size(&self->text));
  return ZORI_HANDLE_OK;
}

/** Activates or deactivates the console. */
void zori_console_active_(struct zori_console * self, int active) {
  if(!self) return;
  zori_widget_active_(&self->widget, active);
  zori_widget_visible_(&self->widget, active);
}

/** Returns nonzero if console is active zero if not. */
int zori_console_active(struct zori_console * self) {
  if(!self) return 0;
  return zori_widget_active(&self->widget);
}

/** scrolls the console 1 step in the given direction. */
int zori_console_scroll(struct zori_console * self, int direction) {
  if((!self) || (!direction)) return FALSE;
  if(direction < 0) self->start--;
  if(direction > 0) self->start++;
  /* Clamp start between 0 and size of list. */
  self->start = bad_clampi(self->start, 0, ustrlist_size(&self->text));
  return ZORI_HANDLE_OK;
}



/* Key input event handler for console. */
int zori_console_handle_keychar(union zori_event * zevent) { 
  struct zori_console * self  = zori_widget_console(zori_event_widget(zevent));
  zori_system_event * event   = zori_event_system_event(zevent);
  int ch = event->keyboard.unichar;
  int kc = event->keyboard.keycode;
  switch(kc) {
    // ignore the start-console key
    case ALLEGRO_KEY_F1:
    case ALLEGRO_KEY_F3:
      return ZORI_HANDLE_OK;
    case ALLEGRO_KEY_PGUP: return zori_console_scroll(self, 1);
    case ALLEGRO_KEY_PGDN: return zori_console_scroll(self, -1);
    case ALLEGRO_KEY_BACKSPACE:
      // remove last character typed.
      ustr_remove_chr(self->input, ustr_offset(self->input, -1));
      return ZORI_HANDLE_OK;
    break;    
    case ALLEGRO_KEY_ENTER: {
      const char * command = ustr_c(self->input);
      // execute command
      if(zori_console_docommand(self, command)) { 
        zori_console_puts(self, "Error in running comand");
        zori_console_puts(self, command);
      }
      ustr_truncate(self->input, 0);
      // empty string by truncating it
      return ZORI_HANDLE_OK;
      }
    default:
    break;
  }
  
  ustr_appendch(self->input, ch);
  return ZORI_HANDLE_OK;
}


/* Key down event handler for console. */
int zori_console_handle_keydown(union zori_event * zevent) { 
  struct zori_console * self  = zori_widget_console(zori_event_widget(zevent));
  zori_system_event * event   = zori_event_system_event(zevent);
  int ch = event->keyboard.unichar;
  int kc = event->keyboard.keycode;
  switch(kc) {
    case ALLEGRO_KEY_F1:  
    case ALLEGRO_KEY_F3:
      zori_console_active_(self, false); 
      /* disable console if F1 is pressed. 
       * Note: this shouldnever happen if react is set up well.
       */ 
      return ZORI_HANDLE_OK;
    default:
    break;
  }
  return ZORI_HANDLE_IGNORE;
}

/* Mouse axe event handler for console */
int zori_console_handle_mouseaxes(union zori_event * zevent) { 
  struct zori_console * self  = zori_widget_console(zori_event_widget(zevent));
  zori_system_event * event   = zori_event_system_event(zevent);
  int z                       = event->mouse.dz;
  // only capture mouse scroll wheel...
  if(z == 0) return ZORI_HANDLE_IGNORE;
  if(z < 0) return zori_console_scroll(self, -1);
  if(z > 0) return zori_console_scroll(self, +1);
  return ZORI_HANDLE_OK;
}

int zori_console_handle_ignore(union zori_event * zevent) { 
  return ZORI_HANDLE_IGNORE;
}


static struct zori_handler zori_console_actions[] = {  
  { ZORI_SYSTEM_EVENT_KEY_DOWN  , zori_console_handle_keydown   , NULL }, 
  { ZORI_SYSTEM_EVENT_KEY_UP    , zori_console_handle_ignore    , NULL },
  { ZORI_SYSTEM_EVENT_KEY_CHAR  , zori_console_handle_keychar   , NULL },
  { ZORI_SYSTEM_EVENT_MOUSE_AXES, zori_console_handle_mouseaxes , NULL },
  { ZORI_EVENT_DRAW             , zori_console_draw             , NULL },
  { ZORI_EVENT_DONE             , zori_console_handle_ignore    , NULL },
  { ZORI_EVENT_FREE             , zori_console_handle_ignore    , NULL },
  { -1, NULL, NULL }
};


/** Let the console handle allegro events. Returns 0 if event was consumed,
positive if not, and negative on error. */

int zori_console_handle(struct zori_widget * widget, zori_system_event * sevent) { 
  union zori_event zevent;
  if (!widget) return ZORI_HANDLE_ERROR;
  if (!zori_widget_active(widget)) return ZORI_HANDLE_IGNORE;
  return zori_widget_raise_system_event(widget, sevent);
}


/** Cleans up a console. */
int zori_console_done(struct zori_widget * widget) {
  struct zori_console * self = zori_widget_console(widget);
  if(!self) return ZORI_HANDLE_IGNORE;
  free(self->buf);
  self->buf     = NULL;
  ustr_free(self->input);
  self->input   = NULL;
  ustrlist_done(&self->text);
  return ZORI_HANDLE_OK;
}


/** Deallocates a console. */
int zori_console_free(struct zori_widget * widget) {
  struct zori_console * self = zori_widget_console(widget);
  zori_console_done(&self->widget);
  free(self);
  return ZORI_HANDLE_OK;
}

/** Allocates a console. */
struct zori_console * zori_console_alloc() {
  return calloc(1 , sizeof(struct zori_console));
}

/* Amount of lines of display the console hat o keep track of. */
#define ZORI_CONSOLE_MAX 200

/** Initializes a console. */
struct zori_console * zori_console_initall(struct zori_console * self, int id, zori_rebox * bounds, struct zori_style * style) {
  if(!self) return NULL;
935
  if(!zori_widget_initall(&self->widget, id, &the_zori_root->widget, bounds, style, 7, zori_console_actions)) { 
936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966
    return NULL;
  }
  ustrlist_init(&self->text);
  // ustrlist_shiftcstr(&self->text, "empty line");
  zori_widget_active_(&self->widget, FALSE);
  self->count = 0;
  // max MUST be at least 2, 3 to see anything...
  self->max   = ZORI_CONSOLE_MAX;
  self->start = 0;
  self->charw = 80; 
  self->buf   = calloc(self->charw + 1, 1);
   // one extra for NULL at end . 
  if (!self->buf) { zori_console_done(&self->widget); return NULL; }
  self->input = ustr_new("");
  self->cursor= 0;
  if (!self->input) { zori_console_done(&self->widget); return NULL; }
  self->command      = NULL;
  self->command_data = NULL;
  return self;
}

/** Initializes a new console. */
struct zori_console * zori_console_new(int id, zori_rebox * bounds, struct zori_style * style) {
  struct zori_console * self = zori_console_alloc();
  if(!zori_console_initall(self, id, bounds, style)) {
    zori_console_free(&self->widget);
    return NULL;
  }
  return self;
}

967 968 969 970 971 972