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
|
tools/kdetrayproxy/kdetrayproxy
|
||||||
|
|
||||||
plugin_LTLIBRARIES = \
|
plugin_LTLIBRARIES = \
|
||||||
plugins/placement/placement.la \
|
plugins/placement/placement.la
|
||||||
plugins/menu/timed_menu.la \
|
# plugins/menu/timed_menu.la \
|
||||||
plugins/menu/fifo_menu.la \
|
# plugins/menu/fifo_menu.la \
|
||||||
plugins/menu/client_menu.la \
|
# plugins/menu/client_menu.la \
|
||||||
plugins/menu/include_menu.la \
|
# plugins/menu/include_menu.la \
|
||||||
plugins/menu/client_list_menu.la
|
# plugins/menu/client_list_menu.la
|
||||||
|
|
||||||
if OBCONF
|
if OBCONF
|
||||||
bin_PROGRAMS += \
|
bin_PROGRAMS += \
|
||||||
|
@ -156,9 +156,10 @@ kernel_openbox_SOURCES = \
|
||||||
kernel/keyboard.h \
|
kernel/keyboard.h \
|
||||||
kernel/keytree.c \
|
kernel/keytree.c \
|
||||||
kernel/keytree.h \
|
kernel/keytree.h \
|
||||||
|
kernel/menuframe.c \
|
||||||
|
kernel/menuframe.h \
|
||||||
kernel/menu.c \
|
kernel/menu.c \
|
||||||
kernel/menu.h \
|
kernel/menu.h \
|
||||||
kernel/menu_render.c \
|
|
||||||
kernel/misc.h \
|
kernel/misc.h \
|
||||||
kernel/mouse.c \
|
kernel/mouse.c \
|
||||||
kernel/mouse.h \
|
kernel/mouse.h \
|
||||||
|
@ -277,7 +278,7 @@ plugins_menu_client_list_menu_la_LDFLAGS = \
|
||||||
-module \
|
-module \
|
||||||
-avoid-version
|
-avoid-version
|
||||||
plugins_menu_client_list_menu_la_SOURCES = \
|
plugins_menu_client_list_menu_la_SOURCES = \
|
||||||
plugins/menu/client_list_menu.c
|
o plugins/menu/client_list_menu.c
|
||||||
|
|
||||||
|
|
||||||
## obconf ##
|
## obconf ##
|
||||||
|
|
|
@ -350,13 +350,6 @@ void client_unmanage_all()
|
||||||
client_unmanage(client_list->data);
|
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)
|
void client_unmanage(ObClient *self)
|
||||||
{
|
{
|
||||||
guint j;
|
guint j;
|
||||||
|
@ -413,9 +406,7 @@ void client_unmanage(ObClient *self)
|
||||||
if (moveresize_client == self)
|
if (moveresize_client == self)
|
||||||
moveresize_end(TRUE);
|
moveresize_end(TRUE);
|
||||||
|
|
||||||
/* close any windows that are attached to this window */
|
/* XXX close any windows that are attached to this window */
|
||||||
g_hash_table_foreach(menu_hash, client_close_menus, self);
|
|
||||||
|
|
||||||
|
|
||||||
if (focus_client == self) {
|
if (focus_client == self) {
|
||||||
XEvent e;
|
XEvent e;
|
||||||
|
|
111
openbox/event.c
111
openbox/event.c
|
@ -8,6 +8,7 @@
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "frame.h"
|
#include "frame.h"
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
|
#include "menuframe.h"
|
||||||
#include "keyboard.h"
|
#include "keyboard.h"
|
||||||
#include "mouse.h"
|
#include "mouse.h"
|
||||||
#include "framerender.h"
|
#include "framerender.h"
|
||||||
|
@ -41,10 +42,10 @@
|
||||||
|
|
||||||
static void event_process(XEvent *e);
|
static void event_process(XEvent *e);
|
||||||
static void event_handle_root(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_dock(ObDock *s, XEvent *e);
|
||||||
static void event_handle_dockapp(ObDockApp *app, XEvent *e);
|
static void event_handle_dockapp(ObDockApp *app, XEvent *e);
|
||||||
static void event_handle_client(ObClient *c, 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();
|
static void fd_event_handle();
|
||||||
#ifdef USE_SM
|
#ifdef USE_SM
|
||||||
static void ice_watch(IceConn conn, IcePointer data, Bool opening,
|
static void ice_watch(IceConn conn, IcePointer data, Bool opening,
|
||||||
|
@ -298,6 +299,7 @@ static void event_hack_mods(XEvent *e)
|
||||||
break;
|
break;
|
||||||
case MotionNotify:
|
case MotionNotify:
|
||||||
STRIP_MODS(e->xmotion.state);
|
STRIP_MODS(e->xmotion.state);
|
||||||
|
#if 0
|
||||||
/* compress events */
|
/* compress events */
|
||||||
{
|
{
|
||||||
XEvent ce;
|
XEvent ce;
|
||||||
|
@ -307,6 +309,7 @@ static void event_hack_mods(XEvent *e)
|
||||||
e->xmotion.y_root = ce.xmotion.y_root;
|
e->xmotion.y_root = ce.xmotion.y_root;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -461,7 +464,6 @@ static void event_process(XEvent *e)
|
||||||
ObClient *client = NULL;
|
ObClient *client = NULL;
|
||||||
ObDock *dock = NULL;
|
ObDock *dock = NULL;
|
||||||
ObDockApp *dockapp = NULL;
|
ObDockApp *dockapp = NULL;
|
||||||
ObMenu *menu = NULL;
|
|
||||||
ObWindow *obwin = NULL;
|
ObWindow *obwin = NULL;
|
||||||
|
|
||||||
window = event_get_window(e);
|
window = event_get_window(e);
|
||||||
|
@ -473,12 +475,10 @@ static void event_process(XEvent *e)
|
||||||
case Window_DockApp:
|
case Window_DockApp:
|
||||||
dockapp = WINDOW_AS_DOCKAPP(obwin);
|
dockapp = WINDOW_AS_DOCKAPP(obwin);
|
||||||
break;
|
break;
|
||||||
case Window_Menu:
|
|
||||||
menu = WINDOW_AS_MENU(obwin);
|
|
||||||
break;
|
|
||||||
case Window_Client:
|
case Window_Client:
|
||||||
client = WINDOW_AS_CLIENT(obwin);
|
client = WINDOW_AS_CLIENT(obwin);
|
||||||
break;
|
break;
|
||||||
|
case Window_Menu:
|
||||||
case Window_Internal:
|
case Window_Internal:
|
||||||
/* not to be used for events */
|
/* not to be used for events */
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
|
@ -523,11 +523,11 @@ static void event_process(XEvent *e)
|
||||||
xerror_set_ignore(FALSE);
|
xerror_set_ignore(FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (menu_visible)
|
if (menu_frame_visible)
|
||||||
if (e->type == MotionNotify || e->type == ButtonRelease ||
|
if (e->type == MotionNotify || e->type == ButtonRelease ||
|
||||||
e->type == ButtonPress ||
|
e->type == ButtonPress ||
|
||||||
e->type == KeyPress || e->type == KeyRelease) {
|
e->type == KeyPress || e->type == KeyRelease) {
|
||||||
event_handle_menu(client, e);
|
event_handle_menu(e);
|
||||||
|
|
||||||
return; /* no dispatch! */
|
return; /* no dispatch! */
|
||||||
}
|
}
|
||||||
|
@ -539,7 +539,6 @@ static void event_process(XEvent *e)
|
||||||
moveresize_event(e);
|
moveresize_event(e);
|
||||||
|
|
||||||
return; /* no dispatch! */
|
return; /* no dispatch! */
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* user input (action-bound) events */
|
/* 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) {
|
void event_add_fd_handler(event_fd_handler *h) {
|
||||||
g_datalist_id_set_data(&fd_handler_list, h->fd, h);
|
g_datalist_id_set_data(&fd_handler_list, h->fd, h);
|
||||||
FD_SET(h->fd, &allset);
|
FD_SET(h->fd, &allset);
|
||||||
|
@ -1231,3 +1162,31 @@ static void event_handle_dockapp(ObDockApp *app, XEvent *e)
|
||||||
break;
|
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 "grab.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
#include "menuframe.h"
|
||||||
#include "geom.h"
|
#include "geom.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "parser/parse.h"
|
#include "parser/parse.h"
|
||||||
|
|
||||||
GHashTable *menu_hash = NULL;
|
GHashTable *menu_hash = NULL;
|
||||||
GList *menu_visible = NULL;
|
|
||||||
|
|
||||||
#define FRAME_EVENTMASK (ButtonPressMask |ButtonMotionMask | EnterWindowMask |\
|
typedef struct _ObMenuParseState ObMenuParseState;
|
||||||
LeaveWindowMask)
|
|
||||||
#define TITLE_EVENTMASK (ButtonPressMask | ButtonMotionMask)
|
|
||||||
#define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \
|
|
||||||
ButtonPressMask | ButtonReleaseMask)
|
|
||||||
|
|
||||||
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,
|
static void parse_menu(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
|
||||||
gpointer data)
|
gpointer data)
|
||||||
{
|
{
|
||||||
g_message("%s", __FUNCTION__);
|
ObMenuParseState *state = data;
|
||||||
parse_menu_full(i, doc, node, data, TRUE);
|
gchar *name = NULL, *title = NULL;
|
||||||
}
|
|
||||||
|
|
||||||
|
if (!parse_attr_string("id", node, &name))
|
||||||
|
goto parse_menu_fail;
|
||||||
|
|
||||||
void parse_menu_full(ObParseInst *i, xmlDocPtr doc, xmlNodePtr node,
|
if (!g_hash_table_lookup(menu_hash, name)) {
|
||||||
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))
|
|
||||||
goto parse_menu_fail;
|
|
||||||
if (!parse_attr_string("label", node, &title))
|
if (!parse_attr_string("label", node, &title))
|
||||||
goto parse_menu_fail;
|
goto parse_menu_fail;
|
||||||
ob_debug("menu label %s\n", title);
|
|
||||||
|
|
||||||
if (parse_attr_string("plugin", node, &plugin)) {
|
if (menu_new(name, title, NULL)) {
|
||||||
PluginMenuCreateData data;
|
state->menus = g_slist_prepend(state->menus, name);
|
||||||
data.parse_inst = i;
|
parse_tree(i, doc, node->xmlChildrenNode);
|
||||||
data.doc = doc;
|
state->menus = g_slist_delete_link(state->menus, state->menus);
|
||||||
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;
|
if (state->menus)
|
||||||
|
menu_add_submenu(state->menus->data, name);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
node = node->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
parse_menu_fail:
|
parse_menu_fail:
|
||||||
g_free(id);
|
g_free(name);
|
||||||
g_free(title);
|
g_free(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
void menu_destroy_hash_key(ObMenu *menu)
|
|
||||||
{
|
|
||||||
g_free(menu);
|
|
||||||
}
|
|
||||||
|
|
||||||
void menu_destroy_hash_value(ObMenu *self)
|
void menu_destroy_hash_value(ObMenu *self)
|
||||||
{
|
{
|
||||||
GList *it;
|
menu_clear_entries_internal(self);
|
||||||
|
|
||||||
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);
|
|
||||||
g_free(self->name);
|
g_free(self->name);
|
||||||
|
g_free(self->title);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void menu_startup(ObParseInst *i)
|
void menu_startup(ObParseInst *i)
|
||||||
{
|
{
|
||||||
menu_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
|
menu_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
|
||||||
(GDestroyNotify)menu_destroy_hash_key,
|
|
||||||
(GDestroyNotify)menu_destroy_hash_value);
|
(GDestroyNotify)menu_destroy_hash_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void menu_shutdown()
|
void menu_shutdown()
|
||||||
{
|
{
|
||||||
|
menu_frame_hide_all();
|
||||||
g_hash_table_destroy(menu_hash);
|
g_hash_table_destroy(menu_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
void menu_parse()
|
void menu_parse()
|
||||||
{
|
{
|
||||||
ObParseInst *i;
|
ObParseInst *i;
|
||||||
|
ObMenuParseState parse_state;
|
||||||
xmlDocPtr doc;
|
xmlDocPtr doc;
|
||||||
xmlNodePtr node;
|
xmlNodePtr node;
|
||||||
gchar *p;
|
gchar *p;
|
||||||
|
@ -199,460 +143,137 @@ void menu_parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loaded) {
|
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_tree(i, doc, node->xmlChildrenNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
parse_shutdown(i);
|
parse_shutdown(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Window createWindow(Window parent, unsigned long mask,
|
gboolean menu_new(gchar *name, gchar *title, gpointer data)
|
||||||
XSetWindowAttributes *attrib)
|
|
||||||
{
|
{
|
||||||
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;
|
ObMenu *self;
|
||||||
|
|
||||||
|
if (g_hash_table_lookup(menu_hash, name)) return FALSE;
|
||||||
|
|
||||||
self = g_new0(ObMenu, 1);
|
self = g_new0(ObMenu, 1);
|
||||||
self->obwin.type = Window_Menu;
|
|
||||||
self->label = g_strdup(label);
|
|
||||||
self->name = g_strdup(name);
|
self->name = g_strdup(name);
|
||||||
self->parent = parent;
|
self->title = g_strdup(title);
|
||||||
self->open_submenu = NULL;
|
self->data = data;
|
||||||
self->over = NULL;
|
|
||||||
|
|
||||||
self->entries = NULL;
|
g_hash_table_insert(menu_hash, self->name, self);
|
||||||
self->shown = FALSE;
|
|
||||||
self->invalid = TRUE;
|
|
||||||
|
|
||||||
/* default controllers */
|
return TRUE;
|
||||||
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);
|
|
||||||
|
|
||||||
self->plugin = NULL;
|
void menu_show(gchar *name, gint x, gint y, ObClient *client)
|
||||||
self->plugin_data = NULL;
|
{
|
||||||
|
ObMenu *self;
|
||||||
|
ObMenuFrame *frame;
|
||||||
|
|
||||||
attrib.override_redirect = TRUE;
|
if (!(self = menu_from_name(name))) return;
|
||||||
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);
|
|
||||||
|
|
||||||
self->a_title = self->a_items = NULL;
|
/* XXX update entries */
|
||||||
|
|
||||||
XMapWindow(ob_display, self->title);
|
frame = menu_frame_new(self, client);
|
||||||
XMapWindow(ob_display, self->items);
|
menu_frame_move(frame, x, y);
|
||||||
|
menu_frame_show(frame, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
g_hash_table_insert(window_map, &self->frame, self);
|
static ObMenuEntry* menu_entry_new(ObMenu *menu, ObMenuEntryType type)
|
||||||
g_hash_table_insert(window_map, &self->title, self);
|
{
|
||||||
g_hash_table_insert(window_map, &self->items, self);
|
ObMenuEntry *self;
|
||||||
g_hash_table_insert(menu_hash, g_strdup(name), self);
|
|
||||||
|
|
||||||
stacking_add(MENU_AS_WINDOW(self));
|
g_assert(menu);
|
||||||
stacking_raise(MENU_AS_WINDOW(self));
|
|
||||||
|
|
||||||
|
self = g_new0(ObMenuEntry, 1);
|
||||||
|
self->type = type;
|
||||||
|
self->menu = menu;
|
||||||
|
self->enabled = TRUE;
|
||||||
return self;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(self);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ObMenuEntry *menu_entry_new_full(char *label, ObAction *action,
|
void menu_clear_entries(gchar *name)
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
ObMenu *self;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
menu_show_full(self, x, y, client);
|
if (!(self = menu_from_name(name))) return;
|
||||||
}
|
|
||||||
|
|
||||||
void menu_show_full(ObMenu *self, int x, int y, ObClient *client)
|
menu_clear_entries_internal(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
while (self->entries) {
|
||||||
if (!(self->parent && self->parent->shown)) {
|
menu_entry_free(self->entries->data);
|
||||||
grab_pointer(TRUE, None);
|
self->entries = g_list_delete_link(self->entries, self->entries);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void menu_clear(ObMenu *self) {
|
void menu_add_normal(gchar *name, gchar *label, GSList *actions)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
GList *it;
|
ObMenu *self;
|
||||||
|
|
||||||
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;
|
|
||||||
ObMenuEntry *e;
|
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 = (ObMenuEntry *) self->parent->over->data;
|
|
||||||
e->hilite = FALSE;
|
|
||||||
menu_entry_render(e);
|
|
||||||
self->parent->open_submenu->hide(self->parent->open_submenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self->submenu && self->parent->open_submenu != self->submenu) {
|
|
||||||
self->parent->open_submenu = self->submenu;
|
|
||||||
|
|
||||||
/* shouldn't be invalid since it must be displayed */
|
e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_NORMAL);
|
||||||
g_assert(!self->parent->invalid);
|
e->data.normal.label = g_strdup(label);
|
||||||
/* TODO: I don't understand why these bevels should be here.
|
e->data.normal.actions = actions;
|
||||||
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->entries = g_list_append(self->entries, e);
|
||||||
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_submenu(gchar *name, gchar *submenu)
|
||||||
{
|
{
|
||||||
static ObMenu *current_menu = NULL;
|
ObMenu *self, *sub;
|
||||||
ObMenuEntry *e = NULL;
|
ObMenuEntry *e;
|
||||||
|
|
||||||
ObKey obkey = OB_NUM_KEYS;
|
if (!(self = menu_from_name(name))) return;
|
||||||
|
if (!(sub = menu_from_name(submenu))) return;
|
||||||
|
|
||||||
/* hrmm. could be fixed */
|
e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SUBMENU);
|
||||||
if (key == ob_keycode(OB_KEY_DOWN))
|
e->data.submenu.submenu = sub;
|
||||||
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;
|
|
||||||
|
|
||||||
|
self->entries = g_list_append(self->entries, e);
|
||||||
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()
|
void menu_add_separator(gchar *name)
|
||||||
{
|
{
|
||||||
/* This noop brought to you by OLS 2003 Email Garden. */
|
ObMenu *self;
|
||||||
|
ObMenuEntry *e;
|
||||||
|
|
||||||
|
if (!(self = menu_from_name(name))) return;
|
||||||
|
|
||||||
|
e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_SEPARATOR);
|
||||||
|
|
||||||
|
self->entries = g_list_append(self->entries, e);
|
||||||
}
|
}
|
||||||
|
|
209
openbox/menu.h
209
openbox/menu.h
|
@ -9,199 +9,82 @@
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
struct _ObClient;
|
struct _ObClient;
|
||||||
struct _ObParseInst;
|
struct _ObMenuFrame;
|
||||||
|
|
||||||
typedef struct _ObMenu ObMenu;
|
typedef struct _ObMenu ObMenu;
|
||||||
typedef struct _ObMenuEntry ObMenuEntry;
|
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;
|
extern GList *menu_visible;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct _ObMenu
|
struct _ObMenu
|
||||||
{
|
{
|
||||||
ObWindow obwin;
|
/* Name of the menu. Used in the showmenu action. */
|
||||||
|
|
||||||
/* The title displayed above the menu.
|
|
||||||
NULL for no titlebar */
|
|
||||||
gchar *label;
|
|
||||||
|
|
||||||
/* Name of the menu.
|
|
||||||
Used in the action showmenu */
|
|
||||||
gchar *name;
|
gchar *name;
|
||||||
|
/* Displayed title */
|
||||||
|
gchar *title;
|
||||||
|
|
||||||
/* ObMenuEntry list */
|
/* ObMenuEntry list */
|
||||||
GList *entries;
|
GList *entries;
|
||||||
|
|
||||||
/* If the menu is currently displayed */
|
/* plugin data */
|
||||||
gboolean shown;
|
gpointer data;
|
||||||
|
|
||||||
/* 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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
OB_MENU_ENTRY_RENDER_TYPE_NONE,
|
OB_MENU_ENTRY_TYPE_NORMAL,
|
||||||
OB_MENU_ENTRY_RENDER_TYPE_SUBMENU,
|
OB_MENU_ENTRY_TYPE_SUBMENU,
|
||||||
OB_MENU_ENTRY_RENDER_TYPE_BOOLEAN,
|
OB_MENU_ENTRY_TYPE_SEPARATOR
|
||||||
OB_MENU_ENTRY_RENDER_TYPE_SEPARATOR,
|
} ObMenuEntryType;
|
||||||
OB_MENU_ENTRY_RENDER_TYPE_OTHER /* XXX what is this? */
|
|
||||||
} ObMenuEntryRenderType;
|
struct _ObNormalMenuEntry {
|
||||||
|
gchar *label;
|
||||||
|
|
||||||
|
/* List of ObActions */
|
||||||
|
GSList *actions;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _ObSubmenuMenuEntry {
|
||||||
|
ObMenu *submenu;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _ObSeparatorMenuEntry {
|
||||||
|
gchar foo; /* placeholder */
|
||||||
|
};
|
||||||
|
|
||||||
struct _ObMenuEntry
|
struct _ObMenuEntry
|
||||||
{
|
{
|
||||||
char *label;
|
ObMenuEntryType type;
|
||||||
ObMenu *parent;
|
ObMenu *menu;
|
||||||
|
|
||||||
ObAction *action;
|
/* state */
|
||||||
|
|
||||||
ObMenuEntryRenderType render_type;
|
|
||||||
gboolean hilite;
|
|
||||||
gboolean enabled;
|
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_startup();
|
||||||
void menu_shutdown();
|
void menu_shutdown();
|
||||||
|
|
||||||
void menu_parse();
|
void menu_parse();
|
||||||
|
|
||||||
void menu_noop();
|
gboolean menu_new(gchar *name, gchar *title, gpointer data);
|
||||||
|
|
||||||
#define menu_new(l, n, p) \
|
void menu_show(gchar *name, gint x, gint y, struct _ObClient *client);
|
||||||
menu_new_full(l, n, p, menu_show_full, menu_render, menu_entry_fire, \
|
|
||||||
menu_hide, menu_control_mouseover, NULL)
|
|
||||||
|
|
||||||
ObMenu *menu_new_full(char *label, char *name, ObMenu *parent,
|
/* functions for building menus */
|
||||||
menu_controller_show show,
|
void menu_clear_entries(gchar *name);
|
||||||
menu_controller_update update,
|
void menu_add_normal(gchar *name, gchar *label, GSList *actions);
|
||||||
menu_controller_selected selected,
|
void menu_add_submenu(gchar *name, gchar *submenu);
|
||||||
menu_controller_hide hide,
|
void menu_add_separator(gchar *name);
|
||||||
menu_controller_mouseover mouseover,
|
|
||||||
menu_controller_destroy destroy);
|
|
||||||
|
|
||||||
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
|
#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)
|
if (!self->a_bg)
|
||||||
self->a_bg = RrAppearanceCopy(ob_rr_theme->app_hilite_bg);
|
self->a_bg = RrAppearanceCopy(ob_rr_theme->app_hilite_bg);
|
||||||
if (self->hasicon && !self->a_icon)
|
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)
|
if (!self->a_text)
|
||||||
self->a_text = RrAppearanceCopy(ob_rr_theme->app_hilite_label);
|
self->a_text = RrAppearanceCopy(ob_rr_theme->app_hilite_label);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#include "window.h"
|
#include "window.h"
|
||||||
#include "menu.h"
|
#include "menuframe.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "dock.h"
|
#include "dock.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
|
@ -21,7 +21,7 @@ Window window_top(ObWindow *self)
|
||||||
{
|
{
|
||||||
switch (self->type) {
|
switch (self->type) {
|
||||||
case Window_Menu:
|
case Window_Menu:
|
||||||
return ((ObMenu*)self)->frame;
|
return ((ObMenuFrame*)self)->window;
|
||||||
case Window_Dock:
|
case Window_Dock:
|
||||||
return ((ObDock*)self)->frame;
|
return ((ObDock*)self)->frame;
|
||||||
case Window_DockApp:
|
case Window_DockApp:
|
||||||
|
|
|
@ -38,7 +38,7 @@ struct _ObDock;
|
||||||
struct _ObDockApp;
|
struct _ObDockApp;
|
||||||
struct _ObClient;
|
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_DOCK(win) ((struct _ObDock*)win)
|
||||||
#define WINDOW_AS_DOCKAPP(win) ((struct _ObDockApp*)win)
|
#define WINDOW_AS_DOCKAPP(win) ((struct _ObDockApp*)win)
|
||||||
#define WINDOW_AS_CLIENT(win) ((struct _ObClient*)win)
|
#define WINDOW_AS_CLIENT(win) ((struct _ObClient*)win)
|
||||||
|
|
|
@ -133,7 +133,7 @@ int RrFontMeasureString(const RrFont *f, const gchar *str)
|
||||||
{
|
{
|
||||||
gint x, y;
|
gint x, y;
|
||||||
font_measure_full (f, str, &x, &y);
|
font_measure_full (f, str, &x, &y);
|
||||||
return x;
|
return x + 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RrFontHeight(const RrFont *f)
|
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_unfocused_handle = RrAppearanceNew(inst, 0);
|
||||||
theme->a_menu = RrAppearanceNew(inst, 0);
|
theme->a_menu = RrAppearanceNew(inst, 0);
|
||||||
theme->a_menu_title = RrAppearanceNew(inst, 1);
|
theme->a_menu_title = RrAppearanceNew(inst, 1);
|
||||||
theme->a_menu_item = RrAppearanceNew(inst, 1);
|
theme->a_menu_item = RrAppearanceNew(inst, 0);
|
||||||
theme->a_menu_disabled = RrAppearanceNew(inst, 1);
|
theme->a_menu_disabled = RrAppearanceNew(inst, 0);
|
||||||
theme->a_menu_hilite = RrAppearanceNew(inst, 1);
|
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_menu_bullet = RrAppearanceNew(inst, 1);
|
||||||
theme->a_clear = RrAppearanceNew(inst, 0);
|
theme->a_clear = RrAppearanceNew(inst, 0);
|
||||||
|
theme->a_clear_tex = RrAppearanceNew(inst, 1);
|
||||||
|
|
||||||
theme->app_hilite_bg = RrAppearanceNew(inst, 0);
|
theme->app_hilite_bg = RrAppearanceNew(inst, 0);
|
||||||
theme->app_unhilite_bg = RrAppearanceNew(inst, 0);
|
theme->app_unhilite_bg = RrAppearanceNew(inst, 0);
|
||||||
theme->app_hilite_label = RrAppearanceNew(inst, 1);
|
theme->app_hilite_label = RrAppearanceNew(inst, 1);
|
||||||
theme->app_unhilite_label = RrAppearanceNew(inst, 1);
|
theme->app_unhilite_label = RrAppearanceNew(inst, 1);
|
||||||
theme->app_icon = RrAppearanceNew(inst, 1);
|
|
||||||
|
|
||||||
if (name) {
|
if (name) {
|
||||||
db = loaddb(theme, name);
|
db = loaddb(theme, name);
|
||||||
|
@ -607,8 +610,15 @@ RrTheme* RrThemeNew(const RrInstance *inst, gchar *name)
|
||||||
theme->a_focused_pressed_iconify =
|
theme->a_focused_pressed_iconify =
|
||||||
RrAppearanceCopy(theme->a_focused_pressed_max);
|
RrAppearanceCopy(theme->a_focused_pressed_max);
|
||||||
|
|
||||||
theme->a_icon->surface.grad = RR_SURFACE_PARENTREL;
|
theme->a_icon->surface.grad =
|
||||||
theme->a_clear->surface.grad = RR_SURFACE_PARENTREL;
|
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 */
|
/* set up the textures */
|
||||||
theme->a_focused_label->texture[0].type =
|
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.font = theme->mtitlefont;
|
||||||
theme->a_menu_title->texture[0].data.text.color = theme->menu_title_color;
|
theme->a_menu_title->texture[0].data.text.color = theme->menu_title_color;
|
||||||
|
|
||||||
theme->a_menu_item->surface.grad =
|
theme->a_menu_text_item->texture[0].type =
|
||||||
theme->a_menu_disabled->surface.grad =
|
theme->a_menu_text_disabled->texture[0].type =
|
||||||
theme->a_menu_bullet->surface.grad =
|
theme->a_menu_text_hilite->texture[0].type = RR_TEXTURE_TEXT;
|
||||||
theme->app_icon->surface.grad = RR_SURFACE_PARENTREL;
|
theme->a_menu_text_item->texture[0].data.text.justify =
|
||||||
|
theme->a_menu_text_disabled->texture[0].data.text.justify =
|
||||||
theme->a_menu_item->texture[0].type =
|
theme->a_menu_text_hilite->texture[0].data.text.justify = mjust;
|
||||||
theme->a_menu_disabled->texture[0].type =
|
theme->a_menu_text_item->texture[0].data.text.font =
|
||||||
theme->a_menu_hilite->texture[0].type = RR_TEXTURE_TEXT;
|
theme->a_menu_text_disabled->texture[0].data.text.font =
|
||||||
theme->a_menu_item->texture[0].data.text.justify =
|
theme->a_menu_text_hilite->texture[0].data.text.font = theme->mfont;
|
||||||
theme->a_menu_disabled->texture[0].data.text.justify =
|
theme->a_menu_text_item->texture[0].data.text.color = theme->menu_color;
|
||||||
theme->a_menu_hilite->texture[0].data.text.justify = mjust;
|
theme->a_menu_text_disabled->texture[0].data.text.color =
|
||||||
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->menu_disabled_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->menu_hilite_color;
|
||||||
theme->a_menu_bullet->texture[0].data.mask.color =
|
theme->a_menu_bullet->texture[0].data.mask.color =
|
||||||
theme->menu_bullet_color;
|
theme->menu_bullet_color;
|
||||||
|
@ -843,7 +848,7 @@ RrTheme* RrThemeNew(const RrInstance *inst, gchar *name)
|
||||||
theme->label_height = theme->winfont_height;
|
theme->label_height = theme->winfont_height;
|
||||||
theme->title_height = theme->label_height + theme->bevel * 2;
|
theme->title_height = theme->label_height + theme->bevel * 2;
|
||||||
theme->button_size = theme->label_height - 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;
|
return theme;
|
||||||
}
|
}
|
||||||
|
@ -963,12 +968,15 @@ void RrThemeFree(RrTheme *theme)
|
||||||
RrAppearanceFree(theme->a_menu_item);
|
RrAppearanceFree(theme->a_menu_item);
|
||||||
RrAppearanceFree(theme->a_menu_disabled);
|
RrAppearanceFree(theme->a_menu_disabled);
|
||||||
RrAppearanceFree(theme->a_menu_hilite);
|
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);
|
||||||
|
RrAppearanceFree(theme->a_clear_tex);
|
||||||
RrAppearanceFree(theme->app_hilite_bg);
|
RrAppearanceFree(theme->app_hilite_bg);
|
||||||
RrAppearanceFree(theme->app_unhilite_bg);
|
RrAppearanceFree(theme->app_unhilite_bg);
|
||||||
RrAppearanceFree(theme->app_hilite_label);
|
RrAppearanceFree(theme->app_hilite_label);
|
||||||
RrAppearanceFree(theme->app_unhilite_label);
|
RrAppearanceFree(theme->app_unhilite_label);
|
||||||
RrAppearanceFree(theme->app_icon);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -143,14 +143,17 @@ struct _RrTheme {
|
||||||
RrAppearance *a_menu_item;
|
RrAppearance *a_menu_item;
|
||||||
RrAppearance *a_menu_disabled;
|
RrAppearance *a_menu_disabled;
|
||||||
RrAppearance *a_menu_hilite;
|
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_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_hilite_bg;
|
||||||
RrAppearance *app_unhilite_bg;
|
RrAppearance *app_unhilite_bg;
|
||||||
RrAppearance *app_hilite_label;
|
RrAppearance *app_hilite_label;
|
||||||
RrAppearance *app_unhilite_label;
|
RrAppearance *app_unhilite_label;
|
||||||
RrAppearance *app_icon;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue