half way through the changes to new menu code/design. hot shit. static menus work, on to plugins next.
This commit is contained in:
parent
cadab91e52
commit
c34ef4028e
14 changed files with 920 additions and 977 deletions
17
Makefile.am
17
Makefile.am
|
@ -26,12 +26,12 @@ bin_PROGRAMS = \
|
|||
tools/kdetrayproxy/kdetrayproxy
|
||||
|
||||
plugin_LTLIBRARIES = \
|
||||
plugins/placement/placement.la \
|
||||
plugins/menu/timed_menu.la \
|
||||
plugins/menu/fifo_menu.la \
|
||||
plugins/menu/client_menu.la \
|
||||
plugins/menu/include_menu.la \
|
||||
plugins/menu/client_list_menu.la
|
||||
plugins/placement/placement.la
|
||||
# plugins/menu/timed_menu.la \
|
||||
# plugins/menu/fifo_menu.la \
|
||||
# plugins/menu/client_menu.la \
|
||||
# plugins/menu/include_menu.la \
|
||||
# plugins/menu/client_list_menu.la
|
||||
|
||||
if OBCONF
|
||||
bin_PROGRAMS += \
|
||||
|
@ -156,9 +156,10 @@ kernel_openbox_SOURCES = \
|
|||
kernel/keyboard.h \
|
||||
kernel/keytree.c \
|
||||
kernel/keytree.h \
|
||||
kernel/menuframe.c \
|
||||
kernel/menuframe.h \
|
||||
kernel/menu.c \
|
||||
kernel/menu.h \
|
||||
kernel/menu_render.c \
|
||||
kernel/misc.h \
|
||||
kernel/mouse.c \
|
||||
kernel/mouse.h \
|
||||
|
@ -277,7 +278,7 @@ plugins_menu_client_list_menu_la_LDFLAGS = \
|
|||
-module \
|
||||
-avoid-version
|
||||
plugins_menu_client_list_menu_la_SOURCES = \
|
||||
plugins/menu/client_list_menu.c
|
||||
o plugins/menu/client_list_menu.c
|
||||
|
||||
|
||||
## obconf ##
|
||||
|
|
|
@ -350,13 +350,6 @@ void client_unmanage_all()
|
|||
client_unmanage(client_list->data);
|
||||
}
|
||||
|
||||
/* called by client_unmanage() to close any menus referencing this client */
|
||||
void client_close_menus(gpointer key, gpointer value, gpointer self)
|
||||
{
|
||||
if (((ObMenu *)value)->client == (ObClient *)self)
|
||||
menu_hide((ObMenu *)value);
|
||||
}
|
||||
|
||||
void client_unmanage(ObClient *self)
|
||||
{
|
||||
guint j;
|
||||
|
@ -413,9 +406,7 @@ void client_unmanage(ObClient *self)
|
|||
if (moveresize_client == self)
|
||||
moveresize_end(TRUE);
|
||||
|
||||
/* close any windows that are attached to this window */
|
||||
g_hash_table_foreach(menu_hash, client_close_menus, self);
|
||||
|
||||
/* XXX close any windows that are attached to this window */
|
||||
|
||||
if (focus_client == self) {
|
||||
XEvent e;
|
||||
|
|
111
openbox/event.c
111
openbox/event.c
|
@ -8,6 +8,7 @@
|
|||
#include "screen.h"
|
||||
#include "frame.h"
|
||||
#include "menu.h"
|
||||
#include "menuframe.h"
|
||||
#include "keyboard.h"
|
||||
#include "mouse.h"
|
||||
#include "framerender.h"
|
||||
|
@ -41,10 +42,10 @@
|
|||
|
||||
static void event_process(XEvent *e);
|
||||
static void event_handle_root(XEvent *e);
|
||||
static void event_handle_menu(XEvent *e);
|
||||
static void event_handle_dock(ObDock *s, XEvent *e);
|
||||
static void event_handle_dockapp(ObDockApp *app, XEvent *e);
|
||||
static void event_handle_client(ObClient *c, XEvent *e);
|
||||
static void event_handle_menu(ObClient *c, XEvent *e);
|
||||
static void fd_event_handle();
|
||||
#ifdef USE_SM
|
||||
static void ice_watch(IceConn conn, IcePointer data, Bool opening,
|
||||
|
@ -298,6 +299,7 @@ static void event_hack_mods(XEvent *e)
|
|||
break;
|
||||
case MotionNotify:
|
||||
STRIP_MODS(e->xmotion.state);
|
||||
#if 0
|
||||
/* compress events */
|
||||
{
|
||||
XEvent ce;
|
||||
|
@ -307,6 +309,7 @@ static void event_hack_mods(XEvent *e)
|
|||
e->xmotion.y_root = ce.xmotion.y_root;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -461,7 +464,6 @@ static void event_process(XEvent *e)
|
|||
ObClient *client = NULL;
|
||||
ObDock *dock = NULL;
|
||||
ObDockApp *dockapp = NULL;
|
||||
ObMenu *menu = NULL;
|
||||
ObWindow *obwin = NULL;
|
||||
|
||||
window = event_get_window(e);
|
||||
|
@ -473,12 +475,10 @@ static void event_process(XEvent *e)
|
|||
case Window_DockApp:
|
||||
dockapp = WINDOW_AS_DOCKAPP(obwin);
|
||||
break;
|
||||
case Window_Menu:
|
||||
menu = WINDOW_AS_MENU(obwin);
|
||||
break;
|
||||
case Window_Client:
|
||||
client = WINDOW_AS_CLIENT(obwin);
|
||||
break;
|
||||
case Window_Menu:
|
||||
case Window_Internal:
|
||||
/* not to be used for events */
|
||||
g_assert_not_reached();
|
||||
|
@ -523,11 +523,11 @@ static void event_process(XEvent *e)
|
|||
xerror_set_ignore(FALSE);
|
||||
}
|
||||
|
||||
if (menu_visible)
|
||||
if (menu_frame_visible)
|
||||
if (e->type == MotionNotify || e->type == ButtonRelease ||
|
||||
e->type == ButtonPress ||
|
||||
e->type == KeyPress || e->type == KeyRelease) {
|
||||
event_handle_menu(client, e);
|
||||
event_handle_menu(e);
|
||||
|
||||
return; /* no dispatch! */
|
||||
}
|
||||
|
@ -539,7 +539,6 @@ static void event_process(XEvent *e)
|
|||
moveresize_event(e);
|
||||
|
||||
return; /* no dispatch! */
|
||||
|
||||
}
|
||||
|
||||
/* user input (action-bound) events */
|
||||
|
@ -1081,74 +1080,6 @@ static void event_handle_client(ObClient *client, XEvent *e)
|
|||
}
|
||||
}
|
||||
|
||||
static void event_handle_menu(ObClient *client, XEvent *e)
|
||||
{
|
||||
ObMenuEntry *entry;
|
||||
ObMenu *top;
|
||||
GList *it = NULL;
|
||||
|
||||
top = g_list_nth_data(menu_visible, 0);
|
||||
|
||||
ob_debug("EVENT %d\n", e->type);
|
||||
switch (e->type) {
|
||||
case KeyPress:
|
||||
menu_control_keyboard_nav(e->xkey.keycode);
|
||||
break;
|
||||
case ButtonPress:
|
||||
ob_debug("BUTTON PRESS\n");
|
||||
|
||||
break;
|
||||
case ButtonRelease:
|
||||
ob_debug("BUTTON RELEASED\n");
|
||||
|
||||
for (it = menu_visible; it; it = g_list_next(it)) {
|
||||
ObMenu *m = it->data;
|
||||
if (e->xbutton.x_root >= m->location.x - ob_rr_theme->bwidth &&
|
||||
e->xbutton.y_root >= m->location.y - ob_rr_theme->bwidth &&
|
||||
e->xbutton.x_root < m->location.x + m->size.width +
|
||||
ob_rr_theme->bwidth &&
|
||||
e->xbutton.y_root < m->location.y + m->size.height +
|
||||
ob_rr_theme->bwidth) {
|
||||
if ((entry = menu_find_entry_by_pos(it->data,
|
||||
e->xbutton.x_root -
|
||||
m->location.x,
|
||||
e->xbutton.y_root -
|
||||
m->location.y))) {
|
||||
m->selected(entry, e->xbutton.button,
|
||||
e->xbutton.x_root,
|
||||
e->xbutton.y_root);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* will call the menu_hide() for each submenu as well */
|
||||
if (!it)
|
||||
menu_control_keyboard_nav(ob_keycode(OB_KEY_ESCAPE));
|
||||
|
||||
break;
|
||||
case MotionNotify:
|
||||
ob_debug("motion\n");
|
||||
for (it = menu_visible; it; it = g_list_next(it)) {
|
||||
ObMenu *m = it->data;
|
||||
if ((entry = menu_find_entry_by_pos(it->data,
|
||||
e->xmotion.x_root -
|
||||
m->location.x,
|
||||
e->xmotion.y_root -
|
||||
m->location.y))) {
|
||||
if (m->over && m->over->data != entry)
|
||||
m->mouseover(m->over->data, FALSE);
|
||||
|
||||
m->mouseover(entry, TRUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void event_add_fd_handler(event_fd_handler *h) {
|
||||
g_datalist_id_set_data(&fd_handler_list, h->fd, h);
|
||||
FD_SET(h->fd, &allset);
|
||||
|
@ -1231,3 +1162,31 @@ static void event_handle_dockapp(ObDockApp *app, XEvent *e)
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void event_handle_menu(XEvent *ev)
|
||||
{
|
||||
ObMenuFrame *f;
|
||||
ObMenuEntryFrame *e;
|
||||
|
||||
switch (ev->type) {
|
||||
case ButtonRelease:
|
||||
if (!(f = menu_frame_under(ev->xmotion.x_root,
|
||||
ev->xmotion.y_root)))
|
||||
menu_frame_hide_all();
|
||||
else {
|
||||
if ((e = menu_entry_frame_under(ev->xmotion.x_root,
|
||||
ev->xmotion.y_root)))
|
||||
menu_entry_frame_execute(e);
|
||||
}
|
||||
break;
|
||||
case MotionNotify:
|
||||
if ((f = menu_frame_under(ev->xmotion.x_root,
|
||||
ev->xmotion.y_root))) {
|
||||
menu_frame_move_on_screen(f);
|
||||
if (e = menu_entry_frame_under(ev->xmotion.x_root,
|
||||
ev->xmotion.y_root))
|
||||
menu_frame_select(f, e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
681
openbox/menu.c
681
openbox/menu.c
|
@ -6,172 +6,116 @@
|
|||
#include "grab.h"
|
||||
#include "config.h"
|
||||
#include "screen.h"
|
||||
#include "menuframe.h"
|
||||
#include "geom.h"
|
||||
#include "plugin.h"
|
||||
#include "misc.h"
|
||||
#include "parser/parse.h"
|
||||
|
||||
GHashTable *menu_hash = NULL;
|
||||
GList *menu_visible = NULL;
|
||||
|
||||
#define FRAME_EVENTMASK (ButtonPressMask |ButtonMotionMask | EnterWindowMask |\
|
||||
LeaveWindowMask)
|
||||
#define TITLE_EVENTMASK (ButtonPressMask | ButtonMotionMask)
|
||||
#define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
|
||||
ButtonPressMask | ButtonReleaseMask)
|
||||
typedef struct _ObMenuParseState ObMenuParseState;
|
||||
|
||||
void menu_control_show(ObMenu *self, int x, int y, ObClient *client);
|
||||
struct _ObMenuParseState
|
||||
{
|
||||
GSList *menus;
|
||||
};
|
||||
|
||||
static void menu_clear_entries_internal(ObMenu *self);
|
||||
|
||||
static ObMenu* menu_from_name(gchar *name)
|
||||
{
|
||||
ObMenu *self = NULL;
|
||||
|
||||
g_assert(name != NULL);
|
||||
|
||||
if (!(self = g_hash_table_lookup(menu_hash, name)))
|
||||
g_warning("Attempted to access menu '%s' but it does not exist.",
|
||||
name);
|
||||
return self;
|
||||
}
|
||||
|
||||
static void parse_menu_item(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
|
||||
gpointer data)
|
||||
{
|
||||
ObMenuParseState *state = data;
|
||||
gchar *label;
|
||||
|
||||
if (state->menus) {
|
||||
if (parse_attr_string("label", node, &label)) {
|
||||
GSList *acts = NULL;
|
||||
|
||||
for (node = node->xmlChildrenNode; node; node = node->next)
|
||||
if (!xmlStrcasecmp(node->name, (const xmlChar*) "action"))
|
||||
acts = g_slist_append(acts, action_parse(doc, node));
|
||||
menu_add_normal(state->menus->data, label, acts);
|
||||
g_free(label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_menu_separator(ObParseInst *i,
|
||||
xmlDocPtr doc, xmlNodePtr node,
|
||||
gpointer data)
|
||||
{
|
||||
ObMenuParseState *state = data;
|
||||
|
||||
if (state->menus)
|
||||
menu_add_separator(state->menus->data);
|
||||
}
|
||||
|
||||
static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
|
||||
gpointer data)
|
||||
{
|
||||
g_message("%s", __FUNCTION__);
|
||||
parse_menu_full(i, doc, node, data, TRUE);
|
||||
}
|
||||
ObMenuParseState *state = data;
|
||||
gchar *name = NULL, *title = NULL;
|
||||
|
||||
|
||||
void parse_menu_full(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
|
||||
gpointer data, gboolean newmenu)
|
||||
{
|
||||
ObAction *act;
|
||||
xmlNodePtr nact;
|
||||
|
||||
gchar *id = NULL, *title = NULL, *label = NULL, *plugin;
|
||||
ObMenu *menu = NULL, *parent = NULL;
|
||||
|
||||
if (newmenu == TRUE) {
|
||||
if (!parse_attr_string("id", node, &id))
|
||||
if (!parse_attr_string("id", node, &name))
|
||||
goto parse_menu_fail;
|
||||
|
||||
if (!g_hash_table_lookup(menu_hash, name)) {
|
||||
if (!parse_attr_string("label", node, &title))
|
||||
goto parse_menu_fail;
|
||||
ob_debug("menu label %s\n", title);
|
||||
|
||||
if (parse_attr_string("plugin", node, &plugin)) {
|
||||
PluginMenuCreateData data;
|
||||
data.parse_inst = i;
|
||||
data.doc = doc;
|
||||
data.node = node;
|
||||
data.parent = menu;
|
||||
|
||||
if (plugin_open_reopen(plugin, i))
|
||||
menu = plugin_create(plugin, &data);
|
||||
g_free(plugin);
|
||||
} else
|
||||
menu = menu_new(title, id, data ? *((ObMenu**)data) : NULL);
|
||||
|
||||
if (data && menu)
|
||||
*((ObMenu**)data) = menu;
|
||||
} else {
|
||||
menu = (ObMenu *)data;
|
||||
}
|
||||
|
||||
node = node->xmlChildrenNode;
|
||||
|
||||
while (node) {
|
||||
if (!xmlStrcasecmp(node->name, (const xmlChar*) "menu")) {
|
||||
if (parse_attr_string("plugin", node, &plugin)) {
|
||||
PluginMenuCreateData data;
|
||||
data.doc = doc;
|
||||
data.node = node;
|
||||
data.parent = menu;
|
||||
if (plugin_open_reopen(plugin, i))
|
||||
parent = plugin_create(plugin, &data);
|
||||
g_free(plugin);
|
||||
} else {
|
||||
parent = menu;
|
||||
parse_menu(i, doc, node, &parent);
|
||||
}
|
||||
|
||||
if (parent)
|
||||
menu_add_entry(menu, menu_entry_new_submenu(parent->label,
|
||||
parent));
|
||||
|
||||
}
|
||||
else if (!xmlStrcasecmp(node->name, (const xmlChar*) "item")) {
|
||||
if (parse_attr_string("label", node, &label)) {
|
||||
if ((nact = parse_find_node("action", node->xmlChildrenNode)))
|
||||
act = action_parse(doc, nact);
|
||||
else
|
||||
act = NULL;
|
||||
if (act)
|
||||
menu_add_entry(menu, menu_entry_new(label, act));
|
||||
else
|
||||
menu_add_entry(menu, menu_entry_new_separator(label));
|
||||
g_free(label);
|
||||
if (menu_new(name, title, NULL)) {
|
||||
state->menus = g_slist_prepend(state->menus, name);
|
||||
parse_tree(i, doc, node->xmlChildrenNode);
|
||||
state->menus = g_slist_delete_link(state->menus, state->menus);
|
||||
}
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
if (state->menus)
|
||||
menu_add_submenu(state->menus->data, name);
|
||||
|
||||
parse_menu_fail:
|
||||
g_free(id);
|
||||
g_free(name);
|
||||
g_free(title);
|
||||
}
|
||||
|
||||
void menu_destroy_hash_key(ObMenu *menu)
|
||||
{
|
||||
g_free(menu);
|
||||
}
|
||||
|
||||
void menu_destroy_hash_value(ObMenu *self)
|
||||
{
|
||||
GList *it;
|
||||
|
||||
if (self->destroy) self->destroy(self);
|
||||
|
||||
for (it = self->entries; it; it = it->next)
|
||||
menu_entry_free(it->data);
|
||||
g_list_free(self->entries);
|
||||
|
||||
g_free(self->label);
|
||||
menu_clear_entries_internal(self);
|
||||
g_free(self->name);
|
||||
|
||||
g_hash_table_remove(window_map, &self->title);
|
||||
g_hash_table_remove(window_map, &self->frame);
|
||||
g_hash_table_remove(window_map, &self->items);
|
||||
|
||||
stacking_remove(self);
|
||||
|
||||
RrAppearanceFree(self->a_title);
|
||||
RrAppearanceFree(self->a_items);
|
||||
XDestroyWindow(ob_display, self->title);
|
||||
XDestroyWindow(ob_display, self->frame);
|
||||
XDestroyWindow(ob_display, self->items);
|
||||
|
||||
g_free(self);
|
||||
}
|
||||
|
||||
void menu_entry_free(ObMenuEntry *self)
|
||||
{
|
||||
g_free(self->label);
|
||||
action_free(self->action);
|
||||
|
||||
g_hash_table_remove(window_map, &self->item);
|
||||
|
||||
RrAppearanceFree(self->a_item);
|
||||
RrAppearanceFree(self->a_disabled);
|
||||
RrAppearanceFree(self->a_hilite);
|
||||
RrAppearanceFree(self->a_submenu);
|
||||
XDestroyWindow(ob_display, self->item);
|
||||
XDestroyWindow(ob_display, self->submenu_pic);
|
||||
g_free(self);
|
||||
g_free(self->title);
|
||||
}
|
||||
|
||||
void menu_startup(ObParseInst *i)
|
||||
{
|
||||
menu_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
|
||||
(GDestroyNotify)menu_destroy_hash_key,
|
||||
menu_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
|
||||
(GDestroyNotify)menu_destroy_hash_value);
|
||||
}
|
||||
|
||||
void menu_shutdown()
|
||||
{
|
||||
menu_frame_hide_all();
|
||||
g_hash_table_destroy(menu_hash);
|
||||
}
|
||||
|
||||
void menu_parse()
|
||||
{
|
||||
ObParseInst *i;
|
||||
ObMenuParseState parse_state;
|
||||
xmlDocPtr doc;
|
||||
xmlNodePtr node;
|
||||
gchar *p;
|
||||
|
@ -199,460 +143,137 @@ void menu_parse()
|
|||
}
|
||||
|
||||
if (loaded) {
|
||||
parse_register(i, "menu", parse_menu, NULL);
|
||||
parse_state.menus = NULL;
|
||||
|
||||
parse_register(i, "menu", parse_menu, &parse_state);
|
||||
parse_register(i, "item", parse_menu_item, &parse_state);
|
||||
parse_register(i, "separator", parse_menu_separator, &parse_state);
|
||||
parse_tree(i, doc, node->xmlChildrenNode);
|
||||
}
|
||||
|
||||
parse_shutdown(i);
|
||||
}
|
||||
|
||||
static Window createWindow(Window parent, unsigned long mask,
|
||||
XSetWindowAttributes *attrib)
|
||||
gboolean menu_new(gchar *name, gchar *title, gpointer data)
|
||||
{
|
||||
return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
|
||||
RrDepth(ob_rr_inst), InputOutput,
|
||||
RrVisual(ob_rr_inst), mask, attrib);
|
||||
|
||||
}
|
||||
|
||||
ObMenu *menu_new_full(char *label, char *name, ObMenu *parent,
|
||||
menu_controller_show show, menu_controller_update update,
|
||||
menu_controller_selected selected,
|
||||
menu_controller_hide hide,
|
||||
menu_controller_mouseover mouseover,
|
||||
menu_controller_destroy destroy)
|
||||
{
|
||||
XSetWindowAttributes attrib;
|
||||
ObMenu *self;
|
||||
|
||||
if (g_hash_table_lookup(menu_hash, name)) return FALSE;
|
||||
|
||||
self = g_new0(ObMenu, 1);
|
||||
self->obwin.type = Window_Menu;
|
||||
self->label = g_strdup(label);
|
||||
self->name = g_strdup(name);
|
||||
self->parent = parent;
|
||||
self->open_submenu = NULL;
|
||||
self->over = NULL;
|
||||
self->title = g_strdup(title);
|
||||
self->data = data;
|
||||
|
||||
self->entries = NULL;
|
||||
self->shown = FALSE;
|
||||
self->invalid = TRUE;
|
||||
g_hash_table_insert(menu_hash, self->name, self);
|
||||
|
||||
/* default controllers */
|
||||
self->destroy = destroy;
|
||||
self->show = (show != NULL ? show : menu_show_full);
|
||||
self->hide = (hide != NULL ? hide : menu_hide);
|
||||
self->update = (update != NULL ? update : menu_render);
|
||||
self->mouseover = (mouseover != NULL ? mouseover :
|
||||
menu_control_mouseover);
|
||||
self->selected = (selected != NULL ? selected : menu_entry_fire);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
self->plugin = NULL;
|
||||
self->plugin_data = NULL;
|
||||
void menu_show(gchar *name, gint x, gint y, ObClient *client)
|
||||
{
|
||||
ObMenu *self;
|
||||
ObMenuFrame *frame;
|
||||
|
||||
attrib.override_redirect = TRUE;
|
||||
attrib.event_mask = FRAME_EVENTMASK;
|
||||
self->frame = createWindow(RootWindow(ob_display, ob_screen),
|
||||
CWOverrideRedirect|CWEventMask, &attrib);
|
||||
attrib.event_mask = TITLE_EVENTMASK;
|
||||
self->title = createWindow(self->frame, CWEventMask, &attrib);
|
||||
self->items = createWindow(self->frame, 0, &attrib);
|
||||
if (!(self = menu_from_name(name))) return;
|
||||
|
||||
self->a_title = self->a_items = NULL;
|
||||
/* XXX update entries */
|
||||
|
||||
XMapWindow(ob_display, self->title);
|
||||
XMapWindow(ob_display, self->items);
|
||||
frame = menu_frame_new(self, client);
|
||||
menu_frame_move(frame, x, y);
|
||||
menu_frame_show(frame, NULL);
|
||||
}
|
||||
|
||||
g_hash_table_insert(window_map, &self->frame, self);
|
||||
g_hash_table_insert(window_map, &self->title, self);
|
||||
g_hash_table_insert(window_map, &self->items, self);
|
||||
g_hash_table_insert(menu_hash, g_strdup(name), self);
|
||||
static ObMenuEntry* menu_entry_new(ObMenu *menu, ObMenuEntryType type)
|
||||
{
|
||||
ObMenuEntry *self;
|
||||
|
||||
stacking_add(MENU_AS_WINDOW(self));
|
||||
stacking_raise(MENU_AS_WINDOW(self));
|
||||
g_assert(menu);
|
||||
|
||||
self = g_new0(ObMenuEntry, 1);
|
||||
self->type = type;
|
||||
self->menu = menu;
|
||||
self->enabled = TRUE;
|
||||
return self;
|
||||
}
|
||||
|
||||
void menu_free(char *name)
|
||||
static void menu_entry_free(ObMenuEntry *self)
|
||||
{
|
||||
g_hash_table_remove(menu_hash, name);
|
||||
if (self) {
|
||||
switch (self->type) {
|
||||
case OB_MENU_ENTRY_TYPE_NORMAL:
|
||||
g_free(self->data.normal.label);
|
||||
while (self->data.normal.actions) {
|
||||
action_free(self->data.normal.actions->data);
|
||||
self->data.normal.actions =
|
||||
g_slist_delete_link(self->data.normal.actions,
|
||||
self->data.normal.actions);
|
||||
}
|
||||
break;
|
||||
case OB_MENU_ENTRY_TYPE_SUBMENU:
|
||||
case OB_MENU_ENTRY_TYPE_SEPARATOR:
|
||||
break;
|
||||
}
|
||||
|
||||
ObMenuEntry *menu_entry_new_full(char *label, ObAction *action,
|
||||
ObMenuEntryRenderType render_type,
|
||||
gpointer submenu)
|
||||
{
|
||||
ObMenuEntry *menu_entry = g_new0(ObMenuEntry, 1);
|
||||
|
||||
menu_entry->label = g_strdup(label);
|
||||
menu_entry->render_type = render_type;
|
||||
menu_entry->action = action;
|
||||
|
||||
menu_entry->hilite = FALSE;
|
||||
menu_entry->enabled = TRUE;
|
||||
|
||||
menu_entry->submenu = submenu;
|
||||
|
||||
return menu_entry;
|
||||
g_free(self);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_entry_set_submenu(ObMenuEntry *entry, ObMenu *submenu)
|
||||
{
|
||||
g_assert(entry != NULL);
|
||||
|
||||
entry->submenu = submenu;
|
||||
|
||||
if(entry->parent != NULL)
|
||||
entry->parent->invalid = TRUE;
|
||||
}
|
||||
|
||||
void menu_add_entry(ObMenu *menu, ObMenuEntry *entry)
|
||||
{
|
||||
XSetWindowAttributes attrib;
|
||||
|
||||
g_assert(menu != NULL);
|
||||
g_assert(entry != NULL);
|
||||
g_assert(entry->item == None);
|
||||
|
||||
menu->entries = g_list_append(menu->entries, entry);
|
||||
entry->parent = menu;
|
||||
|
||||
attrib.event_mask = ENTRY_EVENTMASK;
|
||||
entry->item = createWindow(menu->items, CWEventMask, &attrib);
|
||||
entry->submenu_pic = createWindow(menu->items, CWEventMask, &attrib);
|
||||
XMapWindow(ob_display, entry->item);
|
||||
XMapWindow(ob_display, entry->submenu_pic);
|
||||
|
||||
entry->a_item = entry->a_disabled = entry->a_hilite = entry->a_submenu
|
||||
= NULL;
|
||||
|
||||
menu->invalid = TRUE;
|
||||
|
||||
g_hash_table_insert(window_map, &entry->item, menu);
|
||||
g_hash_table_insert(window_map, &entry->submenu_pic, menu);
|
||||
}
|
||||
|
||||
void menu_show(char *name, int x, int y, ObClient *client)
|
||||
void menu_clear_entries(gchar *name)
|
||||
{
|
||||
ObMenu *self;
|
||||
|
||||
self = g_hash_table_lookup(menu_hash, name);
|
||||
if (!self) {
|
||||
g_warning("Attempted to show menu '%s' but it does not exist.",
|
||||
name);
|
||||
return;
|
||||
if (!(self = menu_from_name(name))) return;
|
||||
|
||||
menu_clear_entries_internal(self);
|
||||
}
|
||||
|
||||
menu_show_full(self, x, y, client);
|
||||
}
|
||||
|
||||
void menu_show_full(ObMenu *self, int x, int y, ObClient *client)
|
||||
static void menu_clear_entries_internal(ObMenu *self)
|
||||
{
|
||||
g_assert(self != NULL);
|
||||
/* XXX assert that the menu isn't visible */
|
||||
|
||||
self->update(self);
|
||||
|
||||
self->client = client;
|
||||
|
||||
if (!self->shown) {
|
||||
if (!(self->parent && self->parent->shown)) {
|
||||
grab_pointer(TRUE, None);
|
||||
grab_keyboard(TRUE);
|
||||
}
|
||||
menu_visible = g_list_append(menu_visible, self);
|
||||
}
|
||||
|
||||
menu_control_show(self, x, y, client);
|
||||
}
|
||||
|
||||
void menu_hide(ObMenu *self) {
|
||||
if (self->shown) {
|
||||
XUnmapWindow(ob_display, self->frame);
|
||||
self->shown = FALSE;
|
||||
if (self->open_submenu)
|
||||
self->open_submenu->hide(self->open_submenu);
|
||||
if (self->parent && self->parent->open_submenu == self) {
|
||||
self->parent->open_submenu = NULL;
|
||||
}
|
||||
|
||||
if (!(self->parent && self->parent->shown)) {
|
||||
grab_keyboard(FALSE);
|
||||
grab_pointer(FALSE, None);
|
||||
}
|
||||
menu_visible = g_list_remove(menu_visible, self);
|
||||
if (self->over) {
|
||||
((ObMenuEntry *)self->over->data)->hilite = FALSE;
|
||||
menu_entry_render(self->over->data);
|
||||
self->over = NULL;
|
||||
}
|
||||
while (self->entries) {
|
||||
menu_entry_free(self->entries->data);
|
||||
self->entries = g_list_delete_link(self->entries, self->entries);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_clear(ObMenu *self) {
|
||||
GList *it;
|
||||
|
||||
for (it = self->entries; it; it = it->next) {
|
||||
ObMenuEntry *entry = it->data;
|
||||
menu_entry_free(entry);
|
||||
}
|
||||
self->entries = NULL;
|
||||
self->invalid = TRUE;
|
||||
}
|
||||
|
||||
|
||||
ObMenuEntry *menu_find_entry(ObMenu *menu, Window win)
|
||||
void menu_add_normal(gchar *name, gchar *label, GSList *actions)
|
||||
{
|
||||
GList *it;
|
||||
|
||||
for (it = menu->entries; it; it = it->next) {
|
||||
ObMenuEntry *entry = it->data;
|
||||
if (entry->item == win)
|
||||
return entry;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ObMenuEntry *menu_find_entry_by_submenu(ObMenu *menu, ObMenu *submenu)
|
||||
{
|
||||
GList *it;
|
||||
|
||||
for (it = menu->entries; it; it = it->next) {
|
||||
ObMenuEntry *entry = it->data;
|
||||
if (entry->submenu == submenu)
|
||||
return entry;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ObMenuEntry *menu_find_entry_by_pos(ObMenu *menu, int x, int y)
|
||||
{
|
||||
if (x < 0 || x >= menu->size.width || y < 0 || y >= menu->size.height)
|
||||
return NULL;
|
||||
|
||||
y -= menu->title_h + ob_rr_theme->bwidth;
|
||||
if (y < 0) return NULL;
|
||||
|
||||
ob_debug("%d %p\n", y/menu->item_h,
|
||||
g_list_nth_data(menu->entries, y / menu->item_h));
|
||||
return g_list_nth_data(menu->entries, y / menu->item_h);
|
||||
}
|
||||
|
||||
void menu_entry_fire(ObMenuEntry *self, unsigned int button, unsigned int x,
|
||||
unsigned int y)
|
||||
{
|
||||
ObMenu *m;
|
||||
|
||||
/* ignore wheel scrolling */
|
||||
if (button == 4 || button == 5) return;
|
||||
|
||||
if (self->action) {
|
||||
self->action->data.any.c = self->parent->client;
|
||||
self->action->func(&self->action->data);
|
||||
|
||||
/* hide the whole thing */
|
||||
m = self->parent;
|
||||
while (m->parent) m = m->parent;
|
||||
m->hide(m);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Default menu controller action for showing.
|
||||
*/
|
||||
|
||||
void menu_control_show(ObMenu *self, int x, int y, ObClient *client)
|
||||
{
|
||||
guint i;
|
||||
Rect *a = NULL;
|
||||
|
||||
g_assert(!self->invalid);
|
||||
|
||||
for (i = 0; i < screen_num_monitors; ++i) {
|
||||
a = screen_physical_area_monitor(i);
|
||||
if (RECT_CONTAINS(*a, x, y))
|
||||
break;
|
||||
}
|
||||
g_assert(a != NULL);
|
||||
self->xin_area = i;
|
||||
|
||||
POINT_SET(self->location,
|
||||
MIN(x, a->x + a->width - 1 - self->size.width),
|
||||
MIN(y, a->y + a->height - 1 - self->size.height));
|
||||
XMoveWindow(ob_display, self->frame, self->location.x, self->location.y);
|
||||
|
||||
if (!self->shown) {
|
||||
XMapWindow(ob_display, self->frame);
|
||||
stacking_raise(MENU_AS_WINDOW(self));
|
||||
self->shown = TRUE;
|
||||
} else if (self->shown && self->open_submenu) {
|
||||
self->open_submenu->hide(self->open_submenu);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_control_mouseover(ObMenuEntry *self, gboolean enter)
|
||||
{
|
||||
int x;
|
||||
Rect *a;
|
||||
ObMenu *self;
|
||||
ObMenuEntry *e;
|
||||
|
||||
g_assert(self != NULL);
|
||||
if (!(self = menu_from_name(name))) return;
|
||||
|
||||
if (enter) {
|
||||
/* TODO: we prolly don't need open_submenu */
|
||||
if (self->parent->open_submenu && self->submenu
|
||||
!= self->parent->open_submenu)
|
||||
e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_NORMAL);
|
||||
e->data.normal.label = g_strdup(label);
|
||||
e->data.normal.actions = actions;
|
||||
|
||||
self->entries = g_list_append(self->entries, e);
|
||||
}
|
||||
|
||||
void menu_add_submenu(gchar *name, gchar *submenu)
|
||||
{
|
||||
e = (ObMenuEntry *) self->parent->over->data;
|
||||
e->hilite = FALSE;
|
||||
menu_entry_render(e);
|
||||
self->parent->open_submenu->hide(self->parent->open_submenu);
|
||||
ObMenu *self, *sub;
|
||||
ObMenuEntry *e;
|
||||
|
||||
if (!(self = menu_from_name(name))) return;
|
||||
if (!(sub = menu_from_name(submenu))) return;
|
||||
|
||||
e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SUBMENU);
|
||||
e->data.submenu.submenu = sub;
|
||||
|
||||
self->entries = g_list_append(self->entries, e);
|
||||
}
|
||||
|
||||
if (self->submenu && self->parent->open_submenu != self->submenu) {
|
||||
self->parent->open_submenu = self->submenu;
|
||||
|
||||
/* shouldn't be invalid since it must be displayed */
|
||||
g_assert(!self->parent->invalid);
|
||||
/* TODO: I don't understand why these bevels should be here.
|
||||
Something must be wrong in the width calculation */
|
||||
x = self->parent->location.x + self->parent->size.width +
|
||||
ob_rr_theme->bwidth - ob_rr_theme->menu_overlap;
|
||||
|
||||
/* need to get the width. is this bad?*/
|
||||
self->submenu->update(self->submenu);
|
||||
|
||||
a = screen_physical_area_monitor(self->parent->xin_area);
|
||||
|
||||
if (self->submenu->size.width + x + ob_rr_theme->bwidth >=
|
||||
a->x + a->width) {
|
||||
int newparentx = a->x + a->width
|
||||
- self->submenu->size.width
|
||||
- self->parent->size.width
|
||||
- ob_rr_theme->bwidth * 2
|
||||
- ob_rr_theme->menu_overlap;
|
||||
|
||||
x = a->x + a->width - self->submenu->size.width
|
||||
- ob_rr_theme->menu_overlap;
|
||||
XWarpPointer(ob_display, None, None, 0, 0, 0, 0,
|
||||
newparentx - self->parent->location.x, 0);
|
||||
|
||||
menu_show_full(self->parent, newparentx,
|
||||
self->parent->location.y, self->parent->client);
|
||||
}
|
||||
|
||||
menu_show_full(self->submenu, x,
|
||||
self->parent->location.y + self->y +
|
||||
self->parent->title_h + ob_rr_theme->bwidth,
|
||||
self->parent->client);
|
||||
}
|
||||
self->hilite = TRUE;
|
||||
self->parent->over = g_list_find(self->parent->entries, self);
|
||||
|
||||
} else
|
||||
self->hilite = FALSE;
|
||||
|
||||
menu_entry_render(self);
|
||||
}
|
||||
|
||||
void menu_control_keyboard_nav(unsigned int key)
|
||||
void menu_add_separator(gchar *name)
|
||||
{
|
||||
static ObMenu *current_menu = NULL;
|
||||
ObMenuEntry *e = NULL;
|
||||
ObMenu *self;
|
||||
ObMenuEntry *e;
|
||||
|
||||
ObKey obkey = OB_NUM_KEYS;
|
||||
if (!(self = menu_from_name(name))) return;
|
||||
|
||||
/* hrmm. could be fixed */
|
||||
if (key == ob_keycode(OB_KEY_DOWN))
|
||||
obkey = OB_KEY_DOWN;
|
||||
else if (key == ob_keycode(OB_KEY_UP))
|
||||
obkey = OB_KEY_UP;
|
||||
else if (key == ob_keycode(OB_KEY_RIGHT)) /* fuck */
|
||||
obkey = OB_KEY_RIGHT;
|
||||
else if (key == ob_keycode(OB_KEY_LEFT)) /* users */
|
||||
obkey = OB_KEY_LEFT;
|
||||
else if (key == ob_keycode(OB_KEY_RETURN))
|
||||
obkey = OB_KEY_RETURN;
|
||||
e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SEPARATOR);
|
||||
|
||||
|
||||
if (current_menu == NULL)
|
||||
current_menu = menu_visible->data;
|
||||
|
||||
switch (obkey) {
|
||||
case OB_KEY_DOWN: {
|
||||
if (current_menu->over) {
|
||||
current_menu->mouseover(current_menu->over->data, FALSE);
|
||||
current_menu->over = (current_menu->over->next != NULL ?
|
||||
current_menu->over->next :
|
||||
current_menu->entries);
|
||||
}
|
||||
else
|
||||
current_menu->over = current_menu->entries;
|
||||
|
||||
if (current_menu->over)
|
||||
current_menu->mouseover(current_menu->over->data, TRUE);
|
||||
|
||||
break;
|
||||
}
|
||||
case OB_KEY_UP: {
|
||||
if (current_menu->over) {
|
||||
current_menu->mouseover(current_menu->over->data, FALSE);
|
||||
current_menu->over = (current_menu->over->prev != NULL ?
|
||||
current_menu->over->prev :
|
||||
g_list_last(current_menu->entries));
|
||||
} else
|
||||
current_menu->over = g_list_last(current_menu->entries);
|
||||
|
||||
if (current_menu->over)
|
||||
current_menu->mouseover(current_menu->over->data, TRUE);
|
||||
|
||||
break;
|
||||
}
|
||||
case OB_KEY_RIGHT: {
|
||||
if (current_menu->over == NULL)
|
||||
return;
|
||||
e = (ObMenuEntry *)current_menu->over->data;
|
||||
if (e->submenu) {
|
||||
current_menu->mouseover(e, TRUE);
|
||||
current_menu = e->submenu;
|
||||
current_menu->over = current_menu->entries;
|
||||
if (current_menu->over)
|
||||
current_menu->mouseover(current_menu->over->data, TRUE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OB_KEY_RETURN: {
|
||||
if (current_menu->over == NULL)
|
||||
return;
|
||||
e = (ObMenuEntry *)current_menu->over->data;
|
||||
|
||||
current_menu->mouseover(e, FALSE);
|
||||
current_menu->over = NULL;
|
||||
/* zero is enter */
|
||||
menu_entry_fire(e, 0, 0, 0);
|
||||
}
|
||||
|
||||
case OB_KEY_LEFT: {
|
||||
if (current_menu->over != NULL) {
|
||||
current_menu->mouseover(current_menu->over->data, FALSE);
|
||||
current_menu->over = NULL;
|
||||
}
|
||||
|
||||
current_menu->hide(current_menu);
|
||||
|
||||
if (current_menu->parent)
|
||||
current_menu = current_menu->parent;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
((ObMenu *)menu_visible->data)->hide(menu_visible->data);
|
||||
current_menu = NULL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void menu_noop()
|
||||
{
|
||||
/* This noop brought to you by OLS 2003 Email Garden. */
|
||||
self->entries = g_list_append(self->entries, e);
|
||||
}
|
||||
|
|
209
openbox/menu.h
209
openbox/menu.h
|
@ -9,199 +9,82 @@
|
|||
#include <glib.h>
|
||||
|
||||
struct _ObClient;
|
||||
struct _ObParseInst;
|
||||
struct _ObMenuFrame;
|
||||
|
||||
typedef struct _ObMenu ObMenu;
|
||||
typedef struct _ObMenuEntry ObMenuEntry;
|
||||
typedef struct _ObNormalMenuEntry ObNormalMenuEntry;
|
||||
typedef struct _ObSubmenuMenuEntry ObSubmenuMenuEntry;
|
||||
typedef struct _ObSeparatorMenuEntry ObSeparatorMenuEntry;
|
||||
|
||||
typedef void(*menu_controller_destroy)(ObMenu *self);
|
||||
typedef void(*menu_controller_show)(ObMenu *self, int x, int y,
|
||||
struct _ObClient *);
|
||||
typedef void(*menu_controller_update)(ObMenu *self);
|
||||
typedef void(*menu_controller_mouseover)(ObMenuEntry *self, gboolean enter);
|
||||
typedef void(*menu_controller_selected)(ObMenuEntry *entry,
|
||||
unsigned int button,
|
||||
unsigned int x, unsigned int y);
|
||||
typedef void(*menu_controller_hide)(ObMenu *self);
|
||||
|
||||
|
||||
extern GHashTable *menu_hash;
|
||||
extern GList *menu_visible;
|
||||
|
||||
|
||||
|
||||
struct _ObMenu
|
||||
{
|
||||
ObWindow obwin;
|
||||
|
||||
/* The title displayed above the menu.
|
||||
NULL for no titlebar */
|
||||
gchar *label;
|
||||
|
||||
/* Name of the menu.
|
||||
Used in the action showmenu */
|
||||
/* Name of the menu. Used in the showmenu action. */
|
||||
gchar *name;
|
||||
/* Displayed title */
|
||||
gchar *title;
|
||||
|
||||
/* ObMenuEntry list */
|
||||
GList *entries;
|
||||
|
||||
/* If the menu is currently displayed */
|
||||
gboolean shown;
|
||||
|
||||
/* If the rendering of the menu has changed and needs to be rerendered. */
|
||||
gboolean invalid;
|
||||
|
||||
/* Kind of lame.Each menu can only be a submenu, and each menu can only
|
||||
have one submenu open */
|
||||
ObMenu *parent;
|
||||
ObMenu *open_submenu;
|
||||
GList *over;
|
||||
|
||||
/* destructor */
|
||||
menu_controller_destroy destroy;
|
||||
|
||||
/* behaviour callbacks
|
||||
TODO: Document and split code that HAS to be in the overridden callback */
|
||||
/* place a menu on screen */
|
||||
menu_controller_show show;
|
||||
/* Hide the menu */
|
||||
menu_controller_hide hide;
|
||||
/* render a menu */
|
||||
menu_controller_update update;
|
||||
/* Event for a mouse enter/exit on an entry
|
||||
TODO: May have to split from simple render updating?
|
||||
*/
|
||||
menu_controller_mouseover mouseover;
|
||||
/* Entry is clicked/hit enter on */
|
||||
menu_controller_selected selected;
|
||||
|
||||
|
||||
/* render stuff */
|
||||
struct _ObClient *client;
|
||||
Window frame;
|
||||
Window title;
|
||||
RrAppearance *a_title;
|
||||
gint title_min_w, title_h;
|
||||
Window items;
|
||||
RrAppearance *a_items;
|
||||
gint bullet_w;
|
||||
gint item_h;
|
||||
Point location;
|
||||
Size size;
|
||||
guint xin_area; /* index of the xinerama head/area */
|
||||
|
||||
/* Name of plugin for menu */
|
||||
char *plugin;
|
||||
/* plugin's data */
|
||||
void *plugin_data;
|
||||
/* plugin data */
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
OB_MENU_ENTRY_RENDER_TYPE_NONE,
|
||||
OB_MENU_ENTRY_RENDER_TYPE_SUBMENU,
|
||||
OB_MENU_ENTRY_RENDER_TYPE_BOOLEAN,
|
||||
OB_MENU_ENTRY_RENDER_TYPE_SEPARATOR,
|
||||
OB_MENU_ENTRY_RENDER_TYPE_OTHER /* XXX what is this? */
|
||||
} ObMenuEntryRenderType;
|
||||
OB_MENU_ENTRY_TYPE_NORMAL,
|
||||
OB_MENU_ENTRY_TYPE_SUBMENU,
|
||||
OB_MENU_ENTRY_TYPE_SEPARATOR
|
||||
} ObMenuEntryType;
|
||||
|
||||
struct _ObNormalMenuEntry {
|
||||
gchar *label;
|
||||
|
||||
/* List of ObActions */
|
||||
GSList *actions;
|
||||
};
|
||||
|
||||
struct _ObSubmenuMenuEntry {
|
||||
ObMenu *submenu;
|
||||
};
|
||||
|
||||
struct _ObSeparatorMenuEntry {
|
||||
gchar foo; /* placeholder */
|
||||
};
|
||||
|
||||
struct _ObMenuEntry
|
||||
{
|
||||
char *label;
|
||||
ObMenu *parent;
|
||||
ObMenuEntryType type;
|
||||
ObMenu *menu;
|
||||
|
||||
ObAction *action;
|
||||
|
||||
ObMenuEntryRenderType render_type;
|
||||
gboolean hilite;
|
||||
/* state */
|
||||
gboolean enabled;
|
||||
gboolean boolean_value;
|
||||
|
||||
ObMenu *submenu;
|
||||
|
||||
/* render stuff */
|
||||
Window item;
|
||||
Window submenu_pic;
|
||||
|
||||
RrAppearance *a_item;
|
||||
RrAppearance *a_disabled;
|
||||
RrAppearance *a_hilite;
|
||||
RrAppearance *a_submenu;
|
||||
gint y;
|
||||
gint min_w;
|
||||
} MenuEntry;
|
||||
|
||||
typedef struct PluginMenuCreateData{
|
||||
struct _ObParseInst *parse_inst;
|
||||
xmlDocPtr doc;
|
||||
xmlNodePtr node;
|
||||
ObMenu *parent;
|
||||
} PluginMenuCreateData;
|
||||
|
||||
union u {
|
||||
ObNormalMenuEntry normal;
|
||||
ObSubmenuMenuEntry submenu;
|
||||
ObSeparatorMenuEntry separator;
|
||||
} data;
|
||||
};
|
||||
|
||||
void menu_startup();
|
||||
void menu_shutdown();
|
||||
|
||||
void menu_parse();
|
||||
|
||||
void menu_noop();
|
||||
gboolean menu_new(gchar *name, gchar *title, gpointer data);
|
||||
|
||||
#define menu_new(l, n, p) \
|
||||
menu_new_full(l, n, p, menu_show_full, menu_render, menu_entry_fire, \
|
||||
menu_hide, menu_control_mouseover, NULL)
|
||||
void menu_show(gchar *name, gint x, gint y, struct _ObClient *client);
|
||||
|
||||
ObMenu *menu_new_full(char *label, char *name, ObMenu *parent,
|
||||
menu_controller_show show,
|
||||
menu_controller_update update,
|
||||
menu_controller_selected selected,
|
||||
menu_controller_hide hide,
|
||||
menu_controller_mouseover mouseover,
|
||||
menu_controller_destroy destroy);
|
||||
/* functions for building menus */
|
||||
void menu_clear_entries(gchar *name);
|
||||
void menu_add_normal(gchar *name, gchar *label, GSList *actions);
|
||||
void menu_add_submenu(gchar *name, gchar *submenu);
|
||||
void menu_add_separator(gchar *name);
|
||||
|
||||
void menu_free(char *name);
|
||||
|
||||
void menu_show(char *name, int x, int y, struct _ObClient *client);
|
||||
void menu_show_full(ObMenu *menu, int x, int y, struct _ObClient *client);
|
||||
|
||||
void menu_hide(ObMenu *self);
|
||||
|
||||
void menu_clear(ObMenu *self);
|
||||
|
||||
ObMenuEntry *menu_entry_new_full(char *label, ObAction *action,
|
||||
ObMenuEntryRenderType render_type,
|
||||
gpointer submenu);
|
||||
|
||||
#define menu_entry_new(label, action) \
|
||||
menu_entry_new_full(label, action, OB_MENU_ENTRY_RENDER_TYPE_NONE, NULL)
|
||||
|
||||
#define menu_entry_new_separator(label) \
|
||||
menu_entry_new_full(label, NULL, OB_MENU_ENTRY_RENDER_TYPE_SEPARATOR, NULL)
|
||||
|
||||
#define menu_entry_new_submenu(label, submenu) \
|
||||
menu_entry_new_full(label, NULL, OB_MENU_ENTRY_RENDER_TYPE_SUBMENU, submenu)
|
||||
|
||||
#define menu_entry_new_boolean(label, action) \
|
||||
menu_entry_new_full(label, action, OB_MENU_ENTRY_RENDER_TYPE_BOOLEAN, NULL)
|
||||
|
||||
void menu_entry_free(ObMenuEntry *entry);
|
||||
|
||||
void menu_entry_set_submenu(ObMenuEntry *entry, ObMenu *submenu);
|
||||
|
||||
void menu_add_entry(ObMenu *menu, ObMenuEntry *entry);
|
||||
|
||||
ObMenuEntry *menu_find_entry(ObMenu *menu, Window win);
|
||||
ObMenuEntry *menu_find_entry_by_submenu(ObMenu *menu, ObMenu *submenu);
|
||||
ObMenuEntry *menu_find_entry_by_pos(ObMenu *menu, int x, int y);
|
||||
|
||||
void menu_entry_render(ObMenuEntry *self);
|
||||
|
||||
void menu_entry_fire(ObMenuEntry *entry,
|
||||
unsigned int button, unsigned int x, unsigned int y);
|
||||
|
||||
void menu_render(ObMenu *self);
|
||||
void menu_render_full(ObMenu *self);
|
||||
|
||||
/*so plugins can call it? */
|
||||
void parse_menu_full(struct _ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
|
||||
void *data, gboolean new);
|
||||
void menu_control_mouseover(ObMenuEntry *entry, gboolean enter);
|
||||
void menu_control_keyboard_nav(unsigned int key);
|
||||
#endif
|
||||
|
|
|
@ -1,158 +0,0 @@
|
|||
/* Functions for default rendering of menus. Might become pluginnable */
|
||||
|
||||
#include "debug.h"
|
||||
#include "menu.h"
|
||||
#include "openbox.h"
|
||||
#include "render/theme.h"
|
||||
|
||||
void menu_render(ObMenu *self) {
|
||||
GList *it;
|
||||
int items_h = 0;
|
||||
int nitems = 0; /* each item, only one is used */
|
||||
int item_y;
|
||||
|
||||
self->size.width = 1;
|
||||
self->item_h = 1;
|
||||
|
||||
if (self->a_title == NULL) {
|
||||
XSetWindowBorderWidth(ob_display, self->frame, ob_rr_theme->bwidth);
|
||||
XSetWindowBackground(ob_display, self->frame,
|
||||
RrColorPixel(ob_rr_theme->b_color));
|
||||
XSetWindowBorderWidth(ob_display, self->title, ob_rr_theme->bwidth);
|
||||
XSetWindowBorder(ob_display, self->frame,
|
||||
RrColorPixel(ob_rr_theme->b_color));
|
||||
XSetWindowBorder(ob_display, self->title,
|
||||
RrColorPixel(ob_rr_theme->b_color));
|
||||
|
||||
self->a_title = RrAppearanceCopy(ob_rr_theme->a_menu_title);
|
||||
self->a_items = RrAppearanceCopy(ob_rr_theme->a_menu);
|
||||
}
|
||||
|
||||
/* set texture data and size them mofos out */
|
||||
if (self->label && !(self->parent && self->parent->shown)) {
|
||||
self->a_title->texture[0].data.text.string = self->label;
|
||||
RrMinsize(self->a_title, &self->title_min_w, &self->title_h);
|
||||
self->title_min_w += ob_rr_theme->bevel * 2;
|
||||
self->title_h += ob_rr_theme->bevel * 2;
|
||||
self->size.width = MAX(self->size.width, self->title_min_w);
|
||||
} else
|
||||
self->title_h = -ob_rr_theme->bwidth;
|
||||
|
||||
for (it = self->entries; it; it = it->next) {
|
||||
ObMenuEntry *e = it->data;
|
||||
int h;
|
||||
|
||||
if (e->a_item == NULL) {
|
||||
e->a_item = RrAppearanceCopy(ob_rr_theme->a_menu_item);
|
||||
e->a_disabled = RrAppearanceCopy(ob_rr_theme->a_menu_disabled);
|
||||
e->a_hilite = RrAppearanceCopy(ob_rr_theme->a_menu_hilite);
|
||||
e->a_submenu = RrAppearanceCopy(ob_rr_theme->a_menu_bullet);
|
||||
}
|
||||
|
||||
e->a_item->texture[0].data.text.string = e->label;
|
||||
RrMinsize(e->a_item, &e->min_w, &self->item_h);
|
||||
self->size.width = MAX(self->size.width, e->min_w);
|
||||
|
||||
e->a_disabled->texture[0].data.text.string = e->label;
|
||||
RrMinsize(e->a_disabled, &e->min_w, &h);
|
||||
self->item_h = MAX(self->item_h, h);
|
||||
self->size.width = MAX(self->size.width, e->min_w);
|
||||
|
||||
e->a_hilite->texture[0].data.text.string = e->label;
|
||||
RrMinsize(e->a_hilite, &e->min_w, &h);
|
||||
self->item_h = MAX(self->item_h, h);
|
||||
self->size.width = MAX(self->size.width, e->min_w);
|
||||
|
||||
e->min_w += ob_rr_theme->bevel * 2;
|
||||
++nitems;
|
||||
}
|
||||
self->bullet_w = self->item_h + ob_rr_theme->bevel;
|
||||
self->size.width += 2 * self->bullet_w + 2 * ob_rr_theme->bevel;
|
||||
self->item_h += ob_rr_theme->bevel * 2;
|
||||
items_h = self->item_h * MAX(nitems, 1);
|
||||
|
||||
self->size.height = MAX(self->title_h + items_h + ob_rr_theme->bwidth, 1);
|
||||
XResizeWindow(ob_display, self->frame, self->size.width,self->size.height);
|
||||
if (self->label && !(self->parent && self->parent->shown)) {
|
||||
XMoveResizeWindow(ob_display, self->title, -ob_rr_theme->bwidth,
|
||||
-ob_rr_theme->bwidth,
|
||||
self->size.width, self->title_h);
|
||||
XMapWindow(ob_display, self->title);
|
||||
} else
|
||||
XUnmapWindow(ob_display, self->title);
|
||||
|
||||
XMoveResizeWindow(ob_display, self->items, 0,
|
||||
self->title_h + ob_rr_theme->bwidth, self->size.width,
|
||||
items_h);
|
||||
|
||||
if (self->label && !(self->parent && self->parent->shown))
|
||||
RrPaint(self->a_title, self->title, self->size.width, self->title_h);
|
||||
RrPaint(self->a_items, self->items, self->size.width, items_h);
|
||||
|
||||
item_y = 0;
|
||||
for (it = self->entries; it; it = it->next) {
|
||||
((ObMenuEntry*)it->data)->y = item_y;
|
||||
menu_entry_render(it->data);
|
||||
item_y += self->item_h;
|
||||
}
|
||||
|
||||
self->invalid = FALSE;
|
||||
}
|
||||
|
||||
void menu_entry_render(ObMenuEntry *self)
|
||||
{
|
||||
ObMenu *menu = self->parent;
|
||||
RrAppearance *a, *s = NULL;
|
||||
|
||||
switch (self->render_type) {
|
||||
case OB_MENU_ENTRY_RENDER_TYPE_SUBMENU:
|
||||
a = self->enabled ? (self->hilite ? self->a_hilite : self->a_item)
|
||||
: self->a_disabled;
|
||||
s = self->a_submenu;
|
||||
break;
|
||||
case OB_MENU_ENTRY_RENDER_TYPE_BOOLEAN:
|
||||
/* TODO: boolean check */
|
||||
a = self->enabled ? (self->hilite ? self->a_hilite : self->a_item)
|
||||
: self->a_disabled;
|
||||
break;
|
||||
case OB_MENU_ENTRY_RENDER_TYPE_NONE:
|
||||
a = self->enabled ? (self->hilite ? self->a_hilite : self->a_item )
|
||||
: self->a_disabled;
|
||||
break;
|
||||
case OB_MENU_ENTRY_RENDER_TYPE_SEPARATOR:
|
||||
a = self->a_item;
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached(); /* unhandled rendering type */
|
||||
break;
|
||||
}
|
||||
|
||||
XMoveResizeWindow(ob_display, self->item, 0, self->y,
|
||||
menu->size.width, menu->item_h);
|
||||
if (s) {
|
||||
XMoveResizeWindow(ob_display, self->submenu_pic,
|
||||
menu->size.width -
|
||||
menu->bullet_w + (menu->bullet_w - 8) / 2,
|
||||
self->y + (menu->item_h - 8) / 2,
|
||||
8, 8);
|
||||
XMapWindow(ob_display, self->submenu_pic);
|
||||
} else
|
||||
XUnmapWindow(ob_display, self->submenu_pic);
|
||||
|
||||
a->surface.parent = menu->a_items;
|
||||
a->surface.parentx = 0;
|
||||
a->surface.parenty = self->y;
|
||||
|
||||
if (s) {
|
||||
s->surface.parent = a;
|
||||
s->surface.parentx = menu->size.width -
|
||||
menu->bullet_w + (menu->bullet_w - 8) / 2,
|
||||
s->surface.parenty = (menu->item_h - 8) / 2;
|
||||
}
|
||||
|
||||
RrPaint(a, self->item, menu->size.width, menu->item_h);
|
||||
|
||||
if (s)
|
||||
RrPaint(s, self->submenu_pic, 8, 8);
|
||||
}
|
540
openbox/menuframe.c
Normal file
540
openbox/menuframe.c
Normal file
|
@ -0,0 +1,540 @@
|
|||
#include "menuframe.h"
|
||||
#include "client.h"
|
||||
#include "menu.h"
|
||||
#include "screen.h"
|
||||
#include "grab.h"
|
||||
#include "openbox.h"
|
||||
#include "render/theme.h"
|
||||
|
||||
#define SEPARATOR_HEIGHT 5
|
||||
|
||||
#define FRAME_EVENTMASK (ButtonPressMask |ButtonMotionMask | EnterWindowMask |\
|
||||
LeaveWindowMask)
|
||||
#define TITLE_EVENTMASK (ButtonPressMask | ButtonMotionMask)
|
||||
#define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
|
||||
ButtonPressMask | ButtonReleaseMask)
|
||||
|
||||
GList *menu_frame_visible;
|
||||
|
||||
static void menu_frame_render(ObMenuFrame *self);
|
||||
static void menu_frame_update(ObMenuFrame *self);
|
||||
|
||||
static Window createWindow(Window parent, unsigned long mask,
|
||||
XSetWindowAttributes *attrib)
|
||||
{
|
||||
return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0,
|
||||
RrDepth(ob_rr_inst), InputOutput,
|
||||
RrVisual(ob_rr_inst), mask, attrib);
|
||||
|
||||
}
|
||||
|
||||
ObMenuFrame* menu_frame_new(ObMenu *menu, ObClient *client)
|
||||
{
|
||||
ObMenuFrame *self;
|
||||
XSetWindowAttributes attr;
|
||||
|
||||
self = g_new0(ObMenuFrame, 1);
|
||||
self->type = Window_Menu;
|
||||
self->menu = menu;
|
||||
self->selected = NULL;
|
||||
self->client = client;
|
||||
|
||||
attr.event_mask = FRAME_EVENTMASK;
|
||||
self->window = createWindow(RootWindow(ob_display, ob_screen),
|
||||
CWEventMask, &attr);
|
||||
attr.event_mask = TITLE_EVENTMASK;
|
||||
self->title = createWindow(self->window, CWEventMask, &attr);
|
||||
self->items = createWindow(self->window, 0, NULL);
|
||||
|
||||
XMapWindow(ob_display, self->items);
|
||||
|
||||
self->a_title = RrAppearanceCopy(ob_rr_theme->a_menu_title);
|
||||
self->a_items = RrAppearanceCopy(ob_rr_theme->a_menu);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void menu_frame_free(ObMenuFrame *self)
|
||||
{
|
||||
if (self) {
|
||||
XDestroyWindow(ob_display, self->items);
|
||||
XDestroyWindow(ob_display, self->title);
|
||||
XDestroyWindow(ob_display, self->window);
|
||||
|
||||
RrAppearanceFree(self->a_items);
|
||||
RrAppearanceFree(self->a_title);
|
||||
|
||||
g_free(self);
|
||||
}
|
||||
}
|
||||
|
||||
ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry, ObMenuFrame *frame)
|
||||
{
|
||||
ObMenuEntryFrame *self;
|
||||
XSetWindowAttributes attr;
|
||||
|
||||
self = g_new0(ObMenuEntryFrame, 1);
|
||||
self->entry = entry;
|
||||
self->frame = frame;
|
||||
|
||||
attr.event_mask = ENTRY_EVENTMASK;
|
||||
self->window = createWindow(self->frame->items, CWEventMask, &attr);
|
||||
self->icon = createWindow(self->window, 0, NULL);
|
||||
self->text = createWindow(self->window, 0, NULL);
|
||||
self->bullet = createWindow(self->window, 0, NULL);
|
||||
|
||||
XMapWindow(ob_display, self->window);
|
||||
XMapWindow(ob_display, self->text);
|
||||
|
||||
self->a_normal = RrAppearanceCopy(ob_rr_theme->a_menu_item);
|
||||
self->a_disabled = RrAppearanceCopy(ob_rr_theme->a_menu_disabled);
|
||||
self->a_selected = RrAppearanceCopy(ob_rr_theme->a_menu_hilite);
|
||||
|
||||
self->a_icon = RrAppearanceCopy(ob_rr_theme->a_clear_tex);
|
||||
self->a_icon->texture[0].type = RR_TEXTURE_RGBA;
|
||||
self->a_bullet = RrAppearanceCopy(ob_rr_theme->a_menu_bullet);
|
||||
self->a_bullet->texture[0].type = RR_TEXTURE_MASK;
|
||||
|
||||
self->a_text_normal =
|
||||
RrAppearanceCopy(ob_rr_theme->a_menu_text_item);
|
||||
self->a_text_disabled =
|
||||
RrAppearanceCopy(ob_rr_theme->a_menu_text_disabled);
|
||||
self->a_text_selected =
|
||||
RrAppearanceCopy(ob_rr_theme->a_menu_text_hilite);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void menu_entry_frame_free(ObMenuEntryFrame *self)
|
||||
{
|
||||
if (self) {
|
||||
XDestroyWindow(ob_display, self->icon);
|
||||
XDestroyWindow(ob_display, self->text);
|
||||
XDestroyWindow(ob_display, self->bullet);
|
||||
XDestroyWindow(ob_display, self->window);
|
||||
|
||||
RrAppearanceFree(self->a_normal);
|
||||
RrAppearanceFree(self->a_disabled);
|
||||
RrAppearanceFree(self->a_selected);
|
||||
|
||||
RrAppearanceFree(self->a_icon);
|
||||
RrAppearanceFree(self->a_text_normal);
|
||||
RrAppearanceFree(self->a_text_disabled);
|
||||
RrAppearanceFree(self->a_text_selected);
|
||||
RrAppearanceFree(self->a_bullet);
|
||||
|
||||
g_free(self);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_frame_move(ObMenuFrame *self, gint x, gint y)
|
||||
{
|
||||
/* XXX screen constraints */
|
||||
RECT_SET_POINT(self->area, x, y);
|
||||
XMoveWindow(ob_display, self->window, self->area.x, self->area.y);
|
||||
}
|
||||
|
||||
void menu_frame_move_on_screen(ObMenuFrame *self)
|
||||
{
|
||||
Rect *a;
|
||||
guint i;
|
||||
gint dx = 0, dy = 0;
|
||||
|
||||
for (i = 0; i < screen_num_monitors; ++i) {
|
||||
a = screen_physical_area_monitor(i);
|
||||
if (RECT_INTERSECTS_RECT(*a, self->area))
|
||||
break;
|
||||
}
|
||||
if (a) a = screen_physical_area_monitor(0);
|
||||
|
||||
dx = MIN(0, (a->x + a->width) - (self->area.x + self->area.width));
|
||||
dy = MIN(0, (a->y + a->height) - (self->area.y + self->area.height));
|
||||
if (!dx) dx = MAX(0, a->x - self->area.x);
|
||||
if (!dy) dy = MAX(0, a->y - self->area.y);
|
||||
|
||||
if (dx || dy) {
|
||||
ObMenuFrame *f;
|
||||
|
||||
for (f = self; f; f = f->parent)
|
||||
menu_frame_move(f, f->area.x + dx, f->area.y + dy);
|
||||
XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy);
|
||||
}
|
||||
}
|
||||
|
||||
static void menu_entry_frame_render(ObMenuEntryFrame *self)
|
||||
{
|
||||
RrAppearance *item_a, *text_a;
|
||||
gint th; /* temp */
|
||||
|
||||
item_a = (!self->entry->enabled ?
|
||||
self->a_disabled :
|
||||
(self == self->frame->selected ?
|
||||
self->a_selected :
|
||||
self->a_normal));
|
||||
switch (self->entry->type) {
|
||||
case OB_MENU_ENTRY_TYPE_NORMAL:
|
||||
case OB_MENU_ENTRY_TYPE_SUBMENU:
|
||||
th = self->frame->item_h;
|
||||
break;
|
||||
case OB_MENU_ENTRY_TYPE_SEPARATOR:
|
||||
th = SEPARATOR_HEIGHT;
|
||||
break;
|
||||
}
|
||||
RECT_SET_SIZE(self->area, self->frame->inner_w, th);
|
||||
XResizeWindow(ob_display, self->window,
|
||||
self->area.width, self->area.height);
|
||||
item_a->surface.parent = self->frame->a_items;
|
||||
item_a->surface.parentx = self->area.x;
|
||||
item_a->surface.parenty = self->area.y;
|
||||
RrPaint(item_a, self->window, self->area.width, self->area.height);
|
||||
|
||||
text_a = (!self->entry->enabled ?
|
||||
self->a_text_disabled :
|
||||
(self == self->frame->selected ?
|
||||
self->a_text_selected :
|
||||
self->a_text_normal));
|
||||
switch (self->entry->type) {
|
||||
case OB_MENU_ENTRY_TYPE_NORMAL:
|
||||
text_a->texture[0].data.text.string = self->entry->data.normal.label;
|
||||
break;
|
||||
case OB_MENU_ENTRY_TYPE_SUBMENU:
|
||||
text_a->texture[0].data.text.string =
|
||||
self->entry->data.submenu.submenu->title;
|
||||
break;
|
||||
case OB_MENU_ENTRY_TYPE_SEPARATOR:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (self->entry->type) {
|
||||
case OB_MENU_ENTRY_TYPE_NORMAL:
|
||||
case OB_MENU_ENTRY_TYPE_SUBMENU:
|
||||
XMoveResizeWindow(ob_display, self->text,
|
||||
self->frame->text_x, 0,
|
||||
self->frame->text_w, self->frame->item_h);
|
||||
text_a->surface.parent = item_a;
|
||||
text_a->surface.parentx = self->frame->text_x;
|
||||
text_a->surface.parenty = 0;
|
||||
RrPaint(text_a, self->text, self->frame->text_w, self->frame->item_h);
|
||||
XMapWindow(ob_display, self->text);
|
||||
break;
|
||||
case OB_MENU_ENTRY_TYPE_SEPARATOR:
|
||||
XUnmapWindow(ob_display, self->text);
|
||||
break;
|
||||
}
|
||||
|
||||
/* XXX draw icons */
|
||||
if (0) {
|
||||
XMoveResizeWindow(ob_display, self->icon, 0, 0,
|
||||
self->frame->item_h, self->frame->item_h);
|
||||
/* XXX set the RGBA surface stuff */
|
||||
self->a_icon->surface.parent = item_a;
|
||||
self->a_icon->surface.parentx = 0;
|
||||
self->a_icon->surface.parenty = 0;
|
||||
RrPaint(self->a_icon, self->icon,
|
||||
self->frame->item_h, self->frame->item_h);
|
||||
XMapWindow(ob_display, self->icon);
|
||||
} else
|
||||
XUnmapWindow(ob_display, self->icon);
|
||||
|
||||
if (self->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) {
|
||||
XMoveResizeWindow(ob_display, self->bullet,
|
||||
self->frame->text_x + self->frame->text_w, 0,
|
||||
self->frame->item_h, self->frame->item_h);
|
||||
self->a_bullet->surface.parent = item_a;
|
||||
self->a_bullet->surface.parentx =
|
||||
self->frame->text_x + self->frame->text_w;
|
||||
self->a_bullet->surface.parenty = 0;
|
||||
RrPaint(self->a_bullet, self->bullet,
|
||||
self->frame->item_h, self->frame->item_h);
|
||||
XMapWindow(ob_display, self->bullet);
|
||||
} else
|
||||
XUnmapWindow(ob_display, self->bullet);
|
||||
}
|
||||
|
||||
static void menu_frame_render(ObMenuFrame *self)
|
||||
{
|
||||
gint w = 0, h = 0;
|
||||
gint allitems_h = 0;
|
||||
gint tw, th; /* temps */
|
||||
GList *it;
|
||||
gboolean has_icon = FALSE, has_bullet = FALSE;
|
||||
|
||||
XSetWindowBorderWidth(ob_display, self->window, ob_rr_theme->bwidth);
|
||||
XSetWindowBorder(ob_display, self->window,
|
||||
RrColorPixel(ob_rr_theme->b_color));
|
||||
|
||||
if (!self->parent && self->menu->title) {
|
||||
XMoveWindow(ob_display, self->title,
|
||||
-ob_rr_theme->bwidth, h - ob_rr_theme->bwidth);
|
||||
|
||||
self->a_title->texture[0].data.text.string = self->menu->title;
|
||||
RrMinsize(self->a_title, &tw, &th);
|
||||
w = MAX(w, tw);
|
||||
h += (self->title_h = th + ob_rr_theme->bwidth);
|
||||
|
||||
XSetWindowBorderWidth(ob_display, self->title, ob_rr_theme->bwidth);
|
||||
XSetWindowBorder(ob_display, self->title,
|
||||
RrColorPixel(ob_rr_theme->b_color));
|
||||
}
|
||||
|
||||
XMoveWindow(ob_display, self->items, 0, h);
|
||||
|
||||
if (self->entries) {
|
||||
ObMenuEntryFrame *e = self->entries->data;
|
||||
e->a_text_normal->texture[0].data.text.string = "";
|
||||
RrMinsize(e->a_text_normal, &tw, &th);
|
||||
self->item_h = th;
|
||||
} else
|
||||
self->item_h = 0;
|
||||
|
||||
for (it = self->entries; it; it = g_list_next(it)) {
|
||||
RrAppearance *text_a;
|
||||
ObMenuEntryFrame *e = it->data;
|
||||
|
||||
RECT_SET_POINT(e->area, 0, allitems_h);
|
||||
XMoveWindow(ob_display, e->window, 0, e->area.y);
|
||||
|
||||
text_a = (!e->entry->enabled ?
|
||||
e->a_text_disabled :
|
||||
(e == self->selected ?
|
||||
e->a_text_selected :
|
||||
e->a_text_normal));
|
||||
switch (e->entry->type) {
|
||||
case OB_MENU_ENTRY_TYPE_NORMAL:
|
||||
text_a->texture[0].data.text.string = e->entry->data.normal.label;
|
||||
RrMinsize(text_a, &tw, &th);
|
||||
|
||||
/* XXX has_icon = TRUE; */
|
||||
break;
|
||||
case OB_MENU_ENTRY_TYPE_SUBMENU:
|
||||
text_a->texture[0].data.text.string =
|
||||
e->entry->data.submenu.submenu->title;
|
||||
RrMinsize(text_a, &tw, &th);
|
||||
|
||||
has_bullet = TRUE;
|
||||
break;
|
||||
case OB_MENU_ENTRY_TYPE_SEPARATOR:
|
||||
tw = 0;
|
||||
th = SEPARATOR_HEIGHT;
|
||||
break;
|
||||
}
|
||||
w = MAX(w, tw);
|
||||
h += th;
|
||||
allitems_h += th;
|
||||
}
|
||||
|
||||
self->text_x = 0;
|
||||
self->text_w = w;
|
||||
|
||||
if (self->entries) {
|
||||
if (has_bullet)
|
||||
w += self->item_h;
|
||||
if (has_icon) {
|
||||
w += self->item_h;
|
||||
self->text_x += self->item_h;
|
||||
}
|
||||
}
|
||||
|
||||
if (!w) w = 10;
|
||||
if (!allitems_h) allitems_h = 3;
|
||||
if (!h) h = 3;
|
||||
|
||||
XResizeWindow(ob_display, self->window, w, h);
|
||||
XResizeWindow(ob_display, self->items, w, allitems_h);
|
||||
|
||||
self->inner_w = w;
|
||||
|
||||
if (!self->parent && self->title) {
|
||||
XResizeWindow(ob_display, self->title,
|
||||
w, self->title_h - ob_rr_theme->bwidth);
|
||||
RrPaint(self->a_title, self->title,
|
||||
w, self->title_h - ob_rr_theme->bwidth);
|
||||
XMapWindow(ob_display, self->title);
|
||||
} else
|
||||
XUnmapWindow(ob_display, self->title);
|
||||
|
||||
RrPaint(self->a_items, self->items, w, allitems_h);
|
||||
|
||||
for (it = self->entries; it; it = g_list_next(it))
|
||||
menu_entry_frame_render(it->data);
|
||||
|
||||
w += ob_rr_theme->bwidth * 2;
|
||||
h += ob_rr_theme->bwidth * 2;
|
||||
|
||||
RECT_SET_SIZE(self->area, w, h);
|
||||
}
|
||||
|
||||
static void menu_frame_update(ObMenuFrame *self)
|
||||
{
|
||||
GList *mit, *fit;
|
||||
|
||||
self->selected = NULL;
|
||||
|
||||
for (mit = self->menu->entries, fit = self->entries; mit && fit;
|
||||
mit = g_list_next(mit), fit = g_list_next(fit))
|
||||
{
|
||||
ObMenuEntryFrame *f = fit->data;
|
||||
f->entry = mit->data;
|
||||
}
|
||||
|
||||
while (mit) {
|
||||
ObMenuEntryFrame *e = menu_entry_frame_new(mit->data, self);
|
||||
self->entries = g_list_append(self->entries, e);
|
||||
mit = g_list_next(mit);
|
||||
}
|
||||
|
||||
while (fit) {
|
||||
GList *n = g_list_next(fit);
|
||||
menu_entry_frame_free(fit->data);
|
||||
self->entries = g_list_delete_link(self->entries, fit);
|
||||
fit = n;
|
||||
}
|
||||
|
||||
menu_frame_render(self);
|
||||
}
|
||||
|
||||
void menu_frame_show(ObMenuFrame *self, ObMenuFrame *parent)
|
||||
{
|
||||
if (parent) {
|
||||
if (parent->child)
|
||||
menu_frame_hide(parent->child);
|
||||
parent->child = self;
|
||||
}
|
||||
self->parent = parent;
|
||||
|
||||
if (menu_frame_visible == NULL) {
|
||||
/* no menus shown yet */
|
||||
grab_pointer(TRUE, None);
|
||||
grab_keyboard(TRUE);
|
||||
}
|
||||
|
||||
if (!g_list_find(menu_frame_visible, self)) {
|
||||
menu_frame_visible = g_list_prepend(menu_frame_visible, self);
|
||||
|
||||
menu_frame_update(self);
|
||||
}
|
||||
|
||||
menu_frame_move_on_screen(self);
|
||||
|
||||
XMapWindow(ob_display, self->window);
|
||||
}
|
||||
|
||||
void menu_frame_hide(ObMenuFrame *self)
|
||||
{
|
||||
menu_frame_visible = g_list_remove(menu_frame_visible, self);
|
||||
|
||||
if (self->child)
|
||||
menu_frame_hide(self->child);
|
||||
|
||||
if (self->parent)
|
||||
self->parent->child = NULL;
|
||||
self->parent = NULL;
|
||||
|
||||
if (menu_frame_visible == NULL) {
|
||||
/* last menu shown */
|
||||
grab_pointer(FALSE, None);
|
||||
grab_keyboard(FALSE);
|
||||
}
|
||||
|
||||
XUnmapWindow(ob_display, self->window);
|
||||
|
||||
menu_frame_free(self);
|
||||
}
|
||||
|
||||
void menu_frame_hide_all()
|
||||
{
|
||||
while (menu_frame_visible)
|
||||
menu_frame_hide(menu_frame_visible->data);
|
||||
}
|
||||
|
||||
ObMenuFrame* menu_frame_under(gint x, gint y)
|
||||
{
|
||||
ObMenuFrame *ret = NULL;
|
||||
GList *it;
|
||||
|
||||
for (it = menu_frame_visible; it; it = g_list_next(it)) {
|
||||
ObMenuFrame *f = it->data;
|
||||
|
||||
if (RECT_CONTAINS(f->area, x, y)) {
|
||||
ret = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y)
|
||||
{
|
||||
ObMenuFrame *frame;
|
||||
ObMenuEntryFrame *ret = NULL;
|
||||
GList *it;
|
||||
|
||||
if ((frame = menu_frame_under(x, y))) {
|
||||
x -= ob_rr_theme->bwidth + frame->area.x;
|
||||
y -= frame->title_h + ob_rr_theme->bwidth + frame->area.y;
|
||||
|
||||
for (it = frame->entries; it; it = g_list_next(it)) {
|
||||
ObMenuEntryFrame *e = it->data;
|
||||
|
||||
if (RECT_CONTAINS(e->area, x, y)) {
|
||||
ret = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry)
|
||||
{
|
||||
ObMenuEntryFrame *old = self->selected;
|
||||
|
||||
if (old == entry) return;
|
||||
|
||||
if (entry && entry->entry->type != OB_MENU_ENTRY_TYPE_SEPARATOR)
|
||||
self->selected = entry;
|
||||
else
|
||||
self->selected = NULL;
|
||||
|
||||
if (old) {
|
||||
menu_entry_frame_render(old);
|
||||
if (old->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
|
||||
menu_frame_hide(self->child);
|
||||
}
|
||||
if (self->selected) {
|
||||
menu_entry_frame_render(self->selected);
|
||||
|
||||
if (self->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
|
||||
menu_entry_frame_show_submenu(self->selected);
|
||||
}
|
||||
}
|
||||
|
||||
void menu_entry_frame_show_submenu(ObMenuEntryFrame *self)
|
||||
{
|
||||
ObMenuFrame *f;
|
||||
|
||||
f = menu_frame_new(self->entry->data.submenu.submenu,
|
||||
self->frame->client);
|
||||
menu_frame_move(f,
|
||||
self->frame->area.x + self->frame->area.width
|
||||
- ob_rr_theme->menu_overlap,
|
||||
self->frame->area.y + self->frame->title_h +
|
||||
self->area.y);
|
||||
menu_frame_show(f, self->frame);
|
||||
}
|
||||
|
||||
void menu_entry_frame_execute(ObMenuEntryFrame *self)
|
||||
{
|
||||
if (self->entry->type == OB_MENU_ENTRY_TYPE_NORMAL) {
|
||||
GSList *it;
|
||||
|
||||
for (it = self->entry->data.normal.actions; it;
|
||||
it = g_slist_next(it))
|
||||
{
|
||||
ObAction *act = it->data;
|
||||
act->data.any.c = self->frame->client;
|
||||
act->func(&act->data);
|
||||
}
|
||||
menu_frame_hide_all();
|
||||
}
|
||||
}
|
95
openbox/menuframe.h
Normal file
95
openbox/menuframe.h
Normal file
|
@ -0,0 +1,95 @@
|
|||
#ifndef ob__menuframe_h
|
||||
#define ob__menuframe_h
|
||||
|
||||
#include "geom.h"
|
||||
#include "window.h"
|
||||
#include "render/render.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
struct _ObClient;
|
||||
struct _ObMenu;
|
||||
struct _ObMenuEntry;
|
||||
|
||||
typedef struct _ObMenuFrame ObMenuFrame;
|
||||
typedef struct _ObMenuEntryFrame ObMenuEntryFrame;
|
||||
|
||||
extern GList *menu_frame_visible;
|
||||
|
||||
struct _ObMenuFrame
|
||||
{
|
||||
/* stuff to be an ObWindow */
|
||||
Window_InternalType type;
|
||||
Window window;
|
||||
|
||||
struct _ObMenu *menu;
|
||||
|
||||
/* The client that the visual instance of the menu is associated with for
|
||||
its actions */
|
||||
struct _ObClient *client;
|
||||
|
||||
ObMenuFrame *parent;
|
||||
ObMenuFrame *child;
|
||||
|
||||
/* On-screen area (including borders!) */
|
||||
Rect area;
|
||||
gint inner_w; /* inside the borders */
|
||||
gint title_h; /* includes the bwidth below it */
|
||||
gint item_h; /* height of all normal items */
|
||||
gint text_x; /* offset at which the text appears in the items */
|
||||
gint text_w; /* width of the text area in the items */
|
||||
|
||||
GList *entries;
|
||||
ObMenuEntryFrame *selected;
|
||||
|
||||
Window title;
|
||||
Window items;
|
||||
|
||||
RrAppearance *a_title;
|
||||
RrAppearance *a_items;
|
||||
};
|
||||
|
||||
struct _ObMenuEntryFrame
|
||||
{
|
||||
struct _ObMenuEntry *entry;
|
||||
ObMenuFrame *frame;
|
||||
|
||||
Rect area;
|
||||
|
||||
Window window;
|
||||
Window icon;
|
||||
Window text;
|
||||
Window bullet;
|
||||
|
||||
RrAppearance *a_normal;
|
||||
RrAppearance *a_disabled;
|
||||
RrAppearance *a_selected;
|
||||
|
||||
RrAppearance *a_icon;
|
||||
RrAppearance *a_bullet;
|
||||
RrAppearance *a_text_normal;
|
||||
RrAppearance *a_text_disabled;
|
||||
RrAppearance *a_text_selected;
|
||||
};
|
||||
|
||||
ObMenuFrame* menu_frame_new(struct _ObMenu *menu, struct _ObClient *client);
|
||||
void menu_frame_free(ObMenuFrame *self);
|
||||
|
||||
void menu_frame_move(ObMenuFrame *self, gint x, gint y);
|
||||
void menu_frame_move_on_screen(ObMenuFrame *self);
|
||||
|
||||
void menu_frame_show(ObMenuFrame *self, ObMenuFrame *parent);
|
||||
void menu_frame_hide(ObMenuFrame *self);
|
||||
|
||||
void menu_frame_hide_all();
|
||||
|
||||
void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry);
|
||||
|
||||
ObMenuFrame* menu_frame_under(gint x, gint y);
|
||||
ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y);
|
||||
|
||||
void menu_entry_frame_show_submenu(ObMenuEntryFrame *self);
|
||||
|
||||
void menu_entry_frame_execute(ObMenuEntryFrame *self);
|
||||
|
||||
#endif
|
|
@ -127,7 +127,7 @@ void popup_show(Popup *self, gchar *text, ObClientIcon *icon)
|
|||
if (!self->a_bg)
|
||||
self->a_bg = RrAppearanceCopy(ob_rr_theme->app_hilite_bg);
|
||||
if (self->hasicon && !self->a_icon)
|
||||
self->a_icon = RrAppearanceCopy(ob_rr_theme->app_icon);
|
||||
self->a_icon = RrAppearanceCopy(ob_rr_theme->a_clear_tex);
|
||||
if (!self->a_text)
|
||||
self->a_text = RrAppearanceCopy(ob_rr_theme->app_hilite_label);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "window.h"
|
||||
#include "menu.h"
|
||||
#include "menuframe.h"
|
||||
#include "config.h"
|
||||
#include "dock.h"
|
||||
#include "client.h"
|
||||
|
@ -21,7 +21,7 @@ Window window_top(ObWindow *self)
|
|||
{
|
||||
switch (self->type) {
|
||||
case Window_Menu:
|
||||
return ((ObMenu*)self)->frame;
|
||||
return ((ObMenuFrame*)self)->window;
|
||||
case Window_Dock:
|
||||
return ((ObDock*)self)->frame;
|
||||
case Window_DockApp:
|
||||
|
|
|
@ -38,7 +38,7 @@ struct _ObDock;
|
|||
struct _ObDockApp;
|
||||
struct _ObClient;
|
||||
|
||||
#define WINDOW_AS_MENU(win) ((struct _ObMenu*)win)
|
||||
#define WINDOW_AS_MENU(win) ((struct _ObMenuFrame*)win)
|
||||
#define WINDOW_AS_DOCK(win) ((struct _ObDock*)win)
|
||||
#define WINDOW_AS_DOCKAPP(win) ((struct _ObDockApp*)win)
|
||||
#define WINDOW_AS_CLIENT(win) ((struct _ObClient*)win)
|
||||
|
|
|
@ -133,7 +133,7 @@ int RrFontMeasureString(const RrFont *f, const gchar *str)
|
|||
{
|
||||
gint x, y;
|
||||
font_measure_full (f, str, &x, &y);
|
||||
return x;
|
||||
return x + 4;
|
||||
}
|
||||
|
||||
int RrFontHeight(const RrFont *f)
|
||||
|
|
|
@ -56,17 +56,20 @@ RrTheme* RrThemeNew(const RrInstance *inst, gchar *name)
|
|||
theme->a_unfocused_handle = RrAppearanceNew(inst, 0);
|
||||
theme->a_menu = RrAppearanceNew(inst, 0);
|
||||
theme->a_menu_title = RrAppearanceNew(inst, 1);
|
||||
theme->a_menu_item = RrAppearanceNew(inst, 1);
|
||||
theme->a_menu_disabled = RrAppearanceNew(inst, 1);
|
||||
theme->a_menu_hilite = RrAppearanceNew(inst, 1);
|
||||
theme->a_menu_item = RrAppearanceNew(inst, 0);
|
||||
theme->a_menu_disabled = RrAppearanceNew(inst, 0);
|
||||
theme->a_menu_hilite = RrAppearanceNew(inst, 0);
|
||||
theme->a_menu_text_item = RrAppearanceNew(inst, 1);
|
||||
theme->a_menu_text_disabled = RrAppearanceNew(inst, 1);
|
||||
theme->a_menu_text_hilite = RrAppearanceNew(inst, 1);
|
||||
theme->a_menu_bullet = RrAppearanceNew(inst, 1);
|
||||
theme->a_clear = RrAppearanceNew(inst, 0);
|
||||
theme->a_clear_tex = RrAppearanceNew(inst, 1);
|
||||
|
||||
theme->app_hilite_bg = RrAppearanceNew(inst, 0);
|
||||
theme->app_unhilite_bg = RrAppearanceNew(inst, 0);
|
||||
theme->app_hilite_label = RrAppearanceNew(inst, 1);
|
||||
theme->app_unhilite_label = RrAppearanceNew(inst, 1);
|
||||
theme->app_icon = RrAppearanceNew(inst, 1);
|
||||
|
||||
if (name) {
|
||||
db = loaddb(theme, name);
|
||||
|
@ -607,8 +610,15 @@ RrTheme* RrThemeNew(const RrInstance *inst, gchar *name)
|
|||
theme->a_focused_pressed_iconify =
|
||||
RrAppearanceCopy(theme->a_focused_pressed_max);
|
||||
|
||||
theme->a_icon->surface.grad = RR_SURFACE_PARENTREL;
|
||||
theme->a_clear->surface.grad = RR_SURFACE_PARENTREL;
|
||||
theme->a_icon->surface.grad =
|
||||
theme->a_clear->surface.grad =
|
||||
theme->a_clear_tex->surface.grad =
|
||||
theme->a_menu_item->surface.grad =
|
||||
theme->a_menu_disabled->surface.grad =
|
||||
theme->a_menu_text_item->surface.grad =
|
||||
theme->a_menu_text_disabled->surface.grad =
|
||||
theme->a_menu_text_hilite->surface.grad =
|
||||
theme->a_menu_bullet->surface.grad = RR_SURFACE_PARENTREL;
|
||||
|
||||
/* set up the textures */
|
||||
theme->a_focused_label->texture[0].type =
|
||||
|
@ -638,24 +648,19 @@ RrTheme* RrThemeNew(const RrInstance *inst, gchar *name)
|
|||
theme->a_menu_title->texture[0].data.text.font = theme->mtitlefont;
|
||||
theme->a_menu_title->texture[0].data.text.color = theme->menu_title_color;
|
||||
|
||||
theme->a_menu_item->surface.grad =
|
||||
theme->a_menu_disabled->surface.grad =
|
||||
theme->a_menu_bullet->surface.grad =
|
||||
theme->app_icon->surface.grad = RR_SURFACE_PARENTREL;
|
||||
|
||||
theme->a_menu_item->texture[0].type =
|
||||
theme->a_menu_disabled->texture[0].type =
|
||||
theme->a_menu_hilite->texture[0].type = RR_TEXTURE_TEXT;
|
||||
theme->a_menu_item->texture[0].data.text.justify =
|
||||
theme->a_menu_disabled->texture[0].data.text.justify =
|
||||
theme->a_menu_hilite->texture[0].data.text.justify = mjust;
|
||||
theme->a_menu_item->texture[0].data.text.font =
|
||||
theme->a_menu_disabled->texture[0].data.text.font =
|
||||
theme->a_menu_hilite->texture[0].data.text.font = theme->mfont;
|
||||
theme->a_menu_item->texture[0].data.text.color = theme->menu_color;
|
||||
theme->a_menu_disabled->texture[0].data.text.color =
|
||||
theme->a_menu_text_item->texture[0].type =
|
||||
theme->a_menu_text_disabled->texture[0].type =
|
||||
theme->a_menu_text_hilite->texture[0].type = RR_TEXTURE_TEXT;
|
||||
theme->a_menu_text_item->texture[0].data.text.justify =
|
||||
theme->a_menu_text_disabled->texture[0].data.text.justify =
|
||||
theme->a_menu_text_hilite->texture[0].data.text.justify = mjust;
|
||||
theme->a_menu_text_item->texture[0].data.text.font =
|
||||
theme->a_menu_text_disabled->texture[0].data.text.font =
|
||||
theme->a_menu_text_hilite->texture[0].data.text.font = theme->mfont;
|
||||
theme->a_menu_text_item->texture[0].data.text.color = theme->menu_color;
|
||||
theme->a_menu_text_disabled->texture[0].data.text.color =
|
||||
theme->menu_disabled_color;
|
||||
theme->a_menu_hilite->texture[0].data.text.color =
|
||||
theme->a_menu_text_hilite->texture[0].data.text.color =
|
||||
theme->menu_hilite_color;
|
||||
theme->a_menu_bullet->texture[0].data.mask.color =
|
||||
theme->menu_bullet_color;
|
||||
|
@ -843,7 +848,7 @@ RrTheme* RrThemeNew(const RrInstance *inst, gchar *name)
|
|||
theme->label_height = theme->winfont_height;
|
||||
theme->title_height = theme->label_height + theme->bevel * 2;
|
||||
theme->button_size = theme->label_height - 2;
|
||||
theme->grip_width = theme->button_size * 2;
|
||||
theme->grip_width = theme->title_height * 1.5;
|
||||
|
||||
return theme;
|
||||
}
|
||||
|
@ -963,12 +968,15 @@ void RrThemeFree(RrTheme *theme)
|
|||
RrAppearanceFree(theme->a_menu_item);
|
||||
RrAppearanceFree(theme->a_menu_disabled);
|
||||
RrAppearanceFree(theme->a_menu_hilite);
|
||||
RrAppearanceFree(theme->a_menu_text_item);
|
||||
RrAppearanceFree(theme->a_menu_text_disabled);
|
||||
RrAppearanceFree(theme->a_menu_text_hilite);
|
||||
RrAppearanceFree(theme->a_clear);
|
||||
RrAppearanceFree(theme->a_clear_tex);
|
||||
RrAppearanceFree(theme->app_hilite_bg);
|
||||
RrAppearanceFree(theme->app_unhilite_bg);
|
||||
RrAppearanceFree(theme->app_hilite_label);
|
||||
RrAppearanceFree(theme->app_unhilite_label);
|
||||
RrAppearanceFree(theme->app_icon);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -143,14 +143,17 @@ struct _RrTheme {
|
|||
RrAppearance *a_menu_item;
|
||||
RrAppearance *a_menu_disabled;
|
||||
RrAppearance *a_menu_hilite;
|
||||
RrAppearance *a_menu_text_item;
|
||||
RrAppearance *a_menu_text_disabled;
|
||||
RrAppearance *a_menu_text_hilite;
|
||||
RrAppearance *a_menu_bullet;
|
||||
RrAppearance *a_clear;
|
||||
RrAppearance *a_clear; /* clear with no texture */
|
||||
RrAppearance *a_clear_tex; /* clear with a texture */
|
||||
|
||||
RrAppearance *app_hilite_bg;
|
||||
RrAppearance *app_unhilite_bg;
|
||||
RrAppearance *app_hilite_label;
|
||||
RrAppearance *app_unhilite_label;
|
||||
RrAppearance *app_icon;
|
||||
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue