menu.c (8042B)
1 /* 2 * Copyright (c) 2022-2026 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 #include <ltk/array.h> 30 31 /* [sub]menu <menu id> create */ 32 static int 33 ltkd_menu_cmd_create( 34 ltk_widget_id windowid, 35 ltkd_widget *widget_unneeded, 36 ltkd_cmd_token *tokens, 37 size_t num_tokens, 38 ltkd_error *err) { 39 (void)widget_unneeded; 40 ltkd_cmdarg_parseinfo cmd[] = { 41 {.type = CMDARG_STRING, .optional = 0}, 42 {.type = CMDARG_STRING, .optional = 0}, 43 {.type = CMDARG_IGNORE, .optional = 0}, 44 }; 45 if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err)) 46 return 1; 47 ltk_widget_id menu; 48 if (!strcmp(cmd[0].val.str, "menu")) { 49 menu = ltk_menu_create(windowid); 50 } else { 51 menu = ltk_submenu_create(windowid); 52 } 53 if (!ltkd_widget_create(menu, cmd[1].val.str, NULL, 0, err)) { 54 ltk_widget_id_destroy(menu, 1); 55 err->arg = 1; 56 return 1; 57 } 58 return 0; 59 } 60 61 /* menu <menu id> insert-entry <entry widget id> <index> */ 62 static int 63 ltkd_menu_cmd_insert_entry( 64 ltk_widget_id windowid, 65 ltkd_widget *widget, 66 ltkd_cmd_token *tokens, 67 size_t num_tokens, 68 ltkd_error *err) { 69 (void)windowid; 70 ltk_widget *menuw = ltk_get_widget_from_id(widget->ltkid); 71 ltk_menu *menu = LTK_CAST_MENU(menuw); 72 ltkd_cmdarg_parseinfo cmd[] = { 73 {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_MENUENTRY, .optional = 0}, 74 {.type = CMDARG_INT, .min = 0, .max = ltk_array_len(menu->entries), .optional = 0}, 75 }; 76 if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err)) 77 return 1; 78 int ret; 79 if ((ret = ltk_menu_insert_entry(widget->ltkid, cmd[0].val.widget->ltkid, cmd[1].val.i))) { 80 err->type = ret == 1 ? ERR_WIDGET_IN_CONTAINER : ERR_INVALID_INDEX; 81 err->arg = err->type == ERR_WIDGET_IN_CONTAINER ? 0 : 1; 82 return 1; 83 } 84 return 0; 85 } 86 87 /* menu <menu id> add-entry <entry widget id> */ 88 static int 89 ltkd_menu_cmd_add_entry( 90 ltk_widget_id windowid, 91 ltkd_widget *widget, 92 ltkd_cmd_token *tokens, 93 size_t num_tokens, 94 ltkd_error *err) { 95 (void)windowid; 96 ltkd_cmdarg_parseinfo cmd[] = { 97 {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_MENUENTRY, .optional = 0}, 98 }; 99 if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err)) 100 return 1; 101 if (ltk_menu_add_entry(widget->ltkid, cmd[0].val.widget->ltkid)) { 102 err->type = ERR_WIDGET_IN_CONTAINER; 103 err->arg = 0; 104 return 1; 105 } 106 return 0; 107 } 108 109 /* menu <menu id> remove-entry-index <entry index> */ 110 static int 111 ltkd_menu_cmd_remove_entry_index( 112 ltk_widget_id windowid, 113 ltkd_widget *widget, 114 ltkd_cmd_token *tokens, 115 size_t num_tokens, 116 ltkd_error *err) { 117 (void)windowid; 118 ltk_widget *menuw = ltk_get_widget_from_id(widget->ltkid); 119 ltk_menu *menu = LTK_CAST_MENU(menuw); 120 ltkd_cmdarg_parseinfo cmd[] = { 121 {.type = CMDARG_INT, .min = 0, .max = ltk_array_len(menu->entries) - 1, .optional = 0}, 122 }; 123 if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err)) 124 return 1; 125 ltk_widget_id ret = ltk_menu_remove_entry_index(widget->ltkid, cmd[0].val.i); 126 if (LTK_WIDGET_ID_IS_NONE(ret)) { 127 err->type = ERR_WIDGET_NOT_IN_CONTAINER; 128 err->arg = 0; 129 return 1; 130 } 131 return 0; 132 } 133 134 /* menu <menu id> remove-entry-id <entry id> */ 135 static int 136 ltkd_menu_cmd_remove_entry_id( 137 ltk_widget_id windowid, 138 ltkd_widget *widget, 139 ltkd_cmd_token *tokens, 140 size_t num_tokens, 141 ltkd_error *err) { 142 (void)windowid; 143 ltkd_cmdarg_parseinfo cmd[] = { 144 {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_MENUENTRY, .optional = 0}, 145 }; 146 if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err)) 147 return 1; 148 if (!ltk_menu_remove_entry(widget->ltkid, cmd[0].val.widget->ltkid)) { 149 err->type = ERR_WIDGET_NOT_IN_CONTAINER; 150 err->arg = 0; 151 return 1; 152 } 153 return 0; 154 } 155 156 /* menu <menu id> remove-all-entries */ 157 static int 158 ltkd_menu_cmd_remove_all_entries( 159 ltk_widget_id windowid, 160 ltkd_widget *widget, 161 ltkd_cmd_token *tokens, 162 size_t num_tokens, 163 ltkd_error *err) { 164 (void)windowid; 165 (void)tokens; 166 (void)num_tokens; 167 (void)err; 168 ltk_menu_remove_all_entries(widget->ltkid); 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_widget_id windowid, 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_widget_id e = ltk_menuentry_create(windowid, cmd[3].val.str); 202 if (!ltkd_widget_create(e, cmd[1].val.str, entry_handlers, LENGTH(entry_handlers), err)) { 203 ltk_widget_id_destroy(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_widget_id windowid, 214 ltkd_widget *widget, 215 ltkd_cmd_token *tokens, 216 size_t num_tokens, 217 ltkd_error *err) { 218 (void)windowid; 219 ltkd_cmdarg_parseinfo cmd[] = { 220 {.type = CMDARG_WIDGET, .widget_type = LTK_WIDGET_MENU, .optional = 0}, 221 }; 222 if (ltkd_parse_cmd(tokens, num_tokens, cmd, LENGTH(cmd), err)) 223 return 1; 224 int ret; 225 if ((ret = ltk_menuentry_attach_submenu(widget->ltkid, cmd[0].val.widget->ltkid))) { 226 /* FIXME: allow setting err->arg to arg before the args given to function */ 227 /*err->arg = err->type == ERR_MENU_NOT_SUBMENU ? 0 : -2;*/ 228 err->type = ret == 1 ? ERR_MENU_NOT_SUBMENU : ERR_MENU_ENTRY_CONTAINS_SUBMENU; 229 err->arg = 0; 230 return 1; 231 } 232 return 0; 233 } 234 235 /* menuentry <menuentry id> detach-submenu */ 236 static int 237 ltkd_menuentry_cmd_detach_submenu( 238 ltk_widget_id windowid, 239 ltkd_widget *widget, 240 ltkd_cmd_token *tokens, 241 size_t num_tokens, 242 ltkd_error *err) { 243 (void)windowid; 244 (void)tokens; 245 (void)num_tokens; 246 (void)err; 247 ltk_menuentry_detach_submenu(widget->ltkid); 248 return 0; 249 } 250 251 /* FIXME: sort out menu/submenu - it's weird right now */ 252 /* FIXME: distinguish between menu/submenu in commands other than create? */ 253 254 static ltkd_cmd_info menu_cmds[] = { 255 {"add-entry", <kd_menu_cmd_add_entry, 0}, 256 {"create", <kd_menu_cmd_create, 1}, 257 {"insert-entry", <kd_menu_cmd_insert_entry, 0}, 258 {"remove-all-entries", <kd_menu_cmd_remove_all_entries, 0}, 259 {"remove-entry-index", <kd_menu_cmd_remove_entry_index, 0}, 260 {"remove-entry-id", <kd_menu_cmd_remove_entry_id, 0}, 261 }; 262 263 static ltkd_cmd_info menuentry_cmds[] = { 264 {"attach-submenu", <kd_menuentry_cmd_attach_submenu, 0}, 265 {"create", <kd_menuentry_cmd_create, 1}, 266 {"detach-submenu", <kd_menuentry_cmd_detach_submenu, 0}, 267 }; 268 269 GEN_CMD_HELPERS(ltkd_menu_cmd, LTK_WIDGET_MENU, menu_cmds) 270 GEN_CMD_HELPERS(ltkd_menuentry_cmd, LTK_WIDGET_MENUENTRY, menuentry_cmds)