ltk

Socket-based GUI for X11 (WIP)
git clone git://lumidify.org/ltk.git (fast, but not encrypted)
git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
Log | Files | Refs | README | LICENSE

commit 25a158327d3d12d3eba052f202278ffa0d5539d5
parent 0d69e94ed01d94f82ba2b6f960aa672de7ed2ff9
Author: lumidify <nobody@lumidify.org>
Date:   Fri, 27 Oct 2023 12:16:10 +0200

Remove some boilerplate from command handling

Diffstat:
MMakefile | 4+++-
Msrc/box.c | 146+++++++++++++++++++++++--------------------------------------------------------
Msrc/button.c | 52++++++++++++++++++++++------------------------------
Asrc/cmd.c | 141+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/cmd.h | 106+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/entry.c | 51+++++++++++++++++++++------------------------------
Msrc/grid.c | 262+++++++++++++++++++++++--------------------------------------------------------
Msrc/label.c | 50+++++++++++++++++++++-----------------------------
Msrc/ltkd.c | 1+
Msrc/menu.c | 301++++++++++++++++++++++++++-----------------------------------------------------
Msrc/widget.h | 1+
11 files changed, 530 insertions(+), 585 deletions(-)

diff --git a/Makefile b/Makefile @@ -63,6 +63,7 @@ OBJ = \ src/clipboard_xlib.o \ src/txtbuf.o \ src/ctrlsel.o \ + src/cmd.o \ $(EXTRA_OBJ) # Note: This could be improved so a change in a header only causes the .c files @@ -101,7 +102,8 @@ HDR = \ src/clipboard_xlib.h \ src/clipboard.h \ src/txtbuf.h \ - src/ctrlsel.h + src/ctrlsel.h \ + src/cmd.h all: src/ltkd src/ltkc diff --git a/src/box.c b/src/box.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2022 lumidify <nobody@lumidify.org> + * Copyright (c) 2021-2023 lumidify <nobody@lumidify.org> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -29,6 +29,7 @@ #include "util.h" #include "scrollbar.h" #include "box.h" +#include "cmd.h" static void ltk_box_draw(ltk_widget *self, ltk_surface *s, int x, int y, ltk_rect clip); static ltk_box *ltk_box_create(ltk_window *window, const char *id, ltk_orientation orient); @@ -88,11 +89,13 @@ static struct ltk_widget_vtable vtable = { static int ltk_box_cmd_add( ltk_window *window, + ltk_box *box, char **tokens, size_t num_tokens, ltk_error *err); static int ltk_box_cmd_remove( ltk_window *window, + ltk_box *box, char **tokens, size_t num_tokens, ltk_error *err); @@ -105,6 +108,7 @@ static int ltk_box_cmd_clear */ static int ltk_box_cmd_create( ltk_window *window, + ltk_box *box, char **tokens, size_t num_tokens, ltk_error *err); @@ -490,49 +494,18 @@ ltk_box_mouse_scroll(ltk_widget *self, ltk_scroll_event *event) { static int ltk_box_cmd_add( ltk_window *window, + ltk_box *box, char **tokens, size_t num_tokens, ltk_error *err) { - const char *c; - ltk_box *box; - ltk_widget *widget; - - int sticky = 0; - if (num_tokens != 4 && num_tokens != 5) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_ANY, .optional = 0}, + {.type = CMDARG_STICKY, .optional = 1}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - box = (ltk_box *)ltk_get_widget(tokens[1], LTK_WIDGET_BOX, err); - widget = ltk_get_widget(tokens[3], LTK_WIDGET_ANY, err); - if (!box) { - err->arg = 1; - return 1; - } else if (!widget) { - err->arg = 3; - return 1; - } - - if (num_tokens == 5) { - for (c = tokens[4]; *c != '\0'; c++) { - if (*c == 'n') { - sticky |= LTK_STICKY_TOP; - } else if (*c == 's') { - sticky |= LTK_STICKY_BOTTOM; - } else if (*c == 'e') { - sticky |= LTK_STICKY_RIGHT; - } else if (*c == 'w') { - sticky |= LTK_STICKY_LEFT; - } else { - err->type = ERR_INVALID_ARGUMENT; - err->arg = 4; - return 1; - } - } - } - - if (ltk_box_add(window, widget, box, sticky, err)) { - err->arg = 3; + if (ltk_box_add(window, cmd[0].val.widget, box, cmd[1].initialized ? cmd[1].val.sticky : 0, err)) { + err->arg = 0; return 1; } return 0; @@ -542,30 +515,17 @@ ltk_box_cmd_add( static int ltk_box_cmd_remove( ltk_window *window, + ltk_box *box, char **tokens, size_t num_tokens, ltk_error *err) { - (void)window; - ltk_box *box; - ltk_widget *widget; - - if (num_tokens != 4) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; - return 1; - } - box = (ltk_box *)ltk_get_widget(tokens[1], LTK_WIDGET_BOX, err); - widget = ltk_get_widget(tokens[3], LTK_WIDGET_ANY, err); - if (!box) { - err->arg = 1; - return 1; - } else if (!widget) { - err->arg = 3; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_ANY, .optional = 0}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - - if (ltk_box_remove(widget, (ltk_widget *)box, err)) { - err->arg = 3; + if (ltk_box_remove(cmd[0].val.widget, (ltk_widget *)box, err)) { + err->arg = 0; return 1; } return 0; @@ -575,60 +535,38 @@ ltk_box_cmd_remove( static int ltk_box_cmd_create( ltk_window *window, + ltk_box *box_unneeded, char **tokens, size_t num_tokens, ltk_error *err) { - ltk_box *box; - ltk_orientation orient; - - if (num_tokens != 4) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; + (void)box_unneeded; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_IGNORE, .optional = 0}, + {.type = CMDARG_STRING, .optional = 0}, + {.type = CMDARG_IGNORE, .optional = 0}, + {.type = CMDARG_ORIENTATION, .optional = 0}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - if (!ltk_widget_id_free(tokens[1])) { + if (!ltk_widget_id_free(cmd[1].val.str)) { err->type = ERR_WIDGET_ID_IN_USE; err->arg = 1; return 1; } - if (!strcmp(tokens[3], "horizontal")) { - orient = LTK_HORIZONTAL; - } else if (!strcmp(tokens[3], "vertical")) { - orient = LTK_VERTICAL; - } else { - err->type = ERR_INVALID_ARGUMENT; - err->arg = 3; - return 1; - } - box = (ltk_box *)ltk_box_create(window, tokens[1], orient); - ltk_set_widget((ltk_widget *)box, tokens[1]); + ltk_box *box = ltk_box_create(window, cmd[1].val.str, cmd[3].val.orient); + ltk_set_widget((ltk_widget *)box, cmd[1].val.str); return 0; } -/* box <box id> <command> ... */ -int -ltk_box_cmd( - ltk_window *window, - char **tokens, - size_t num_tokens, - ltk_error *err) { - if (num_tokens < 3) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; - return 1; - } - if (strcmp(tokens[2], "add") == 0) { - return ltk_box_cmd_add(window, tokens, num_tokens, err); - } else if (strcmp(tokens[2], "remove") == 0) { - return ltk_box_cmd_remove(window, tokens, num_tokens, err); - } else if (strcmp(tokens[2], "create") == 0) { - return ltk_box_cmd_create(window, tokens, num_tokens, err); - } else { - err->type = ERR_INVALID_COMMAND; - err->arg = -1; - return 1; - } +static struct box_cmd { + char *name; + int (*func)(ltk_window *, ltk_box *, char **, size_t, ltk_error *); + int needs_all; +} box_cmds[] = { + {"add", &ltk_box_cmd_add, 0}, + {"create", &ltk_box_cmd_create, 1}, + {"remove", &ltk_box_cmd_remove, 0}, +}; - return 0; /* Well, I guess this is impossible anyways... */ -} +GEN_CMD_HELPERS(ltk_box_cmd, LTK_WIDGET_BOX, ltk_box, box_cmds, struct box_cmd) diff --git a/src/button.c b/src/button.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, 2018, 2020, 2022 lumidify <nobody@lumidify.org> + * Copyright (c) 2016-2023 lumidify <nobody@lumidify.org> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -33,6 +33,7 @@ #include "graphics.h" #include "surface_cache.h" #include "theme.h" +#include "cmd.h" #define MAX_BUTTON_BORDER_WIDTH 100 #define MAX_BUTTON_PADDING 500 @@ -209,45 +210,36 @@ ltk_button_destroy(ltk_widget *self, int shallow) { static int ltk_button_cmd_create( ltk_window *window, + ltk_button *button_unneeded, char **tokens, size_t num_tokens, ltk_error *err) { - ltk_button *button; - if (num_tokens != 4) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; + (void)button_unneeded; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_IGNORE, .optional = 0}, + {.type = CMDARG_STRING, .optional = 0}, + {.type = CMDARG_IGNORE, .optional = 0}, + {.type = CMDARG_STRING, .optional = 0}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - if (!ltk_widget_id_free(tokens[1])) { + if (!ltk_widget_id_free(cmd[1].val.str)) { err->type = ERR_WIDGET_ID_IN_USE; err->arg = 1; return 1; } - button = ltk_button_create(window, tokens[1], tokens[3]); - ltk_set_widget((ltk_widget *)button, tokens[1]); + ltk_button *button = ltk_button_create(window, cmd[1].val.str, cmd[3].val.str); + ltk_set_widget((ltk_widget *)button, cmd[1].val.str); return 0; } -/* button <button id> <command> ... */ -int -ltk_button_cmd( - ltk_window *window, - char **tokens, - size_t num_tokens, - ltk_error *err) { - if (num_tokens < 3) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; - return 1; - } - if (strcmp(tokens[2], "create") == 0) { - return ltk_button_cmd_create(window, tokens, num_tokens, err); - } else { - err->type = ERR_INVALID_COMMAND; - err->arg = -1; - return 1; - } +static struct button_cmd { + char *name; + int (*func)(ltk_window *, ltk_button *, char **, size_t, ltk_error *); + int needs_all; +} button_cmds[] = { + {"create", &ltk_button_cmd_create, 1}, +}; - return 0; -} +GEN_CMD_HELPERS(ltk_button_cmd, LTK_WIDGET_BUTTON, ltk_button, button_cmds, struct button_cmd) diff --git a/src/cmd.c b/src/cmd.c @@ -0,0 +1,141 @@ +#include "graphics.h" +#include "surface_cache.h" +#include "util.h" +#include "cmd.h" +#include "memory.h" + +int +ltk_parse_cmd( + ltk_window *window, char **tokens, size_t num_tokens, + ltk_cmdarg_parseinfo *parseinfo, size_t num_arg, ltk_error *err) { + const char *errstr = NULL; + if (num_tokens > num_arg || (num_tokens < num_arg && !parseinfo[num_tokens].optional)) { + err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; + err->arg = -1; + return 1; + } + size_t i = 0; + for (; i < num_tokens; i++) { + switch (parseinfo[i].type) { + case CMDARG_INT: + parseinfo[i].val.i = ltk_strtonum(tokens[i], parseinfo[i].min, parseinfo[i].max, &errstr); + if (errstr) { + err->type = ERR_INVALID_ARGUMENT; + err->arg = i; + goto error; + } + parseinfo[i].initialized = 1; + break; + case CMDARG_STICKY: + parseinfo[i].val.sticky = LTK_STICKY_NONE; + for (const char *c = tokens[i]; *c != '\0'; c++) { + switch (*c) { + case 'n': + parseinfo[i].val.sticky |= LTK_STICKY_TOP; + break; + case 's': + parseinfo[i].val.sticky |= LTK_STICKY_BOTTOM; + break; + case 'e': + parseinfo[i].val.sticky |= LTK_STICKY_RIGHT; + break; + case 'w': + parseinfo[i].val.sticky |= LTK_STICKY_LEFT; + break; + default: + err->type = ERR_INVALID_ARGUMENT; + err->arg = i; + goto error; + } + } + parseinfo[i].initialized = 1; + break; + case CMDARG_WIDGET: + parseinfo[i].val.widget = ltk_get_widget(tokens[i], parseinfo[i].widget_type, err); + if (!parseinfo[i].val.widget) { + err->arg = i; + goto error; + } + parseinfo[i].initialized = 1; + break; + case CMDARG_STRING: + parseinfo[i].val.str = tokens[i]; + parseinfo[i].initialized = 1; + break; + case CMDARG_COLOR: + if (ltk_color_create(window->renderdata, tokens[i], &parseinfo[i].val.color)) { + /* FIXME: this could fail even if the argument is fine */ + err->type = ERR_INVALID_ARGUMENT; + err->arg = i; + goto error; + } + parseinfo[i].initialized = 1; + break; + case CMDARG_BOOL: + if (strcmp(tokens[i], "true") == 0) { + parseinfo[i].val.b = 1; + } else if (strcmp(tokens[i], "false") == 0) { + parseinfo[i].val.b = 0; + } else { + err->type = ERR_INVALID_ARGUMENT; + err->arg = i; + goto error; + } + parseinfo[i].initialized = 1; + break; + case CMDARG_ORIENTATION: + if (strcmp(tokens[i], "horizontal") == 0) { + parseinfo[i].val.orient = LTK_HORIZONTAL; + } else if (strcmp(tokens[i], "vertical") == 0) { + parseinfo[i].val.orient = LTK_VERTICAL; + } else { + err->type = ERR_INVALID_ARGUMENT; + err->arg = i; + goto error; + } + parseinfo[i].initialized = 1; + break; + case CMDARG_BORDERSIDES: + parseinfo[i].val.border = LTK_BORDER_NONE; + for (const char *c = tokens[i]; *c != '\0'; c++) { + switch (*c) { + case 't': + parseinfo[i].val.border |= LTK_BORDER_TOP; + break; + case 'b': + parseinfo[i].val.border |= LTK_BORDER_BOTTOM; + break; + case 'l': + parseinfo[i].val.border |= LTK_BORDER_LEFT; + break; + case 'r': + parseinfo[i].val.border |= LTK_BORDER_RIGHT; + break; + default: + err->type = ERR_INVALID_ARGUMENT; + err->arg = i; + goto error; + } + } + parseinfo[i].initialized = 1; + break; + case CMDARG_IGNORE: + parseinfo[i].initialized = 1; + break; + default: + ltk_fatal("Invalid command argument type. This should not happen.\n"); + /* TODO: ltk_assert(0); */ + } + } + for (; i < num_arg; i++) { + parseinfo[i].initialized = 0; + } + return 0; +error: + for (; i-- > 0;) { + if (parseinfo[i].type == CMDARG_COLOR) { + ltk_color_destroy(window->renderdata, &parseinfo[i].val.color); + } + } + return 1; +} diff --git a/src/cmd.h b/src/cmd.h @@ -0,0 +1,106 @@ +#ifndef LTK_CMD_H +#define LTK_CMD_H + +#include "ltk.h" + +typedef enum { + CMDARG_IGNORE, + CMDARG_STRING, + CMDARG_COLOR, + CMDARG_INT, + CMDARG_BOOL, + CMDARG_BORDERSIDES, + CMDARG_STICKY, + CMDARG_WIDGET, + CMDARG_ORIENTATION +} ltk_cmdarg_datatype; + +/* color needs to be destroyed by cmd handling function if it is not needed anymore */ +/* str must *not* be freed because it is a pointer to the original argument + -> it must be copied if it needs to be kept around */ +typedef struct { + ltk_cmdarg_datatype type; + /* Note: Bool and int are both integers, but they are + separate just to make it a bit clearer */ + union { + char *str; + ltk_color color; + int i; + int b; + ltk_border_sides border; + ltk_sticky_mask sticky; + ltk_orientation orient; + ltk_widget *widget; + } val; + int min, max; /* only for integers */ /* FIXME: which integer type is sensible here? */ + ltk_widget_type widget_type; /* only for widgets */ + int optional; + int initialized; +} ltk_cmdarg_parseinfo; + +/* Returns 1 on error, 0 on success */ +/* All optional arguments must be in one block at the end */ +int ltk_parse_cmd(ltk_window *window, char **tokens, size_t num_tokens, ltk_cmdarg_parseinfo *parseinfo, size_t num_arg, ltk_error *err); + +#define GEN_CMD_HELPERS(func_name, widget_type, widget_typename, array_name, entry_type) \ +/* FIXME: maybe just get rid of this and rely on array already being sorted? */ \ +static int array_name##_sorted = 0; \ + \ +static int \ +array_name##_search_helper(const void *keyv, const void *entryv) { \ + const char *key = (const char *)keyv; \ + entry_type *entry = (entry_type *)entryv; \ + return strcmp(key, entry->name); \ +} \ + \ +static int \ +array_name##_sort_helper(const void *entry1v, const void *entry2v) { \ + entry_type *entry1 = (entry_type *)entry1v; \ + entry_type *entry2 = (entry_type *)entry2v; \ + return strcmp(entry1->name, entry2->name); \ +} \ + \ +int \ +func_name( \ + ltk_window *window, \ + char **tokens, \ + size_t num_tokens, \ + ltk_error *err) { \ + if (num_tokens < 3) { \ + err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; \ + err->arg = -1; \ + return 1; \ + } \ + /* just in case */ \ + if (!array_name##_sorted) { \ + qsort( \ + array_name, LENGTH(array_name), \ + sizeof(array_name[0]), &array_name##_sort_helper); \ + array_name##_sorted = 1; \ + } \ + entry_type *e = bsearch( \ + tokens[2], array_name, LENGTH(array_name), \ + sizeof(array_name[0]), &array_name##_search_helper \ + ); \ + if (!e) { \ + err->type = ERR_INVALID_COMMAND; \ + err->arg = -1; \ + return 1; \ + } \ + if (e->needs_all) { \ + return e->func(window, NULL, tokens, num_tokens, err); \ + } else { \ + widget_typename *widget = (widget_typename *)ltk_get_widget(tokens[1], widget_type, err); \ + if (!widget) { \ + err->arg = 1; \ + return 1; \ + } \ + int ret = e->func(window, widget, tokens + 3, num_tokens - 3, err); \ + if (ret && err->arg >= 0) \ + err->arg += 3; \ + return ret; \ + } \ + return 0; /* Well, I guess this is impossible anyways... */ \ +} + +#endif /* LTK_CMD_H */ diff --git a/src/entry.c b/src/entry.c @@ -41,6 +41,7 @@ #include "theme.h" #include "array.h" #include "keys.h" +#include "cmd.h" #define MAX_ENTRY_BORDER_WIDTH 100 #define MAX_ENTRY_PADDING 500 @@ -790,46 +791,36 @@ ltk_entry_destroy(ltk_widget *self, int shallow) { static int ltk_entry_cmd_create( ltk_window *window, + ltk_entry *entry_unneeded, char **tokens, size_t num_tokens, ltk_error *err) { - ltk_entry *entry; - /* FIXME: factor out these repeated pieces */ - if (num_tokens != 4) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; + (void)entry_unneeded; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_IGNORE, .optional = 0}, + {.type = CMDARG_STRING, .optional = 0}, + {.type = CMDARG_IGNORE, .optional = 0}, + {.type = CMDARG_STRING, .optional = 0}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - if (!ltk_widget_id_free(tokens[1])) { + if (!ltk_widget_id_free(cmd[1].val.str)) { err->type = ERR_WIDGET_ID_IN_USE; err->arg = 1; return 1; } - entry = ltk_entry_create(window, tokens[1], tokens[3]); - ltk_set_widget((ltk_widget *)entry, tokens[1]); + ltk_entry *entry = ltk_entry_create(window, cmd[1].val.str, cmd[3].val.str); + ltk_set_widget((ltk_widget *)entry, cmd[1].val.str); return 0; } -/* entry <entry id> <command> ... */ -int -ltk_entry_cmd( - ltk_window *window, - char **tokens, - size_t num_tokens, - ltk_error *err) { - if (num_tokens < 3) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; - return 1; - } - if (strcmp(tokens[2], "create") == 0) { - return ltk_entry_cmd_create(window, tokens, num_tokens, err); - } else { - err->type = ERR_INVALID_COMMAND; - err->arg = -1; - return 1; - } +static struct entry_cmd { + char *name; + int (*func)(ltk_window *, ltk_entry *, char **, size_t, ltk_error *); + int needs_all; +} entry_cmds[] = { + {"create", &ltk_entry_cmd_create, 1}, +}; - return 0; -} +GEN_CMD_HELPERS(ltk_entry_cmd, LTK_WIDGET_ENTRY, ltk_entry, entry_cmds, struct entry_cmd) diff --git a/src/grid.c b/src/grid.c @@ -1,6 +1,6 @@ /* FIXME: sometimes, resizing doesn't work properly when running test.sh */ /* - * Copyright (c) 2016, 2017, 2018, 2020, 2021, 2022 lumidify <nobody@lumidify.org> + * Copyright (c) 2016-2023 lumidify <nobody@lumidify.org> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -36,6 +36,7 @@ #include "ltk.h" #include "util.h" #include "grid.h" +#include "cmd.h" static void ltk_grid_set_row_weight(ltk_grid *grid, int row, int weight); static void ltk_grid_set_column_weight(ltk_grid *grid, int column, int weight); @@ -95,26 +96,31 @@ static struct ltk_widget_vtable vtable = { static int ltk_grid_cmd_add( ltk_window *window, + ltk_grid *grid, char **tokens, size_t num_tokens, ltk_error *err); static int ltk_grid_cmd_ungrid( ltk_window *window, + ltk_grid *grid, char **tokens, size_t num_tokens, ltk_error *err); static int ltk_grid_cmd_create( ltk_window *window, + ltk_grid *grid, char **tokens, size_t num_tokens, ltk_error *err); static int ltk_grid_cmd_set_row_weight( ltk_window *window, + ltk_grid *grid, char **tokens, size_t num_tokens, ltk_error *err); static int ltk_grid_cmd_set_column_weight( ltk_window *window, + ltk_grid *grid, char **tokens, size_t num_tokens, ltk_error *err); @@ -536,76 +542,28 @@ ltk_grid_last_child(ltk_widget *self) { static int ltk_grid_cmd_add( ltk_window *window, + ltk_grid *grid, char **tokens, size_t num_tokens, ltk_error *err) { - const char *c; - ltk_grid *grid; - ltk_widget *widget; - const char *errstr_num; - - int row, column, row_span, column_span, sticky = 0; - if (num_tokens != 8 && num_tokens != 9) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_ANY, .optional = 0}, + {.type = CMDARG_INT, .min = 0, .max = grid->rows - 1, .optional = 0}, + {.type = CMDARG_INT, .min = 0, .max = grid->columns - 1, .optional = 0}, + {.type = CMDARG_INT, .min = 0, .max = grid->rows, .optional = 0}, + {.type = CMDARG_INT, .min = 0, .max = grid->columns, .optional = 0}, + {.type = CMDARG_STICKY, .optional = 1}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - grid = (ltk_grid *)ltk_get_widget(tokens[1], LTK_WIDGET_GRID, err); - widget = ltk_get_widget(tokens[3], LTK_WIDGET_ANY, err); - if (!grid) { - err->arg = 1; - return 1; - } else if (!widget) { - err->arg = 3; - return 1; - } - - row = ltk_strtonum(tokens[4], 0, grid->rows - 1, &errstr_num); - if (errstr_num) { - err->type = ERR_INVALID_ARGUMENT; - err->arg = 4; - return 1; - } - column = ltk_strtonum(tokens[5], 0, grid->columns - 1, &errstr_num); - if (errstr_num) { - err->type = ERR_INVALID_ARGUMENT; - err->arg = 5; - return 1; - } - row_span = ltk_strtonum(tokens[6], 1, grid->rows, &errstr_num); - if (errstr_num) { - err->type = ERR_INVALID_ARGUMENT; - err->arg = 6; - return 1; - } - column_span = ltk_strtonum(tokens[7], 1, grid->columns, &errstr_num); - if (errstr_num) { - err->type = ERR_INVALID_ARGUMENT; - err->arg = 7; - return 1; - } - - if (num_tokens == 9) { - for (c = tokens[8]; *c != '\0'; c++) { - if (*c == 'n') { - sticky |= LTK_STICKY_TOP; - } else if (*c == 's') { - sticky |= LTK_STICKY_BOTTOM; - } else if (*c == 'e') { - sticky |= LTK_STICKY_RIGHT; - } else if (*c == 'w') { - sticky |= LTK_STICKY_LEFT; - } else { - err->type = ERR_INVALID_ARGUMENT; - err->arg = 8; - return 1; - } - } - } - /* FIXME: better error reporting for invalid grid position */ - if (ltk_grid_add(window, widget, grid, row, column, row_span, column_span, sticky, err)) { - err->arg = err->type == ERR_WIDGET_IN_CONTAINER ? 3 : -1; + /* FIXME: check if recalculation deals properly with rowspan/columnspan + that goes over the edge of the grid */ + if (ltk_grid_add( + window, cmd[0].val.widget, grid, + cmd[1].val.i, cmd[2].val.i, cmd[3].val.i, cmd[4].val.i, + cmd[5].initialized ? cmd[5].val.sticky : 0, err)) { + err->arg = err->type == ERR_WIDGET_IN_CONTAINER ? 0 : -1; return 1; } return 0; @@ -615,174 +573,104 @@ ltk_grid_cmd_add( static int ltk_grid_cmd_ungrid( ltk_window *window, + ltk_grid *grid, char **tokens, size_t num_tokens, ltk_error *err) { - (void)window; - ltk_grid *grid; - ltk_widget *widget; - if (num_tokens != 4) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; - return 1; - } - grid = (ltk_grid *)ltk_get_widget(tokens[1], LTK_WIDGET_GRID, err); - widget = ltk_get_widget(tokens[3], LTK_WIDGET_ANY, err); - if (!grid) { - err->arg = 1; - return 1; - } else if (!widget) { - err->arg = 3; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_ANY, .optional = 0} + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - if (ltk_grid_ungrid(widget, (ltk_widget *)grid, err)) { - err->arg = 3; + if (ltk_grid_ungrid(cmd[0].val.widget, (ltk_widget *)grid, err)) { + err->arg = 0; return 1; } return 0; } +/* FIXME: max size of 64 is completely arbitrary! */ /* grid <grid id> create <rows> <columns> */ static int ltk_grid_cmd_create( ltk_window *window, + ltk_grid *grid_unneeded, char **tokens, size_t num_tokens, ltk_error *err) { - int rows, columns; - ltk_grid *grid; - const char *errstr_num; - if (num_tokens != 5) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; + (void)grid_unneeded; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_IGNORE, .optional = 0}, + {.type = CMDARG_STRING, .optional = 0}, + {.type = CMDARG_IGNORE, .optional = 0}, + {.type = CMDARG_INT, .min = 0, .max = 64, .optional = 0}, + {.type = CMDARG_INT, .min = 0, .max = 64, .optional = 0}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - if (!ltk_widget_id_free(tokens[1])) { + if (!ltk_widget_id_free(cmd[1].val.str)) { err->type = ERR_WIDGET_ID_IN_USE; err->arg = 1; return 1; } - rows = ltk_strtonum(tokens[3], 1, 64, &errstr_num); - if (errstr_num) { - err->type = ERR_INVALID_ARGUMENT; - err->arg = 3; - return 1; - } - columns = ltk_strtonum(tokens[4], 1, 64, &errstr_num); - if (errstr_num) { - err->type = ERR_INVALID_ARGUMENT; - err->arg = 4; - return 1; - } - grid = ltk_grid_create(window, tokens[1], rows, columns); - ltk_set_widget((ltk_widget *)grid, tokens[1]); + ltk_grid *grid = ltk_grid_create(window, cmd[1].val.str, cmd[3].val.i, cmd[4].val.i); + ltk_set_widget((ltk_widget *)grid, cmd[1].val.str); return 0; } +/* FIXME: 64 is completely arbitrary */ /* grid <grid id> set-row-weight <row> <weight> */ static int ltk_grid_cmd_set_row_weight( ltk_window *window, + ltk_grid *grid, char **tokens, size_t num_tokens, ltk_error *err) { - (void)window; - ltk_grid *grid; - int row, weight; - const char *errstr_num; - if (num_tokens != 5) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; - return 1; - } - grid = (ltk_grid *)ltk_get_widget(tokens[1], LTK_WIDGET_GRID, err); - if (!grid) { - err->arg = 1; - return 1; - } - row = ltk_strtonum(tokens[3], 0, grid->rows, &errstr_num); - if (errstr_num) { - err->type = ERR_INVALID_ARGUMENT; - err->arg = 3; - return 1; - } - weight = ltk_strtonum(tokens[4], 0, 64, &errstr_num); - if (errstr_num) { - err->type = ERR_INVALID_ARGUMENT; - err->arg = 4; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_INT, .min = 0, .max = grid->rows - 1, .optional = 0}, + {.type = CMDARG_INT, .min = 0, .max = 64, .optional = 0}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - ltk_grid_set_row_weight(grid, row, weight); + ltk_grid_set_row_weight(grid, cmd[0].val.i, cmd[1].val.i); return 0; } + +/* FIXME: 64 is completely arbitrary */ +/* FIXME: check for overflows in various grid calculations (at least when larger values are allowed) */ /* grid <grid id> set-column-weight <column> <weight> */ static int ltk_grid_cmd_set_column_weight( ltk_window *window, + ltk_grid *grid, char **tokens, size_t num_tokens, ltk_error *err) { - (void)window; - ltk_grid *grid; - int column, weight; - const char *errstr_num; - if (num_tokens != 5) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; - return 1; - } - grid = (ltk_grid *)ltk_get_widget(tokens[1], LTK_WIDGET_GRID, err); - if (!grid) { - err->arg = 1; - return 1; - } - column = ltk_strtonum(tokens[3], 0, grid->columns, &errstr_num); - if (errstr_num) { - err->type = ERR_INVALID_ARGUMENT; - err->arg = 3; - return 1; - } - weight = ltk_strtonum(tokens[4], 0, 64, &errstr_num); - if (errstr_num) { - err->type = ERR_INVALID_ARGUMENT; - err->arg = 4; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_INT, .min = 0, .max = grid->columns - 1, .optional = 0}, + {.type = CMDARG_INT, .min = 0, .max = 64, .optional = 0}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - ltk_grid_set_column_weight(grid, column, weight); + ltk_grid_set_column_weight(grid, cmd[0].val.i, cmd[1].val.i); return 0; } -/* grid <grid id> <command> ... */ -int -ltk_grid_cmd( - ltk_window *window, - char **tokens, - size_t num_tokens, - ltk_error *err) { - if (num_tokens < 3) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; - return 1; - } - if (strcmp(tokens[2], "add") == 0) { - return ltk_grid_cmd_add(window, tokens, num_tokens, err); - } else if (strcmp(tokens[2], "ungrid") == 0) { - return ltk_grid_cmd_ungrid(window, tokens, num_tokens, err); - } else if (strcmp(tokens[2], "create") == 0) { - return ltk_grid_cmd_create(window, tokens, num_tokens, err); - } else if (strcmp(tokens[2], "set-row-weight") == 0) { - return ltk_grid_cmd_set_row_weight(window, tokens, num_tokens, err); - } else if (strcmp(tokens[2], "set-column-weight") == 0) { - return ltk_grid_cmd_set_column_weight(window, tokens, num_tokens, err); - } else { - err->type = ERR_INVALID_COMMAND; - err->arg = -1; - return 1; - } +static struct grid_cmd { + char *name; + int (*func)(ltk_window *, ltk_grid *, char **, size_t, ltk_error *); + int needs_all; +} grid_cmds[] = { + {"add", &ltk_grid_cmd_add, 0}, + {"create", &ltk_grid_cmd_create, 1}, + {"remove", &ltk_grid_cmd_ungrid, 0}, + {"set-column-weight", &ltk_grid_cmd_set_column_weight, 0}, + {"set-row-weight", &ltk_grid_cmd_set_row_weight, 0}, +}; - return 0; /* Well, I guess this is impossible anyways... */ -} +GEN_CMD_HELPERS(ltk_grid_cmd, LTK_WIDGET_GRID, ltk_grid, grid_cmds, struct grid_cmd) diff --git a/src/label.c b/src/label.c @@ -32,6 +32,7 @@ #include "graphics.h" #include "surface_cache.h" #include "theme.h" +#include "cmd.h" #define MAX_LABEL_PADDING 500 @@ -153,45 +154,36 @@ ltk_label_destroy(ltk_widget *self, int shallow) { static int ltk_label_cmd_create( ltk_window *window, + ltk_label *label_unneeded, char **tokens, size_t num_tokens, ltk_error *err) { - ltk_label *label; - if (num_tokens != 4) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; + (void)label_unneeded; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_IGNORE, .optional = 0}, + {.type = CMDARG_STRING, .optional = 0}, + {.type = CMDARG_IGNORE, .optional = 0}, + {.type = CMDARG_STRING, .optional = 0}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - if (!ltk_widget_id_free(tokens[1])) { + if (!ltk_widget_id_free(cmd[1].val.str)) { err->type = ERR_WIDGET_ID_IN_USE; err->arg = 1; return 1; } - label = ltk_label_create(window, tokens[1], tokens[3]); - ltk_set_widget((ltk_widget *)label, tokens[1]); + ltk_label *label = ltk_label_create(window, cmd[1].val.str, cmd[3].val.str); + ltk_set_widget((ltk_widget *)label, cmd[1].val.str); return 0; } -/* label <button id> <command> ... */ -int -ltk_label_cmd( - ltk_window *window, - char **tokens, - size_t num_tokens, - ltk_error *err) { - if (num_tokens < 3) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; - return 1; - } - if (strcmp(tokens[2], "create") == 0) { - return ltk_label_cmd_create(window, tokens, num_tokens, err); - } else { - err->type = ERR_INVALID_COMMAND; - err->arg = -1; - return 1; - } +static struct label_cmd { + char *name; + int (*func)(ltk_window *, ltk_label *, char **, size_t, ltk_error *); + int needs_all; +} label_cmds[] = { + {"create", &ltk_label_cmd_create, 1}, +}; - return 0; -} +GEN_CMD_HELPERS(ltk_label_cmd, LTK_WIDGET_LABEL, ltk_label, label_cmds, struct label_cmd) diff --git a/src/ltkd.c b/src/ltkd.c @@ -1162,6 +1162,7 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int Fontconfig is initialized. Getting the size is important because Pango doesn't actually do much until you try to use the line for something. */ /* FIXME: I guess just calling FcInit manually in the text backend could work as well. */ + /* FIXME: Maybe just call this when actually daemonizing. */ ltk_text_line *tmp = ltk_text_line_create(window->text_context, 10, "hi", 0, -1); int tw, th; ltk_text_line_get_size(tmp, &tw, &th); diff --git a/src/menu.c b/src/menu.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 lumidify <nobody@lumidify.org> + * Copyright (c) 2022-2023 lumidify <nobody@lumidify.org> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -40,6 +40,7 @@ #include "graphics.h" #include "surface_cache.h" #include "theme.h" +#include "cmd.h" #define MAX_MENU_BORDER_WIDTH 100 #define MAX_MENU_PAD 500 @@ -1338,34 +1339,36 @@ ltk_menu_destroy(ltk_widget *self, int shallow) { ltk_free(menu); } -/* FIXME: simplify command handling to avoid all this boilerplate */ /* TODO: get-index-for-id */ /* [sub]menu <menu id> create */ static int ltk_menu_cmd_create( ltk_window *window, + ltk_menu *menu_unneeded, char **tokens, size_t num_tokens, ltk_error *err) { - ltk_menu *menu; - if (num_tokens != 3) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; + (void)menu_unneeded; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_STRING, .optional = 0}, + {.type = CMDARG_STRING, .optional = 0}, + {.type = CMDARG_IGNORE, .optional = 0}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - if (!ltk_widget_id_free(tokens[1])) { + if (!ltk_widget_id_free(cmd[1].val.str)) { err->type = ERR_WIDGET_ID_IN_USE; err->arg = 1; return 1; } - if (!strcmp(tokens[0], "menu")) { - menu = ltk_menu_create(window, tokens[1], 0); + ltk_menu *menu; + if (!strcmp(cmd[0].val.str, "menu")) { + menu = ltk_menu_create(window, cmd[1].val.str, 0); } else { - menu = ltk_menu_create(window, tokens[1], 1); + menu = ltk_menu_create(window, cmd[1].val.str, 1); } - ltk_set_widget((ltk_widget *)menu, tokens[1]); - + ltk_set_widget((ltk_widget *)menu, cmd[1].val.str); return 0; } @@ -1373,36 +1376,18 @@ ltk_menu_cmd_create( static int ltk_menu_cmd_insert_entry( ltk_window *window, + ltk_menu *menu, char **tokens, size_t num_tokens, ltk_error *err) { - (void)window; - ltk_menu *menu; - ltk_menuentry *e; - const char *errstr_num; - if (num_tokens != 5) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_MENUENTRY, .optional = 0}, + {.type = CMDARG_INT, .min = 0, .max = menu->num_entries, .optional = 0}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - menu = (ltk_menu *)ltk_get_widget(tokens[1], LTK_WIDGET_MENU, err); - if (!menu) { - err->arg = 1; - return 1; - } - e = (ltk_menuentry *)ltk_get_widget(tokens[3], LTK_WIDGET_MENUENTRY, err); - if (!e) { - err->arg = 3; - return 1; - } - size_t idx = (size_t)ltk_strtonum(tokens[5], 0, (long long)menu->num_entries, &errstr_num); - if (errstr_num) { - err->type = ERR_INVALID_ARGUMENT; - err->arg = 5; - return 1; - } - if (ltk_menu_insert_entry(menu, e, idx, err)) { - err->arg = err->type == ERR_WIDGET_IN_CONTAINER ? 3 : 5; + if (ltk_menu_insert_entry(menu, (ltk_menuentry *)cmd[0].val.widget, cmd[1].val.i, err)) { + err->arg = err->type == ERR_WIDGET_IN_CONTAINER ? 0 : 1; return 1; } return 0; @@ -1412,29 +1397,17 @@ ltk_menu_cmd_insert_entry( static int ltk_menu_cmd_add_entry( ltk_window *window, + ltk_menu *menu, char **tokens, size_t num_tokens, ltk_error *err) { - (void)window; - ltk_menu *menu; - ltk_menuentry *e; - if (num_tokens != 4) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_MENUENTRY, .optional = 0}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - menu = (ltk_menu *)ltk_get_widget(tokens[1], LTK_WIDGET_MENU, err); - if (!menu) { - err->arg = 1; - return 1; - } - e = (ltk_menuentry *)ltk_get_widget(tokens[3], LTK_WIDGET_MENUENTRY, err); - if (!e) { - err->arg = 3; - return 1; - } - if (ltk_menu_add_entry(menu, e, err)) { - err->arg = err->type == ERR_WIDGET_IN_CONTAINER ? 3 : 5; + if (ltk_menu_add_entry(menu, (ltk_menuentry *)cmd[0].val.widget, err)) { + err->arg = 0; return 1; } return 0; @@ -1444,33 +1417,19 @@ ltk_menu_cmd_add_entry( static int ltk_menu_cmd_remove_entry_index( ltk_window *window, + ltk_menu *menu, char **tokens, size_t num_tokens, ltk_error *err) { - (void)window; - ltk_menu *menu; - const char *errstr_num; - if (num_tokens != 4) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; - return 1; - } - menu = (ltk_menu *)ltk_get_widget(tokens[1], LTK_WIDGET_MENU, err); - if (!menu) { - err->arg = 1; - return 1; - } - size_t idx = (size_t)ltk_strtonum(tokens[3], 0, (long long)menu->num_entries, &errstr_num); - if (errstr_num) { - err->type = ERR_INVALID_ARGUMENT; - err->arg = 3; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_INT, .min = 0, .max = menu->num_entries - 1, .optional = 0}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - if (!ltk_menu_remove_entry_index(menu, idx, err)) { - err->arg = 3; + if (!ltk_menu_remove_entry_index(menu, cmd[0].val.i, err)) { + err->arg = 0; return 1; } - return 0; } @@ -1478,26 +1437,19 @@ ltk_menu_cmd_remove_entry_index( static int ltk_menu_cmd_remove_entry_id( ltk_window *window, + ltk_menu *menu, char **tokens, size_t num_tokens, ltk_error *err) { - (void)window; - ltk_menu *menu; - if (num_tokens != 4) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; - return 1; - } - menu = (ltk_menu *)ltk_get_widget(tokens[1], LTK_WIDGET_MENU, err); - if (!menu) { - err->arg = 1; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_STRING, .optional = 0}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - if (!ltk_menu_remove_entry_id(menu, tokens[3], err)) { - err->arg = 3; + if (!ltk_menu_remove_entry_id(menu, cmd[0].val.str, err)) { + err->arg = 0; return 1; } - return 0; } @@ -1505,23 +1457,15 @@ ltk_menu_cmd_remove_entry_id( static int ltk_menu_cmd_remove_all_entries( ltk_window *window, + ltk_menu *menu, char **tokens, size_t num_tokens, ltk_error *err) { (void)window; - ltk_menu *menu; - if (num_tokens != 3) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; - return 1; - } - menu = (ltk_menu *)ltk_get_widget(tokens[1], LTK_WIDGET_MENU, err); - if (!menu) { - err->arg = 1; - return 1; - } + (void)tokens; + (void)num_tokens; + (void)err; ltk_menu_remove_all_entries(menu); - return 0; } @@ -1529,23 +1473,26 @@ ltk_menu_cmd_remove_all_entries( static int ltk_menuentry_cmd_create( ltk_window *window, + ltk_menuentry *e_unneeded, char **tokens, size_t num_tokens, ltk_error *err) { - ltk_menuentry *e; - if (num_tokens != 4) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; + (void)e_unneeded; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_STRING, .optional = 0}, + {.type = CMDARG_STRING, .optional = 0}, + {.type = CMDARG_IGNORE, .optional = 0}, + {.type = CMDARG_STRING, .optional = 0}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - if (!ltk_widget_id_free(tokens[1])) { + if (!ltk_widget_id_free(cmd[1].val.str)) { err->type = ERR_WIDGET_ID_IN_USE; err->arg = 1; return 1; } - e = ltk_menuentry_create(window, tokens[1], tokens[3]); - ltk_set_widget((ltk_widget *)e, tokens[1]); - + ltk_menuentry *e = ltk_menuentry_create(window, cmd[1].val.str, cmd[3].val.str); + ltk_set_widget((ltk_widget *)e, cmd[1].val.str); return 0; } @@ -1553,30 +1500,19 @@ ltk_menuentry_cmd_create( static int ltk_menuentry_cmd_attach_submenu( ltk_window *window, + ltk_menuentry *e, char **tokens, size_t num_tokens, ltk_error *err) { - (void)window; - ltk_menuentry *e; - ltk_menu *submenu; - if (num_tokens != 4) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; - return 1; - } - e = (ltk_menuentry *)ltk_get_widget(tokens[1], LTK_WIDGET_MENUENTRY, err); - if (!e) { - err->arg = 1; + ltk_cmdarg_parseinfo cmd[] = { + {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_MENU, .optional = 0}, + }; + if (ltk_parse_cmd(window, tokens, num_tokens, cmd, LENGTH(cmd), err)) return 1; - } - submenu = (ltk_menu *)ltk_get_widget(tokens[3], LTK_WIDGET_MENU, err); - if (!submenu) { - err->arg = 3; - return 1; - } - - if (ltk_menuentry_attach_submenu(e, submenu, err)) { - err->arg = err->type == ERR_MENU_NOT_SUBMENU ? 3 : 1; + if (ltk_menuentry_attach_submenu(e, (ltk_menu *)cmd[0].val.widget, err)) { + /* FIXME: allow setting err->arg to arg before the args given to function */ + /*err->arg = err->type == ERR_MENU_NOT_SUBMENU ? 0 : -2;*/ + err->arg = 0; return 1; } return 0; @@ -1586,86 +1522,43 @@ ltk_menuentry_cmd_attach_submenu( static int ltk_menuentry_cmd_detach_submenu( ltk_window *window, + ltk_menuentry *e, char **tokens, size_t num_tokens, ltk_error *err) { (void)window; - ltk_menuentry *e; - if (num_tokens != 3) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; - return 1; - } - e = (ltk_menuentry *)ltk_get_widget(tokens[1], LTK_WIDGET_MENUENTRY, err); - if (!e) { - err->arg = 1; - return 1; - } - + (void)tokens; + (void)num_tokens; + (void)err; ltk_menuentry_detach_submenu(e); - return 0; } /* FIXME: sort out menu/submenu - it's weird right now */ -/* FIXME: binary search for command handler */ /* FIXME: distinguish between menu/submenu in commands other than create? */ -/* [sub]menu <menu id> <command> ... */ -int -ltk_menu_cmd( - ltk_window *window, - char **tokens, - size_t num_tokens, - ltk_error *err) { - if (num_tokens < 3) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; - return 1; - } - if (strcmp(tokens[2], "create") == 0) { - return ltk_menu_cmd_create(window, tokens, num_tokens, err); - } else if (strcmp(tokens[2], "insert-entry") == 0) { - return ltk_menu_cmd_insert_entry(window, tokens, num_tokens, err); - } else if (strcmp(tokens[2], "add-entry") == 0) { - return ltk_menu_cmd_add_entry(window, tokens, num_tokens, err); - } else if (strcmp(tokens[2], "remove-entry-index") == 0) { - return ltk_menu_cmd_remove_entry_index(window, tokens, num_tokens, err); - } else if (strcmp(tokens[2], "remove-entry-id") == 0) { - return ltk_menu_cmd_remove_entry_id(window, tokens, num_tokens, err); - } else if (strcmp(tokens[2], "remove-all-entries") == 0) { - return ltk_menu_cmd_remove_all_entries(window, tokens, num_tokens, err); - } else { - err->type = ERR_INVALID_COMMAND; - err->arg = -1; - return 1; - } - return 0; -} +static struct menu_cmd { + char *name; + int (*func)(ltk_window *, ltk_menu *, char **, size_t, ltk_error *); + int needs_all; +} menu_cmds[] = { + {"add-entry", &ltk_menu_cmd_add_entry, 0}, + {"create", &ltk_menu_cmd_create, 1}, + {"insert-entry", &ltk_menu_cmd_insert_entry, 0}, + {"remove-all-entries", &ltk_menu_cmd_remove_all_entries, 0}, + {"remove-entry-index", &ltk_menu_cmd_remove_entry_index, 0}, + {"remove-entry-id", &ltk_menu_cmd_remove_entry_id, 0}, +}; -/* menuentry <menuentry id> <command> ... */ -int -ltk_menuentry_cmd( - ltk_window *window, - char **tokens, - size_t num_tokens, - ltk_error *err) { - if (num_tokens < 3) { - err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; - err->arg = -1; - return 1; - } - if (strcmp(tokens[2], "create") == 0) { - return ltk_menuentry_cmd_create(window, tokens, num_tokens, err); - } else if (strcmp(tokens[2], "attach-submenu") == 0) { - return ltk_menuentry_cmd_attach_submenu(window, tokens, num_tokens, err); - } else if (strcmp(tokens[2], "detach-submenu") == 0) { - return ltk_menuentry_cmd_detach_submenu(window, tokens, num_tokens, err); - } else { - err->type = ERR_INVALID_COMMAND; - err->arg = -1; - return 1; - } +static struct menuentry_cmd { + char *name; + int (*func)(ltk_window *, ltk_menuentry *, char **, size_t, ltk_error *); + int needs_all; +} menuentry_cmds[] = { + {"attach-submenu", &ltk_menuentry_cmd_attach_submenu, 0}, + {"create", &ltk_menuentry_cmd_create, 1}, + {"detach-submenu", &ltk_menuentry_cmd_detach_submenu, 0}, +}; - return 0; -} +GEN_CMD_HELPERS(ltk_menu_cmd, LTK_WIDGET_MENU, ltk_menu, menu_cmds, struct menu_cmd) +GEN_CMD_HELPERS(ltk_menuentry_cmd, LTK_WIDGET_MENUENTRY, ltk_menuentry, menuentry_cmds, struct menuentry_cmd) diff --git a/src/widget.h b/src/widget.h @@ -40,6 +40,7 @@ typedef enum { } ltk_widget_flags; typedef enum { + LTK_STICKY_NONE = 0, LTK_STICKY_LEFT = 1 << 0, LTK_STICKY_RIGHT = 1 << 1, LTK_STICKY_TOP = 1 << 2,