half way through the changes to new menu code/design. hot shit. static menus work, on to plugins next.

This commit is contained in:
Dana Jansens 2003-08-28 02:10:23 +00:00
parent cadab91e52
commit c34ef4028e
14 changed files with 920 additions and 977 deletions

View file

@ -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 ##

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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;
if (!parse_attr_string("id", node, &name))
goto parse_menu_fail;
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))
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;
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->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);
}
}
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);
g_free(self->title);
}
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)
{
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;
}
g_free(self);
}
}
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;
}
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;
}
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);
self->update(self);
self->client = client;
/* XXX assert that the menu isn't visible */
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 (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;
if (!(self = menu_from_name(name))) return;
/* 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;
e = menu_entry_new(self, OB_MENU_ENTRY_TYPE_NORMAL);
e->data.normal.label = g_strdup(label);
e->data.normal.actions = actions;
/* 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);
self->entries = g_list_append(self->entries, e);
}
void menu_control_keyboard_nav(unsigned int key)
void menu_add_submenu(gchar *name, gchar *submenu)
{
static ObMenu *current_menu = NULL;
ObMenuEntry *e = NULL;
ObMenu *self, *sub;
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 */
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_SUBMENU);
e->data.submenu.submenu = sub;
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;
self->entries = g_list_append(self->entries, e);
}
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);
}

View file

@ -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

View file

@ -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
View 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
View 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

View file

@ -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);

View file

@ -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:

View file

@ -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)

View file

@ -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)

View file

@ -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);
}
}

View file

@ -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;
};