mirror of
https://github.com/labwc/labwc.git
synced 2025-10-29 05:40:24 -04:00
menu: refactor parser
...with the same approach as rcxml.c - `If` actions now works for menus - `name` argument no longer have to be the first argument of <action> - `label` argument no longer have to be the first argument of <item>
This commit is contained in:
parent
bfaab101af
commit
17d66e5603
2 changed files with 133 additions and 246 deletions
|
|
@ -134,8 +134,7 @@ example: *LABWC_DEBUG_FOO=1 labwc*.
|
|||
Increase logging of paths for config files (for example rc.xml,
|
||||
autostart, environment and menu.xml) as well as titlebar buttons.
|
||||
|
||||
*LABWC_DEBUG_CONFIG_NODENAMES*++
|
||||
*LABWC_DEBUG_MENU_NODENAMES*
|
||||
*LABWC_DEBUG_CONFIG_NODENAMES*
|
||||
Enable logging of all nodenames (for example *policy.placement: Cascade*
|
||||
for *<placement><policy>Cascade</policy></placement>*) for config and
|
||||
menu files respectively.
|
||||
|
|
|
|||
376
src/menu/menu.c
376
src/menu/menu.c
|
|
@ -20,12 +20,12 @@
|
|||
#include "common/lab-scene-rect.h"
|
||||
#include "common/list.h"
|
||||
#include "common/mem.h"
|
||||
#include "common/nodename.h"
|
||||
#include "common/scaled-font-buffer.h"
|
||||
#include "common/scaled-icon-buffer.h"
|
||||
#include "common/scene-helpers.h"
|
||||
#include "common/spawn.h"
|
||||
#include "common/string-helpers.h"
|
||||
#include "common/xml.h"
|
||||
#include "labwc.h"
|
||||
#include "output.h"
|
||||
#include "workspaces.h"
|
||||
|
|
@ -38,15 +38,6 @@
|
|||
|
||||
#define ICON_SIZE (rc.theme->menu_item_height - 2 * rc.theme->menu_items_padding_y)
|
||||
|
||||
/* state-machine variables for processing <item></item> */
|
||||
struct menu_parse_context {
|
||||
struct server *server;
|
||||
struct menu *menu;
|
||||
struct menuitem *item;
|
||||
struct action *action;
|
||||
bool in_item;
|
||||
};
|
||||
|
||||
static bool waiting_for_pipe_menu;
|
||||
static struct menuitem *selected_item;
|
||||
|
||||
|
|
@ -140,7 +131,7 @@ validate(struct server *server)
|
|||
}
|
||||
|
||||
static struct menuitem *
|
||||
item_create(struct menu *menu, const char *text, bool show_arrow)
|
||||
item_create(struct menu *menu, const char *text, const char *icon_name, bool show_arrow)
|
||||
{
|
||||
assert(menu);
|
||||
assert(text);
|
||||
|
|
@ -152,6 +143,13 @@ item_create(struct menu *menu, const char *text, bool show_arrow)
|
|||
menuitem->text = xstrdup(text);
|
||||
menuitem->arrow = show_arrow ? "›" : NULL;
|
||||
|
||||
#if HAVE_LIBSFDO
|
||||
if (rc.menu_show_icons && !string_null_or_empty(icon_name)) {
|
||||
menuitem->icon_name = xstrdup(icon_name);
|
||||
menu->has_icons = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
menuitem->native_width = font_width(&rc.font_menuitem, text);
|
||||
if (menuitem->arrow) {
|
||||
menuitem->native_width += font_width(&rc.font_menuitem, menuitem->arrow);
|
||||
|
|
@ -473,34 +471,22 @@ menu_create_scene(struct menu *menu)
|
|||
* </item>
|
||||
*/
|
||||
static void
|
||||
fill_item(struct menu_parse_context *ctx, const char *nodename,
|
||||
const char *content)
|
||||
fill_item(struct menu *menu, xmlNode *node)
|
||||
{
|
||||
/* <item label=""> defines the start of a new item */
|
||||
if (!strcmp(nodename, "label")) {
|
||||
ctx->item = item_create(ctx->menu, content, false);
|
||||
ctx->action = NULL;
|
||||
} else if (!ctx->item) {
|
||||
wlr_log(WLR_ERROR, "expect <item label=\"\"> element first. "
|
||||
"nodename: '%s' content: '%s'", nodename, content);
|
||||
} else if (!strcmp(nodename, "icon")) {
|
||||
#if HAVE_LIBSFDO
|
||||
if (rc.menu_show_icons && !string_null_or_empty(content)) {
|
||||
xstrdup_replace(ctx->item->icon_name, content);
|
||||
ctx->menu->has_icons = true;
|
||||
}
|
||||
#endif
|
||||
} else if (!strcmp(nodename, "name.action")) {
|
||||
ctx->action = action_create(content);
|
||||
if (ctx->action) {
|
||||
wl_list_append(&ctx->item->actions, &ctx->action->link);
|
||||
}
|
||||
} else if (!ctx->action) {
|
||||
wlr_log(WLR_ERROR, "expect <action name=\"\"> element first. "
|
||||
"nodename: '%s' content: '%s'", nodename, content);
|
||||
} else {
|
||||
action_arg_from_xml_node(ctx->action, nodename, content);
|
||||
char *label = (char *)xmlGetProp(node, (xmlChar *)"label");
|
||||
char *icon_name = (char *)xmlGetProp(node, (xmlChar *)"icon");
|
||||
if (!label) {
|
||||
wlr_log(WLR_ERROR, "missing label in <item>");
|
||||
goto out;
|
||||
}
|
||||
|
||||
struct menuitem *item = item_create(menu, (char *)label, icon_name, false);
|
||||
lab_xml_expand_dotted_attributes(node);
|
||||
append_parsed_actions(node, &item->actions);
|
||||
|
||||
out:
|
||||
free(label);
|
||||
free(icon_name);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -516,103 +502,10 @@ item_destroy(struct menuitem *item)
|
|||
free(item);
|
||||
}
|
||||
|
||||
/*
|
||||
* We support XML CDATA for <command> in menu.xml in order to provide backward
|
||||
* compatibility with obmenu-generator. For example:
|
||||
*
|
||||
* <menu id="" label="">
|
||||
* <item label="">
|
||||
* <action name="Execute">
|
||||
* <command><![CDATA[xdg-open .]]></command>
|
||||
* </action>
|
||||
* </item>
|
||||
* </menu>
|
||||
*
|
||||
* <execute> is an old, deprecated openbox variety of <command>. We support it
|
||||
* for backward compatibility with old openbox-menu generators. It has the same
|
||||
* function and <command>
|
||||
*
|
||||
* The following nodenames support CDATA.
|
||||
* - command.action.item.*menu.openbox_menu
|
||||
* - execute.action.item.*menu.openbox_menu
|
||||
* - command.action.item.openbox_pipe_menu
|
||||
* - execute.action.item.openbox_pipe_menu
|
||||
* - command.action.item.*menu.openbox_pipe_menu
|
||||
* - execute.action.item.*menu.openbox_pipe_menu
|
||||
*
|
||||
* The *menu allows nested menus with nodenames such as ...menu.menu... or
|
||||
* ...menu.menu.menu... and so on. We could use match_glob() for all of the
|
||||
* above but it seems simpler to just check the first three fields.
|
||||
*/
|
||||
static bool
|
||||
nodename_supports_cdata(char *nodename)
|
||||
{
|
||||
return !strncmp("command.action.", nodename, 15)
|
||||
|| !strncmp("execute.action.", nodename, 15);
|
||||
}
|
||||
|
||||
static void
|
||||
entry(struct menu_parse_context *ctx, xmlNode *node, char *nodename,
|
||||
char *content)
|
||||
{
|
||||
if (!nodename) {
|
||||
return;
|
||||
}
|
||||
xmlChar *cdata = NULL;
|
||||
if (!content && nodename_supports_cdata(nodename)) {
|
||||
cdata = xmlNodeGetContent(node);
|
||||
}
|
||||
if (!content && !cdata) {
|
||||
return;
|
||||
}
|
||||
string_truncate_at_pattern(nodename, ".openbox_menu");
|
||||
string_truncate_at_pattern(nodename, ".openbox_pipe_menu");
|
||||
if (getenv("LABWC_DEBUG_MENU_NODENAMES")) {
|
||||
printf("%s: %s\n", nodename, content ? content : (char *)cdata);
|
||||
}
|
||||
if (ctx->in_item) {
|
||||
/*
|
||||
* Nodenames for most menu-items end with '.item.menu'
|
||||
* but top-level pipemenu items do not have the associated
|
||||
* <menu> element so merely end with '.item'
|
||||
*/
|
||||
string_truncate_at_pattern(nodename, ".item.menu");
|
||||
string_truncate_at_pattern(nodename, ".item");
|
||||
fill_item(ctx, nodename, content ? content : (char *)cdata);
|
||||
}
|
||||
xmlFree(cdata);
|
||||
}
|
||||
|
||||
static void
|
||||
process_node(struct menu_parse_context *ctx, xmlNode *node)
|
||||
{
|
||||
static char buffer[256];
|
||||
|
||||
char *content = (char *)node->content;
|
||||
if (xmlIsBlankNode(node)) {
|
||||
return;
|
||||
}
|
||||
char *name = nodename(node, buffer, sizeof(buffer));
|
||||
entry(ctx, node, name, content);
|
||||
}
|
||||
|
||||
static void xml_tree_walk(struct menu_parse_context *ctx, xmlNode *node);
|
||||
|
||||
static void
|
||||
traverse(struct menu_parse_context *ctx, xmlNode *n)
|
||||
{
|
||||
xmlAttr *attr;
|
||||
|
||||
process_node(ctx, n);
|
||||
for (attr = n->properties; attr; attr = attr->next) {
|
||||
xml_tree_walk(ctx, attr->children);
|
||||
}
|
||||
xml_tree_walk(ctx, n->children);
|
||||
}
|
||||
|
||||
static bool parse_buf(struct menu_parse_context *ctx, struct buf *buf);
|
||||
static bool parse_buf(struct server *server, struct menu *menu, struct buf *buf);
|
||||
static int handle_pipemenu_readable(int fd, uint32_t mask, void *_ctx);
|
||||
static int handle_pipemenu_timeout(void *_ctx);
|
||||
static void fill_menu_children(struct server *server, struct menu *parent, xmlNode *n);
|
||||
|
||||
/*
|
||||
* <menu> elements have three different roles:
|
||||
|
|
@ -621,7 +514,7 @@ static int handle_pipemenu_timeout(void *_ctx);
|
|||
* * Menuitem of submenu type - has ID only
|
||||
*/
|
||||
static void
|
||||
handle_menu_element(struct menu_parse_context *ctx, xmlNode *n)
|
||||
fill_menu(struct server *server, struct menu *parent, xmlNode *n)
|
||||
{
|
||||
char *label = (char *)xmlGetProp(n, (const xmlChar *)"label");
|
||||
char *icon_name = (char *)xmlGetProp(n, (const xmlChar *)"icon");
|
||||
|
|
@ -636,10 +529,9 @@ handle_menu_element(struct menu_parse_context *ctx, xmlNode *n)
|
|||
if (execute && label) {
|
||||
wlr_log(WLR_DEBUG, "pipemenu '%s:%s:%s'", id, label, execute);
|
||||
|
||||
struct menu *pipemenu =
|
||||
menu_create(ctx->server, ctx->menu, id, label);
|
||||
struct menu *pipemenu = menu_create(server, parent, id, label);
|
||||
pipemenu->execute = xstrdup(execute);
|
||||
if (!ctx->menu) {
|
||||
if (!parent) {
|
||||
/*
|
||||
* A pipemenu may not have its parent like:
|
||||
*
|
||||
|
|
@ -649,18 +541,16 @@ handle_menu_element(struct menu_parse_context *ctx, xmlNode *n)
|
|||
* </openbox_menu>
|
||||
*/
|
||||
} else {
|
||||
ctx->item = item_create(ctx->menu, label,
|
||||
/* arrow */ true);
|
||||
fill_item(ctx, "icon", icon_name);
|
||||
ctx->action = NULL;
|
||||
ctx->item->submenu = pipemenu;
|
||||
struct menuitem *item = item_create(parent, label,
|
||||
icon_name, /* arrow */ true);
|
||||
item->submenu = pipemenu;
|
||||
}
|
||||
} else if ((label && ctx->menu) || !ctx->menu) {
|
||||
} else if ((label && parent) || !parent) {
|
||||
/*
|
||||
* (label && ctx->menu) refers to <menu id="" label="">
|
||||
* (label && parent) refers to <menu id="" label="">
|
||||
* which is an nested (inline) menu definition.
|
||||
*
|
||||
* (!ctx->menu) catches:
|
||||
* (!parent) catches:
|
||||
* <openbox_menu>
|
||||
* <menu id=""></menu>
|
||||
* </openbox_menu>
|
||||
|
|
@ -676,22 +566,20 @@ handle_menu_element(struct menu_parse_context *ctx, xmlNode *n)
|
|||
* attribute to make it easier for users to define "root-menu"
|
||||
* and "client-menu".
|
||||
*/
|
||||
struct menu *parent_menu = ctx->menu;
|
||||
ctx->menu = menu_create(ctx->server, parent_menu, id, label);
|
||||
struct menu *menu = menu_create(server, parent, id, label);
|
||||
if (icon_name) {
|
||||
ctx->menu->icon_name = xstrdup(icon_name);
|
||||
menu->icon_name = xstrdup(icon_name);
|
||||
}
|
||||
if (label && parent_menu) {
|
||||
if (label && parent) {
|
||||
/*
|
||||
* In a nested (inline) menu definition we need to
|
||||
* create an item pointing to the new submenu
|
||||
*/
|
||||
ctx->item = item_create(parent_menu, label, true);
|
||||
fill_item(ctx, "icon", icon_name);
|
||||
ctx->item->submenu = ctx->menu;
|
||||
struct menuitem *item = item_create(parent, label,
|
||||
icon_name, true);
|
||||
item->submenu = menu;
|
||||
}
|
||||
traverse(ctx, n);
|
||||
ctx->menu = parent_menu;
|
||||
fill_menu_children(server, menu, n);
|
||||
} else {
|
||||
/*
|
||||
* <menu id=""> (when inside another <menu> element) creates an
|
||||
|
|
@ -708,13 +596,13 @@ handle_menu_element(struct menu_parse_context *ctx, xmlNode *n)
|
|||
goto error;
|
||||
}
|
||||
|
||||
struct menu *menu = menu_get_by_id(ctx->server, id);
|
||||
struct menu *menu = menu_get_by_id(server, id);
|
||||
if (!menu) {
|
||||
wlr_log(WLR_ERROR, "no menu with id '%s'", id);
|
||||
goto error;
|
||||
}
|
||||
|
||||
struct menu *iter = ctx->menu;
|
||||
struct menu *iter = parent;
|
||||
while (iter) {
|
||||
if (iter == menu) {
|
||||
wlr_log(WLR_ERROR, "menus with the same id '%s' "
|
||||
|
|
@ -724,9 +612,9 @@ handle_menu_element(struct menu_parse_context *ctx, xmlNode *n)
|
|||
iter = iter->parent;
|
||||
}
|
||||
|
||||
ctx->item = item_create(ctx->menu, menu->label, true);
|
||||
fill_item(ctx, "icon", menu->icon_name);
|
||||
ctx->item->submenu = menu;
|
||||
struct menuitem *item = item_create(parent, menu->label,
|
||||
parent->icon_name, true);
|
||||
item->submenu = menu;
|
||||
}
|
||||
error:
|
||||
free(label);
|
||||
|
|
@ -737,50 +625,42 @@ error:
|
|||
|
||||
/* This can be one of <separator> and <separator label=""> */
|
||||
static void
|
||||
handle_separator_element(struct menu_parse_context *ctx, xmlNode *n)
|
||||
fill_separator(struct menu *menu, xmlNode *n)
|
||||
{
|
||||
char *label = (char *)xmlGetProp(n, (const xmlChar *)"label");
|
||||
ctx->item = separator_create(ctx->menu, label);
|
||||
separator_create(menu, label);
|
||||
free(label);
|
||||
}
|
||||
|
||||
/* parent==NULL when processing toplevel menus in menu.xml */
|
||||
static void
|
||||
xml_tree_walk(struct menu_parse_context *ctx, xmlNode *node)
|
||||
fill_menu_children(struct server *server, struct menu *parent, xmlNode *n)
|
||||
{
|
||||
for (xmlNode *n = node; n && n->name; n = n->next) {
|
||||
if (!strcasecmp((char *)n->name, "comment")) {
|
||||
continue;
|
||||
}
|
||||
if (!strcasecmp((char *)n->name, "menu")) {
|
||||
handle_menu_element(ctx, n);
|
||||
continue;
|
||||
}
|
||||
if (!strcasecmp((char *)n->name, "separator")) {
|
||||
if (!ctx->menu) {
|
||||
xmlNode *child;
|
||||
char *key, *content;
|
||||
LAB_XML_FOR_EACH(n, child, key, content) {
|
||||
if (!strcasecmp(key, "menu")) {
|
||||
fill_menu(server, parent, child);
|
||||
} else if (!strcasecmp(key, "separator")) {
|
||||
if (!parent) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"ignoring <separator> without parent <menu>");
|
||||
continue;
|
||||
}
|
||||
handle_separator_element(ctx, n);
|
||||
continue;
|
||||
}
|
||||
if (!strcasecmp((char *)n->name, "item")) {
|
||||
if (!ctx->menu) {
|
||||
fill_separator(parent, child);
|
||||
} else if (!strcasecmp(key, "item")) {
|
||||
if (!parent) {
|
||||
wlr_log(WLR_ERROR,
|
||||
"ignoring <item> without parent <menu>");
|
||||
continue;
|
||||
}
|
||||
ctx->in_item = true;
|
||||
traverse(ctx, n);
|
||||
ctx->in_item = false;
|
||||
continue;
|
||||
fill_item(parent, child);
|
||||
}
|
||||
traverse(ctx, n);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_buf(struct menu_parse_context *ctx, struct buf *buf)
|
||||
parse_buf(struct server *server, struct menu *parent, struct buf *buf)
|
||||
{
|
||||
int options = 0;
|
||||
xmlDoc *d = xmlReadMemory(buf->data, buf->len, NULL, NULL, options);
|
||||
|
|
@ -788,7 +668,10 @@ parse_buf(struct menu_parse_context *ctx, struct buf *buf)
|
|||
wlr_log(WLR_ERROR, "xmlParseMemory()");
|
||||
return false;
|
||||
}
|
||||
xml_tree_walk(ctx, xmlDocGetRootElement(d));
|
||||
|
||||
xmlNode *root = xmlDocGetRootElement(d);
|
||||
fill_menu_children(server, parent, root);
|
||||
|
||||
xmlFreeDoc(d);
|
||||
xmlCleanupParser();
|
||||
return true;
|
||||
|
|
@ -814,8 +697,7 @@ parse_stream(struct server *server, FILE *stream)
|
|||
buf_add(&b, line);
|
||||
}
|
||||
free(line);
|
||||
struct menu_parse_context ctx = {.server = server};
|
||||
parse_buf(&ctx, &b);
|
||||
parse_buf(server, NULL, &b);
|
||||
buf_reset(&b);
|
||||
}
|
||||
|
||||
|
|
@ -939,6 +821,14 @@ init_client_send_to_menu(struct server *server)
|
|||
menu_create(server, NULL, "client-send-to-menu", "");
|
||||
}
|
||||
|
||||
static struct action *
|
||||
item_add_action(struct menuitem *item, const char *action_name)
|
||||
{
|
||||
struct action *action = action_create(action_name);
|
||||
wl_list_append(&item->actions, &action->link);
|
||||
return action;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is client-send-to-menu
|
||||
* an internal menu similar to root-menu and client-menu
|
||||
|
|
@ -955,21 +845,22 @@ update_client_send_to_menu(struct server *server)
|
|||
|
||||
reset_menu(menu);
|
||||
|
||||
struct menu_parse_context ctx = {.server = server};
|
||||
struct workspace *workspace;
|
||||
|
||||
wl_list_for_each(workspace, &server->workspaces.all, link) {
|
||||
struct buf buf = BUF_INIT;
|
||||
if (workspace == server->workspaces.current) {
|
||||
char *label = strdup_printf(">%s<", workspace->name);
|
||||
ctx.item = item_create(menu, label,
|
||||
/*show arrow*/ false);
|
||||
free(label);
|
||||
buf_add_fmt(&buf, ">%s<", workspace->name);
|
||||
} else {
|
||||
ctx.item = item_create(menu, workspace->name,
|
||||
/*show arrow*/ false);
|
||||
buf_add(&buf, workspace->name);
|
||||
}
|
||||
fill_item(&ctx, "name.action", "SendToDesktop");
|
||||
fill_item(&ctx, "to.action", workspace->name);
|
||||
struct menuitem *item = item_create(menu, buf.data,
|
||||
NULL, /*show arrow*/ false);
|
||||
|
||||
struct action *action = item_add_action(item, "SendToDesktop");
|
||||
action_arg_add_str(action, "to", "name");
|
||||
|
||||
buf_clear(&buf);
|
||||
}
|
||||
|
||||
menu_create_scene(menu);
|
||||
|
|
@ -998,7 +889,7 @@ update_client_list_combined_menu(struct server *server)
|
|||
|
||||
reset_menu(menu);
|
||||
|
||||
struct menu_parse_context ctx = {.server = server};
|
||||
struct menuitem *item;
|
||||
struct workspace *workspace;
|
||||
struct view *view;
|
||||
struct buf buffer = BUF_INIT;
|
||||
|
|
@ -1006,7 +897,7 @@ update_client_list_combined_menu(struct server *server)
|
|||
wl_list_for_each(workspace, &server->workspaces.all, link) {
|
||||
buf_add_fmt(&buffer, workspace == server->workspaces.current ? ">%s<" : "%s",
|
||||
workspace->name);
|
||||
ctx.item = separator_create(menu, buffer.data);
|
||||
separator_create(menu, buffer.data);
|
||||
buf_clear(&buffer);
|
||||
|
||||
wl_list_for_each(view, &server->views, link) {
|
||||
|
|
@ -1021,19 +912,19 @@ update_client_list_combined_menu(struct server *server)
|
|||
}
|
||||
buf_add(&buffer, title);
|
||||
|
||||
ctx.item = item_create(menu, buffer.data,
|
||||
item = item_create(menu, buffer.data, NULL,
|
||||
/*show arrow*/ false);
|
||||
ctx.item->client_list_view = view;
|
||||
fill_item(&ctx, "name.action", "Focus");
|
||||
fill_item(&ctx, "name.action", "Raise");
|
||||
item->client_list_view = view;
|
||||
item_add_action(item, "Focus");
|
||||
item_add_action(item, "Raise");
|
||||
buf_clear(&buffer);
|
||||
menu->has_icons = true;
|
||||
}
|
||||
}
|
||||
ctx.item = item_create(menu, _("Go there..."),
|
||||
item = item_create(menu, _("Go there..."), NULL,
|
||||
/*show arrow*/ false);
|
||||
fill_item(&ctx, "name.action", "GoToDesktop");
|
||||
fill_item(&ctx, "to.action", workspace->name);
|
||||
struct action *action = item_add_action(item, "GoToDesktop");
|
||||
action_arg_add_str(action, "to", workspace->name);
|
||||
}
|
||||
buf_reset(&buffer);
|
||||
menu_create_scene(menu);
|
||||
|
|
@ -1043,22 +934,22 @@ static void
|
|||
init_rootmenu(struct server *server)
|
||||
{
|
||||
struct menu *menu = menu_get_by_id(server, "root-menu");
|
||||
struct menuitem *item;
|
||||
|
||||
/* Default menu if no menu.xml found */
|
||||
if (!menu) {
|
||||
struct menu_parse_context ctx = {.server = server};
|
||||
menu = menu_create(server, NULL, "root-menu", "");
|
||||
|
||||
ctx.item = item_create(menu, _("Terminal"), false);
|
||||
fill_item(&ctx, "name.action", "Execute");
|
||||
fill_item(&ctx, "command.action", "lab-sensible-terminal");
|
||||
item = item_create(menu, _("Terminal"), NULL, false);
|
||||
struct action *action = item_add_action(item, "Execute");
|
||||
action_arg_add_str(action, "command", "lab-sensible-terminal");
|
||||
|
||||
ctx.item = separator_create(menu, NULL);
|
||||
separator_create(menu, NULL);
|
||||
|
||||
ctx.item = item_create(menu, _("Reconfigure"), false);
|
||||
fill_item(&ctx, "name.action", "Reconfigure");
|
||||
ctx.item = item_create(menu, _("Exit"), false);
|
||||
fill_item(&ctx, "name.action", "Exit");
|
||||
item = item_create(menu, _("Reconfigure"), NULL, false);
|
||||
item_add_action(item, "Reconfigure");
|
||||
item = item_create(menu, _("Exit"), NULL, false);
|
||||
item_add_action(item, "Exit");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1066,47 +957,48 @@ static void
|
|||
init_windowmenu(struct server *server)
|
||||
{
|
||||
struct menu *menu = menu_get_by_id(server, "client-menu");
|
||||
struct menuitem *item;
|
||||
struct action *action;
|
||||
|
||||
/* Default menu if no menu.xml found */
|
||||
if (!menu) {
|
||||
struct menu_parse_context ctx = {.server = server};
|
||||
menu = menu_create(server, NULL, "client-menu", "");
|
||||
ctx.item = item_create(menu, _("Minimize"), false);
|
||||
fill_item(&ctx, "name.action", "Iconify");
|
||||
ctx.item = item_create(menu, _("Maximize"), false);
|
||||
fill_item(&ctx, "name.action", "ToggleMaximize");
|
||||
ctx.item = item_create(menu, _("Fullscreen"), false);
|
||||
fill_item(&ctx, "name.action", "ToggleFullscreen");
|
||||
ctx.item = item_create(menu, _("Roll Up/Down"), false);
|
||||
fill_item(&ctx, "name.action", "ToggleShade");
|
||||
ctx.item = item_create(menu, _("Decorations"), false);
|
||||
fill_item(&ctx, "name.action", "ToggleDecorations");
|
||||
ctx.item = item_create(menu, _("Always on Top"), false);
|
||||
fill_item(&ctx, "name.action", "ToggleAlwaysOnTop");
|
||||
item = item_create(menu, _("Minimize"), NULL, false);
|
||||
item_add_action(item, "Iconify");
|
||||
item = item_create(menu, _("Maximize"), NULL, false);
|
||||
item_add_action(item, "ToggleMaximize");
|
||||
item = item_create(menu, _("Fullscreen"), NULL, false);
|
||||
item_add_action(item, "ToggleFullscreen");
|
||||
item = item_create(menu, _("Roll Up/Down"), NULL, false);
|
||||
item_add_action(item, "ToggleShade");
|
||||
item = item_create(menu, _("Decorations"), NULL, false);
|
||||
item_add_action(item, "ToggleDecorations");
|
||||
item = item_create(menu, _("Always on Top"), NULL, false);
|
||||
item_add_action(item, "ToggleAlwaysOnTop");
|
||||
|
||||
/* Workspace sub-menu */
|
||||
struct menu *workspace_menu =
|
||||
menu_create(server, NULL, "workspaces", "");
|
||||
ctx.item = item_create(workspace_menu, _("Move Left"), false);
|
||||
item = item_create(workspace_menu, _("Move Left"), NULL, false);
|
||||
/*
|
||||
* <action name="SendToDesktop"><follow> is true by default so
|
||||
* GoToDesktop will be called as part of the action.
|
||||
*/
|
||||
fill_item(&ctx, "name.action", "SendToDesktop");
|
||||
fill_item(&ctx, "to.action", "left");
|
||||
ctx.item = item_create(workspace_menu, _("Move Right"), false);
|
||||
fill_item(&ctx, "name.action", "SendToDesktop");
|
||||
fill_item(&ctx, "to.action", "right");
|
||||
ctx.item = separator_create(workspace_menu, "");
|
||||
ctx.item = item_create(workspace_menu,
|
||||
_("Always on Visible Workspace"), false);
|
||||
fill_item(&ctx, "name.action", "ToggleOmnipresent");
|
||||
action = item_add_action(item, "SendToDesktop");
|
||||
action_arg_add_str(action, "to", "left");
|
||||
item = item_create(workspace_menu, _("Move Right"), NULL, false);
|
||||
action = item_add_action(item, "SendToDesktop");
|
||||
action_arg_add_str(action, "to", "right");
|
||||
separator_create(workspace_menu, "");
|
||||
item = item_create(workspace_menu,
|
||||
_("Always on Visible Workspace"), NULL, false);
|
||||
item_add_action(item, "ToggleOmnipresent");
|
||||
|
||||
ctx.item = item_create(menu, _("Workspace"), true);
|
||||
ctx.item->submenu = workspace_menu;
|
||||
item = item_create(menu, _("Workspace"), NULL, true);
|
||||
item->submenu = workspace_menu;
|
||||
|
||||
ctx.item = item_create(menu, _("Close"), false);
|
||||
fill_item(&ctx, "name.action", "Close");
|
||||
item = item_create(menu, _("Close"), NULL, false);
|
||||
item_add_action(item, "Close");
|
||||
}
|
||||
|
||||
if (wl_list_length(&rc.workspace_config.workspaces) == 1) {
|
||||
|
|
@ -1348,11 +1240,7 @@ static void
|
|||
create_pipe_menu(struct menu_pipe_context *ctx)
|
||||
{
|
||||
struct server *server = ctx->pipemenu->server;
|
||||
struct menu_parse_context parse_ctx = {
|
||||
.server = server,
|
||||
.menu = ctx->pipemenu,
|
||||
};
|
||||
if (!parse_buf(&parse_ctx, &ctx->buf)) {
|
||||
if (!parse_buf(server, ctx->pipemenu, &ctx->buf)) {
|
||||
return;
|
||||
}
|
||||
/* TODO: apply validate() only for generated pipemenus */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue