|
@@ -1,5 +1,6 @@
|
|
|
|
|
|
#include "zori.h"
|
|
|
+#include "zori_console.h"
|
|
|
#include "miao.h"
|
|
|
#include <allegro5/allegro_color.h>
|
|
|
#include "str.h"
|
|
@@ -404,7 +405,7 @@ zori_id zori_shutdown() {
|
|
|
struct zori_widget *
|
|
|
zori_widget_initall(struct zori_widget * widget, int id,
|
|
|
struct zori_widget * parent, zori_rebox * box, struct zori_style * style,
|
|
|
-size_t amount, struct zori_handler * handlers) {
|
|
|
+struct zori_handler * handlers, size_t amount) {
|
|
|
if (zori_widget_init(widget, id, parent, box, style)) {
|
|
|
zori_widget_add_handlers(widget, handlers, amount);
|
|
|
}
|
|
@@ -507,6 +508,39 @@ int zori_widget_visible_(struct zori_widget * widget, int set) {
|
|
|
}
|
|
|
|
|
|
|
|
|
+int zori_widget_hover(struct zori_widget * widget) {
|
|
|
+ return widget && ((widget->flags & ZORI_FLAG_HOVERED) != ZORI_FLAG_HOVERED);
|
|
|
+}
|
|
|
+
|
|
|
+int zori_widget_hover_(struct zori_widget * widget, int set) {
|
|
|
+ if (set) {
|
|
|
+ widget->flags = widget->flags & (~ZORI_FLAG_HOVERED);
|
|
|
+ } else {
|
|
|
+ widget->flags = widget->flags | ZORI_FLAG_HOVERED;
|
|
|
+ }
|
|
|
+ return zori_widget_hover(widget);
|
|
|
+}
|
|
|
+
|
|
|
+int zori_widget_marked(struct zori_widget * widget) {
|
|
|
+ return widget && ((widget->flags & ZORI_FLAG_MARKED) != ZORI_FLAG_MARKED);
|
|
|
+}
|
|
|
+
|
|
|
+int zori_widget_marked_(struct zori_widget * widget, int set) {
|
|
|
+ if (set) {
|
|
|
+ widget->flags = widget->flags & (~ZORI_FLAG_MARKED);
|
|
|
+ } else {
|
|
|
+ widget->flags = widget->flags | ZORI_FLAG_MARKED;
|
|
|
+ }
|
|
|
+ return zori_widget_hover(widget);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+int zori_xy_inside_widget_p(struct zori_widget * widget, double x, double y) {
|
|
|
+ return (rebox_coordinates_inside_p(&widget->box, x, y));
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
|
|
|
void zori_update(double dt)
|
|
|
{
|
|
@@ -547,499 +581,6 @@ int zori_widget_y(struct zori_widget * widget) {
|
|
|
}
|
|
|
|
|
|
|
|
|
-
|
|
|
-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;
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- 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)
|
|
|
- );
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-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 ' ':
|
|
|
- self->stop_char = now_char;
|
|
|
- return self;
|
|
|
- case '\n':
|
|
|
- self->stop_char = now_char;
|
|
|
- return self;
|
|
|
- default:
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- * by checking with al_get_ustr_width but it's not a pressing matter yet.
|
|
|
- */
|
|
|
- ch = ustr_getnext(ustr, &now_char);
|
|
|
- }
|
|
|
-
|
|
|
- self->stop_char = now_char;
|
|
|
-
|
|
|
- return NULL;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- 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;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-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) {
|
|
|
-
|
|
|
- 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;
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- 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;
|
|
|
- }
|
|
|
-
|
|
|
- self->start_char = self->from_char;
|
|
|
- self->stop_char = wordinfo.stop_char;
|
|
|
-
|
|
|
- return NULL;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-#define ZORI_WIDGET_BORDER 3
|
|
|
-
|
|
|
-
|
|
|
-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));
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-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;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- 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);
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-void zori_console_command_(struct zori_console * self, zori_console_command * command, void * data) {
|
|
|
- self->command = command;
|
|
|
- self->command_data = data;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-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);
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-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) {
|
|
|
- ustrlist_droplast(&self->text);
|
|
|
- }
|
|
|
- return ustrlist_size(&self->text);
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-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) {
|
|
|
- ustrlist_droplast(&self->text);
|
|
|
- }
|
|
|
- return ustrlist_size(&self->text);
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-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);
|
|
|
-
|
|
|
- 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
|
|
|
-
|
|
|
-
|
|
|
-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);
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-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;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-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_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);
|
|
|
- }
|
|
|
-
|
|
|
- if (self->input) {
|
|
|
- zori_font_drawstr(font, color, x, y + high - linehigh, 0, self->input);
|
|
|
- }
|
|
|
-
|
|
|
- linew = al_get_ustr_width(font, self->input);
|
|
|
- al_draw_line(x + linew, y + high - linehigh, x + linew, y + high, color, 1);
|
|
|
-
|
|
|
- al_draw_textf(font, color, x, y, 0, "start: %d, size: %d", self->start,
|
|
|
- ustrlist_size(&self->text));
|
|
|
- return ZORI_HANDLE_PASS;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-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);
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-int zori_console_active(struct zori_console * self) {
|
|
|
- if(!self) return 0;
|
|
|
- return zori_widget_active(&self->widget);
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-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++;
|
|
|
-
|
|
|
- self->start = bad_clampi(self->start, 0, ustrlist_size(&self->text));
|
|
|
- return ZORI_HANDLE_DONE;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-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) {
|
|
|
-
|
|
|
- case ALLEGRO_KEY_F1:
|
|
|
- case ALLEGRO_KEY_F3:
|
|
|
- return ZORI_HANDLE_DONE;
|
|
|
- case ALLEGRO_KEY_PGUP: return zori_console_scroll(self, 1);
|
|
|
- case ALLEGRO_KEY_PGDN: return zori_console_scroll(self, -1);
|
|
|
- case ALLEGRO_KEY_BACKSPACE:
|
|
|
-
|
|
|
- ustr_remove_chr(self->input, ustr_offset(self->input, -1));
|
|
|
- return ZORI_HANDLE_DONE;
|
|
|
- break;
|
|
|
- case ALLEGRO_KEY_ENTER: {
|
|
|
- const char * command = ustr_c(self->input);
|
|
|
-
|
|
|
- if(zori_console_docommand(self, command)) {
|
|
|
- zori_console_puts(self, "Error in running comand");
|
|
|
- zori_console_puts(self, command);
|
|
|
- }
|
|
|
- ustr_truncate(self->input, 0);
|
|
|
-
|
|
|
- return ZORI_HANDLE_DONE;
|
|
|
- }
|
|
|
- default:
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- ustr_appendch(self->input, ch);
|
|
|
- return ZORI_HANDLE_DONE;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-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);
|
|
|
-
|
|
|
- * Note: this shouldnever happen if react is set up well.
|
|
|
- */
|
|
|
- return ZORI_HANDLE_DONE;
|
|
|
- default:
|
|
|
- break;
|
|
|
- }
|
|
|
- return ZORI_HANDLE_IGNORE;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-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;
|
|
|
-
|
|
|
- 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_DONE;
|
|
|
-}
|
|
|
-
|
|
|
-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 }
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-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);
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-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_DONE;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-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_DONE;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-struct zori_console * zori_console_alloc() {
|
|
|
- return calloc(1 , sizeof(struct zori_console));
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-#define ZORI_CONSOLE_MAX 200
|
|
|
-
|
|
|
-
|
|
|
-struct zori_console * zori_console_initall(struct zori_console * self, int id, zori_rebox * bounds, struct zori_style * style) {
|
|
|
- if(!self) return NULL;
|
|
|
-
|
|
|
- if (the_zori_root->console) return NULL;
|
|
|
- if(!zori_widget_initall(&self->widget, id, &the_zori_root->widget, bounds, style, 7, zori_console_actions)) {
|
|
|
- return NULL;
|
|
|
- }
|
|
|
- ustrlist_init(&self->text);
|
|
|
-
|
|
|
- zori_widget_active_(&self->widget, FALSE);
|
|
|
- self->count = 0;
|
|
|
-
|
|
|
- self->max = ZORI_CONSOLE_MAX;
|
|
|
- self->start = 0;
|
|
|
- self->charw = 80;
|
|
|
- self->buf = calloc(self->charw + 1, 1);
|
|
|
-
|
|
|
- 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;
|
|
|
-
|
|
|
- the_zori_root->console = self;
|
|
|
- return self;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-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;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
|
|
|
|
|
|
|