* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
+/**
+ * SECTION:gtkmenu
+ * @Short_description: A menu widget
+ * @Title: GtkMenu
+ *
+ * A #GtkMenu is a #GtkMenuShell that implements a drop down menu
+ * consisting of a list of #GtkMenuItem objects which can be navigated
+ * and activated by the user to perform application functions.
+ *
+ * A #GtkMenu is most commonly dropped down by activating a
+ * #GtkMenuItem in a #GtkMenuBar or popped up by activating a
+ * #GtkMenuItem in another #GtkMenu.
+ *
+ * A #GtkMenu can also be popped up by activating a #GtkOptionMenu.
+ * Other composite widgets such as the #GtkNotebook can pop up a
+ * #GtkMenu as well.
+ *
+ * Applications can display a #GtkMenu as a popup menu by calling the
+ * gtk_menu_popup() function. The example below shows how an application
+ * can pop up a menu when the 3rd mouse button is pressed.
+ *
+ * <example>
+ * <title>Connecting the popup signal handler.</title>
+ * <programlisting>
+ * /<!---->* connect our handler which will popup the menu *<!---->/
+ * g_signal_connect_swapped (window, "button_press_event",
+ * G_CALLBACK (my_popup_handler), menu);
+ * </programlisting>
+ * </example>
+ *
+ * <example>
+ * <title>Signal handler which displays a popup menu.</title>
+ * <programlisting>
+ * static gint
+ * my_popup_handler (GtkWidget *widget, GdkEvent *event)
+ * {
+ * GtkMenu *menu;
+ * GdkEventButton *event_button;
+ *
+ * g_return_val_if_fail (widget != NULL, FALSE);
+ * g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
+ * g_return_val_if_fail (event != NULL, FALSE);
+ *
+ * /<!---->* The "widget" is the menu that was supplied when
+ * * g_signal_connect_swapped() was called.
+ * *<!---->/
+ * menu = GTK_MENU (widget);
+ *
+ * if (event->type == GDK_BUTTON_PRESS)
+ * {
+ * event_button = (GdkEventButton *) event;
+ * if (event_button->button == 3)
+ * {
+ * gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
+ * event_button->button, event_button->time);
+ * return TRUE;
+ * }
+ * }
+ *
+ * return FALSE;
+ * }
+ * </programlisting>
+ * </example>
+ */
+
#include "config.h"
#include <string.h>
#include "gtksettings.h"
#include "gtkprivate.h"
#include "gtkintl.h"
+#include "gtktypebuiltins.h"
#define NAVIGATION_REGION_OVERSHOOT 50 /* How much the navigation region
* extends below the submenu
menu_shell_class->get_popup_delay = gtk_menu_get_popup_delay;
menu_shell_class->move_current = gtk_menu_move_current;
+ /**
+ * GtkMenu::move-scroll:
+ * @menu: a #GtkMenu
+ * @scroll_type: a #GtkScrollType
+ */
menu_signals[MOVE_SCROLL] =
g_signal_new_class_handler (I_("move-scroll"),
G_OBJECT_CLASS_TYPE (gobject_class),
GTK_PARAM_READWRITE));
/**
- * GtkMenu::arrow-scaling
+ * GtkMenu:arrow-scaling
*
* Arbitrary constant to scale down the size of the scroll arrow.
*
menu_change_screen (menu, gtk_widget_get_screen (attach_widget));
}
+/**
+ * gtk_menu_attach_to_widget: (skip)
+ * @menu: a #GtkMenu
+ * @attach_widget: the #GtkWidget that the menu will be attached to
+ * @detacher: the user supplied callback function that will be called
+ * when the menu calls gtk_menu_detach()
+ *
+ * Attaches the menu to the widget and provides a callback function
+ * that will be invoked when the menu calls gtk_menu_detach() during
+ * its destruction.
+ */
void
gtk_menu_attach_to_widget (GtkMenu *menu,
GtkWidget *attach_widget,
g_object_notify (G_OBJECT (menu), "attach-widget");
}
+/**
+ * gtk_menu_get_attach_widget:
+ * @menu: a #GtkMenu
+ *
+ * Returns the #GtkWidget that the menu is attached to.
+ *
+ * Returns: (transfer none): the #GtkWidget that the menu is attached to
+ */
GtkWidget*
gtk_menu_get_attach_widget (GtkMenu *menu)
{
return NULL;
}
+/**
+ * gtk_menu_detach:
+ * @menu: a #GtkMenu
+ *
+ * Detaches the menu from the widget to which it had been attached.
+ * This function will call the callback function, @detacher, provided
+ * when the gtk_menu_attach_to_widget() function was called.
+ */
void
gtk_menu_detach (GtkMenu *menu)
{
menu_queue_resize (menu);
}
+/**
+ * gtk_menu_new:
+ *
+ * Creates a new #GtkMenu
+ *
+ * Returns: a new #GtkMenu
+ */
GtkWidget*
gtk_menu_new (void)
{
gtk_menu_tearoff_bg_copy (GtkMenu *menu)
{
GtkMenuPrivate *priv = menu->priv;
- GtkWidget *widget;
gint width, height;
- widget = GTK_WIDGET (menu);
-
if (priv->torn_off)
{
GdkWindow *window;
/**
* gtk_menu_popup_for_device:
- * @menu: a #GtkMenu.
+ * @menu: a #GtkMenu
* @device: (allow-none): a #GdkDevice
* @parent_menu_shell: (allow-none): the menu shell containing the triggering
* menu item, or %NULL
}
/**
- * gtk_menu_popup:
- * @menu: a #GtkMenu.
+ * gtk_menu_popup: (skip)
+ * @menu: a #GtkMenu
* @parent_menu_shell: (allow-none): the menu shell containing the
* triggering menu item, or %NULL
* @parent_menu_item: (allow-none): the menu item whose activation
* triggered the popup, or %NULL
* @func: (allow-none): a user supplied function used to position
* the menu, or %NULL
- * @data: (allow-none): user supplied data to be passed to @func.
+ * @data: user supplied data to be passed to @func.
* @button: the mouse button which was pressed to initiate the event.
* @activate_time: the time at which the activation event occurred.
*
button, activate_time);
}
+/**
+ * gtk_menu_popdown:
+ * @menu: a #GtkMenu
+ *
+ * Removes the menu from the screen.
+ */
void
gtk_menu_popdown (GtkMenu *menu)
{
menu_grab_transfer_window_destroy (menu);
}
+/**
+ * gtk_menu_get_active:
+ * @menu: a #GtkMenu
+ *
+ * Returns the selected menu item from the menu. This is used by the
+ * #GtkOptionMenu.
+ *
+ * Returns: (transfer none): the #GtkMenuItem that was last selected
+ * in the menu. If a selection has not yet been made, the
+ * first menu item is selected.
+ */
GtkWidget*
gtk_menu_get_active (GtkMenu *menu)
{
return priv->old_active_menu_item;
}
+/**
+ * gtk_menu_set_active:
+ * @menu: a #GtkMenu
+ * @index: the index of the menu item to select. Iindex values are
+ * from 0 to n-1
+ *
+ * Selects the specified menu item within the menu. This is used by
+ * the #GtkOptionMenu and should not be used by anyone else.
+ */
void
gtk_menu_set_active (GtkMenu *menu,
guint index)
/**
* gtk_menu_set_accel_group:
- * @accel_group: (allow-none):
+ * @menu: a #GtkMenu
+ * @accel_group: (allow-none): the #GtkAccelGroup to be associated
+ * with the menu.
+ *
+ * Set the #GtkAccelGroup which holds global accelerators for the
+ * menu. This accelerator group needs to also be added to all windows
+ * that this menu is being used in with gtk_window_add_accel_group(),
+ * in order for those windows to support all the accelerators
+ * contained in this group.
*/
void
gtk_menu_set_accel_group (GtkMenu *menu,
}
}
+/**
+ * gtk_menu_get_accel_group:
+ * @menu: a #GtkMenu
+ *
+ * Gets the #GtkAccelGroup which holds global accelerators for the
+ * menu. See gtk_menu_set_accel_group().
+ *
+ * Returns: (transfer none): the #GtkAccelGroup associated with the menu
+ */
GtkAccelGroup*
gtk_menu_get_accel_group (GtkMenu *menu)
{
}
}
+/**
+ * gtk_menu_reposition:
+ * @menu: a #GtkMenu
+ *
+ * Repositions the menu according to its position function.
+ */
void
gtk_menu_reposition (GtkMenu *menu)
{
gtk_menu_scrollbar_changed (GtkAdjustment *adjustment,
GtkMenu *menu)
{
- g_return_if_fail (GTK_IS_MENU (menu));
+ double value;
- if (adjustment->value != menu->priv->scroll_offset)
- gtk_menu_scroll_to (menu, adjustment->value);
+ value = gtk_adjustment_get_value (adjustment);
+ if (menu->priv->scroll_offset != value)
+ gtk_menu_scroll_to (menu, value);
}
static void
gtk_menu_set_tearoff_state (menu, FALSE);
}
+/**
+ * gtk_menu_set_tearoff_state:
+ * @menu: a #GtkMenu
+ * @torn_off: If %TRUE, menu is displayed as a tearoff menu.
+ *
+ * Changes the tearoff state of the menu. A menu is normally
+ * displayed as drop down menu which persists as long as the menu is
+ * active. It can also be displayed as a tearoff menu which persists
+ * until it is closed or reattached.
+ */
void
gtk_menu_set_tearoff_state (GtkMenu *menu,
gboolean torn_off)
priv->tearoff_scrollbar,
FALSE, FALSE, 0);
- if (priv->tearoff_adjustment->upper > height)
+ if (gtk_adjustment_get_upper (priv->tearoff_adjustment) > height)
gtk_widget_show (priv->tearoff_scrollbar);
gtk_widget_show (priv->tearoff_hbox);
return menu->priv->title;
}
+/**
+ * gtk_menu_reorder_child:
+ * @menu: a #GtkMenu
+ * @child: the #GtkMenuItem to move
+ * @position: the new position to place @child.
+ * Positions are numbered from 0 to n - 1
+ *
+ * Moves @child to a new @position in the list of @menu
+ * children.
+ */
void
gtk_menu_reorder_child (GtkMenu *menu,
GtkWidget *child,
static void
gtk_menu_style_updated (GtkWidget *widget)
{
+ GTK_WIDGET_CLASS (gtk_menu_parent_class)->style_updated (widget);
+
if (gtk_widget_get_realized (widget))
{
GtkMenu *menu = GTK_MENU (widget);
{
GtkStyleContext *context;
GtkStateFlags state;
- GtkBorder *border_width;
+ GtkBorder padding, border_width;
context = gtk_widget_get_style_context (widget);
state = gtk_widget_get_state_flags (widget);
- gtk_style_context_get (context, state,
- "border-width", &border_width,
- NULL);
+ gtk_style_context_get_padding (context, state, &padding);
+ gtk_style_context_get_border (context, state, &border_width);
- *border = *border_width;
- gtk_border_free (border_width);
+ border->left = border_width.left + padding.left;
+ border->right = border_width.right + padding.right;
+ border->top = border_width.top + padding.top;
+ border->bottom = border_width.bottom + padding.bottom;
}
static void
guint **ret_min_heights,
guint **ret_nat_heights)
{
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GtkBorder padding;
GtkMenuPrivate *priv;
GtkMenuShell *menu_shell;
GtkWidget *child, *widget;
"horizontal-padding", &horizontal_padding,
NULL);
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
+ gtk_style_context_get_padding (context, state, &padding);
+
border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
- avail_width -= (border_width + horizontal_padding + gtk_widget_get_style (widget)->xthickness) * 2;
+ avail_width -= (border_width + horizontal_padding) * 2 + padding.left + padding.right;
for (children = menu_shell->priv->children; children; children = children->next)
{
}
else
{
- priv->tearoff_adjustment->upper = priv->requested_height;
- priv->tearoff_adjustment->page_size = allocation->height;
-
- if (priv->tearoff_adjustment->value + priv->tearoff_adjustment->page_size >
- priv->tearoff_adjustment->upper)
- {
- gint value;
- value = priv->tearoff_adjustment->upper - priv->tearoff_adjustment->page_size;
- if (value < 0)
- value = 0;
- gtk_menu_scroll_to (menu, value);
- }
-
- gtk_adjustment_changed (priv->tearoff_adjustment);
+ gtk_adjustment_configure (priv->tearoff_adjustment,
+ gtk_adjustment_get_value (priv->tearoff_adjustment),
+ 0,
+ priv->requested_height,
+ gtk_adjustment_get_step_increment (priv->tearoff_adjustment),
+ gtk_adjustment_get_page_increment (priv->tearoff_adjustment),
+ allocation->height);
if (!gtk_widget_get_visible (priv->tearoff_scrollbar))
{
GdkRectangle border;
GdkRectangle upper;
GdkRectangle lower;
- GdkWindow *window;
gint arrow_space;
- GtkStateFlags state;
GtkBorder menu_border;
menu = GTK_MENU (widget);
priv = menu->priv;
context = gtk_widget_get_style_context (widget);
- window = gtk_widget_get_window (widget);
- state = gtk_widget_get_state_flags (widget);
get_arrows_visible_area (menu, &border, &upper, &lower, &arrow_space);
get_menu_border (widget, &menu_border);
upper.width, upper.height);
gtk_render_arrow (context, cr, 0,
- upper.x + (upper.width - arrow_size) / 2,
- upper.y + menu_border.top + (arrow_space - arrow_size) / 2,
- arrow_size);
+ upper.x + (upper.width - arrow_size) / 2,
+ upper.y + menu_border.top + (arrow_space - arrow_size) / 2,
+ arrow_size);
gtk_style_context_restore (context);
}
gint *minimum_size,
gint *natural_size)
{
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GtkBorder padding, border;
GtkMenu *menu = GTK_MENU (widget);
GtkMenuPrivate *priv = menu->priv;
guint *min_heights, *nat_heights;
gtk_widget_style_get (widget, "vertical-padding", &vertical_padding, NULL);
border_width = gtk_container_get_border_width (GTK_CONTAINER (menu));
- min_height = nat_height = (border_width + vertical_padding + gtk_widget_get_style (widget)->ythickness) * 2;
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
+ gtk_style_context_get_padding (context, state, &padding);
+ gtk_style_context_get_border (context, state, &border);
+
+ min_height = nat_height = (border_width + vertical_padding) * 2 +
+ padding.left + padding.right + border.left + border.right;
n_heights =
calculate_line_heights (menu, for_size, &min_heights, &nat_heights);
guint vertical_padding;
gint win_x, win_y;
gint scroll_arrow_height;
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GtkBorder padding;
window = gtk_widget_get_window (widget);
width = gdk_window_get_width (window);
"arrow-placement", &arrow_placement,
NULL);
- border = gtk_container_get_border_width (GTK_CONTAINER (menu)) +
- gtk_widget_get_style (widget)->ythickness + vertical_padding;
+ border = gtk_container_get_border_width (GTK_CONTAINER (menu)) + vertical_padding;
+
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
+ gtk_style_context_get_padding (context, state, &padding);
gdk_window_get_position (window, &win_x, &win_y);
upper->x = win_x;
upper->y = win_y;
upper->width = width;
- upper->height = scroll_arrow_height + border;
+ upper->height = scroll_arrow_height + border + padding.top;
}
if (lower)
{
lower->x = win_x;
- lower->y = win_y + height - border - scroll_arrow_height;
+ lower->y = win_y + height - border - padding.bottom - scroll_arrow_height;
lower->width = width;
- lower->height = scroll_arrow_height + border;
+ lower->height = scroll_arrow_height + border + padding.bottom;
}
break;
upper->x = win_x;
upper->y = win_y;
upper->width = width / 2;
- upper->height = scroll_arrow_height + border;
+ upper->height = scroll_arrow_height + border + padding.top;
}
if (lower)
lower->x = win_x + width / 2;
lower->y = win_y;
lower->width = width / 2;
- lower->height = scroll_arrow_height + border;
+ lower->height = scroll_arrow_height + border + padding.bottom;
}
break;
upper->x = win_x;
upper->y = win_y + height - border - scroll_arrow_height;
upper->width = width / 2;
- upper->height = scroll_arrow_height + border;
+ upper->height = scroll_arrow_height + border + padding.top;
}
if (lower)
lower->x = win_x + width / 2;
lower->y = win_y + height - border - scroll_arrow_height;
lower->width = width / 2;
- lower->height = scroll_arrow_height + border;
+ lower->height = scroll_arrow_height + border + padding.bottom;
}
break;
}
gint submenu_top = 0;
gint submenu_bottom = 0;
gint width = 0;
- gint height = 0;
GtkWidget *event_widget;
GtkMenuPopdownData *popdown_data;
GdkWindow *window;
submenu_bottom = submenu_top + gdk_window_get_height (window);
width = gdk_window_get_width (gtk_widget_get_window (event_widget));
- height = gdk_window_get_height (gtk_widget_get_window (event_widget));
if (event->x >= 0 && event->x < width)
{
widget = GTK_WIDGET (menu);
- if (priv->tearoff_active &&
- priv->tearoff_adjustment &&
- (priv->tearoff_adjustment->value != offset))
- {
- priv->tearoff_adjustment->value =
- CLAMP (offset,
- 0, priv->tearoff_adjustment->upper - priv->tearoff_adjustment->page_size);
- gtk_adjustment_value_changed (priv->tearoff_adjustment);
- }
+ if (priv->tearoff_active && priv->tearoff_adjustment)
+ gtk_adjustment_set_value (priv->tearoff_adjustment, offset);
/* Move/resize the viewport according to arrows: */
gtk_widget_get_allocation (widget, &allocation);
if (!priv->heights || priv->heights_length < gtk_menu_get_n_rows (menu))
return FALSE;
- /* when we have a row with only invisible children, it's height will
+ /* when we have a row with only invisible children, its height will
* be zero, so there's no need to check WIDGET_VISIBLE here
*/
for (i = 0; i < item_top_attach; i++)
GtkMenuPrivate *priv = menu->priv;
GtkWidget *widget = GTK_WIDGET (menu_shell);
gint child_offset, child_height;
- gint width, height;
+ gint height;
gint y;
gint arrow_height;
gboolean last_child = 0;
* If not we need to scroll the menu so that it becomes fully
* visible.
*/
-
if (compute_child_offset (menu, menu_item,
&child_offset, &child_height, &last_child))
{
guint vertical_padding;
gboolean double_arrows;
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GtkBorder padding;
y = priv->scroll_offset;
- width = gdk_window_get_width (gtk_widget_get_window (widget));
height = gdk_window_get_height (gtk_widget_get_window (widget));
gtk_widget_style_get (widget,
double_arrows = get_double_arrows (menu);
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
+ gtk_style_context_get_padding (context, state, &padding);
+
height -= 2 * gtk_container_get_border_width (GTK_CONTAINER (menu)) +
- 2 * gtk_widget_get_style (widget)->ythickness +
+ padding.top + padding.bottom +
2 * vertical_padding;
if (child_offset < y)
{
GtkAllocation allocation;
GtkWidget *widget = GTK_WIDGET (menu);
GtkContainer *container = GTK_CONTAINER (menu);
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GtkBorder padding;
gint menu_height;
gtk_widget_get_allocation (widget, &allocation);
- menu_height = (allocation.height
- - 2 * (gtk_container_get_border_width (container)
- + gtk_widget_get_style (widget)->ythickness));
+
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
+ gtk_style_context_get_padding (context, state, &padding);
+
+ menu_height = (allocation.height -
+ (2 * gtk_container_get_border_width (container)) -
+ padding.top - padding.bottom);
if (!priv->tearoff_active)
{
GtkMenuPrivate *priv = menu->priv;
GtkAllocation allocation;
GtkWidget *widget = GTK_WIDGET (menu);
+ GtkStyleContext *context;
+ GtkStateFlags state;
+ GtkBorder padding, border;
gint height;
gtk_widget_get_allocation (widget, &allocation);
+ context = gtk_widget_get_style_context (widget);
+ state = gtk_widget_get_state_flags (widget);
+ gtk_style_context_get_padding (context, state, &padding);
+ gtk_style_context_get_border (context, state, &border);
+
height = allocation.height;
- height -= (gtk_container_get_border_width (GTK_CONTAINER (widget)) + gtk_widget_get_style (widget)->ythickness) * 2;
+ height -= (gtk_container_get_border_width (GTK_CONTAINER (widget)) * 2) +
+ padding.top + padding.bottom + border.top + border.bottom;
if (!priv->tearoff_active)
{