menu.c (8150B)
1 /* 2 * Copyright (c) 2022-2024 lumidify <nobody@lumidify.org> 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include <stddef.h> 18 #include <string.h> 19 20 #include "err.h" 21 #include "ltkd.h" 22 #include "widget.h" 23 #include "cmd.h" 24 #include "proto_types.h" 25 26 #include <ltk/ltk.h> 27 #include <ltk/util.h> 28 #include <ltk/menu.h> 29 30 /* [sub]menu <menu id> create */ 31 static int 32 ltkd_menu_cmd_create( 33 ltk_window *window, 34 ltkd_widget *widget_unneeded, 35 ltkd_cmd_token *tokens, 36 size_t num_tokens, 37 ltkd_error *err) { 38 (void)widget_unneeded; 39 ltkd_cmdarg_parseinfo cmd[] = { 40 {.type = CMDARG_STRING, .optional = 0}, 41 {.type = CMDARG_STRING, .optional = 0}, 42 {.type = CMDARG_IGNORE, .optional = 0}, 43 }; 44 if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err)) 45 return 1; 46 ltk_menu *menu; 47 if (!strcmp(cmd[0].val.str, "menu")) { 48 menu = ltk_menu_create(window); 49 } else { 50 menu = ltk_submenu_create(window); 51 } 52 if (!ltkd_widget_create(LTK_CAST_WIDGET(menu), cmd[1].val.str, NULL, 0, err)) { 53 ltk_widget_destroy(LTK_CAST_WIDGET(menu), 1); 54 err->arg = 1; 55 return 1; 56 } 57 return 0; 58 } 59 60 /* menu <menu id> insert-entry <entry widget id> <index> */ 61 static int 62 ltkd_menu_cmd_insert_entry( 63 ltk_window *window, 64 ltkd_widget *widget, 65 ltkd_cmd_token *tokens, 66 size_t num_tokens, 67 ltkd_error *err) { 68 (void)window; 69 ltk_menu *menu = LTK_CAST_MENU(widget->widget); 70 ltkd_cmdarg_parseinfo cmd[] = { 71 {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_MENUENTRY, .optional = 0}, 72 {.type = CMDARG_INT, .min = 0, .max = menu->num_entries, .optional = 0}, 73 }; 74 if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err)) 75 return 1; 76 int ret; 77 if ((ret = ltk_menu_insert_entry(menu, LTK_CAST_MENUENTRY(cmd[0].val.widget->widget), cmd[1].val.i))) { 78 err->type = ret == 1 ? ERR_WIDGET_IN_CONTAINER : ERR_INVALID_INDEX; 79 err->arg = err->type == ERR_WIDGET_IN_CONTAINER ? 0 : 1; 80 return 1; 81 } 82 return 0; 83 } 84 85 /* menu <menu id> add-entry <entry widget id> */ 86 static int 87 ltkd_menu_cmd_add_entry( 88 ltk_window *window, 89 ltkd_widget *widget, 90 ltkd_cmd_token *tokens, 91 size_t num_tokens, 92 ltkd_error *err) { 93 (void)window; 94 ltk_menu *menu = LTK_CAST_MENU(widget->widget); 95 ltkd_cmdarg_parseinfo cmd[] = { 96 {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_MENUENTRY, .optional = 0}, 97 }; 98 if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err)) 99 return 1; 100 if (ltk_menu_add_entry(menu, LTK_CAST_MENUENTRY(cmd[0].val.widget->widget))) { 101 err->type = ERR_WIDGET_IN_CONTAINER; 102 err->arg = 0; 103 return 1; 104 } 105 return 0; 106 } 107 108 /* menu <menu id> remove-entry-index <entry index> */ 109 static int 110 ltkd_menu_cmd_remove_entry_index( 111 ltk_window *window, 112 ltkd_widget *widget, 113 ltkd_cmd_token *tokens, 114 size_t num_tokens, 115 ltkd_error *err) { 116 (void)window; 117 ltk_menu *menu = LTK_CAST_MENU(widget->widget); 118 ltkd_cmdarg_parseinfo cmd[] = { 119 {.type = CMDARG_INT, .min = 0, .max = menu->num_entries - 1, .optional = 0}, 120 }; 121 if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err)) 122 return 1; 123 if (!ltk_menu_remove_entry_index(menu, cmd[0].val.i)) { 124 err->type = ERR_WIDGET_NOT_IN_CONTAINER; 125 err->arg = 0; 126 return 1; 127 } 128 return 0; 129 } 130 131 /* menu <menu id> remove-entry-id <entry id> */ 132 static int 133 ltkd_menu_cmd_remove_entry_id( 134 ltk_window *window, 135 ltkd_widget *widget, 136 ltkd_cmd_token *tokens, 137 size_t num_tokens, 138 ltkd_error *err) { 139 (void)window; 140 ltk_menu *menu = LTK_CAST_MENU(widget->widget); 141 ltkd_cmdarg_parseinfo cmd[] = { 142 {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_MENUENTRY, .optional = 0}, 143 }; 144 if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err)) 145 return 1; 146 ltk_menuentry *entry = LTK_CAST_MENUENTRY(cmd[0].val.widget->widget); 147 if (!ltk_menu_remove_entry(menu, entry)) { 148 err->type = ERR_WIDGET_NOT_IN_CONTAINER; 149 err->arg = 0; 150 return 1; 151 } 152 return 0; 153 } 154 155 /* menu <menu id> remove-all-entries */ 156 static int 157 ltkd_menu_cmd_remove_all_entries( 158 ltk_window *window, 159 ltkd_widget *widget, 160 ltkd_cmd_token *tokens, 161 size_t num_tokens, 162 ltkd_error *err) { 163 (void)window; 164 (void)tokens; 165 (void)num_tokens; 166 (void)err; 167 ltk_menu *menu = LTK_CAST_MENU(widget->widget); 168 ltk_menu_remove_all_entries(menu); 169 return 0; 170 } 171 172 static int 173 ltkd_menuentry_press(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg) { 174 (void)widget_unused; 175 (void)args; 176 ltkd_widget *widget = LTK_CAST_ARG_VOIDP(arg); 177 return ltkd_widget_queue_specific_event(widget, "menuentry", LTKD_PWEVENTMASK_MENUENTRY_PRESS, "press"); 178 } 179 180 static ltkd_event_handler entry_handlers[] = { 181 {<kd_menuentry_press, LTK_MENUENTRY_SIGNAL_PRESSED}, 182 }; 183 184 /* menuentry <id> create <text> */ 185 static int 186 ltkd_menuentry_cmd_create( 187 ltk_window *window, 188 ltkd_widget *widget_unneeded, 189 ltkd_cmd_token *tokens, 190 size_t num_tokens, 191 ltkd_error *err) { 192 (void)widget_unneeded; 193 ltkd_cmdarg_parseinfo cmd[] = { 194 {.type = CMDARG_STRING, .optional = 0}, 195 {.type = CMDARG_STRING, .optional = 0}, 196 {.type = CMDARG_IGNORE, .optional = 0}, 197 {.type = CMDARG_STRING, .optional = 0}, 198 }; 199 if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err)) 200 return 1; 201 ltk_menuentry *e = ltk_menuentry_create(window, cmd[3].val.str); 202 if (!ltkd_widget_create(LTK_CAST_WIDGET(e), cmd[1].val.str, entry_handlers, LENGTH(entry_handlers), err)) { 203 ltk_widget_destroy(LTK_CAST_WIDGET(e), 1); 204 err->arg = 1; 205 return 1; 206 } 207 return 0; 208 } 209 210 /* menuentry <menuentry id> attach-submenu <submenu id> */ 211 static int 212 ltkd_menuentry_cmd_attach_submenu( 213 ltk_window *window, 214 ltkd_widget *widget, 215 ltkd_cmd_token *tokens, 216 size_t num_tokens, 217 ltkd_error *err) { 218 (void)window; 219 ltk_menuentry *e = LTK_CAST_MENUENTRY(widget->widget); 220 ltkd_cmdarg_parseinfo cmd[] = { 221 {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_MENU, .optional = 0}, 222 }; 223 if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err)) 224 return 1; 225 int ret; 226 if ((ret = ltk_menuentry_attach_submenu(e, LTK_CAST_MENU(cmd[0].val.widget->widget)))) { 227 /* FIXME: allow setting err->arg to arg before the args given to function */ 228 /*err->arg = err->type == ERR_MENU_NOT_SUBMENU ? 0 : -2;*/ 229 err->type = ret == 1 ? ERR_MENU_NOT_SUBMENU : ERR_MENU_ENTRY_CONTAINS_SUBMENU; 230 err->arg = 0; 231 return 1; 232 } 233 return 0; 234 } 235 236 /* menuentry <menuentry id> detach-submenu */ 237 static int 238 ltkd_menuentry_cmd_detach_submenu( 239 ltk_window *window, 240 ltkd_widget *widget, 241 ltkd_cmd_token *tokens, 242 size_t num_tokens, 243 ltkd_error *err) { 244 (void)window; 245 (void)tokens; 246 (void)num_tokens; 247 (void)err; 248 ltk_menuentry *e = LTK_CAST_MENUENTRY(widget->widget); 249 ltk_menuentry_detach_submenu(e); 250 return 0; 251 } 252 253 /* FIXME: sort out menu/submenu - it's weird right now */ 254 /* FIXME: distinguish between menu/submenu in commands other than create? */ 255 256 static ltkd_cmd_info menu_cmds[] = { 257 {"add-entry", <kd_menu_cmd_add_entry, 0}, 258 {"create", <kd_menu_cmd_create, 1}, 259 {"insert-entry", <kd_menu_cmd_insert_entry, 0}, 260 {"remove-all-entries", <kd_menu_cmd_remove_all_entries, 0}, 261 {"remove-entry-index", <kd_menu_cmd_remove_entry_index, 0}, 262 {"remove-entry-id", <kd_menu_cmd_remove_entry_id, 0}, 263 }; 264 265 static ltkd_cmd_info menuentry_cmds[] = { 266 {"attach-submenu", <kd_menuentry_cmd_attach_submenu, 0}, 267 {"create", <kd_menuentry_cmd_create, 1}, 268 {"detach-submenu", <kd_menuentry_cmd_detach_submenu, 0}, 269 }; 270 271 GEN_CMD_HELPERS(ltkd_menu_cmd, LTK_WIDGET_MENU, menu_cmds) 272 GEN_CMD_HELPERS(ltkd_menuentry_cmd, LTK_WIDGET_MENUENTRY, menuentry_cmds)