URI:
       menu.c - ltk - GUI toolkit for X11 (WIP)
  HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
  HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltk.git (over tor)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR 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)