diff options
Diffstat (limited to 'module/menuitem-proxy.c')
-rw-r--r-- | module/menuitem-proxy.c | 338 |
1 files changed, 324 insertions, 14 deletions
diff --git a/module/menuitem-proxy.c b/module/menuitem-proxy.c index 6a3e555..fbab6f1 100644 --- a/module/menuitem-proxy.c +++ b/module/menuitem-proxy.c @@ -39,6 +39,152 @@ static void init_static_data() } } +#if GTK_MAJOR_VERSION == 3 +/* What could possibly go wrong? */ +typedef enum +{ + GTK_DIRECTION_LEFT, + GTK_DIRECTION_RIGHT +} GtkSubmenuDirection; + +typedef enum +{ + GTK_TOP_BOTTOM, + GTK_LEFT_RIGHT +} GtkSubmenuPlacement; + +struct _GtkMenuPrivate +{ + GtkWidget *parent_menu_item; + GtkWidget *old_active_menu_item; + + GtkAccelGroup *accel_group; + gchar *accel_path; + + GtkMenuPositionFunc position_func; + gpointer position_func_data; + GDestroyNotify position_func_data_destroy; + gint position_x; + gint position_y; + + guint toggle_size; + guint accel_size; + + GtkWidget *toplevel; + + GtkWidget *tearoff_window; + GtkWidget *tearoff_hbox; + GtkWidget *tearoff_scrollbar; + GtkAdjustment *tearoff_adjustment; + + GdkWindow *view_window; + GdkWindow *bin_window; + + gint scroll_offset; + gint saved_scroll_offset; + gint scroll_step; + + guint scroll_timeout; + + guint needs_destruction_ref : 1; + guint torn_off : 1; + /* The tearoff is active when it is torn off and the not-torn-off + * menu is not popped up. + */ + guint tearoff_active : 1; + guint scroll_fast : 1; + + guint upper_arrow_visible : 1; + guint lower_arrow_visible : 1; + guint upper_arrow_prelight : 1; + guint lower_arrow_prelight : 1; + + guint have_position : 1; + guint have_layout : 1; + guint seen_item_enter : 1; + guint ignore_button_release : 1; + guint no_toggle_size : 1; + guint drag_already_pressed : 1; + guint drag_scroll_started : 1; + + /* info used for the table */ + guint *heights; + gint heights_length; + gint requested_height; + + gboolean initially_pushed_in; + gint monitor_num; + + /* Cached layout information */ + gint n_rows; + gint n_columns; + + gchar *title; + + /* Arrow states */ + GtkStateFlags lower_arrow_state; + GtkStateFlags upper_arrow_state; + + /* navigation region */ + gint navigation_x; + gint navigation_y; + gint navigation_width; + gint navigation_height; + + guint navigation_timeout; + + gdouble drag_start_y; + gint initial_drag_offset; +}; + +struct _GtkMenuShellPrivate +{ + GList *children; + + GtkWidget *active_menu_item; + GtkWidget *parent_menu_shell; + void *tracker; + + guint button; + guint32 activate_time; + + guint active : 1; + guint have_grab : 1; + guint have_xgrab : 1; + guint ignore_enter : 1; + guint keyboard_mode : 1; + + guint take_focus : 1; + guint activated_submenu : 1; + guint in_unselectable_item : 1; +}; + +struct _GtkMenuItemPrivate +{ + GtkWidget *submenu; + GdkWindow *event_window; + + guint16 toggle_size; + guint16 accelerator_width; + + guint timer; + + gchar *accel_path; + + GtkAction *action; + void *action_helper; + + guint show_submenu_indicator : 1; + guint submenu_placement : 1; + guint submenu_direction : 1; + guint right_justify : 1; + guint timer_from_keypress : 1; + guint from_menubar : 1; + guint use_action_appearance : 1; + guint reserve_indicator : 1; +}; +#endif + static void free_timeval (GTimeVal *val) { @@ -50,6 +196,23 @@ get_offsets (GtkMenu *menu, gint *horizontal_offset, gint *vertical_offset) { +#if GTK_MAJOR_VERSION == 3 + GtkStyleContext *context; + GtkStateFlags state; + GtkBorder padding; + + gtk_widget_style_get (GTK_WIDGET (menu), + "horizontal-offset", horizontal_offset, + "vertical-offset", vertical_offset, + NULL); + + context = gtk_widget_get_style_context (GTK_WIDGET (menu)); + state = gtk_widget_get_state_flags (GTK_WIDGET (menu)); + gtk_style_context_get_padding (context, state, &padding); + + *vertical_offset -= padding.top; + *horizontal_offset += padding.left; +#else gint vertical_padding; gint horizontal_padding; @@ -63,6 +226,7 @@ get_offsets (GtkMenu *menu, *vertical_offset -= GTK_WIDGET (menu)->style->ythickness; *vertical_offset -= vertical_padding; *horizontal_offset += horizontal_padding; +#endif } static void @@ -72,6 +236,153 @@ menu_item_position_menu (GtkMenu *menu, gboolean *push_in, gpointer user_data) { +#if GTK_MAJOR_VERSION == 3 + GtkMenuItem *menu_item = GTK_MENU_ITEM (user_data); + GtkMenuItemPrivate *priv = menu_item->priv; + GtkAllocation allocation; + GtkWidget *widget; + GtkMenuItem *parent_menu_item; + GtkWidget *parent; + GdkScreen *screen; + gint twidth, theight; + gint tx, ty; + GtkTextDirection direction; + GdkRectangle monitor; + gint monitor_num; + gint horizontal_offset; + gint vertical_offset; + gint available_left, available_right; + GtkStyleContext *context; + GtkStateFlags state; + GtkBorder parent_padding; + + g_return_if_fail (menu != NULL); + g_return_if_fail (x != NULL); + g_return_if_fail (y != NULL); + + widget = GTK_WIDGET (user_data); + + if (push_in) + *push_in = FALSE; + + direction = gtk_widget_get_direction (widget); + + twidth = gtk_widget_get_allocated_width (GTK_WIDGET (menu)); + theight = gtk_widget_get_allocated_height (GTK_WIDGET (menu)); + + screen = gtk_widget_get_screen (GTK_WIDGET (menu)); + monitor_num = gdk_screen_get_monitor_at_window (screen, priv->event_window); + if (monitor_num < 0) + monitor_num = 0; + gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor); + + if (!gdk_window_get_origin (gtk_widget_get_window (widget), &tx, &ty)) + { + g_warning ("Menu not on screen"); + return; + } + + gtk_widget_get_allocation (widget, &allocation); + + tx += allocation.x; + ty += allocation.y; + + get_offsets (menu, &horizontal_offset, &vertical_offset); + + available_left = tx - monitor.x; + available_right = monitor.x + monitor.width - (tx + allocation.width); + + parent = gtk_widget_get_parent (widget); + priv->from_menubar = GTK_IS_MENU_BAR (parent); + + switch (priv->submenu_placement) + { + case GTK_TOP_BOTTOM: + if (direction == GTK_TEXT_DIR_LTR) + priv->submenu_direction = GTK_DIRECTION_RIGHT; + else + { + priv->submenu_direction = GTK_DIRECTION_LEFT; + tx += allocation.width - twidth; + } + if ((ty + allocation.height + theight) <= monitor.y + monitor.height) + ty += allocation.height; + else if ((ty - theight) >= monitor.y) + ty -= theight; + else if (monitor.y + monitor.height - (ty + allocation.height) > ty) + ty += allocation.height; + else + ty -= theight; + break; + + case GTK_LEFT_RIGHT: + if (GTK_IS_MENU (parent)) + parent_menu_item = GTK_MENU_ITEM (GTK_MENU (parent)->priv->parent_menu_item); + else + parent_menu_item = NULL; + + context = gtk_widget_get_style_context (parent); + state = gtk_widget_get_state_flags (parent); + gtk_style_context_get_padding (context, state, &parent_padding); + + if (parent_menu_item && !GTK_MENU (parent)->priv->torn_off) + { + priv->submenu_direction = parent_menu_item->priv->submenu_direction; + } + else + { + if (direction == GTK_TEXT_DIR_LTR) + priv->submenu_direction = GTK_DIRECTION_RIGHT; + else + priv->submenu_direction = GTK_DIRECTION_LEFT; + } + + switch (priv->submenu_direction) + { + case GTK_DIRECTION_LEFT: + if (tx - twidth - parent_padding.left - horizontal_offset >= monitor.x || + available_left >= available_right) + tx -= twidth + parent_padding.left + horizontal_offset; + else + { + priv->submenu_direction = GTK_DIRECTION_RIGHT; + tx += allocation.width + parent_padding.right + horizontal_offset; + } + break; + + case GTK_DIRECTION_RIGHT: + if (tx + allocation.width + parent_padding.right + horizontal_offset + twidth <= monitor.x + monitor.width || + available_right >= available_left) + tx += allocation.width + parent_padding.right + horizontal_offset; + else + { + priv->submenu_direction = GTK_DIRECTION_LEFT; + tx -= twidth + parent_padding.left + horizontal_offset; + } + break; + } + + ty += vertical_offset; + + /* If the height of the menu doesn't fit we move it upward. */ + ty = CLAMP (ty, monitor.y, MAX (monitor.y, monitor.y + monitor.height - theight)); + break; + } + + /* If we have negative, tx, here it is because we can't get + * the menu all the way on screen. Favor the left portion. + */ + *x = CLAMP (tx, monitor.x, MAX (monitor.x, monitor.x + monitor.width - twidth)); + *y = ty; + + gtk_menu_set_monitor (menu, monitor_num); + + if (!gtk_widget_get_visible (menu->priv->toplevel)) + { + gtk_window_set_type_hint (GTK_WINDOW (menu->priv->toplevel), priv->from_menubar? + GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU : GDK_WINDOW_TYPE_HINT_POPUP_MENU); + } +#else GtkMenuItem *menu_item; GtkWidget *widget; GtkMenuItem *parent_menu_item; @@ -222,6 +533,7 @@ menu_item_position_menu (GtkMenu *menu, gtk_window_set_type_hint (GTK_WINDOW (menu->toplevel), menu_item->from_menubar? GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU : GDK_WINDOW_TYPE_HINT_POPUP_MENU); } +#endif } static void handle_menuitem_notify(GtkMenuItem *item, GParamSpec *pspec, GtkMenuItem *proxy) @@ -251,7 +563,7 @@ static gboolean handle_menuitem_mnemonic_activate(GtkMenuItem *item, gboolean cy if (parent && monitor->available) { GtkMenuShell *parent_shell = GTK_MENU_SHELL(parent); - if (GTK_IS_MENU_BAR(parent_shell) || parent_shell->active) { + if (GTK_IS_MENU_BAR(parent_shell) || parent_shell->priv->active) { gtk_widget_mnemonic_activate(GTK_WIDGET(proxy), cycling); return TRUE; } @@ -278,12 +590,13 @@ static void handle_parent_move_current(GtkMenuShell *shell, GtkMenuDirectionType static void handle_proxy_select(GtkMenuItem *proxy, GtkMenuItem *item) { GtkWidget *submenu = gtk_menu_item_get_submenu(item); + GtkWidget *parent = gtk_widget_get_parent(GTK_WIDGET(proxy)); - if (submenu) { + if (submenu && parent) { if (!gtk_widget_is_sensitive(GTK_WIDGET(submenu))) return; - GtkMenuShell *parent_shell = GTK_MENU_SHELL(GTK_WIDGET(proxy)->parent); + GtkMenuShell *parent_shell = GTK_MENU_SHELL(parent); GTimeVal *popup_time = g_slice_new0(GTimeVal); g_get_current_time(popup_time); @@ -302,7 +615,7 @@ static void handle_proxy_select(GtkMenuItem *proxy, GtkMenuItem *item) GTK_WIDGET(proxy), menu_item_position_menu, proxy, - parent_shell->button, + parent_shell->priv->button, 0); } } @@ -337,11 +650,8 @@ static void handle_proxy_activate_item(GtkMenuItem *proxy, GtkMenuItem *item) if (submenu) { GtkMenuShell *parent = GTK_MENU_SHELL(gtk_widget_get_parent(GTK_WIDGET(proxy))); if (parent) { - if (!parent->active) { - //gtk_grab_add(GTK_WIDGET(parent)); - //parent->have_grab = TRUE; - parent->active = TRUE; - } + parent->priv->active = TRUE; + // We do not add grabs here, like Gtk+ does, because they are already done. gtk_menu_shell_select_item(parent, GTK_WIDGET(proxy)); gtk_menu_shell_select_first(GTK_MENU_SHELL(submenu), TRUE); } @@ -358,9 +668,9 @@ static GtkWidget *construct_image_widget_proxy(GtkImage *widget) gtk_image_get_stock(widget, &icon_name, &icon_size); return gtk_image_new_from_stock(icon_name, icon_size); case GTK_IMAGE_EMPTY: - return gtk_image_new(); + return NULL; default: - return gtk_image_new(); + return NULL; } } @@ -382,15 +692,15 @@ GtkMenuItem *topmenu_create_proxy_menu_item(GtkMenuItem *item) if (iwidget) { // Let's suppport some common widget types if (GTK_IS_IMAGE(iwidget)) { + GtkImage *iwidgetimg = GTK_IMAGE(iwidget); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(proxy), - construct_image_widget_proxy(GTK_IMAGE(iwidget))); + construct_image_widget_proxy(iwidgetimg)); } } } gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(proxy), gtk_image_menu_item_get_always_show_image(iitem)); - } - else if (GTK_IS_SEPARATOR_MENU_ITEM(item)) { + } else if (GTK_IS_SEPARATOR_MENU_ITEM(item)) { proxy = GTK_MENU_ITEM(gtk_separator_menu_item_new()); } else { proxy = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(label)); |