enlightenment/src/bin/e_menu.c

2223 lines
56 KiB
C

/*
* vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
*/
#include "e.h"
/* TODO List:
*
* * support change of menu items after realize
* * support add/del of menu items after realize
* * support text/color classes
* * refcount menu up while looping thru and calling other fn's
* * support alignment (x, y) as well as spawn direction
* * need different menu style support for different menus
* * add menu icon/title support
* * use event timestamps not clock for "click and release" detect
* * menu icons can set if/how they will be scaled
* * support move/resize of "box" that spawned the menu
* * add image item (label is replaced by image/icon)
* * add generic evas object item type (label replaced by object)
* * allow menus to stretch width/height to fit spawner widget/box
* * allow menus to auto-shrink (horizontally) if forced to
* * support auto left/right direction spawn
* * support obscures to indicate offscreen/not visible menu parts
*/
/* local subsystem functions */
static void _e_menu_free (E_Menu *m);
static void _e_menu_item_free (E_Menu_Item *mi);
static void _e_menu_item_realize (E_Menu_Item *mi);
static void _e_menu_realize (E_Menu *m);
static void _e_menu_items_layout_update (E_Menu *m);
static void _e_menu_item_unrealize (E_Menu_Item *mi);
static void _e_menu_unrealize (E_Menu *m);
static void _e_menu_activate_internal (E_Menu *m, E_Zone *zone);
static void _e_menu_deactivate_all (void);
static void _e_menu_deactivate_above (E_Menu *m);
static void _e_menu_submenu_activate (E_Menu_Item *mi);
static void _e_menu_submenu_deactivate (E_Menu_Item *mi);
static void _e_menu_reposition (E_Menu *m);
static int _e_menu_active_call (void);
static void _e_menu_item_activate_next (void);
static void _e_menu_item_activate_previous (void);
static void _e_menu_activate_next (void);
static void _e_menu_activate_previous (void);
static void _e_menu_activate_first (void);
static void _e_menu_activate_nth (int n);
static E_Menu *_e_menu_active_get (void);
static E_Menu_Item *_e_menu_item_active_get (void);
static int _e_menu_outside_bounds_get (int xdir, int ydir);
static void _e_menu_scroll_by (int dx, int dy);
static void _e_menu_mouse_autoscroll_check (void);
static void _e_menu_item_ensure_onscreen (E_Menu_Item *mi);
static void _e_menu_cb_intercept_item_move (void *data, Evas_Object *o, Evas_Coord x, Evas_Coord y);
static void _e_menu_cb_intercept_item_resize (void *data, Evas_Object *o, Evas_Coord w, Evas_Coord h);
static void _e_menu_cb_intercept_container_move (void *data, Evas_Object *o, Evas_Coord x, Evas_Coord y);
static void _e_menu_cb_intercept_container_resize (void *data, Evas_Object *o, Evas_Coord w, Evas_Coord h);
static void _e_menu_cb_ecore_evas_resize (Ecore_Evas *ee);
static void _e_menu_cb_item_in (void *data, Evas *evas, Evas_Object *obj, void *event_info);
static void _e_menu_cb_item_out (void *data, Evas *evas, Evas_Object *obj, void *event_info);
static int _e_menu_cb_key_down (void *data, int type, void *event);
static int _e_menu_cb_key_up (void *data, int type, void *event);
static int _e_menu_cb_mouse_down (void *data, int type, void *event);
static int _e_menu_cb_mouse_up (void *data, int type, void *event);
static int _e_menu_cb_mouse_move (void *data, int type, void *event);
static int _e_menu_cb_mouse_wheel (void *data, int type, void *event);
static int _e_menu_cb_scroll_timer (void *data);
static int _e_menu_cb_window_shape (void *data, int ev_type, void *ev);
static void _e_menu_cb_item_submenu_post_default (void *data, E_Menu *m, E_Menu_Item *mi);
/* local subsystem globals */
static Ecore_X_Window _e_menu_win = 0;
static Evas_List *_e_active_menus = NULL;
static double _e_menu_activate_time = 0.0;
static Ecore_Timer *_e_menu_scroll_timer = NULL;
static double _e_menu_scroll_start = 0.0;
static int _e_menu_x = 0;
static int _e_menu_y = 0;
static Ecore_X_Time _e_menu_time = 0;
static int _e_menu_autoscroll_x = 0;
static int _e_menu_autoscroll_y = 0;
static Ecore_Event_Handler *_e_menu_key_down_handler = NULL;
static Ecore_Event_Handler *_e_menu_key_up_handler = NULL;
static Ecore_Event_Handler *_e_menu_mouse_down_handler = NULL;
static Ecore_Event_Handler *_e_menu_mouse_up_handler = NULL;
static Ecore_Event_Handler *_e_menu_mouse_move_handler = NULL;
static Ecore_Event_Handler *_e_menu_mouse_wheel_handler = NULL;
static Ecore_Event_Handler *_e_menu_window_shape_handler = NULL;
/* externally accessible functions */
int
e_menu_init(void)
{
_e_menu_key_down_handler = ecore_event_handler_add(ECORE_X_EVENT_KEY_DOWN, _e_menu_cb_key_down, NULL);
_e_menu_key_up_handler = ecore_event_handler_add(ECORE_X_EVENT_KEY_UP, _e_menu_cb_key_up, NULL);
_e_menu_mouse_down_handler = ecore_event_handler_add(ECORE_X_EVENT_MOUSE_BUTTON_DOWN, _e_menu_cb_mouse_down, NULL);
_e_menu_mouse_up_handler = ecore_event_handler_add(ECORE_X_EVENT_MOUSE_BUTTON_UP, _e_menu_cb_mouse_up, NULL);
_e_menu_mouse_move_handler = ecore_event_handler_add(ECORE_X_EVENT_MOUSE_MOVE, _e_menu_cb_mouse_move, NULL);
_e_menu_mouse_wheel_handler = ecore_event_handler_add(ECORE_X_EVENT_MOUSE_WHEEL, _e_menu_cb_mouse_wheel, NULL);
_e_menu_window_shape_handler = ecore_event_handler_add(ECORE_X_EVENT_WINDOW_SHAPE, _e_menu_cb_window_shape, NULL);
return 1;
}
int
e_menu_shutdown(void)
{
E_FN_DEL(ecore_event_handler_del, _e_menu_key_down_handler);
E_FN_DEL(ecore_event_handler_del, _e_menu_key_up_handler);
E_FN_DEL(ecore_event_handler_del, _e_menu_mouse_down_handler);
E_FN_DEL(ecore_event_handler_del, _e_menu_mouse_up_handler);
E_FN_DEL(ecore_event_handler_del, _e_menu_mouse_move_handler);
E_FN_DEL(ecore_event_handler_del, _e_menu_mouse_wheel_handler);
E_FN_DEL(ecore_event_handler_del, _e_menu_window_shape_handler);
while (_e_active_menus)
{
E_Menu *m;
m = _e_active_menus->data;
m->active = 0;
_e_menu_unrealize(m);
_e_active_menus = evas_list_remove_list(_e_active_menus, _e_active_menus);
m->in_active_list = 0;
e_object_unref(E_OBJECT(m));
}
_e_active_menus = NULL;
return 1;
}
E_Menu *
e_menu_new(void)
{
E_Menu *m;
m = E_OBJECT_ALLOC(E_Menu, E_MENU_TYPE, _e_menu_free);
if (!m) return NULL;
m->cur.w = 1;
m->cur.h = 1;
return m;
}
void
e_menu_activate_key(E_Menu *m, E_Zone *zone, int x, int y, int w, int h, int dir)
{
E_OBJECT_CHECK(m);
E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
E_OBJECT_CHECK(zone);
E_OBJECT_TYPE_CHECK(zone, E_ZONE_TYPE);
_e_menu_activate_time = 0.0;
_e_menu_activate_internal(m, zone);
m->cur.x = x + w;
m->cur.y = y + h;
_e_menu_activate_first();
}
void
e_menu_activate_mouse(E_Menu *m, E_Zone *zone, int x, int y, int w, int h, int dir)
{
E_Menu_Item *pmi;
E_OBJECT_CHECK(m);
E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
E_OBJECT_CHECK(zone);
E_OBJECT_TYPE_CHECK(zone, E_ZONE_TYPE);
_e_menu_activate_time = ecore_time_get();
_e_menu_activate_internal(m, zone);
m->cur.x = x + w;
m->cur.y = y + h;
pmi = _e_menu_item_active_get();
if (pmi) e_menu_item_active_set(pmi, 0);
}
void
e_menu_activate(E_Menu *m, E_Zone *zone, int x, int y, int w, int h, int dir)
{
E_Menu_Item *pmi;
E_OBJECT_CHECK(m);
E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
E_OBJECT_CHECK(zone);
E_OBJECT_TYPE_CHECK(zone, E_ZONE_TYPE);
_e_menu_activate_time = 0.0;
_e_menu_activate_internal(m, zone);
m->cur.x = x;
m->cur.y = y;
pmi = _e_menu_item_active_get();
if (pmi) e_menu_item_active_set(pmi, 0);
}
void
e_menu_deactivate(E_Menu *m)
{
E_OBJECT_CHECK(m);
E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
m->cur.visible = 0;
m->active = 0;
if (m->post_deactivate_cb.func)
m->post_deactivate_cb.func(m->post_deactivate_cb.data, m);
}
int
e_menu_freeze(E_Menu *m)
{
E_OBJECT_CHECK_RETURN(m, 0);
E_OBJECT_TYPE_CHECK_RETURN(m, E_MENU_TYPE, 0);
m->frozen++;
return m->frozen;
}
int
e_menu_thaw(E_Menu *m)
{
E_OBJECT_CHECK_RETURN(m, 0);
E_OBJECT_TYPE_CHECK_RETURN(m, E_MENU_TYPE, 0);
m->frozen--;
if (m->frozen < 0) m->frozen = 0;
return m->frozen;
}
void
e_menu_title_set(E_Menu *m, char *title)
{
E_OBJECT_CHECK(m);
E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
/* FIXME: support menu titles */
if ((m->header.title) && (title) && (!strcmp(m->header.title, title)))
return;
if (m->header.title)
{
free(m->header.title);
m->header.title = NULL;
}
if (title) m->header.title = strdup(title);
else m->header.title = NULL;
m->changed = 1;
}
void
e_menu_icon_file_set(E_Menu *m, char *icon)
{
E_OBJECT_CHECK(m);
E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
/* FIXME: support menu icons */
}
void
e_menu_pre_activate_callback_set(E_Menu *m, void (*func) (void *data, E_Menu *m), void *data)
{
E_OBJECT_CHECK(m);
E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
m->pre_activate_cb.func = func;
m->pre_activate_cb.data = data;
}
void
e_menu_post_deactivate_callback_set(E_Menu *m, void (*func) (void *data, E_Menu *m), void *data)
{
E_OBJECT_CHECK(m);
E_OBJECT_TYPE_CHECK(m, E_MENU_TYPE);
m->post_deactivate_cb.func = func;
m->post_deactivate_cb.data = data;
}
E_Menu *
e_menu_root_get(E_Menu *m)
{
E_Menu *ret;
E_OBJECT_CHECK_RETURN(m, NULL);
E_OBJECT_TYPE_CHECK_RETURN(m, E_MENU_TYPE, NULL);
ret = m;
while (ret->parent_item && ret->parent_item->menu)
{
ret = ret->parent_item->menu;
}
return ret;
}
E_Menu_Item *
e_menu_item_new(E_Menu *m)
{
E_Menu_Item *mi;
E_OBJECT_CHECK_RETURN(m, NULL);
E_OBJECT_TYPE_CHECK_RETURN(m, E_MENU_TYPE, NULL);
mi = E_OBJECT_ALLOC(E_Menu_Item, E_MENU_ITEM_TYPE, _e_menu_item_free);
mi->menu = m;
mi->menu->items = evas_list_append(mi->menu->items, mi);
return mi;
}
E_Menu_Item *
e_menu_item_nth(E_Menu *m, int n)
{
E_OBJECT_CHECK_RETURN(m, NULL);
E_OBJECT_TYPE_CHECK_RETURN(m, E_MENU_TYPE, NULL);
return (E_Menu_Item *)evas_list_nth(m->items, n);
}
int
e_menu_item_num_get(E_Menu_Item *mi)
{
Evas_List *l;
int i;
E_OBJECT_CHECK_RETURN(mi, -1);
E_OBJECT_TYPE_CHECK_RETURN(mi, E_MENU_TYPE, -1);
for (i = 0, l = mi->menu->items; l; l = l->next, i++)
{
E_Menu_Item *mi2;
mi2 = l->data;
if (mi2 == mi) return i;
}
return -1;
}
void
e_menu_item_icon_file_set(E_Menu_Item *mi, char *icon)
{
E_OBJECT_CHECK(mi);
E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
if (((mi->icon) && (icon) && (!strcmp(icon, mi->icon))) ||
((!mi->icon) && (!icon)))
return;
if (mi->icon) free(mi->icon);
if (mi->icon_key) free(mi->icon_key);
mi->icon = NULL;
mi->icon_key = NULL;
if (icon) mi->icon = strdup(icon);
mi->changed = 1;
mi->menu->changed = 1;
}
void
e_menu_item_icon_edje_set(E_Menu_Item *mi, char *icon, char *key)
{
E_OBJECT_CHECK(mi);
E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
if (((mi->icon) && (icon) && (!strcmp(icon, mi->icon))) ||
((!mi->icon) && (!icon)) ||
((key) && (mi->icon_key) && (!strcmp(key, mi->icon_key))))
return;
if (mi->icon) free(mi->icon);
if (mi->icon_key) free(mi->icon_key);
mi->icon = NULL;
mi->icon_key = NULL;
if (icon) mi->icon = strdup(icon);
if (key) mi->icon_key = strdup(key);
mi->changed = 1;
mi->menu->changed = 1;
}
void
e_menu_item_label_set(E_Menu_Item *mi, char *label)
{
E_OBJECT_CHECK(mi);
E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
if (((mi->label) && (label) && (!strcmp(label, mi->label))) ||
((!mi->label) && (!label)))
return;
if (mi->label) free(mi->label);
mi->label = NULL;
if (label) mi->label = strdup(label);
mi->changed = 1;
mi->menu->changed = 1;
}
void
e_menu_item_submenu_set(E_Menu_Item *mi, E_Menu *sub)
{
E_OBJECT_CHECK(mi);
E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
if (mi->submenu) e_object_unref(E_OBJECT(mi->submenu));
if (sub) e_object_ref(E_OBJECT(sub));
mi->submenu = sub;
mi->changed = 1;
mi->menu->changed = 1;
}
void
e_menu_item_separator_set(E_Menu_Item *mi, int sep)
{
E_OBJECT_CHECK(mi);
E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
if (((mi->separator) && (sep)) ||
((!mi->separator) && (!sep))) return;
mi->separator = sep;
mi->changed = 1;
mi->menu->changed = 1;
}
void
e_menu_item_check_set(E_Menu_Item *mi, int chk)
{
E_OBJECT_CHECK(mi);
E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
if (((mi->check) && (chk)) ||
((!mi->check) && (!chk))) return;
mi->check = chk;
mi->changed = 1;
mi->menu->changed = 1;
}
void
e_menu_item_radio_set(E_Menu_Item *mi, int rad)
{
E_OBJECT_CHECK(mi);
E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
if (((mi->radio) && (rad)) ||
((!mi->radio) && (!rad))) return;
mi->radio = rad;
mi->changed = 1;
mi->menu->changed = 1;
}
void
e_menu_item_radio_group_set(E_Menu_Item *mi, int radg)
{
E_OBJECT_CHECK(mi);
E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
if (mi->radio_group == radg) return;
mi->radio_group = radg;
mi->changed = 1;
mi->menu->changed = 1;
}
void
e_menu_item_toggle_set(E_Menu_Item *mi, int tog)
{
E_OBJECT_CHECK(mi);
E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
if (mi->separator) return;
if (tog)
{
mi->toggle = 1;
if (mi->bg_object)
edje_object_signal_emit(mi->bg_object, "toggle_on", "");
if (mi->icon_bg_object)
edje_object_signal_emit(mi->icon_bg_object, "toggle_on", "");
if (mi->label_object)
edje_object_signal_emit(mi->label_object, "toggle_on", "");
if (mi->submenu_object)
edje_object_signal_emit(mi->submenu_object, "toggle_on", "");
if (mi->toggle_object)
edje_object_signal_emit(mi->toggle_object, "toggle_on", "");
if (mi->menu->bg_object)
edje_object_signal_emit(mi->menu->bg_object, "toggle_on", "");
}
else
{
mi->toggle = 0;
if (mi->bg_object)
edje_object_signal_emit(mi->bg_object, "toggle_off", "");
if (mi->icon_bg_object)
edje_object_signal_emit(mi->icon_bg_object, "toggle_off", "");
if (mi->label_object)
edje_object_signal_emit(mi->label_object, "toggle_off", "");
if (mi->submenu_object)
edje_object_signal_emit(mi->submenu_object, "toggle_off", "");
if (mi->toggle_object)
edje_object_signal_emit(mi->toggle_object, "toggle_off", "");
if (mi->menu->bg_object)
edje_object_signal_emit(mi->menu->bg_object, "toggle_off", "");
}
if (tog)
{
if (mi->radio)
{
Evas_List *l;
for (l = mi->menu->items; l; l = l->next)
{
E_Menu_Item *mi2;
mi2 = l->data;
if ((mi2 != mi) &&
(mi2->radio) &&
(mi2->radio_group == mi->radio_group))
e_menu_item_toggle_set(mi2, 0);
}
}
}
}
int
e_menu_item_toggle_get(E_Menu_Item *mi)
{
E_OBJECT_CHECK_RETURN(mi, 0);
E_OBJECT_TYPE_CHECK_RETURN(mi, E_MENU_ITEM_TYPE, 0);
return mi->toggle;
}
void
e_menu_item_callback_set(E_Menu_Item *mi, void (*func) (void *data, E_Menu *m, E_Menu_Item *mi), void *data)
{
E_OBJECT_CHECK(mi);
E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
mi->cb.func = func;
mi->cb.data = data;
}
void
e_menu_item_submenu_pre_callback_set(E_Menu_Item *mi, void (*func) (void *data, E_Menu *m, E_Menu_Item *mi), void *data)
{
E_OBJECT_CHECK(mi);
E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
mi->submenu_pre_cb.func = func;
mi->submenu_pre_cb.data = data;
if (!mi->submenu_post_cb.func)
mi->submenu_post_cb.func = _e_menu_cb_item_submenu_post_default;
}
void
e_menu_item_submenu_post_callback_set(E_Menu_Item *mi, void (*func) (void *data, E_Menu *m, E_Menu_Item *mi), void *data)
{
E_OBJECT_CHECK(mi);
E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
mi->submenu_post_cb.func = func;
mi->submenu_post_cb.data = data;
}
void
e_menu_item_active_set(E_Menu_Item *mi, int active)
{
E_OBJECT_CHECK(mi);
E_OBJECT_TYPE_CHECK(mi, E_MENU_ITEM_TYPE);
if (mi->separator) return;
if (active)
{
E_Menu_Item *pmi;
pmi = _e_menu_item_active_get();
if (pmi) e_menu_item_active_set(pmi, 0);
mi->active = 1;
if (mi->bg_object)
edje_object_signal_emit(mi->bg_object, "active", "");
if (mi->icon_bg_object)
edje_object_signal_emit(mi->icon_bg_object, "active", "");
if (mi->label_object)
edje_object_signal_emit(mi->label_object, "active", "");
if (mi->submenu_object)
edje_object_signal_emit(mi->submenu_object, "active", "");
if (mi->toggle_object)
edje_object_signal_emit(mi->toggle_object, "active", "");
if (mi->icon_key)
edje_object_signal_emit(mi->icon_object, "active", "");
edje_object_signal_emit(mi->menu->bg_object, "active", "");
_e_menu_submenu_activate(mi);
}
else
{
mi->active = 0;
if (mi->bg_object)
edje_object_signal_emit(mi->bg_object, "passive", "");
if (mi->icon_bg_object)
edje_object_signal_emit(mi->icon_bg_object, "passive", "");
if (mi->label_object)
edje_object_signal_emit(mi->label_object, "passive", "");
if (mi->submenu_object)
edje_object_signal_emit(mi->submenu_object, "passive", "");
if (mi->toggle_object)
edje_object_signal_emit(mi->toggle_object, "passive", "");
if (mi->icon_key)
edje_object_signal_emit(mi->icon_object, "passive", "");
edje_object_signal_emit(mi->menu->bg_object, "passive", "");
_e_menu_submenu_deactivate(mi);
}
}
void
e_menu_idler_before(void)
{
/* when e goes "idle" this gets called so leave all our hard work till */
/* idle time to avoid falling behind the user. just evaluate the high */
/* level state machine */
Evas_List *l, *removals = NULL, *tmp = NULL;
/* add refcount to all menus we will work with */
for (l = _e_active_menus; l; l = l->next)
{
tmp = evas_list_append(tmp, l->data);
e_object_ref(E_OBJECT(l->data));
}
/* phase 1. hide all the menus that want to be hidden */
for (l = _e_active_menus; l; l = l->next)
{
E_Menu *m;
m = l->data;
if ((!m->cur.visible) && (m->prev.visible))
{
m->prev.visible = m->cur.visible;
ecore_evas_hide(m->ecore_evas);
e_container_shape_hide(m->shape);
}
}
/* phase 2. move & reisze all the menus that want to moves/resized */
for (l = _e_active_menus; l; l = l->next)
{
E_Menu *m;
m = l->data;
if (!m->realized) _e_menu_realize(m);
if (m->realized)
{
if (((m->cur.w) != (m->prev.w)) ||
((m->cur.h) != (m->prev.h)))
{
m->prev.w = m->cur.w;
m->prev.h = m->cur.h;
ecore_evas_resize(m->ecore_evas, m->cur.w, m->cur.h);
e_container_shape_resize(m->shape, m->cur.w, m->cur.h);
// evas_obscured_clear(m->evas);
// evas_obscured_rectangle_add(m->evas, 0, 0, m->cur.w, m->cur.h);
}
if (((m->cur.x) != (m->prev.x)) ||
((m->cur.y) != (m->prev.y)))
{
m->prev.x = m->cur.x;
m->prev.y = m->cur.y;
ecore_evas_move(m->ecore_evas, m->cur.x, m->cur.y);
e_container_shape_move(m->shape, m->cur.x, m->cur.y);
}
}
}
/* phase 3. show all the menus that want to be shown */
for (l = _e_active_menus; l; l = l->next)
{
E_Menu *m;
m = l->data;
if ((m->cur.visible) && (!m->prev.visible))
{
m->prev.visible = m->cur.visible;
ecore_evas_raise(m->ecore_evas);
ecore_evas_show(m->ecore_evas);
if (!m->shaped)
e_container_shape_show(m->shape);
}
}
/* phase 4. de-activate... */
for (l = _e_active_menus; l; l = l->next)
{
E_Menu *m;
m = l->data;
if (!m->active)
{
_e_menu_unrealize(m);
removals = evas_list_append(removals, m);
}
}
while (removals)
{
E_Menu *m;
m = removals->data;
removals = evas_list_remove(removals, m);
if (m->in_active_list)
{
_e_active_menus = evas_list_remove(_e_active_menus, m);
m->in_active_list = 0;
e_object_unref(E_OBJECT(m));
}
}
/* phase 5. shapes... */
for (l = _e_active_menus; l; l = l->next)
{
E_Menu *m;
m = l->data;
if (m->need_shape_export)
{
Ecore_X_Rectangle *rects;
int num;
rects = ecore_x_window_shape_rectangles_get(m->evas_win, &num);
if (rects)
{
e_container_shape_rects_set(m->shape, rects, num);
free(rects);
}
m->need_shape_export = 0;
if (m->cur.visible)
e_container_shape_show(m->shape);
}
}
/* del refcount to all menus we worked with */
while (tmp)
{
e_object_unref(E_OBJECT(tmp->data));
tmp = evas_list_remove_list(tmp, tmp);
}
if (!_e_active_menus)
{
ecore_x_window_del(_e_menu_win);
_e_menu_win = 0;
}
}
Ecore_X_Window
e_menu_grab_window_get(void)
{
return _e_menu_win;
}
/* local subsystem functions */
static void
_e_menu_free(E_Menu *m)
{
Evas_List *l, *tmp;
_e_menu_unrealize(m);
for (l = m->items; l;)
{
tmp = l;
l = l->next;
e_object_del(E_OBJECT(tmp->data));
}
if (m->in_active_list)
{
_e_active_menus = evas_list_remove(_e_active_menus, m);
m->in_active_list = 0;
e_object_unref(E_OBJECT(m));
}
free(m);
}
static void
_e_menu_item_free(E_Menu_Item *mi)
{
if (mi->submenu)
{
mi->submenu->parent_item = NULL;
e_object_unref(E_OBJECT(mi->submenu));
}
if (mi->menu->realized) _e_menu_item_unrealize(mi);
mi->menu->items = evas_list_remove(mi->menu->items, mi);
if (mi->icon) free(mi->icon);
if (mi->icon_key) free(mi->icon_key);
if (mi->label) free(mi->label);
free(mi);
}
static void
_e_menu_cb_intercept_item_move(void *data, Evas_Object *o, Evas_Coord x, Evas_Coord y)
{
E_Menu_Item *mi;
mi = data;
mi->x = x;
mi->y = y;
evas_object_move(mi->event_object, x, y);
evas_object_move(o, x, y);
if ((mi->submenu) && (mi->submenu->parent_item))
_e_menu_reposition(mi->submenu);
}
static void
_e_menu_cb_intercept_item_resize(void *data, Evas_Object *o, Evas_Coord w, Evas_Coord h)
{
E_Menu_Item *mi;
mi = data;
mi->w = w;
mi->h = h;
evas_object_resize(mi->event_object, w, h);
evas_object_resize(o, w, h);
if ((mi->submenu) && (mi->submenu->parent_item))
_e_menu_reposition(mi->submenu);
}
static void
_e_menu_cb_intercept_container_move(void *data, Evas_Object *o, Evas_Coord x, Evas_Coord y)
{
E_Menu *m;
m = data;
m->container_x = x;
m->container_y = y;
if (m->parent_item) _e_menu_reposition(m);
evas_object_move(o, x, y);
}
static void
_e_menu_cb_intercept_container_resize(void *data, Evas_Object *o, Evas_Coord w, Evas_Coord h)
{
E_Menu *m;
m = data;
m->container_w = w;
m->container_h = h;
if (m->parent_item) _e_menu_reposition(m);
evas_object_resize(o, w, h);
}
static void
_e_menu_item_realize(E_Menu_Item *mi)
{
Evas_Object *o;
Evas_Coord ww, hh;
/* and set up initial item state */
if (mi->separator)
{
o = edje_object_add(mi->menu->evas);
mi->separator_object = o;
e_theme_edje_object_set(o, "base/theme/menus",
"widgets/menu/default/separator");
evas_object_show(o);
edje_object_size_min_calc(mi->separator_object, &ww, &hh);
mi->separator_w = ww;
mi->separator_h = hh;
e_box_pack_end(mi->menu->container_object, mi->separator_object);
}
else
{
o = edje_object_add(mi->menu->evas);
mi->bg_object = o;
evas_object_intercept_move_callback_add (o, _e_menu_cb_intercept_item_move, mi);
evas_object_intercept_resize_callback_add(o, _e_menu_cb_intercept_item_resize, mi);
if ((mi->submenu) || (mi->submenu_pre_cb.func))
{
if (!e_theme_edje_object_set(mi->bg_object, "base/theme/menus",
"widgets/menu/default/submenu_bg"))
goto no_submenu_item;
}
else
{
no_submenu_item:
e_theme_edje_object_set(mi->bg_object, "base/theme/menus",
"widgets/menu/default/item_bg");
}
evas_object_show(o);
o = e_box_add(mi->menu->evas);
e_box_homogenous_set(o, 0);
mi->container_object = o;
e_box_orientation_set(o, 1);
evas_object_show(o);
e_box_freeze(mi->container_object);
if (mi->check)
{
o = edje_object_add(mi->menu->evas);
mi->toggle_object = o;
e_theme_edje_object_set(o, "base/theme/menus",
"widgets/menu/default/check");
evas_object_pass_events_set(o, 1);
evas_object_show(o);
e_box_pack_end(mi->container_object, o);
edje_object_size_min_calc(mi->toggle_object, &ww, &hh);
mi->toggle_w = ww;
mi->toggle_h = hh;
}
else if (mi->radio)
{
o = edje_object_add(mi->menu->evas);
mi->toggle_object = o;
e_theme_edje_object_set(o, "base/theme/menus",
"widgets/menu/default/radio");
evas_object_pass_events_set(o, 1);
evas_object_show(o);
e_box_pack_end(mi->container_object, o);
edje_object_size_min_calc(mi->toggle_object, &ww, &hh);
mi->toggle_w = ww;
mi->toggle_h = hh;
}
else
{
o = evas_object_rectangle_add(mi->menu->evas);
mi->toggle_object = o;
evas_object_color_set(o, 0, 0, 0, 0);
evas_object_pass_events_set(o, 1);
e_box_pack_end(mi->container_object, o);
}
if (mi->icon)
{
int icon_w, icon_h;
o = edje_object_add(mi->menu->evas);
if (e_theme_edje_object_set(o, "base/theme/menus",
"widgets/menu/default/icon"))
{
mi->icon_bg_object = o;
evas_object_show(o);
}
else
evas_object_del(o);
if (!mi->icon_key)
{
o = e_icon_add(mi->menu->evas);
mi->icon_object = o;
e_icon_file_set(o, mi->icon);
e_icon_fill_inside_set(o, 1);
e_icon_size_get(mi->icon_object, &icon_w, &icon_h);
}
else
{
Evas_Coord iww, ihh;
o = edje_object_add(mi->menu->evas);
mi->icon_object = o;
edje_object_file_set(o, mi->icon, mi->icon_key);
edje_object_size_max_get(o, &iww, &ihh);
icon_w = iww;
icon_h = ihh;
}
evas_object_pass_events_set(o, 1);
evas_object_show(o);
if (mi->icon_bg_object)
{
edje_extern_object_min_size_set(mi->icon_object,
icon_w, icon_h);
edje_object_part_swallow(mi->icon_bg_object, "item",
mi->icon_object);
edje_object_size_min_calc(mi->icon_bg_object, &ww, &hh);
mi->icon_w = ww;
mi->icon_h = hh;
edje_extern_object_min_size_set(mi->icon_object, 0, 0);
edje_object_part_swallow(mi->icon_bg_object, "item",
mi->icon_object);
e_box_pack_end(mi->container_object, mi->icon_bg_object);
}
else
{
e_icon_size_get(mi->icon_object, &icon_w, &icon_h);
mi->icon_w = icon_w;
mi->icon_h = icon_h;
e_box_pack_end(mi->container_object, o);
}
}
else
{
o = evas_object_rectangle_add(mi->menu->evas);
mi->icon_object = o;
evas_object_color_set(o, 0, 0, 0, 0);
evas_object_pass_events_set(o, 1);
e_box_pack_end(mi->container_object, o);
}
if (mi->label)
{
o = edje_object_add(mi->menu->evas);
mi->label_object = o;
e_theme_edje_object_set(o, "base/theme/menus",
"widgets/menu/default/label");
/* default label */
edje_object_part_text_set(o, "label", mi->label);
evas_object_pass_events_set(o, 1);
evas_object_show(o);
e_box_pack_end(mi->container_object, o);
edje_object_size_min_calc(mi->label_object, &ww, &hh);
mi->label_w = ww;
mi->label_h = hh;
}
else
{
o = evas_object_rectangle_add(mi->menu->evas);
mi->label_object = o;
evas_object_color_set(o, 0, 0, 0, 0);
evas_object_pass_events_set(o, 1);
e_box_pack_end(mi->container_object, o);
}
if ((mi->submenu) || (mi->submenu_pre_cb.func))
{
o = edje_object_add(mi->menu->evas);
mi->submenu_object = o;
e_theme_edje_object_set(o, "base/theme/menus",
"widgets/menu/default/submenu");
evas_object_pass_events_set(o, 1);
evas_object_show(o);
e_box_pack_end(mi->container_object, o);
edje_object_size_min_calc(mi->submenu_object, &ww, &hh);
mi->submenu_w = ww;
mi->submenu_h = hh;
}
else
{
o = evas_object_rectangle_add(mi->menu->evas);
mi->submenu_object = o;
evas_object_color_set(o, 0, 0, 0, 0);
evas_object_pass_events_set(o, 1);
e_box_pack_end(mi->container_object, o);
}
edje_object_part_swallow(mi->bg_object, "item", mi->container_object);
o = evas_object_rectangle_add(mi->menu->evas);
evas_object_color_set(o, 0, 0, 0, 0);
evas_object_layer_set(o, 1);
evas_object_repeat_events_set(o, 1);
evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_IN, _e_menu_cb_item_in, mi);
evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_OUT, _e_menu_cb_item_out, mi);
evas_object_show(o);
mi->event_object = o;
e_box_thaw(mi->container_object);
e_box_pack_end(mi->menu->container_object, mi->bg_object);
}
if (mi->active) e_menu_item_active_set(mi, 1);
if (mi->toggle) e_menu_item_toggle_set(mi, 1);
}
static void
_e_menu_realize(E_Menu *m)
{
Evas_Object *o;
Evas_List *l;
int ok;
if (m->realized) return;
m->realized = 1;
m->ecore_evas = ecore_evas_software_x11_new(NULL, m->zone->container->win,
m->cur.x, m->cur.y,
m->cur.w, m->cur.h);
ecore_evas_software_x11_direct_resize_set(m->ecore_evas, 1);
e_canvas_add(m->ecore_evas);
m->shape = e_container_shape_add(m->zone->container);
e_container_shape_move(m->shape, m->cur.x, m->cur.y);
e_container_shape_resize(m->shape, m->cur.w, m->cur.h);
ecore_evas_callback_resize_set(m->ecore_evas, _e_menu_cb_ecore_evas_resize);
m->evas = ecore_evas_get(m->ecore_evas);
/* move cursor out to avoid event cycles during setup */
evas_event_feed_mouse_in(m->evas, NULL);
evas_event_feed_mouse_move(m->evas, -1000000, -1000000, NULL);
m->evas_win = ecore_evas_software_x11_window_get(m->ecore_evas);
ecore_x_window_shape_events_select(m->evas_win, 1);
ecore_evas_name_class_set(m->ecore_evas, "E", "_e_menu_window");
ecore_evas_title_set(m->ecore_evas, "E Menu");
o = edje_object_add(m->evas);
m->bg_object = o;
evas_object_name_set(o, "menu/background");
evas_object_data_set(o, "e_menu", m);
evas_object_move(o, 0, 0);
evas_object_resize(o, m->cur.w, m->cur.h);
ok = e_theme_edje_object_set(o, "base/theme/menus",
"widgets/menu/default/background");
if (ok)
{
const char *shape_option;
shape_option = edje_object_data_get(o, "shaped");
if (shape_option)
{
if (!strcmp(shape_option, "1"))
{
m->shaped = 1;
}
}
}
evas_object_show(o);
if (m->shaped)
ecore_evas_shaped_set(m->ecore_evas, m->shaped);
o = e_box_add(m->evas);
m->container_object = o;
evas_object_intercept_move_callback_add (o, _e_menu_cb_intercept_container_move, m);
evas_object_intercept_resize_callback_add(o, _e_menu_cb_intercept_container_resize, m);
e_box_freeze(o);
evas_object_show(o);
e_box_homogenous_set(o, 0);
edje_object_part_swallow(m->bg_object, "items", m->container_object);
for (l = m->items; l; l = l->next)
{
E_Menu_Item *mi;
mi = l->data;
_e_menu_item_realize(mi);
}
o = m->container_object;
_e_menu_items_layout_update(m);
e_box_thaw(o);
evas_object_resize(m->bg_object, m->cur.w, m->cur.h);
}
static void
_e_menu_items_layout_update(E_Menu *m)
{
Evas_List *l;
Evas_Coord bw, bh, mw, mh;
int toggles_on = 0;
int icons_on = 0;
int labels_on = 0;
int submenus_on = 0;
int min_icon_w = 0, min_icon_h = 0;
int min_label_w = 0, min_label_h = 0;
int min_submenu_w = 0, min_submenu_h = 0;
int min_toggle_w = 0, min_toggle_h = 0;
int min_w = 0, min_h = 0;
e_box_freeze(m->container_object);
for (l = m->items; l; l = l->next)
{
E_Menu_Item *mi;
mi = l->data;
if (mi->icon) icons_on = 1;
if (mi->label) labels_on = 1;
if (mi->submenu) submenus_on = 1;
if (mi->check) toggles_on = 1;
if (mi->radio) toggles_on = 1;
if (mi->icon_w > min_icon_w) min_icon_w = mi->icon_w;
if (mi->icon_h > min_icon_h) min_icon_h = mi->icon_h;
if (mi->label_w > min_label_w) min_label_w = mi->label_w;
if (mi->label_h > min_label_h) min_label_h = mi->label_h;
if (mi->submenu_w > min_submenu_w) min_submenu_w = mi->submenu_w;
if (mi->submenu_h > min_submenu_h) min_submenu_h = mi->submenu_h;
if (mi->toggle_w > min_toggle_w) min_toggle_w = mi->toggle_w;
if (mi->toggle_h > min_toggle_h) min_toggle_h = mi->toggle_h;
}
if (labels_on)
{
if (submenus_on)
{
if (min_label_h < min_submenu_h)
min_label_h = min_submenu_h;
}
if (toggles_on)
{
if (min_label_h < min_toggle_h)
min_label_h = min_toggle_h;
}
if ((icons_on) && (min_icon_h > 0))
{
min_icon_w = (min_icon_w * min_label_h) / min_icon_h;
min_icon_h = min_label_h;
}
min_w = min_label_w + min_icon_w + min_submenu_w + min_toggle_w;
min_h = min_label_h;
}
else if (icons_on)
{
if (submenus_on)
{
if (min_icon_h < min_submenu_h)
min_icon_h = min_submenu_h;
}
if (toggles_on)
{
if (min_icon_h < min_toggle_h)
min_icon_h = min_toggle_h;
}
min_w = min_icon_w + min_toggle_w + min_submenu_w;
min_h = min_icon_h;
}
else if (toggles_on)
{
if (submenus_on)
{
if (min_toggle_h < min_submenu_h)
min_toggle_h = min_submenu_h;
}
min_w = min_toggle_w + min_submenu_w;
min_h = min_toggle_h;
}
for (l = m->items; l; l = l->next)
{
E_Menu_Item *mi;
mi = l->data;
if (mi->separator)
{
e_box_pack_options_set(mi->separator_object,
1, 1, /* fill */
1, 0, /* expand */
0.5, 0.5, /* align */
mi->separator_w, mi->separator_h, /* min */
-1, mi->separator_h /* max */
);
}
else
{
e_box_freeze(mi->container_object);
if (toggles_on)
e_box_pack_options_set(mi->toggle_object,
1, 1, /* fill */
0, 1, /* expand */
0.5, 0.5, /* align */
min_toggle_w, min_toggle_h, /* min */
-1, -1 /* max */
);
else
e_box_pack_options_set(mi->toggle_object,
1, 1, /* fill */
0, 0, /* expand */
0.5, 0.5, /* align */
0, 0, /* min */
0, 0 /* max */
);
if (icons_on)
{
if (mi->icon_bg_object)
e_box_pack_options_set(mi->icon_bg_object,
1, 1, /* fill */
0, 1, /* expand */
0.5, 0.5, /* align */
min_icon_w, min_icon_h, /* min */
-1, -1 /* max */
);
else
e_box_pack_options_set(mi->icon_object,
1, 1, /* fill */
0, 1, /* expand */
0.5, 0.5, /* align */
min_icon_w, min_icon_h, /* min */
-1, -1 /* max */
);
}
else
e_box_pack_options_set(mi->icon_object,
1, 1, /* fill */
0, 1, /* expand */
0.5, 0.5, /* align */
0, 0, /* min */
0, 0 /* max */
);
if (labels_on)
e_box_pack_options_set(mi->label_object,
1, 1, /* fill */
0, 1, /* expand */
0.5, 0.5, /* align */
min_label_w, min_label_h, /* min */
-1, -1 /* max */
);
else
e_box_pack_options_set(mi->label_object,
1, 1, /* fill */
0, 0, /* expand */
0.5, 0.5, /* align */
0, 0, /* min */
0, 0 /* max */
);
if (submenus_on)
e_box_pack_options_set(mi->submenu_object,
1, 1, /* fill */
0, 1, /* expand */
0.5, 0.5, /* align */
min_submenu_w, min_submenu_h, /* min */
-1, -1 /* max */
);
else
e_box_pack_options_set(mi->submenu_object,
1, 1, /* fill */
0, 0, /* expand */
0.5, 0.5, /* align */
0, 0, /* min */
0, 0 /* max */
);
edje_extern_object_min_size_set(mi->container_object, min_w, min_h);
edje_object_part_swallow(mi->bg_object, "item", mi->container_object);
edje_object_size_min_calc(mi->bg_object, &mw, &mh);
e_box_pack_options_set(mi->bg_object,
1, 1, /* fill */
1, 0, /* expand */
0.5, 0.5, /* align */
mw, mh, /* min */
-1, -1 /* max */
);
e_box_thaw(mi->container_object);
}
}
e_box_min_size_get(m->container_object, &bw, &bh);
edje_extern_object_min_size_set(m->container_object, bw, bh);
edje_extern_object_max_size_set(m->container_object, bw, bh);
edje_object_part_swallow(m->bg_object, "items", m->container_object);
edje_object_size_min_calc(m->bg_object, &mw, &mh);
e_box_thaw(m->container_object);
m->cur.w = mw;
m->cur.h = mh;
}
static void
_e_menu_item_unrealize(E_Menu_Item *mi)
{
if (mi->separator_object) evas_object_del(mi->separator_object);
mi->separator_object = NULL;
if (mi->bg_object) evas_object_del(mi->bg_object);
mi->bg_object = NULL;
if (mi->container_object) evas_object_del(mi->container_object);
mi->container_object = NULL;
if (mi->toggle_object) evas_object_del(mi->toggle_object);
mi->toggle_object = NULL;
if (mi->icon_bg_object) evas_object_del(mi->icon_bg_object);
mi->icon_bg_object = NULL;
if (mi->icon_object) evas_object_del(mi->icon_object);
mi->icon_object = NULL;
if (mi->label_object) evas_object_del(mi->label_object);
mi->label_object = NULL;
if (mi->submenu_object) evas_object_del(mi->submenu_object);
mi->submenu_object = NULL;
if (mi->event_object) evas_object_del(mi->event_object);
mi->event_object = NULL;
}
static void
_e_menu_unrealize(E_Menu *m)
{
Evas_List *l;
if (!m->realized) return;
e_container_shape_hide(m->shape);
e_object_del(E_OBJECT(m->shape));
m->shape = NULL;
for (l = m->items; l; l = l->next)
{
E_Menu_Item *mi;
mi = l->data;
_e_menu_item_unrealize(mi);
}
if (m->header.icon) evas_object_del(m->header.icon);
m->header.icon = NULL;
if (m->bg_object) evas_object_del(m->bg_object);
m->bg_object = NULL;
if (m->container_object) evas_object_del(m->container_object);
m->container_object = NULL;
m->cur.visible = 0;
m->prev.visible = 0;
m->realized = 0;
m->zone = NULL;
e_canvas_del(m->ecore_evas);
ecore_evas_free(m->ecore_evas);
m->ecore_evas = NULL;
m->evas = NULL;
m->evas_win = 0;
}
static void
_e_menu_activate_internal(E_Menu *m, E_Zone *zone)
{
if (m->pre_activate_cb.func)
m->pre_activate_cb.func(m->pre_activate_cb.data, m);
m->fast_mouse = 0;
m->pending_new_submenu = 0;
if (!_e_menu_win)
{
_e_menu_win = ecore_x_window_input_new(zone->container->win,
zone->x, zone->y,
zone->w, zone->h);
ecore_x_window_show(_e_menu_win);
/* need menu event win (input win) and grab to that */
ecore_x_pointer_confine_grab(_e_menu_win);
ecore_x_keyboard_grab(_e_menu_win);
}
if ((m->zone) && (m->zone->container != zone->container))
{
printf("FIXME: cannot move menus between containers yet\n");
return;
}
if (!m->active)
{
/* this remove is in case the menu is marked as inactive but hasnt */
/* been removed from the list yet */
if (m->in_active_list)
{
_e_active_menus = evas_list_remove(_e_active_menus, m);
m->in_active_list = 0;
e_object_unref(E_OBJECT(m));
}
_e_active_menus = evas_list_append(_e_active_menus, m);
m->in_active_list = 1;
m->active = 1;
e_object_ref(E_OBJECT(m));
}
m->cur.visible = 1;
m->zone = zone;
}
static void
_e_menu_deactivate_all(void)
{
Evas_List *l, *tmp = NULL;
for (l = _e_active_menus; l; l = l->next)
{
e_object_ref(E_OBJECT(l->data));
tmp = evas_list_append(tmp, l->data);
}
while (tmp)
{
E_Menu *m;
m = tmp->data;
tmp = evas_list_remove_list(tmp, tmp);
e_menu_deactivate(m);
m->parent_item = NULL;
e_object_unref(E_OBJECT(m));
}
}
static void
_e_menu_deactivate_above(E_Menu *ma)
{
Evas_List *l, *tmp = NULL;
int above = 0;
for (l = _e_active_menus; l; l = l->next)
{
e_object_ref(E_OBJECT(l->data));
tmp = evas_list_append(tmp, l->data);
}
while (tmp)
{
E_Menu *m;
m = tmp->data;
tmp = evas_list_remove_list(tmp, tmp);
if (above)
{
e_menu_deactivate(m);
m->parent_item = NULL;
}
if (ma == m) above = 1;
e_object_unref(E_OBJECT(m));
}
}
static void
_e_menu_submenu_activate(E_Menu_Item *mi)
{
if (!mi->menu->active) return;
if (mi->menu->fast_mouse)
{
mi->menu->pending_new_submenu = 1;
return;
}
mi->menu->pending_new_submenu = 0;
_e_menu_deactivate_above(mi->menu);
if (mi->submenu_pre_cb.func)
mi->submenu_pre_cb.func(mi->submenu_pre_cb.data, mi->menu, mi);
if (mi->submenu)
{
E_Menu *m;
m = mi->submenu;
e_object_ref(E_OBJECT(m));
m->parent_item = mi;
_e_menu_activate_internal(m, mi->menu->zone);
_e_menu_reposition(m);
e_object_unref(E_OBJECT(m));
}
}
static void
_e_menu_submenu_deactivate(E_Menu_Item *mi)
{
if (mi->menu->active) return;
if (mi->submenu_post_cb.func)
mi->submenu_post_cb.func(mi->submenu_post_cb.data, mi->menu, mi);
}
static void
_e_menu_reposition(E_Menu *m)
{
Evas_List *l, *tmp = NULL;
if (!m->parent_item) return;
m->cur.x = m->parent_item->menu->cur.x + m->parent_item->menu->cur.w;
m->cur.y = m->parent_item->menu->cur.y + m->parent_item->y - m->container_y;
/* FIXME: this will suck for big menus */
for (l = _e_active_menus; l; l = l->next)
{
tmp = evas_list_append(tmp, l->data);
e_object_ref(E_OBJECT(l->data));
}
for (l = m->items; l; l = l->next)
{
E_Menu_Item *mi;
mi = l->data;
if ((mi->active) && (mi->submenu)) _e_menu_reposition(mi->submenu);
}
while (tmp)
{
e_object_unref(E_OBJECT(tmp->data));
tmp = evas_list_remove_list(tmp, tmp);
}
}
static int
_e_menu_active_call(void)
{
Evas_List *l, *ll;
/* FIXME: inefficient. should track current menu and active item */
for (l = _e_active_menus; l; l = l->next)
{
E_Menu *m;
m = l->data;
for (ll = m->items; ll; ll = ll->next)
{
E_Menu_Item *mi;
mi = ll->data;
if (mi->active)
{
if (mi->submenu) return 0;
if (mi->check)
e_menu_item_toggle_set(mi, !mi->toggle);
if ((mi->radio) && (!e_menu_item_toggle_get(mi)))
e_menu_item_toggle_set(mi, 1);
if (mi->cb.func)
mi->cb.func(mi->cb.data, m, mi);
return 1;
}
}
}
return -1;
}
static void
_e_menu_item_activate_next(void)
{
E_Menu *m;
/* FIXME: inefficient. should track current menu and active item */
m = _e_menu_active_get();
if (m)
{
Evas_List *ll;
for (ll = m->items; ll; ll = ll->next)
{
E_Menu_Item *mi;
mi = ll->data;
if (mi->active)
{
if (ll->next)
{
ll = ll->next;
mi = ll->data;
while ((mi->separator) && (ll->next))
{
ll = ll->next;
mi = ll->data;
}
if ((mi->separator) && (!ll->next))
{
ll = m->items;
mi = ll->data;
while ((mi->separator) && (ll->next))
{
ll = ll->next;
mi = ll->data;
}
}
e_menu_item_active_set(mi, 1);
_e_menu_item_ensure_onscreen(mi);
}
else
{
ll = m->items;
mi = ll->data;
while ((mi->separator) && (ll->next))
{
ll = ll->next;
mi = ll->data;
}
e_menu_item_active_set(mi, 1);
_e_menu_item_ensure_onscreen(mi);
}
return;
}
}
}
_e_menu_activate_first();
}
static void
_e_menu_item_activate_previous(void)
{
E_Menu *m;
/* FIXME: inefficient. should track current menu and active item */
m = _e_menu_active_get();
if (m)
{
Evas_List *ll;
for (ll = m->items; ll; ll = ll->next)
{
E_Menu_Item *mi;
mi = ll->data;
if (mi->active)
{
if (ll->prev)
{
ll = ll->prev;
mi = ll->data;
while ((mi->separator) && (ll->prev))
{
ll = ll->prev;
mi = ll->data;
}
if ((mi->separator) && (!ll->prev))
{
ll = m->items;
mi = ll->data;
while ((mi->separator) && (ll->prev))
{
ll = ll->prev;
mi = ll->data;
}
}
e_menu_item_active_set(mi, 1);
_e_menu_item_ensure_onscreen(mi);
}
else
{
ll = m->items->last;
mi = ll->data;
while ((mi->separator) && (ll->prev))
{
ll = ll->prev;
mi = ll->data;
}
e_menu_item_active_set(mi, 1);
_e_menu_item_ensure_onscreen(mi);
}
return;
}
}
}
_e_menu_activate_first();
}
static void
_e_menu_activate_next(void)
{
E_Menu_Item *mi;
mi = _e_menu_item_active_get();
if (mi)
{
if (mi->submenu)
{
if (mi->submenu->items)
{
mi = mi->submenu->items->data;
e_menu_item_active_set(mi, 1);
_e_menu_item_ensure_onscreen(mi);
}
}
return;
}
_e_menu_activate_first();
}
static void
_e_menu_activate_previous(void)
{
E_Menu_Item *mi;
mi = _e_menu_item_active_get();
if (mi)
{
if (mi->menu->parent_item)
{
mi = mi->menu->parent_item;
e_menu_item_active_set(mi, 1);
_e_menu_item_ensure_onscreen(mi);
}
return;
}
_e_menu_activate_first();
}
static void
_e_menu_activate_first(void)
{
E_Menu *m;
E_Menu_Item *mi;
Evas_List *ll;
if (!_e_active_menus) return;
m = _e_active_menus->data;
if (!m->items) return;
ll = m->items;
mi = ll->data;
while ((mi->separator) && (ll->next))
{
ll = ll->next;
mi = ll->data;
}
if (mi->separator) return;
e_menu_item_active_set(mi, 1);
_e_menu_item_ensure_onscreen(mi);
}
static void
_e_menu_activate_nth(int n)
{
E_Menu *m;
E_Menu_Item *mi;
Evas_List *ll;
int i;
mi = _e_menu_item_active_get();
if (!mi)
{
_e_menu_activate_first();
mi = _e_menu_item_active_get();
if (!mi) return;
}
m = mi->menu;
for (i = -1, ll = m->items; ll; ll = ll->next)
{
E_Menu_Item *mi;
mi = ll->data;
if (!mi->separator) i++;
if (i == n)
{
e_menu_item_active_set(mi, 1);
_e_menu_item_ensure_onscreen(mi);
return;
}
}
}
static E_Menu *
_e_menu_active_get(void)
{
Evas_List *l, *ll;
/* FIXME: inefficient. should track current menu and active item */
for (l = _e_active_menus; l; l = l->next)
{
E_Menu *m;
m = l->data;
for (ll = m->items; ll; ll = ll->next)
{
E_Menu_Item *mi;
mi = ll->data;
if (mi->active) return m;
}
}
return NULL;
}
static E_Menu_Item *
_e_menu_item_active_get(void)
{
Evas_List *l, *ll;
/* FIXME: inefficient. should track current menu and active item */
for (l = _e_active_menus; l; l = l->next)
{
E_Menu *m;
m = l->data;
for (ll = m->items; ll; ll = ll->next)
{
E_Menu_Item *mi;
mi = ll->data;
if (mi->active) return mi;
}
}
return NULL;
}
static int
_e_menu_outside_bounds_get(int xdir, int ydir)
{
Evas_List *l;
int outl = 0;
int outr = 0;
int outt = 0;
int outb = 0;
int i;
for (l = _e_active_menus; l; l = l->next)
{
E_Menu *m;
m = l->data;
if (m->cur.x < m->zone->x)
{
i = m->zone->x - m->cur.x;
if (i > outl) outl = i;
}
if (m->cur.y < m->zone->y)
{
i = m->zone->y - m->cur.y;
if (i > outt) outt = i;
}
if ((m->cur.x + m->cur.w) > (m->zone->w))
{
i = m->cur.x + m->cur.w - (m->zone->x + m->zone->w);
if (i > outr) outr = i;
}
if ((m->cur.y + m->cur.h) > (m->zone->h))
{
i = m->cur.y + m->cur.h - (m->zone->y + m->zone->h);
if (i > outb) outb = i;
}
}
if (xdir == -1)
{
if (outl) return outl;
}
else if (xdir == 1)
{
if (outr) return outr;
}
else if (ydir == -1)
{
if (outt) return outt;
}
else if (ydir == 1)
{
if (outb) return outb;
}
return 0;
}
static void
_e_menu_scroll_by(int dx, int dy)
{
Evas_List *l;
for (l = _e_active_menus; l; l = l->next)
{
E_Menu *m;
m = l->data;
m->cur.x += dx;
m->cur.y += dy;
}
}
static void
_e_menu_mouse_autoscroll_check(void)
{
int autoscroll_x = 0;
int autoscroll_y = 0;
if (_e_menu_x == 0)
{
if (_e_menu_outside_bounds_get(-1, 0)) autoscroll_x = -1;
}
if (_e_menu_y == 0)
{
if (_e_menu_outside_bounds_get(0, -1)) autoscroll_y = -1;
}
if ((!autoscroll_x) && (!autoscroll_y))
{
if (_e_active_menus)
{
E_Menu *m;
m = _e_active_menus->data;
if (_e_menu_x == (m->zone->w - 1))
{
if (_e_menu_outside_bounds_get(1, 0)) autoscroll_x = 1;
}
if (_e_menu_y == (m->zone->h - 1))
{
if (_e_menu_outside_bounds_get(0, 1)) autoscroll_y = 1;
}
}
}
_e_menu_autoscroll_x = autoscroll_x;
_e_menu_autoscroll_y = autoscroll_y;
if ((!autoscroll_x) && (!autoscroll_y)) return;
if (_e_menu_scroll_timer) return;
_e_menu_scroll_timer = ecore_timer_add(1.0 / 60.0,
_e_menu_cb_scroll_timer, NULL);
_e_menu_scroll_start = ecore_time_get();
}
static void
_e_menu_item_ensure_onscreen(E_Menu_Item *mi)
{
int x, y, w, h;
int dx, dy;
x = mi->x + mi->menu->cur.x;
y = mi->y + mi->menu->cur.y;
w = mi->w;
h = mi->h;
dx = 0;
dy = 0;
if ((x + w) > (mi->menu->zone->x + mi->menu->zone->w))
dx = (mi->menu->zone->x + mi->menu->zone->w) - (x + w);
if ((y + h) > (mi->menu->zone->y + mi->menu->zone->h))
dy = (mi->menu->zone->y + mi->menu->zone->h) - (y + h);
if (x < 0) dx = x;
if (y < 0) dy = y;
if ((dx != 0) || (dy != 0))
_e_menu_scroll_by(dx, dy);
}
static void
_e_menu_cb_ecore_evas_resize(Ecore_Evas *ee)
{
Evas *evas;
Evas_Object *o;
E_Menu *m;
Evas_Coord w, h;
evas = ecore_evas_get(ee);
evas_output_viewport_get(evas, NULL, NULL, &w, &h);
o = evas_object_name_find(evas, "menu/background");
m = evas_object_data_get(o, "e_menu");
evas_object_resize(o, w, h);
}
static void
_e_menu_cb_item_in(void *data, Evas *evas, Evas_Object *obj, void *event_info)
{
E_Menu_Item *mi;
mi = data;
e_menu_item_active_set(mi, 1);
}
static void
_e_menu_cb_item_out(void *data, Evas *evas, Evas_Object *obj, void *event_info)
{
E_Menu_Item *mi;
mi = data;
e_menu_item_active_set(mi, 0);
}
static int
_e_menu_cb_key_down(void *data, int type, void *event)
{
Ecore_X_Event_Key_Down *ev;
ev = event;
if (ev->win != _e_menu_win) return 1;
if (!strcmp(ev->keysymbol, "Up"))
_e_menu_item_activate_previous();
else if (!strcmp(ev->keysymbol, "Down"))
_e_menu_item_activate_next();
else if (!strcmp(ev->keysymbol, "Left"))
_e_menu_activate_previous();
else if (!strcmp(ev->keysymbol, "Right"))
_e_menu_activate_next();
else if (!strcmp(ev->keysymbol, "space"))
{
_e_menu_active_call();
}
else if (!strcmp(ev->keysymbol, "Return"))
{
_e_menu_active_call();
_e_menu_deactivate_all();
}
else if (!strcmp(ev->keysymbol, "Escape"))
_e_menu_deactivate_all();
else if (!strcmp(ev->keysymbol, "1"))
_e_menu_activate_nth(0);
else if (!strcmp(ev->keysymbol, "2"))
_e_menu_activate_nth(1);
else if (!strcmp(ev->keysymbol, "3"))
_e_menu_activate_nth(2);
else if (!strcmp(ev->keysymbol, "4"))
_e_menu_activate_nth(3);
else if (!strcmp(ev->keysymbol, "5"))
_e_menu_activate_nth(4);
else if (!strcmp(ev->keysymbol, "6"))
_e_menu_activate_nth(5);
else if (!strcmp(ev->keysymbol, "7"))
_e_menu_activate_nth(6);
else if (!strcmp(ev->keysymbol, "8"))
_e_menu_activate_nth(7);
else if (!strcmp(ev->keysymbol, "9"))
_e_menu_activate_nth(8);
else if (!strcmp(ev->keysymbol, "0"))
_e_menu_activate_nth(9);
printf("kdn \"%s\" \"%s\"\n", ev->keyname, ev->keysymbol);
return 1;
}
static int
_e_menu_cb_key_up(void *data, int type, void *event)
{
Ecore_X_Event_Key_Up *ev;
ev = event;
if (ev->win != _e_menu_win) return 1;
return 1;
}
/* we need all of these because menus are special and grab the mouse and
* keyboard and thus the normal event mechanism doesnt work, so we feed
* events directly to the canvases from our grab window
*/
static int
_e_menu_cb_mouse_down(void *data, int type, void *event)
{
Ecore_X_Event_Mouse_Button_Down *ev;
ev = event;
if (ev->win != _e_menu_win) return 1;
return 1;
}
static int
_e_menu_cb_mouse_up(void *data, int type, void *event)
{
Ecore_X_Event_Mouse_Button_Up *ev;
double t;
int ret;
ev = event;
if (ev->win != _e_menu_win) return 1;
t = ecore_time_get();
if ((_e_menu_activate_time != 0.0) &&
((t - _e_menu_activate_time) < e_config->menus_click_drag_timeout))
return 1;
ret = _e_menu_active_call();
if (ret == 1)
{
/* allow mouse to pop down menu if clicked elsewhere */
/* if (_e_menu_activate_time != 0.0) */
_e_menu_deactivate_all();
}
else if (ret == -1)
_e_menu_deactivate_all();
else
_e_menu_deactivate_all();
return 1;
}
static int
_e_menu_cb_mouse_move(void *data, int type, void *event)
{
Ecore_X_Event_Mouse_Move *ev;
Evas_List *l, *tmp = NULL;
int dx, dy, d;
double dt;
double fast_move_threshold;
int is_fast = 0;
ev = event;
if (ev->win != _e_menu_win) return 1;
fast_move_threshold = e_config->menus_fast_mouse_move_thresthold;
dx = ev->x - _e_menu_x;
dy = ev->y - _e_menu_y;
d = (dx * dx) + (dy * dy);
dt = (double)(ev->time - _e_menu_time) / 1000.0;
dt = dt * dt;
if ((dt > 0.0) && ((d / dt) >= (fast_move_threshold * fast_move_threshold)))
is_fast = 1;
for (l = _e_active_menus; l; l = l->next)
{
tmp = evas_list_append(tmp, l->data);
e_object_ref(E_OBJECT(l->data));
}
for (l = _e_active_menus; l; l = l->next)
{
E_Menu *m;
m = l->data;
if ((m->realized) && (m->cur.visible))
{
if (is_fast)
m->fast_mouse = 1;
else
{
m->fast_mouse = 0;
if (m->pending_new_submenu)
{
E_Menu_Item *mi;
mi = _e_menu_item_active_get();
if (mi)
_e_menu_submenu_activate(mi);
}
}
evas_event_feed_mouse_move(m->evas,
ev->x - m->cur.x + m->zone->x,
ev->y - m->cur.y + m->zone->y,
NULL);
}
}
while (tmp)
{
e_object_unref(E_OBJECT(tmp->data));
tmp = evas_list_remove_list(tmp, tmp);
}
_e_menu_x = ev->x;
_e_menu_y = ev->y;
_e_menu_time = ev->time;
_e_menu_mouse_autoscroll_check();
return 1;
}
static int
_e_menu_cb_mouse_wheel(void *data, int type, void *event)
{
Ecore_X_Event_Mouse_Wheel *ev;
ev = event;
if (ev->win != _e_menu_win) return 1;
if (ev->z < 0) /* up */
{
int i;
for (i = ev->z; i < 0; i++)
_e_menu_item_activate_previous();
}
else if (ev->z > 0) /* down */
{
int i;
for (i = ev->z; i > 0; i--)
_e_menu_item_activate_next();
}
return 1;
}
static int
_e_menu_cb_scroll_timer(void *data)
{
double t, dt;
double dx, dy;
int out;
double spd;
t = ecore_time_get();
spd = e_config->menus_scroll_speed;
dt = t - _e_menu_scroll_start;
_e_menu_scroll_start = t;
dx = 0;
dy = 0;
if (_e_menu_autoscroll_x)
{
out = _e_menu_outside_bounds_get(_e_menu_autoscroll_x, 0);
dx = (-_e_menu_autoscroll_x) * spd * dt;
if (_e_menu_autoscroll_x == -1)
{
if (dx > out) dx = out;
}
else
{
if (dx < -out) dx = -out;
}
}
if (_e_menu_autoscroll_y)
{
out = _e_menu_outside_bounds_get(0, _e_menu_autoscroll_y);
dy = (-_e_menu_autoscroll_y) * spd * dt;
if (_e_menu_autoscroll_y == -1)
{
if (dy > out) dy = out;
}
else
{
if (dy < -out) dy = -out;
}
}
_e_menu_scroll_by(dx, dy);
_e_menu_mouse_autoscroll_check();
if ((_e_menu_autoscroll_x == 0) && (_e_menu_autoscroll_y == 0))
{
_e_menu_scroll_timer = NULL;
return 0;
}
return 1;
}
static int
_e_menu_cb_window_shape(void *data, int ev_type, void *ev)
{
Evas_List *l;
Ecore_X_Event_Window_Shape *e;
e = ev;
for (l = _e_active_menus; l; l = l->next)
{
E_Menu *m;
m = l->data;
if (m->evas_win == e->win)
m->need_shape_export = 1;
}
return 1;
}
static void
_e_menu_cb_item_submenu_post_default(void *data, E_Menu *m, E_Menu_Item *mi)
{
E_Menu *subm;
if (!mi->submenu) return;
subm = mi->submenu;
e_menu_item_submenu_set(mi, NULL);
e_object_del(E_OBJECT(subm));
}