zori.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032
  1. #include "zori.h"
  2. #include "miao.h"
  3. #include <allegro5/allegro_color.h>
  4. #include "str.h"
  5. #include "draw.h"
  6. /*
  7. * Pardon the pun name, but Zori is the submodule that handles the user
  8. * interface and the menus.
  9. */
  10. /* registry functionality */
  11. /** Compare registry entries. */
  12. int zori_registry_entry_compare(const void * v1, const void * v2) {
  13. const struct zori_registry_entry * entry1 = v1;
  14. const struct zori_registry_entry * entry2 = v2;
  15. return (entry2->id - entry1->id);
  16. }
  17. /** Initialize the registry. */
  18. zori_id zori_registry_init(struct zori_registry * registry) {
  19. miao_init(registry);
  20. return ZORI_ID_OK;
  21. }
  22. /** Add an entry to the registry. */
  23. zori_id
  24. zori_registry_add(struct zori_registry * registry, zori_id id,
  25. struct zori_widget * widget) {
  26. struct zori_registry_entry entry = { id, widget };
  27. if (miao_push(registry, entry)) {
  28. miao_qsort(registry, zori_registry_entry_compare);
  29. return ZORI_ID_OK;
  30. }
  31. return ZORI_ID_ENOMEM;
  32. }
  33. /** Look up an entry in the registry. */
  34. struct zori_registry_entry *
  35. zori_registry_lookup_entry(struct zori_registry * registry, zori_id id) {
  36. struct zori_registry_entry key = { id, NULL };
  37. return miao_bsearch(registry, zori_registry_entry_compare, &key);
  38. }
  39. /** Look up a widget in the registry. */
  40. struct zori_widget *
  41. zori_registry_lookup(struct zori_registry * registry, zori_id id) {
  42. struct zori_widget * result = NULL;
  43. struct zori_registry_entry * entry = NULL;
  44. entry = zori_registry_lookup_entry(registry, id);
  45. if (entry) {
  46. result = entry->widget;
  47. }
  48. return result;
  49. }
  50. /** Remove an entry from the registry. */
  51. zori_id
  52. zori_registry_remove(struct zori_registry * registry, zori_id id) {
  53. struct zori_registry_entry * entry = NULL;
  54. entry = zori_registry_lookup_entry(registry, id);
  55. if (entry) {
  56. miao_delete_entry(registry, entry);
  57. }
  58. return ZORI_ID_OK;
  59. }
  60. /** Handler functionality */
  61. int zori_handler_compare(const void * v1, const void * v2) {
  62. const struct zori_handler * h1 = v1;
  63. const struct zori_handler * h2 = v2;
  64. if (h1->type < h2->type) return -1;
  65. if (h1->type > h2->type) return 1;
  66. return 0;
  67. }
  68. struct zori_handler * zori_handlers_add(struct zori_handlers * me, zori_event_type type, zori_handler_func * handler, void * data) {
  69. struct zori_handler * result = NULL;
  70. result = miao_push_ptr(me);
  71. if (result) {
  72. result->type = type;
  73. result->handler = handler;
  74. result->data = data;
  75. }
  76. miao_qsort(me, zori_handler_compare);
  77. return result;
  78. }
  79. void zori_handlers_done(struct zori_handlers * me) {
  80. miao_done(me);
  81. }
  82. void zori_handlers_init(struct zori_handlers * me) {
  83. miao_init(me);
  84. }
  85. struct zori_handler * zori_handlers_search(struct zori_handlers * me, zori_event_type type) {
  86. struct zori_handler * result;
  87. struct zori_handler key;
  88. key.type = type;
  89. key.handler = NULL;
  90. key.data = NULL;
  91. if (!me) return NULL;
  92. result = miao_bsearch(me, zori_handler_compare, &key);
  93. return result;
  94. }
  95. int zori_handlers_handle(struct zori_handlers * me, union zori_event * event) {
  96. struct zori_handler * handler = zori_handlers_search(me, event->type);
  97. if (!handler) return ZORI_HANDLE_IGNORE;
  98. event->any.data = handler->data;
  99. return handler->handler(event);
  100. }
  101. /** Returns whether or not a widget will even handle an event in the first place.
  102. * For example, a hidden widget won't draw, and a disabled widget won't
  103. * accept any system events, and a NULL event doesn't accept events at all.. */
  104. int zori_widget_accepts_event(struct zori_widget * widget, union zori_event * event) {
  105. if (!widget) return 0;
  106. switch(event->any.type) {
  107. case ZORI_EVENT_DRAW:
  108. return zori_widget_visible(widget);
  109. default:
  110. return zori_widget_active(widget);
  111. }
  112. }
  113. /** Raises an event on the widget itself only. */
  114. int zori_widget_self_raise_event(struct zori_widget * widget,
  115. union zori_event * event) {
  116. enum zori_handle_result result;
  117. if (zori_widget_accepts_event(widget, event)) {
  118. event->any.widget = widget;
  119. return zori_handlers_handle(&widget->handlers, event);
  120. }
  121. /* If the event is not accepted, it is passed on to the child widgets. */
  122. return ZORI_HANDLE_PASS;
  123. }
  124. /* Raises an event on the widget, and if necessary, propagates it on to it's
  125. * children automatically and recursively. */
  126. int
  127. zori_widget_raise_event(struct zori_widget * widget, union zori_event * event) {
  128. enum zori_handle_result result =
  129. zori_widget_self_raise_event(widget, event);
  130. if (zori_propagate_event_p(result)) {
  131. enum zori_handle_result sub;
  132. size_t index;
  133. for (index = 0; index < miao_size(&widget->children); index++) {
  134. struct zori_widget * child = miao_unsafe_get(&widget->children, index);
  135. event->any.widget = child;
  136. sub = zori_widget_raise_event(child, event);
  137. if (sub == ZORI_HANDLE_DONE) {
  138. result = ZORI_HANDLE_DONE;
  139. break;
  140. }
  141. }
  142. }
  143. return result;
  144. }
  145. int zori_widget_raise_system_event
  146. (struct zori_widget * widget, zori_system_event * sysev) {
  147. union zori_event event;
  148. event.sys.any.type = sysev->type;
  149. event.sys.any.widget = widget;
  150. event.sys.ev = sysev;
  151. return zori_widget_raise_event(widget, &event);
  152. }
  153. int zori_widget_raise_draw_event(struct zori_widget * widget) {
  154. union zori_event event;
  155. event.draw.any.type = ZORI_EVENT_DRAW;
  156. event.draw.any.widget = widget;
  157. return zori_widget_raise_event(widget, &event);
  158. }
  159. int zori_widget_raise_overdraw_event(struct zori_widget * widget) {
  160. union zori_event event;
  161. event.draw.any.type = ZORI_EVENT_OVERDRAW;
  162. event.draw.any.widget = widget;
  163. return zori_widget_raise_event(widget, &event);
  164. }
  165. int zori_widget_raise_done_event(struct zori_widget * widget) {
  166. union zori_event event;
  167. event.done.any.type = ZORI_EVENT_DONE;
  168. event.done.any.widget = widget;
  169. return zori_widget_raise_event(widget, &event);
  170. }
  171. int zori_widget_raise_free_event(struct zori_widget * widget) {
  172. union zori_event event;
  173. event.free.any.type = ZORI_EVENT_FREE;
  174. event.free.any.widget = widget;
  175. return zori_widget_raise_event(widget, &event);
  176. }
  177. int zori_widget_raise_update_event
  178. (struct zori_widget * widget, double dt) {
  179. union zori_event event;
  180. event.update.any.type = ZORI_EVENT_DONE;
  181. event.update.any.widget = widget;
  182. event.update.dt = dt;
  183. return zori_widget_raise_event(widget, &event);
  184. }
  185. int zori_widget_raise_action_event
  186. (struct zori_widget * widget) {
  187. union zori_event event;
  188. event.action.any.type = ZORI_EVENT_ACTION;
  189. event.action.any.widget = widget;
  190. return zori_widget_raise_event(widget, &event);
  191. }
  192. static struct zori_root * the_zori_root = NULL;
  193. static struct zori_style * the_default_style = NULL;
  194. static struct zori_registry * the_zori_registry = NULL;
  195. struct zori_root * zori_get_root(void) {
  196. return the_zori_root;
  197. }
  198. int zori_widget_compare(const void * v1, const void * v2) {
  199. const struct zori_widget * w1 = v1, * w2 = v2;
  200. return (w2->id - w1->id);
  201. }
  202. struct zori_widget * zori_get_widget(zori_id id) {
  203. if (!the_zori_registry) return NULL;
  204. return zori_registry_lookup(the_zori_registry, id);
  205. }
  206. zori_id zori_get_unused_id(void) {
  207. zori_id id = -1;
  208. struct zori_widget * found;
  209. do {
  210. id++;
  211. if (id == INT_MAX) { return ZORI_ID_ERROR; }
  212. found = zori_get_widget(id);
  213. } while(found);
  214. return id;
  215. }
  216. struct zori_handler * zori_widget_add_handler
  217. (struct zori_widget * widget, zori_event_type type, zori_handler_func * handler, void * data) {
  218. if ((!widget) || (!handler)) return NULL;
  219. return zori_handlers_add(&widget->handlers, type, handler, data);
  220. }
  221. struct zori_handler * zori_widget_add_handlers
  222. (struct zori_widget * widget, struct zori_handler * handlers, size_t amount) {
  223. size_t i;
  224. for (i = 0; i < amount; i++) {
  225. struct zori_handler * handler = handlers + i;
  226. if (!zori_widget_add_handler(widget, handler->type, handler->handler, handler->data)) return NULL;
  227. }
  228. return handlers + amount - 1;
  229. }
  230. zori_id zori_start(struct zori_style * default_style) {
  231. if (the_zori_root) return ZORI_ID_OK;
  232. the_zori_root = calloc(1, sizeof(*the_zori_root));
  233. if (!the_zori_root) return ZORI_ID_ENOMEM;
  234. the_default_style = calloc(1, sizeof(*the_default_style));
  235. if (!the_default_style) return ZORI_ID_ENOMEM;
  236. the_zori_registry = calloc(1, sizeof(*the_zori_registry));
  237. if (!the_zori_registry) return ZORI_ID_ENOMEM;
  238. the_default_style->text.font = al_create_builtin_font();
  239. the_default_style->text.color = al_color_name("white");
  240. the_default_style->border.color = al_color_name("white");
  241. the_default_style->back.color = al_color_name("green");
  242. the_default_style->fore.color = al_color_name("white");
  243. if (default_style) {
  244. the_zori_root->widget.style = *default_style;
  245. } else {
  246. the_zori_root->widget.style = *the_default_style;
  247. }
  248. the_zori_root->widget.id = 0;
  249. zori_registry_add(the_zori_registry, the_zori_root->widget.id, &the_zori_root->widget);
  250. return the_zori_root->widget.id;
  251. }
  252. void zori_widget_free(struct zori_widget * widget);
  253. struct zori_widget * zori_widget_done(struct zori_widget * widget) {
  254. size_t index;
  255. struct zori_widget * aid, * next;
  256. if (!widget) return widget;
  257. for (index = 0; index < miao_size(&widget->children); index ++) {
  258. struct zori_widget * child = miao_unsafe_get(&widget->children, index);
  259. zori_widget_free(child);
  260. }
  261. miao_done(&widget->handlers);
  262. miao_done(&widget->children);
  263. return widget;
  264. }
  265. void zori_widget_free(struct zori_widget * widget) {
  266. if (widget) {
  267. zori_registry_remove(the_zori_registry, widget->id);
  268. }
  269. zori_widget_done(widget);
  270. free(widget);
  271. }
  272. struct zori_widget *
  273. zori_widget_add_child(struct zori_widget * parent, struct zori_widget * child) {
  274. if (parent) {
  275. miao_push((&parent->children), child);
  276. child->parent = child;
  277. }
  278. return child;
  279. }
  280. struct zori_widget * zori_widget_init
  281. ( struct zori_widget * widget,
  282. zori_id id,
  283. struct zori_widget * parent,
  284. zori_rebox * box, struct zori_style * style) {
  285. if (!widget) return NULL;
  286. miao_init(&widget->children);
  287. miao_init(&widget->handlers);
  288. widget->z = 0;
  289. widget->flags = 0;
  290. widget->id = ( (id < 0) ? widget->id : id);
  291. if (style) {
  292. widget->style = *style;
  293. if (!widget->style.text.font) {
  294. widget->style.text.font = the_default_style->text.font;
  295. }
  296. } else {
  297. widget->style = *the_default_style;
  298. }
  299. if (box) {
  300. widget->box = *box;
  301. } else if (parent) {
  302. widget->box = parent->box;
  303. } else {
  304. widget->box = rebox_make(0, 0, ZORI_WIDGET_DEFAULT_W, ZORI_WIDGET_DEFAULT_H);
  305. }
  306. zori_widget_add_child(parent, widget);
  307. zori_registry_add(the_zori_registry, widget->id, widget);
  308. return widget;
  309. }
  310. /* Shut down Zori and destroys all widgets. Return 0 on succes or
  311. * negative on error.
  312. */
  313. zori_id zori_shutdown() {
  314. assert((void *)(&the_zori_root->widget) == (void *)the_zori_root);
  315. zori_widget_free(&the_zori_root->widget);
  316. the_zori_root = NULL;
  317. free(the_default_style);
  318. the_default_style = NULL;
  319. /* clean up registry last so zori_widget fre can unregister it's widgets.*/
  320. miao_done(the_zori_registry);
  321. free(the_zori_registry);
  322. the_zori_registry = NULL;
  323. return ZORI_ID_OK;
  324. }
  325. struct zori_widget *
  326. zori_widget_initall(struct zori_widget * widget, int id,
  327. struct zori_widget * parent, zori_rebox * box, struct zori_style * style,
  328. size_t amount, struct zori_handler * handlers) {
  329. if (zori_widget_init(widget, id, parent, box, style)) {
  330. zori_widget_add_handlers(widget, handlers, amount);
  331. }
  332. return widget;
  333. }
  334. /* Creates a new screen widget. Normally this should be the first widget
  335. * you create after zori_start. */
  336. zori_id zori_new_screen(zori_display * display);
  337. /* Creates a new page widget on the given screen. The page is not
  338. * made the active page, unless if it is the first one created. */
  339. zori_id zori_new_page(zori_id screen);
  340. /* Activates the page on it's display. All other pages are dectivated and
  341. * hidden. */
  342. zori_id zori_activate_page(zori_id page);
  343. /* Creates a new generic widget on the given screen with the given
  344. * dimensions. */
  345. zori_id zori_new(zori_id screen, zori_rebox * box);
  346. /* Sets the flags of a widget. */
  347. zori_id zori_set_flags(zori_id widget, enum zori_flag flags);
  348. /* Sets the whole style of a widget. */
  349. zori_id zori_set_style(zori_id id, struct zori_style * style);
  350. /* Sets the background color of the widget. */
  351. zori_id zori_set_background_color(zori_id id, zori_color color);
  352. /* Sets the foreground color of the widget. */
  353. zori_id zori_set_foreground_color(zori_id id, zori_color color);
  354. /* Creates a new frame widget. */
  355. zori_id zori_new_frame_widget(zori_id parent, zori_rebox box);
  356. /* Creates a new (vertical) menu widget. */
  357. zori_id zori_new_menu_widget(zori_id parent, zori_rebox box, char * text);
  358. /* Creates a new button widget. */
  359. zori_id zori_new_button_widget(zori_id parent, zori_rebox box, char * text);
  360. /* Creates a new conversation widget. */
  361. zori_id zori_new_conversation_widget(zori_id parent, zori_rebox box, char * text);
  362. /* Lets the widget handle an event and bubbles it on to it's children
  363. * recursively depending on the need to do that. */
  364. void zori_widget_handle_event(struct zori_widget * widget, union zori_event * event) {
  365. zori_widget_raise_event(widget, event);
  366. }
  367. /* Draws the whole UI and all visible parts. */
  368. void zori_draw_all(void) {
  369. zori_widget_raise_draw_event(&the_zori_root->widget);
  370. zori_widget_raise_overdraw_event(&the_zori_root->widget);
  371. }
  372. int zori_widget_visible(struct zori_widget * widget) {
  373. return widget && ((widget->flags & ZORI_FLAG_HIDDEN) != ZORI_FLAG_HIDDEN);
  374. }
  375. int zori_widget_active(struct zori_widget * widget) {
  376. return widget && ((widget->flags & ZORI_FLAG_DISABLED) != ZORI_FLAG_DISABLED);
  377. }
  378. int zori_widget_active_(struct zori_widget * widget, int set) {
  379. if (set) {
  380. widget->flags = widget->flags & (~ZORI_FLAG_DISABLED);
  381. } else {
  382. widget->flags = widget->flags | ZORI_FLAG_DISABLED;
  383. }
  384. return zori_widget_active(widget);
  385. }
  386. int zori_widget_visible_(struct zori_widget * widget, int set) {
  387. if (set) {
  388. widget->flags = widget->flags & (~ZORI_FLAG_HIDDEN);
  389. } else {
  390. widget->flags = widget->flags | ZORI_FLAG_HIDDEN;
  391. }
  392. return zori_widget_active(widget);
  393. }
  394. /* Updates the state of the UI. Pass in the time passed since last update. */
  395. void zori_update(double dt)
  396. {
  397. zori_widget_raise_update_event(&the_zori_root->widget, dt);
  398. }
  399. /* Registers an event handler for a widget. */
  400. zori_id zori_register(zori_id id, zori_event_type type, zori_handler_func handler, void * extra);
  401. zori_font * zori_widget_font(struct zori_widget * widget) {
  402. return widget->style.text.font;
  403. }
  404. zori_color zori_widget_forecolor(struct zori_widget * widget) {
  405. return widget->style.text.color;
  406. }
  407. zori_color zori_widget_backcolor(struct zori_widget * widget) {
  408. return widget->style.back.color;
  409. }
  410. int zori_widget_h(struct zori_widget * widget) {
  411. return widget->box.size.y;
  412. }
  413. int zori_widget_w(struct zori_widget * widget) {
  414. return widget->box.size.x;
  415. }
  416. int zori_widget_x(struct zori_widget * widget) {
  417. return widget->box.at.x;
  418. }
  419. int zori_widget_y(struct zori_widget * widget) {
  420. return widget->box.at.y;
  421. }
  422. /* Helper struct that keeps track of the BYTE positions within
  423. a c string or USTR where a line or word starts or ends with a given maxwidth. */
  424. struct zori_textinfo {
  425. int from_char;
  426. int start_char;
  427. int stop_char;
  428. int maxwidth;
  429. };
  430. /* Creates a temporary ustr as per al_ref_ustr but with
  431. start and stop as code positions, not byte positions. */
  432. static const USTR *
  433. ustrinfo_newref(
  434. USTR_INFO * uinfo, const USTR * ustr, int start, int stop) {
  435. return ustr_refustr(uinfo, ustr,
  436. ustr_offset(ustr, start),
  437. ustr_offset(ustr, stop)
  438. );
  439. }
  440. /* Creates a temporary ustr that refers ustr but respecds the bounds of the
  441. textinfo (start_char and enc_char) */
  442. const USTR * zori_textinfo_refustr(struct zori_textinfo * self,
  443. USTR_INFO * uinfo,
  444. const USTR * ustr) {
  445. return ustrinfo_newref(uinfo, ustr, self->start_char, self->stop_char);
  446. }
  447. struct zori_textinfo *
  448. zori_textinfo_wordfromtext(struct zori_textinfo * self, USTR * ustr, zori_font * font) {
  449. int found;
  450. int start_pos;
  451. int end_pos;
  452. int now_char;
  453. int end_char;
  454. int len;
  455. int ch;
  456. if(!self) return NULL;
  457. now_char = self->from_char;
  458. self->start_char = now_char;
  459. ch = ustr_getnext(ustr, &now_char);
  460. while (ch > 0) {
  461. switch(ch) {
  462. case ' ': /* Found a space, here the word ends, include the space. */
  463. self->stop_char = now_char;
  464. return self;
  465. case '\n': /* A newline ends a word, include the newline. */
  466. self->stop_char = now_char;
  467. return self;
  468. default: /* Other characters mean the word is not finished yet. */
  469. break;
  470. }
  471. /* XXX: Should handle the case for languages that use no spaces,
  472. * by checking with al_get_ustr_width but it's not a pressing matter yet.
  473. */
  474. ch = ustr_getnext(ustr, &now_char);
  475. }
  476. // no word found, just set end here and be done.
  477. self->stop_char = now_char;
  478. /* return nULL to signify end */
  479. return NULL;
  480. }
  481. /** Prints a ustring, since puts or printf print too much some
  482. times for a refstring.*/
  483. static int ustr_print(USTR * word) {
  484. size_t index;
  485. for(index = 0; index < ustr_length(word) ; index++) {
  486. putchar(ustr_get(word, index));
  487. }
  488. return index;
  489. }
  490. /** Gets the positions of the next line of text fort he given Unicode string
  491. and store them in the given info. If the info's from is set to 0 or less,
  492. the first line is assumed, otherwise, the line will be started from "from".
  493. Uses the given font to determine the width of the text as it is built.
  494. */
  495. struct zori_textinfo *
  496. zori_textinfo_linefromtext(struct zori_textinfo * self, USTR * ustr, zori_font * font) {
  497. struct zori_textinfo wordinfo;
  498. USTR_INFO lineuinfo;
  499. const USTR * line;
  500. USTR_INFO worduinfo = { 0, 0, 0};
  501. const USTR * word;
  502. int ch;
  503. int index;
  504. int width;
  505. int last_stop;
  506. self->start_char = self->from_char;
  507. wordinfo.from_char = self->from_char;
  508. while(zori_textinfo_wordfromtext(&wordinfo, ustr, font)) {
  509. word = zori_textinfo_refustr(&wordinfo, &worduinfo, ustr);
  510. line = ustrinfo_newref(&lineuinfo, ustr, self->start_char, wordinfo.stop_char);
  511. width = al_get_ustr_width(font, line);
  512. if (width > self->maxwidth) {
  513. /* XXX: handle case of text overflow by bluntly retuning the word as is.
  514. Should split single word based on length too.
  515. There is overflow if this is still the first word as see from wordinfo_start_char.
  516. */
  517. if (wordinfo.start_char == self->start_char) {
  518. self->stop_char = wordinfo.stop_char;
  519. } else {
  520. self->stop_char = wordinfo.start_char;
  521. }
  522. return self;
  523. }
  524. // If we get here, the word way still end on a newline character
  525. // check this case. XXX: It works like this because
  526. // stop_char is a bit wonky... it points at the first character of the
  527. // next word in this case...
  528. ch = ustr_get(ustr, wordinfo.stop_char - 1);
  529. if (ch == '\n') {
  530. self->start_char = self->from_char;
  531. self->stop_char = wordinfo.stop_char - 1;
  532. return self;
  533. }
  534. wordinfo.from_char = wordinfo.stop_char;
  535. }
  536. /* if we get here, the whole string fits. */
  537. self->start_char = self->from_char;
  538. self->stop_char = wordinfo.stop_char;
  539. /* Return NULL to tell caller text has been completely split up. */
  540. return NULL;
  541. }
  542. #define ZORI_WIDGET_BORDER 3
  543. /** Draws a rounded frame as background for a widget. */
  544. void zori_widget_drawroundframe(struct zori_widget * self) {
  545. if(!self) return;
  546. draw_roundframe(zori_widget_x(self), zori_widget_y(self),
  547. zori_widget_w(self), zori_widget_h(self),
  548. ZORI_WIDGET_BORDER,
  549. zori_widget_forecolor(self), zori_widget_backcolor(self));
  550. }
  551. /** Skips the text info to the next word or line of text. Must be called
  552. when looping over zori_textinfo_linefromtext. */
  553. struct zori_textinfo * zori_textinfo_next(struct zori_textinfo * self) {
  554. if(!self) return NULL;
  555. self->from_char = self->stop_char + 1;
  556. self->start_char = self->from_char;
  557. return self;
  558. }
  559. /* Converts a widget to a console. Only works if the pointer is wrapped correctly,
  560. by a console. */
  561. struct zori_console * zori_widget_console(struct zori_widget * widget) {
  562. if (!widget) return NULL;
  563. return ZORI_CONTAINER_OF(widget, struct zori_console, widget);
  564. }
  565. /** Sets the console's command function and data. */
  566. void zori_console_command_(struct zori_console * self, zori_console_command * command, void * data) {
  567. self->command = command;
  568. self->command_data = data;
  569. }
  570. /** Let the console perform a command if possible. returns nonzero on error,
  571. zero if OK. */
  572. int zori_console_docommand(struct zori_console * self, const char * text) {
  573. if(!self) return -1;
  574. if(!self->command) return -2;
  575. return self->command(&self->widget, text, self->command_data);
  576. }
  577. /** Adds a line of text to the console. */
  578. int zori_console_addstr(struct zori_console * self, const char * str) {
  579. if(!self) return -1;
  580. if(!ustrlist_shiftcstr(&self->text, str)) {
  581. return -3;
  582. }
  583. while(ustrlist_size(&self->text) >= self->max) { // remove last node(s)
  584. ustrlist_droplast(&self->text);
  585. }
  586. return ustrlist_size(&self->text);
  587. }
  588. /** Adds a line of text to the console. */
  589. int zori_console_addustr(struct zori_console * self, const USTR * ustr) {
  590. if(!self) return -1;
  591. if(!ustrlist_shiftustr(&self->text, ustr)) {
  592. return -3;
  593. }
  594. while(ustrlist_size(&self->text) >= self->max) { // remove last node(s)
  595. ustrlist_droplast(&self->text);
  596. }
  597. return ustrlist_size(&self->text);
  598. }
  599. /** Puts a string on the console .*/
  600. int zori_console_puts(struct zori_console * self, const char * str) {
  601. int index;
  602. int size = strlen(str);
  603. int leftsize = size;
  604. int lines = 0;
  605. USTR_INFO uinfo;
  606. struct zori_textinfo info = { 0, 0, 0, 0};
  607. info.maxwidth = zori_widget_w(&self->widget) - 10;
  608. USTR * ustr;
  609. const USTR * uline;
  610. ustr = ustr_new(str);
  611. while(zori_textinfo_linefromtext(&info, ustr, self->widget.style.text.font)) {
  612. uline = zori_textinfo_refustr(&info, &uinfo, ustr);
  613. zori_console_addustr(self, uline);
  614. // don't forget to skip to next line!!!
  615. zori_textinfo_next(&info);
  616. }
  617. uline = zori_textinfo_refustr(&info, &uinfo, ustr);
  618. zori_console_addustr(self, uline);
  619. ustr_free(ustr);
  620. return lines;
  621. }
  622. #define BBCONSOLE_VPRINTF_MAX 1024
  623. /** Prints a formatted string on the console, truncaded to 1024 characters. */
  624. int zori_console_vprintf(struct zori_console * self, const char * format, va_list args) {
  625. char buffer[BBCONSOLE_VPRINTF_MAX] = { '\0' };
  626. vsnprintf(buffer, BBCONSOLE_VPRINTF_MAX, format, args);
  627. return zori_console_puts(self, buffer);
  628. }
  629. /** Prints a formatted string on the console, truncaded to 1024 characters. */
  630. int zori_console_printf(struct zori_console * self, const char * format, ...) {
  631. int result;
  632. va_list args;
  633. va_start(args, format);
  634. result = zori_console_vprintf(self, format, args);
  635. va_end(args);
  636. return result;
  637. }
  638. /** Draws a console. */
  639. int zori_console_draw(union zori_event * zevent) {
  640. struct zori_console * self ;
  641. zori_font * font ;
  642. zori_color color ;
  643. USTRListNode * now;
  644. int high, linehigh, index, x, y, skip;
  645. int linew;
  646. struct zori_widget * widget = zevent->any.widget;
  647. if (!zori_widget_visible(widget)) return ZORI_HANDLE_IGNORE;
  648. self = zori_widget_console(widget);
  649. font = zori_widget_font(widget);
  650. color = zori_widget_forecolor(widget);
  651. zori_widget_drawroundframe(widget);
  652. high = zori_widget_h(widget) - 10;
  653. x = zori_widget_x(widget) + 5;
  654. y = zori_widget_y(widget) - 5;
  655. linehigh = zori_font_lineheight(font);
  656. /* now = ustrlist_head(&self->text); */
  657. /* skip start lines (to allow scrolling backwards) */
  658. now = ustrlist_skipnode(&self->text, self->start);
  659. for (index = high-(linehigh*2); index > 0; index -= linehigh) {
  660. USTR * textstr;
  661. if(!now) break;
  662. textstr = ustrlistnode_ustr(now);
  663. if(textstr) {
  664. zori_font_drawstr(font, color, x, y + index, 0, textstr);
  665. }
  666. now = ustrlistnode_next(now);
  667. }
  668. // draw input string
  669. if (self->input) {
  670. zori_font_drawstr(font, color, x, y + high - linehigh, 0, self->input);
  671. }
  672. // Draw cursor
  673. linew = al_get_ustr_width(font, self->input);
  674. al_draw_line(x + linew, y + high - linehigh, x + linew, y + high, color, 1);
  675. // draw start for debugging
  676. al_draw_textf(font, color, x, y, 0, "start: %d, size: %d", self->start,
  677. ustrlist_size(&self->text));
  678. return ZORI_HANDLE_PASS;
  679. }
  680. /** Activates or deactivates the console. */
  681. void zori_console_active_(struct zori_console * self, int active) {
  682. if(!self) return;
  683. zori_widget_active_(&self->widget, active);
  684. zori_widget_visible_(&self->widget, active);
  685. }
  686. /** Returns nonzero if console is active zero if not. */
  687. int zori_console_active(struct zori_console * self) {
  688. if(!self) return 0;
  689. return zori_widget_active(&self->widget);
  690. }
  691. /** scrolls the console 1 step in the given direction. */
  692. int zori_console_scroll(struct zori_console * self, int direction) {
  693. if((!self) || (!direction)) return FALSE;
  694. if(direction < 0) self->start--;
  695. if(direction > 0) self->start++;
  696. /* Clamp start between 0 and size of list. */
  697. self->start = bad_clampi(self->start, 0, ustrlist_size(&self->text));
  698. return ZORI_HANDLE_DONE;
  699. }
  700. /* Key input event handler for console. */
  701. int zori_console_handle_keychar(union zori_event * zevent) {
  702. struct zori_console * self = zori_widget_console(zori_event_widget(zevent));
  703. zori_system_event * event = zori_event_system_event(zevent);
  704. int ch = event->keyboard.unichar;
  705. int kc = event->keyboard.keycode;
  706. switch(kc) {
  707. // ignore the start-console key
  708. case ALLEGRO_KEY_F1:
  709. case ALLEGRO_KEY_F3:
  710. return ZORI_HANDLE_DONE;
  711. case ALLEGRO_KEY_PGUP: return zori_console_scroll(self, 1);
  712. case ALLEGRO_KEY_PGDN: return zori_console_scroll(self, -1);
  713. case ALLEGRO_KEY_BACKSPACE:
  714. // remove last character typed.
  715. ustr_remove_chr(self->input, ustr_offset(self->input, -1));
  716. return ZORI_HANDLE_DONE;
  717. break;
  718. case ALLEGRO_KEY_ENTER: {
  719. const char * command = ustr_c(self->input);
  720. // execute command
  721. if(zori_console_docommand(self, command)) {
  722. zori_console_puts(self, "Error in running comand");
  723. zori_console_puts(self, command);
  724. }
  725. ustr_truncate(self->input, 0);
  726. // empty string by truncating it
  727. return ZORI_HANDLE_DONE;
  728. }
  729. default:
  730. break;
  731. }
  732. ustr_appendch(self->input, ch);
  733. return ZORI_HANDLE_DONE;
  734. }
  735. /* Key down event handler for console. */
  736. int zori_console_handle_keydown(union zori_event * zevent) {
  737. struct zori_console * self = zori_widget_console(zori_event_widget(zevent));
  738. zori_system_event * event = zori_event_system_event(zevent);
  739. int ch = event->keyboard.unichar;
  740. int kc = event->keyboard.keycode;
  741. switch(kc) {
  742. case ALLEGRO_KEY_F1:
  743. case ALLEGRO_KEY_F3:
  744. zori_console_active_(self, false);
  745. /* disable console if F1 is pressed.
  746. * Note: this shouldnever happen if react is set up well.
  747. */
  748. return ZORI_HANDLE_DONE;
  749. default:
  750. break;
  751. }
  752. return ZORI_HANDLE_IGNORE;
  753. }
  754. /* Mouse axe event handler for console */
  755. int zori_console_handle_mouseaxes(union zori_event * zevent) {
  756. struct zori_console * self = zori_widget_console(zori_event_widget(zevent));
  757. zori_system_event * event = zori_event_system_event(zevent);
  758. int z = event->mouse.dz;
  759. // only capture mouse scroll wheel...
  760. if(z == 0) return ZORI_HANDLE_IGNORE;
  761. if(z < 0) return zori_console_scroll(self, -1);
  762. if(z > 0) return zori_console_scroll(self, +1);
  763. return ZORI_HANDLE_DONE;
  764. }
  765. int zori_console_handle_ignore(union zori_event * zevent) {
  766. return ZORI_HANDLE_IGNORE;
  767. }
  768. static struct zori_handler zori_console_actions[] = {
  769. { ZORI_SYSTEM_EVENT_KEY_DOWN , zori_console_handle_keydown , NULL },
  770. { ZORI_SYSTEM_EVENT_KEY_UP , zori_console_handle_ignore , NULL },
  771. { ZORI_SYSTEM_EVENT_KEY_CHAR , zori_console_handle_keychar , NULL },
  772. { ZORI_SYSTEM_EVENT_MOUSE_AXES, zori_console_handle_mouseaxes , NULL },
  773. { ZORI_EVENT_DRAW , zori_console_draw , NULL },
  774. { ZORI_EVENT_DONE , zori_console_handle_ignore , NULL },
  775. { ZORI_EVENT_FREE , zori_console_handle_ignore , NULL },
  776. { -1, NULL, NULL }
  777. };
  778. /** Let the console handle allegro events. Returns 0 if event was consumed,
  779. positive if not, and negative on error. */
  780. int zori_console_handle(struct zori_widget * widget, zori_system_event * sevent) {
  781. union zori_event zevent;
  782. if (!widget) return ZORI_HANDLE_ERROR;
  783. if (!zori_widget_active(widget)) return ZORI_HANDLE_IGNORE;
  784. return zori_widget_raise_system_event(widget, sevent);
  785. }
  786. /** Cleans up a console. */
  787. int zori_console_done(struct zori_widget * widget) {
  788. struct zori_console * self = zori_widget_console(widget);
  789. if(!self) return ZORI_HANDLE_IGNORE;
  790. free(self->buf);
  791. self->buf = NULL;
  792. ustr_free(self->input);
  793. self->input = NULL;
  794. ustrlist_done(&self->text);
  795. return ZORI_HANDLE_DONE;
  796. }
  797. /** Deallocates a console. */
  798. int zori_console_free(struct zori_widget * widget) {
  799. struct zori_console * self = zori_widget_console(widget);
  800. zori_console_done(&self->widget);
  801. free(self);
  802. return ZORI_HANDLE_DONE;
  803. }
  804. /** Allocates a console. */
  805. struct zori_console * zori_console_alloc() {
  806. return calloc(1 , sizeof(struct zori_console));
  807. }
  808. /* Amount of lines of display the console hat o keep track of. */
  809. #define ZORI_CONSOLE_MAX 200
  810. /** Initializes a console. */
  811. struct zori_console * zori_console_initall(struct zori_console * self, int id, zori_rebox * bounds, struct zori_style * style) {
  812. if(!self) return NULL;
  813. if(!zori_widget_initall(&self->widget, id, &the_zori_root->widget, bounds, style, 7, zori_console_actions)) {
  814. return NULL;
  815. }
  816. ustrlist_init(&self->text);
  817. // ustrlist_shiftcstr(&self->text, "empty line");
  818. zori_widget_active_(&self->widget, FALSE);
  819. self->count = 0;
  820. // max MUST be at least 2, 3 to see anything...
  821. self->max = ZORI_CONSOLE_MAX;
  822. self->start = 0;
  823. self->charw = 80;
  824. self->buf = calloc(self->charw + 1, 1);
  825. // one extra for NULL at end .
  826. if (!self->buf) { zori_console_done(&self->widget); return NULL; }
  827. self->input = ustr_new("");
  828. self->cursor= 0;
  829. if (!self->input) { zori_console_done(&self->widget); return NULL; }
  830. self->command = NULL;
  831. self->command_data = NULL;
  832. return self;
  833. }
  834. /** Initializes a new console. */
  835. struct zori_console * zori_console_new(int id, zori_rebox * bounds, struct zori_style * style) {
  836. struct zori_console * self = zori_console_alloc();
  837. if(!zori_console_initall(self, id, bounds, style)) {
  838. zori_console_free(&self->widget);
  839. return NULL;
  840. }
  841. return self;
  842. }