add keyboard shortcuts to the menus. you can specify the shortcut key with & even in root menu and stuff
This commit is contained in:
parent
138d3e38d8
commit
5d5be2ba2a
8 changed files with 246 additions and 41 deletions
|
@ -70,16 +70,14 @@ static void client_update(ObMenuFrame *frame, gpointer data)
|
||||||
e->data.normal.enabled = frame->client->functions & OB_CLIENT_FUNC_ICONIFY;
|
e->data.normal.enabled = frame->client->functions & OB_CLIENT_FUNC_ICONIFY;
|
||||||
|
|
||||||
e = menu_find_entry_id(menu, CLIENT_MAXIMIZE);
|
e = menu_find_entry_id(menu, CLIENT_MAXIMIZE);
|
||||||
g_free(e->data.normal.label);
|
menu_entry_set_label(e,
|
||||||
e->data.normal.label =
|
(frame->client->max_vert || frame->client->max_horz ?
|
||||||
g_strdup(frame->client->max_vert || frame->client->max_horz ?
|
_("Restor&e") : _("Maximiz&e")));
|
||||||
_("Restore") : _("Maximize"));
|
|
||||||
e->data.normal.enabled =frame->client->functions & OB_CLIENT_FUNC_MAXIMIZE;
|
e->data.normal.enabled =frame->client->functions & OB_CLIENT_FUNC_MAXIMIZE;
|
||||||
|
|
||||||
e = menu_find_entry_id(menu, CLIENT_SHADE);
|
e = menu_find_entry_id(menu, CLIENT_SHADE);
|
||||||
g_free(e->data.normal.label);
|
menu_entry_set_label(e, (frame->client->shaded ?
|
||||||
e->data.normal.label = g_strdup(frame->client->shaded ?
|
_("&Roll down") : _("&Roll up")));
|
||||||
_("Roll down") : _("Roll up"));
|
|
||||||
e->data.normal.enabled = frame->client->functions & OB_CLIENT_FUNC_SHADE;
|
e->data.normal.enabled = frame->client->functions & OB_CLIENT_FUNC_SHADE;
|
||||||
|
|
||||||
e = menu_find_entry_id(menu, CLIENT_MOVE);
|
e = menu_find_entry_id(menu, CLIENT_MOVE);
|
||||||
|
@ -165,29 +163,32 @@ void client_menu_startup()
|
||||||
ObMenu *menu;
|
ObMenu *menu;
|
||||||
ObMenuEntry *e;
|
ObMenuEntry *e;
|
||||||
|
|
||||||
menu = menu_new(LAYER_MENU_NAME, _("Layer"), NULL);
|
menu = menu_new(LAYER_MENU_NAME, _("&Layer"), NULL);
|
||||||
|
menu_show_all_shortcuts(menu, TRUE);
|
||||||
menu_set_update_func(menu, layer_update);
|
menu_set_update_func(menu, layer_update);
|
||||||
|
|
||||||
acts = g_slist_prepend(NULL, action_from_string
|
acts = g_slist_prepend(NULL, action_from_string
|
||||||
("SendToTopLayer", OB_USER_ACTION_MENU_SELECTION));
|
("SendToTopLayer", OB_USER_ACTION_MENU_SELECTION));
|
||||||
menu_add_normal(menu, LAYER_TOP, _("Always on top"), acts);
|
menu_add_normal(menu, LAYER_TOP, _("Always on &top"), acts);
|
||||||
|
|
||||||
acts = g_slist_prepend(NULL, action_from_string
|
acts = g_slist_prepend(NULL, action_from_string
|
||||||
("SendToNormalLayer",
|
("SendToNormalLayer",
|
||||||
OB_USER_ACTION_MENU_SELECTION));
|
OB_USER_ACTION_MENU_SELECTION));
|
||||||
menu_add_normal(menu, LAYER_NORMAL, _("Normal"), acts);
|
menu_add_normal(menu, LAYER_NORMAL, _("&Normal"), acts);
|
||||||
|
|
||||||
acts = g_slist_prepend(NULL, action_from_string
|
acts = g_slist_prepend(NULL, action_from_string
|
||||||
("SendToBottomLayer",
|
("SendToBottomLayer",
|
||||||
OB_USER_ACTION_MENU_SELECTION));
|
OB_USER_ACTION_MENU_SELECTION));
|
||||||
menu_add_normal(menu, LAYER_BOTTOM, _("Always on bottom"),acts);
|
menu_add_normal(menu, LAYER_BOTTOM, _("Always on &bottom"),acts);
|
||||||
|
|
||||||
|
|
||||||
menu = menu_new(SEND_TO_MENU_NAME, _("Send to desktop"), NULL);
|
menu = menu_new(SEND_TO_MENU_NAME, _("&Send to desktop"), NULL);
|
||||||
|
menu_show_all_shortcuts(menu, TRUE);
|
||||||
menu_set_update_func(menu, send_to_update);
|
menu_set_update_func(menu, send_to_update);
|
||||||
|
|
||||||
|
|
||||||
menu = menu_new(CLIENT_MENU_NAME, _("Client menu"), NULL);
|
menu = menu_new(CLIENT_MENU_NAME, _("Client menu"), NULL);
|
||||||
|
menu_show_all_shortcuts(menu, TRUE);
|
||||||
menu_set_update_func(menu, client_update);
|
menu_set_update_func(menu, client_update);
|
||||||
|
|
||||||
menu_add_submenu(menu, CLIENT_SEND_TO, SEND_TO_MENU_NAME);
|
menu_add_submenu(menu, CLIENT_SEND_TO, SEND_TO_MENU_NAME);
|
||||||
|
@ -196,7 +197,7 @@ void client_menu_startup()
|
||||||
|
|
||||||
acts = g_slist_prepend(NULL, action_from_string
|
acts = g_slist_prepend(NULL, action_from_string
|
||||||
("Iconify", OB_USER_ACTION_MENU_SELECTION));
|
("Iconify", OB_USER_ACTION_MENU_SELECTION));
|
||||||
e = menu_add_normal(menu, CLIENT_ICONIFY, _("Iconify"), acts);
|
e = menu_add_normal(menu, CLIENT_ICONIFY, _("Ico&nify"), acts);
|
||||||
e->data.normal.mask = ob_rr_theme->iconify_mask;
|
e->data.normal.mask = ob_rr_theme->iconify_mask;
|
||||||
e->data.normal.mask_normal_color = ob_rr_theme->menu_color;
|
e->data.normal.mask_normal_color = ob_rr_theme->menu_color;
|
||||||
e->data.normal.mask_disabled_color = ob_rr_theme->menu_disabled_color;
|
e->data.normal.mask_disabled_color = ob_rr_theme->menu_disabled_color;
|
||||||
|
@ -213,11 +214,11 @@ void client_menu_startup()
|
||||||
|
|
||||||
acts = g_slist_prepend(NULL, action_from_string
|
acts = g_slist_prepend(NULL, action_from_string
|
||||||
("Raise", OB_USER_ACTION_MENU_SELECTION));
|
("Raise", OB_USER_ACTION_MENU_SELECTION));
|
||||||
menu_add_normal(menu, CLIENT_RAISE, _("Raise to top"), acts);
|
menu_add_normal(menu, CLIENT_RAISE, _("Raise to &top"), acts);
|
||||||
|
|
||||||
acts = g_slist_prepend(NULL, action_from_string
|
acts = g_slist_prepend(NULL, action_from_string
|
||||||
("Lower", OB_USER_ACTION_MENU_SELECTION));
|
("Lower", OB_USER_ACTION_MENU_SELECTION));
|
||||||
menu_add_normal(menu, CLIENT_LOWER, _("Lower to bottom"),acts);
|
menu_add_normal(menu, CLIENT_LOWER, _("Lower to &bottom"),acts);
|
||||||
|
|
||||||
acts = g_slist_prepend(NULL, action_from_string
|
acts = g_slist_prepend(NULL, action_from_string
|
||||||
("ToggleShade", OB_USER_ACTION_MENU_SELECTION));
|
("ToggleShade", OB_USER_ACTION_MENU_SELECTION));
|
||||||
|
@ -230,23 +231,23 @@ void client_menu_startup()
|
||||||
acts = g_slist_prepend(NULL, action_from_string
|
acts = g_slist_prepend(NULL, action_from_string
|
||||||
("ToggleDecorations",
|
("ToggleDecorations",
|
||||||
OB_USER_ACTION_MENU_SELECTION));
|
OB_USER_ACTION_MENU_SELECTION));
|
||||||
menu_add_normal(menu, CLIENT_DECORATE, _("Decorate"), acts);
|
menu_add_normal(menu, CLIENT_DECORATE, _("&Decorate"), acts);
|
||||||
|
|
||||||
menu_add_separator(menu, -1, NULL);
|
menu_add_separator(menu, -1, NULL);
|
||||||
|
|
||||||
acts = g_slist_prepend(NULL, action_from_string
|
acts = g_slist_prepend(NULL, action_from_string
|
||||||
("Move", OB_USER_ACTION_MENU_SELECTION));
|
("Move", OB_USER_ACTION_MENU_SELECTION));
|
||||||
menu_add_normal(menu, CLIENT_MOVE, _("Move"), acts);
|
menu_add_normal(menu, CLIENT_MOVE, _("&Move"), acts);
|
||||||
|
|
||||||
acts = g_slist_prepend(NULL, action_from_string
|
acts = g_slist_prepend(NULL, action_from_string
|
||||||
("Resize", OB_USER_ACTION_MENU_SELECTION));
|
("Resize", OB_USER_ACTION_MENU_SELECTION));
|
||||||
menu_add_normal(menu, CLIENT_RESIZE, _("Resize"), acts);
|
menu_add_normal(menu, CLIENT_RESIZE, _("&Resize"), acts);
|
||||||
|
|
||||||
menu_add_separator(menu, -1, NULL);
|
menu_add_separator(menu, -1, NULL);
|
||||||
|
|
||||||
acts = g_slist_prepend(NULL, action_from_string
|
acts = g_slist_prepend(NULL, action_from_string
|
||||||
("Close", OB_USER_ACTION_MENU_SELECTION));
|
("Close", OB_USER_ACTION_MENU_SELECTION));
|
||||||
e = menu_add_normal(menu, CLIENT_CLOSE, _("Close"), acts);
|
e = menu_add_normal(menu, CLIENT_CLOSE, _("&Close"), acts);
|
||||||
e->data.normal.mask = ob_rr_theme->close_mask;
|
e->data.normal.mask = ob_rr_theme->close_mask;
|
||||||
e->data.normal.mask_normal_color = ob_rr_theme->menu_color;
|
e->data.normal.mask_normal_color = ob_rr_theme->menu_color;
|
||||||
e->data.normal.mask_disabled_color = ob_rr_theme->menu_disabled_color;
|
e->data.normal.mask_disabled_color = ob_rr_theme->menu_disabled_color;
|
||||||
|
|
101
openbox/event.c
101
openbox/event.c
|
@ -39,6 +39,7 @@
|
||||||
#include "group.h"
|
#include "group.h"
|
||||||
#include "stacking.h"
|
#include "stacking.h"
|
||||||
#include "extensions.h"
|
#include "extensions.h"
|
||||||
|
#include "translate.h"
|
||||||
|
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <X11/keysym.h>
|
#include <X11/keysym.h>
|
||||||
|
@ -72,6 +73,7 @@ typedef struct
|
||||||
|
|
||||||
static void event_process(const XEvent *e, gpointer data);
|
static void event_process(const XEvent *e, gpointer data);
|
||||||
static void event_handle_root(XEvent *e);
|
static void event_handle_root(XEvent *e);
|
||||||
|
static void event_handle_menu_shortcut(XEvent *e);
|
||||||
static void event_handle_menu(XEvent *e);
|
static void event_handle_menu(XEvent *e);
|
||||||
static void event_handle_dock(ObDock *s, XEvent *e);
|
static void event_handle_dock(ObDock *s, XEvent *e);
|
||||||
static void event_handle_dockapp(ObDockApp *app, XEvent *e);
|
static void event_handle_dockapp(ObDockApp *app, XEvent *e);
|
||||||
|
@ -1262,7 +1264,7 @@ static void event_handle_dockapp(ObDockApp *app, XEvent *e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ObMenuFrame* find_active_menu()
|
static ObMenuFrame* find_active_menu()
|
||||||
{
|
{
|
||||||
GList *it;
|
GList *it;
|
||||||
ObMenuFrame *ret = NULL;
|
ObMenuFrame *ret = NULL;
|
||||||
|
@ -1276,7 +1278,7 @@ ObMenuFrame* find_active_menu()
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ObMenuFrame* find_active_or_last_menu()
|
static ObMenuFrame* find_active_or_last_menu()
|
||||||
{
|
{
|
||||||
ObMenuFrame *ret = NULL;
|
ObMenuFrame *ret = NULL;
|
||||||
|
|
||||||
|
@ -1286,6 +1288,77 @@ ObMenuFrame* find_active_or_last_menu()
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void event_handle_menu_shortcut(XEvent *ev)
|
||||||
|
{
|
||||||
|
gunichar unikey = 0;
|
||||||
|
ObMenuFrame *frame;
|
||||||
|
GList *start;
|
||||||
|
GList *it;
|
||||||
|
ObMenuEntryFrame *found = NULL;
|
||||||
|
guint num_found = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
const char *key;
|
||||||
|
if ((key = translate_keycode(ev->xkey.keycode)) == NULL)
|
||||||
|
return;
|
||||||
|
unikey = g_utf8_get_char_validated(key, -1);
|
||||||
|
if (unikey == (gunichar)-1 || unikey == (gunichar)-2 || unikey == 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((frame = find_active_or_last_menu()) == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
if (!frame->entries)
|
||||||
|
return; /* nothing in the menu anyways */
|
||||||
|
|
||||||
|
/* start after the selected one */
|
||||||
|
start = frame->entries;
|
||||||
|
if (frame->selected) {
|
||||||
|
for (it = start; frame->selected != it->data; it = g_list_next(it))
|
||||||
|
g_assert(it != NULL); /* nothing was selected? */
|
||||||
|
/* next with wraparound */
|
||||||
|
start = g_list_next(it);
|
||||||
|
if (start == NULL) start = frame->entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
it = start;
|
||||||
|
do {
|
||||||
|
ObMenuEntryFrame *e = it->data;
|
||||||
|
gunichar entrykey = 0;
|
||||||
|
|
||||||
|
if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
|
||||||
|
entrykey = e->entry->data.normal.shortcut;
|
||||||
|
else if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
|
||||||
|
entrykey = e->entry->data.submenu.submenu->shortcut;
|
||||||
|
|
||||||
|
if (unikey == entrykey) {
|
||||||
|
if (found == NULL) found = e;
|
||||||
|
++num_found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* next with wraparound */
|
||||||
|
it = g_list_next(it);
|
||||||
|
if (it == NULL) it = frame->entries;
|
||||||
|
} while (it != start);
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
if (found->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
|
||||||
|
num_found == 1)
|
||||||
|
{
|
||||||
|
menu_frame_select(frame, found, TRUE);
|
||||||
|
usleep(50000);
|
||||||
|
menu_entry_frame_execute(found, ev->xkey.state,
|
||||||
|
ev->xkey.time);
|
||||||
|
} else {
|
||||||
|
menu_frame_select(frame, found, TRUE);
|
||||||
|
if (num_found == 1)
|
||||||
|
menu_frame_select_next(frame->child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void event_handle_menu(XEvent *ev)
|
static void event_handle_menu(XEvent *ev)
|
||||||
{
|
{
|
||||||
ObMenuFrame *f;
|
ObMenuFrame *f;
|
||||||
|
@ -1307,7 +1380,7 @@ static void event_handle_menu(XEvent *ev)
|
||||||
if (e->ignore_enters)
|
if (e->ignore_enters)
|
||||||
--e->ignore_enters;
|
--e->ignore_enters;
|
||||||
else
|
else
|
||||||
menu_frame_select(e->frame, e);
|
menu_frame_select(e->frame, e, FALSE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LeaveNotify:
|
case LeaveNotify:
|
||||||
|
@ -1315,28 +1388,35 @@ static void event_handle_menu(XEvent *ev)
|
||||||
(f = find_active_menu()) && f->selected == e &&
|
(f = find_active_menu()) && f->selected == e &&
|
||||||
e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
|
e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU)
|
||||||
{
|
{
|
||||||
menu_frame_select(e->frame, NULL);
|
menu_frame_select(e->frame, NULL, FALSE);
|
||||||
}
|
}
|
||||||
case MotionNotify:
|
case MotionNotify:
|
||||||
if ((e = menu_entry_frame_under(ev->xmotion.x_root,
|
if ((e = menu_entry_frame_under(ev->xmotion.x_root,
|
||||||
ev->xmotion.y_root)))
|
ev->xmotion.y_root)))
|
||||||
menu_frame_select(e->frame, e);
|
menu_frame_select(e->frame, e, FALSE);
|
||||||
break;
|
break;
|
||||||
case KeyPress:
|
case KeyPress:
|
||||||
if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
|
if (ev->xkey.keycode == ob_keycode(OB_KEY_ESCAPE))
|
||||||
|
if ((f = find_active_or_last_menu()) && f->parent)
|
||||||
|
menu_frame_select(f, NULL, TRUE);
|
||||||
|
else
|
||||||
menu_frame_hide_all();
|
menu_frame_hide_all();
|
||||||
else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
|
else if (ev->xkey.keycode == ob_keycode(OB_KEY_RETURN)) {
|
||||||
ObMenuFrame *f;
|
ObMenuFrame *f;
|
||||||
if ((f = find_active_menu()))
|
if ((f = find_active_menu())) {
|
||||||
|
if (f->child)
|
||||||
|
menu_frame_select_next(f->child);
|
||||||
|
else
|
||||||
menu_entry_frame_execute(f->selected, ev->xkey.state,
|
menu_entry_frame_execute(f->selected, ev->xkey.state,
|
||||||
ev->xkey.time);
|
ev->xkey.time);
|
||||||
|
}
|
||||||
} else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
|
} else if (ev->xkey.keycode == ob_keycode(OB_KEY_LEFT)) {
|
||||||
ObMenuFrame *f;
|
ObMenuFrame *f;
|
||||||
if ((f = find_active_or_last_menu()) && f->parent)
|
if ((f = find_active_or_last_menu()))
|
||||||
menu_frame_select(f, NULL);
|
menu_frame_select(f, NULL, TRUE);
|
||||||
} else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
|
} else if (ev->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) {
|
||||||
ObMenuFrame *f;
|
ObMenuFrame *f;
|
||||||
if ((f = find_active_or_last_menu()) && f->child)
|
if ((f = find_active_menu()) && f->child)
|
||||||
menu_frame_select_next(f->child);
|
menu_frame_select_next(f->child);
|
||||||
} else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
|
} else if (ev->xkey.keycode == ob_keycode(OB_KEY_UP)) {
|
||||||
ObMenuFrame *f;
|
ObMenuFrame *f;
|
||||||
|
@ -1346,7 +1426,8 @@ static void event_handle_menu(XEvent *ev)
|
||||||
ObMenuFrame *f;
|
ObMenuFrame *f;
|
||||||
if ((f = find_active_or_last_menu()))
|
if ((f = find_active_or_last_menu()))
|
||||||
menu_frame_select_next(f);
|
menu_frame_select_next(f);
|
||||||
}
|
} else
|
||||||
|
event_handle_menu_shortcut(ev);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,9 @@ static void parse_menu_separator(ObParseInst *i,
|
||||||
gpointer data);
|
gpointer data);
|
||||||
static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
|
static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
|
||||||
gpointer data);
|
gpointer data);
|
||||||
|
static gunichar parse_shortcut(const gchar *label, gchar **strippedlabel,
|
||||||
|
guint *position);
|
||||||
|
|
||||||
|
|
||||||
static void client_dest(ObClient *client, gpointer data)
|
static void client_dest(ObClient *client, gpointer data)
|
||||||
{
|
{
|
||||||
|
@ -178,6 +181,56 @@ static ObMenu* menu_from_name(gchar *name)
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define VALID_SHORTCUT(c) (((c) >= '0' && (c) <= '9') || \
|
||||||
|
((c) >= 'A' && (c) <= 'Z') || \
|
||||||
|
((c) >= 'a' && (c) <= 'z'))
|
||||||
|
|
||||||
|
static gunichar parse_shortcut(const gchar *label, gchar **strippedlabel,
|
||||||
|
guint *position)
|
||||||
|
{
|
||||||
|
gunichar shortcut = 0;
|
||||||
|
|
||||||
|
*position = 0;
|
||||||
|
|
||||||
|
g_assert(strippedlabel != NULL);
|
||||||
|
|
||||||
|
if (label == NULL) {
|
||||||
|
*strippedlabel = NULL;
|
||||||
|
} else {
|
||||||
|
gchar *i;
|
||||||
|
|
||||||
|
*strippedlabel = g_strdup(label);
|
||||||
|
|
||||||
|
i = strchr(*strippedlabel, '&');
|
||||||
|
if (i != NULL) {
|
||||||
|
/* there is an ampersand in the string */
|
||||||
|
|
||||||
|
/* you have to use a printable ascii character for shortcuts
|
||||||
|
don't allow space either, so you can have like "a & b"
|
||||||
|
*/
|
||||||
|
if (VALID_SHORTCUT(*(i+1))) {
|
||||||
|
shortcut = g_unichar_tolower(g_utf8_get_char(i+1));
|
||||||
|
*position = i - *strippedlabel;
|
||||||
|
|
||||||
|
/* remove the & from the string */
|
||||||
|
for (; *i != '\0'; ++i)
|
||||||
|
*i = *(i+1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* there is no ampersand, so find the first valid character to use
|
||||||
|
instead */
|
||||||
|
|
||||||
|
for (i = *strippedlabel; *i != '\0'; ++i)
|
||||||
|
if (VALID_SHORTCUT(*i)) {
|
||||||
|
*position = i - *strippedlabel;
|
||||||
|
shortcut = g_unichar_tolower(g_utf8_get_char(i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return shortcut;
|
||||||
|
}
|
||||||
|
|
||||||
static void parse_menu_item(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
|
static void parse_menu_item(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
|
||||||
gpointer data)
|
gpointer data)
|
||||||
{
|
{
|
||||||
|
@ -262,9 +315,11 @@ ObMenu* menu_new(const gchar *name, const gchar *title, gpointer data)
|
||||||
|
|
||||||
self = g_new0(ObMenu, 1);
|
self = g_new0(ObMenu, 1);
|
||||||
self->name = g_strdup(name);
|
self->name = g_strdup(name);
|
||||||
self->title = g_strdup(title);
|
|
||||||
self->data = data;
|
self->data = data;
|
||||||
|
|
||||||
|
self->shortcut = parse_shortcut(title, &self->title,
|
||||||
|
&self->shortcut_position);
|
||||||
|
|
||||||
g_hash_table_replace(menu_hash, self->name, self);
|
g_hash_table_replace(menu_hash, self->name, self);
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
@ -325,7 +380,7 @@ void menu_show(gchar *name, gint x, gint y, ObClient *client)
|
||||||
ObMenuEntryFrame *e = frame->entries->data;
|
ObMenuEntryFrame *e = frame->entries->data;
|
||||||
if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
|
if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL &&
|
||||||
e->entry->data.normal.enabled)
|
e->entry->data.normal.enabled)
|
||||||
menu_frame_select(frame, e);
|
menu_frame_select(frame, e, FALSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,9 +464,10 @@ ObMenuEntry* menu_add_normal(ObMenu *self, gint id, const gchar *label,
|
||||||
ObMenuEntry *e;
|
ObMenuEntry *e;
|
||||||
|
|
||||||
e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_NORMAL, id);
|
e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_NORMAL, id);
|
||||||
e->data.normal.label = g_strdup(label);
|
|
||||||
e->data.normal.actions = actions;
|
e->data.normal.actions = actions;
|
||||||
|
|
||||||
|
menu_entry_set_label(e, label);
|
||||||
|
|
||||||
self->entries = g_list_append(self->entries, e);
|
self->entries = g_list_append(self->entries, e);
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
@ -432,7 +488,8 @@ ObMenuEntry* menu_add_separator(ObMenu *self, gint id, const gchar *label)
|
||||||
ObMenuEntry *e;
|
ObMenuEntry *e;
|
||||||
|
|
||||||
e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SEPARATOR, id);
|
e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SEPARATOR, id);
|
||||||
e->data.separator.label = g_strdup(label);
|
|
||||||
|
menu_entry_set_label(e, label);
|
||||||
|
|
||||||
self->entries = g_list_append(self->entries, e);
|
self->entries = g_list_append(self->entries, e);
|
||||||
return e;
|
return e;
|
||||||
|
@ -480,3 +537,26 @@ void menu_find_submenus(ObMenu *self)
|
||||||
e->data.submenu.submenu = menu_from_name(e->data.submenu.name);
|
e->data.submenu.submenu = menu_from_name(e->data.submenu.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void menu_entry_set_label(ObMenuEntry *self, const gchar *label)
|
||||||
|
{
|
||||||
|
switch (self->type) {
|
||||||
|
case OB_MENU_ENTRY_TYPE_SEPARATOR:
|
||||||
|
g_free(self->data.separator.label);
|
||||||
|
self->data.separator.label = g_strdup(label);
|
||||||
|
break;
|
||||||
|
case OB_MENU_ENTRY_TYPE_NORMAL:
|
||||||
|
g_free(self->data.normal.label);
|
||||||
|
self->data.normal.shortcut =
|
||||||
|
parse_shortcut(label, &self->data.normal.label,
|
||||||
|
&self->data.normal.shortcut_position);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void menu_show_all_shortcuts(ObMenu *self, gboolean show)
|
||||||
|
{
|
||||||
|
self->show_all_shortcuts = show;
|
||||||
|
}
|
||||||
|
|
|
@ -48,6 +48,15 @@ struct _ObMenu
|
||||||
gchar *name;
|
gchar *name;
|
||||||
/* Displayed title */
|
/* Displayed title */
|
||||||
gchar *title;
|
gchar *title;
|
||||||
|
/*! The shortcut key that would be used to activate this menu if it was
|
||||||
|
displayed as a submenu */
|
||||||
|
gunichar shortcut;
|
||||||
|
/*! The shortcut's position in the string */
|
||||||
|
guint shortcut_position;
|
||||||
|
|
||||||
|
/*! If the shortcut key should be shown in menu entries even when it
|
||||||
|
is the first character in the string */
|
||||||
|
gboolean show_all_shortcuts;
|
||||||
|
|
||||||
/* Command to execute to rebuild the menu */
|
/* Command to execute to rebuild the menu */
|
||||||
gchar *execute;
|
gchar *execute;
|
||||||
|
@ -75,6 +84,10 @@ typedef enum
|
||||||
|
|
||||||
struct _ObNormalMenuEntry {
|
struct _ObNormalMenuEntry {
|
||||||
gchar *label;
|
gchar *label;
|
||||||
|
/*! The shortcut key that would be used to activate this menu entry */
|
||||||
|
gunichar shortcut;
|
||||||
|
/*! The shortcut's position in the string */
|
||||||
|
guint shortcut_position;
|
||||||
|
|
||||||
/* state */
|
/* state */
|
||||||
gboolean enabled;
|
gboolean enabled;
|
||||||
|
@ -126,6 +139,8 @@ void menu_free(ObMenu *menu);
|
||||||
/* Repopulate a pipe-menu by running its command */
|
/* Repopulate a pipe-menu by running its command */
|
||||||
void menu_pipe_execute(ObMenu *self);
|
void menu_pipe_execute(ObMenu *self);
|
||||||
|
|
||||||
|
void menu_show_all_shortcuts(ObMenu *self, gboolean show);
|
||||||
|
|
||||||
void menu_show(gchar *name, gint x, gint y, struct _ObClient *client);
|
void menu_show(gchar *name, gint x, gint y, struct _ObClient *client);
|
||||||
|
|
||||||
void menu_set_update_func(ObMenu *menu, ObMenuUpdateFunc func);
|
void menu_set_update_func(ObMenu *menu, ObMenuUpdateFunc func);
|
||||||
|
@ -141,6 +156,8 @@ ObMenuEntry* menu_add_separator(ObMenu *menu, gint id, const gchar *label);
|
||||||
void menu_clear_entries(ObMenu *menu);
|
void menu_clear_entries(ObMenu *menu);
|
||||||
void menu_entry_remove(ObMenuEntry *self);
|
void menu_entry_remove(ObMenuEntry *self);
|
||||||
|
|
||||||
|
void menu_entry_set_label(ObMenuEntry *self, const gchar *label);
|
||||||
|
|
||||||
ObMenuEntry* menu_find_entry_id(ObMenu *self, gint id);
|
ObMenuEntry* menu_find_entry_id(ObMenu *self, gint id);
|
||||||
|
|
||||||
/* fills in the submenus, for use when a menu is being shown */
|
/* fills in the submenus, for use when a menu is being shown */
|
||||||
|
|
|
@ -318,6 +318,13 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
|
||||||
self->a_text_selected :
|
self->a_text_selected :
|
||||||
self->a_text_normal));
|
self->a_text_normal));
|
||||||
text_a->texture[0].data.text.string = self->entry->data.normal.label;
|
text_a->texture[0].data.text.string = self->entry->data.normal.label;
|
||||||
|
if (self->frame->menu->show_all_shortcuts ||
|
||||||
|
self->entry->data.normal.shortcut_position > 0)
|
||||||
|
{
|
||||||
|
text_a->texture[0].data.text.shortcut =
|
||||||
|
self->entry->data.normal.shortcut;
|
||||||
|
} else
|
||||||
|
text_a->texture[0].data.text.shortcut = 0;
|
||||||
break;
|
break;
|
||||||
case OB_MENU_ENTRY_TYPE_SUBMENU:
|
case OB_MENU_ENTRY_TYPE_SUBMENU:
|
||||||
text_a = (self == self->frame->selected ?
|
text_a = (self == self->frame->selected ?
|
||||||
|
@ -325,6 +332,11 @@ static void menu_entry_frame_render(ObMenuEntryFrame *self)
|
||||||
self->a_text_normal);
|
self->a_text_normal);
|
||||||
sub = self->entry->data.submenu.submenu;
|
sub = self->entry->data.submenu.submenu;
|
||||||
text_a->texture[0].data.text.string = sub ? sub->title : "";
|
text_a->texture[0].data.text.string = sub ? sub->title : "";
|
||||||
|
if (self->frame->menu->show_all_shortcuts ||
|
||||||
|
sub->shortcut_position > 0) {
|
||||||
|
text_a->texture[0].data.text.shortcut = sub->shortcut;
|
||||||
|
} else
|
||||||
|
text_a->texture[0].data.text.shortcut = 0;
|
||||||
break;
|
break;
|
||||||
case OB_MENU_ENTRY_TYPE_SEPARATOR:
|
case OB_MENU_ENTRY_TYPE_SEPARATOR:
|
||||||
if (self->entry->data.separator.label != NULL)
|
if (self->entry->data.separator.label != NULL)
|
||||||
|
@ -886,7 +898,8 @@ static gboolean menu_entry_frame_submenu_timeout(gpointer data)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry)
|
void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry,
|
||||||
|
gboolean immediate)
|
||||||
{
|
{
|
||||||
ObMenuEntryFrame *old = self->selected;
|
ObMenuEntryFrame *old = self->selected;
|
||||||
ObMenuFrame *oldchild = self->child;
|
ObMenuFrame *oldchild = self->child;
|
||||||
|
@ -913,7 +926,7 @@ void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry)
|
||||||
menu_entry_frame_render(self->selected);
|
menu_entry_frame_render(self->selected);
|
||||||
|
|
||||||
if (self->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
|
if (self->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
|
||||||
if (config_submenu_show_delay) {
|
if (config_submenu_show_delay && !immediate) {
|
||||||
/* initiate a new submenu open request */
|
/* initiate a new submenu open request */
|
||||||
ob_main_loop_timeout_add(ob_main_loop,
|
ob_main_loop_timeout_add(ob_main_loop,
|
||||||
config_submenu_show_delay * 1000,
|
config_submenu_show_delay * 1000,
|
||||||
|
@ -988,7 +1001,7 @@ void menu_frame_select_previous(ObMenuFrame *self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
menu_frame_select(self, it ? it->data : NULL);
|
menu_frame_select(self, it ? it->data : NULL, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void menu_frame_select_next(ObMenuFrame *self)
|
void menu_frame_select_next(ObMenuFrame *self)
|
||||||
|
@ -1014,5 +1027,5 @@ void menu_frame_select_next(ObMenuFrame *self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
menu_frame_select(self, it ? it->data : NULL);
|
menu_frame_select(self, it ? it->data : NULL, TRUE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,7 +124,8 @@ void menu_frame_hide(ObMenuFrame *self);
|
||||||
void menu_frame_hide_all();
|
void menu_frame_hide_all();
|
||||||
void menu_frame_hide_all_client(struct _ObClient *client);
|
void menu_frame_hide_all_client(struct _ObClient *client);
|
||||||
|
|
||||||
void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry);
|
void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry,
|
||||||
|
gboolean immediate);
|
||||||
void menu_frame_select_previous(ObMenuFrame *self);
|
void menu_frame_select_previous(ObMenuFrame *self);
|
||||||
void menu_frame_select_next(ObMenuFrame *self);
|
void menu_frame_select_next(ObMenuFrame *self);
|
||||||
|
|
||||||
|
|
|
@ -139,3 +139,13 @@ translation_fail:
|
||||||
g_strfreev(parsed);
|
g_strfreev(parsed);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const gchar *translate_keycode(guint keycode)
|
||||||
|
{
|
||||||
|
KeySym sym;
|
||||||
|
const gchar *ret = NULL;
|
||||||
|
|
||||||
|
if ((sym = XKeycodeToKeysym(ob_display, keycode, 0)) != NoSymbol)
|
||||||
|
ret = XKeysymToString(sym);
|
||||||
|
return g_locale_to_utf8(ret, -1, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
|
@ -24,4 +24,6 @@
|
||||||
gboolean translate_button(const gchar *str, guint *state, guint *keycode);
|
gboolean translate_button(const gchar *str, guint *state, guint *keycode);
|
||||||
gboolean translate_key(const gchar *str, guint *state, guint *keycode);
|
gboolean translate_key(const gchar *str, guint *state, guint *keycode);
|
||||||
|
|
||||||
|
/*! Give the string form of a keycode */
|
||||||
|
const gchar *translate_keycode(guint keycode);
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue