ltk

GUI toolkit for X11 (WIP)
git clone git://lumidify.org/ltk.git (fast, but not encrypted)
git clone https://lumidify.org/ltk.git (encrypted, but very slow)
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltk.git (over tor)
Log | Files | Refs | README | LICENSE

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 	{&ltkd_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", &ltkd_menu_cmd_add_entry, 0},
    256 	{"create", &ltkd_menu_cmd_create, 1},
    257 	{"insert-entry", &ltkd_menu_cmd_insert_entry, 0},
    258 	{"remove-all-entries", &ltkd_menu_cmd_remove_all_entries, 0},
    259 	{"remove-entry-index", &ltkd_menu_cmd_remove_entry_index, 0},
    260 	{"remove-entry-id", &ltkd_menu_cmd_remove_entry_id, 0},
    261 };
    262 
    263 static ltkd_cmd_info menuentry_cmds[] = {
    264 	{"attach-submenu", &ltkd_menuentry_cmd_attach_submenu, 0},
    265 	{"create", &ltkd_menuentry_cmd_create, 1},
    266 	{"detach-submenu", &ltkd_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)