diff options
Diffstat (limited to 'gtk')
-rw-r--r-- | gtk/Makefile.am | 4 | ||||
-rw-r--r-- | gtk/gtk.h | 2 | ||||
-rw-r--r-- | gtk/gtk.symbols | 8 | ||||
-rw-r--r-- | gtk/gtksettings.c | 39 | ||||
-rw-r--r-- | gtk/gtktoolbar.c | 14 | ||||
-rw-r--r-- | gtk/gtktoolbutton.c | 73 | ||||
-rw-r--r-- | gtk/gtktoolitem.c | 106 | ||||
-rw-r--r-- | gtk/gtktoolitem.h | 5 | ||||
-rw-r--r-- | gtk/gtktoolitemgroup.c | 2442 | ||||
-rw-r--r-- | gtk/gtktoolitemgroup.h | 95 | ||||
-rw-r--r-- | gtk/gtktoolpalette.c | 1889 | ||||
-rw-r--r-- | gtk/gtktoolpalette.h | 140 | ||||
-rw-r--r-- | gtk/gtktoolpaletteprivate.h | 55 | ||||
-rw-r--r-- | gtk/gtktoolshell.c | 92 | ||||
-rw-r--r-- | gtk/gtktoolshell.h | 46 |
15 files changed, 4971 insertions, 39 deletions
diff --git a/gtk/Makefile.am b/gtk/Makefile.am index f10bb19eb6..4b1ceded26 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -310,6 +310,8 @@ gtk_public_h_sources = \ gtktoolbar.h \ gtktoolbutton.h \ gtktoolitem.h \ + gtktoolitemgroup.h \ + gtktoolpalette.h \ gtktoolshell.h \ gtktooltip.h \ gtktreednd.h \ @@ -585,6 +587,8 @@ gtk_base_c_sources = \ gtktoolbar.c \ gtktoolbutton.c \ gtktoolitem.c \ + gtktoolitemgroup.c \ + gtktoolpalette.c \ gtktoolshell.c \ gtktooltip.c \ gtktreedatalist.c \ @@ -187,6 +187,8 @@ #include <gtk/gtktoolbar.h> #include <gtk/gtktoolbutton.h> #include <gtk/gtktoolitem.h> +#include <gtk/gtktoolitemgroup.h> +#include <gtk/gtktoolpalette.h> #include <gtk/gtktoolshell.h> #include <gtk/gtktooltip.h> #include <gtk/gtktestutils.h> diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols index 4b39bded36..214832753c 100644 --- a/gtk/gtk.symbols +++ b/gtk/gtk.symbols @@ -4373,10 +4373,14 @@ gtk_toolbar_unset_style #if IN_HEADER(__GTK_TOOL_SHELL_H__) #if IN_FILE(__GTK_TOOL_SHELL_C__) gtk_tool_shell_get_type G_GNUC_CONST +gtk_tool_shell_get_ellipsize_mode gtk_tool_shell_get_icon_size gtk_tool_shell_get_orientation gtk_tool_shell_get_style gtk_tool_shell_get_relief_style +gtk_tool_shell_get_text_alignment +gtk_tool_shell_get_text_orientation +gtk_tool_shell_get_text_size_group gtk_tool_shell_rebuild_menu #endif #endif @@ -4403,6 +4407,7 @@ gtk_tool_button_set_use_underline #if IN_HEADER(__GTK_TOOL_ITEM_H__) #if IN_FILE(__GTK_TOOL_ITEM_C__) +gtk_tool_item_get_ellipsize_mode gtk_tool_item_get_expand gtk_tool_item_get_homogeneous gtk_tool_item_get_icon_size @@ -4410,6 +4415,9 @@ gtk_tool_item_get_is_important gtk_tool_item_get_orientation gtk_tool_item_get_proxy_menu_item gtk_tool_item_get_relief_style +gtk_tool_item_get_text_alignment +gtk_tool_item_get_text_orientation +gtk_tool_item_get_text_size_group gtk_tool_item_get_toolbar_style gtk_tool_item_get_type G_GNUC_CONST gtk_tool_item_get_use_drag_window diff --git a/gtk/gtksettings.c b/gtk/gtksettings.c index 93b9fc4f0d..96ee7f28aa 100644 --- a/gtk/gtksettings.c +++ b/gtk/gtksettings.c @@ -122,7 +122,9 @@ enum { PROP_SOUND_THEME_NAME, PROP_ENABLE_INPUT_FEEDBACK_SOUNDS, PROP_ENABLE_EVENT_SOUNDS, - PROP_ENABLE_TOOLTIPS + PROP_ENABLE_TOOLTIPS, + PROP_TOOLBAR_STYLE, + PROP_TOOLBAR_ICON_SIZE }; @@ -967,6 +969,40 @@ gtk_settings_class_init (GtkSettingsClass *class) GTK_PARAM_READWRITE), NULL); g_assert (result == PROP_ENABLE_TOOLTIPS); + + /** + * GtkSettings:toolbar-style: + * + * The size of icons in default toolbars. + * + * Since: 2.18 + */ + result = settings_install_property_parser (class, + g_param_spec_enum ("gtk-toolbar-style", + P_("Toolbar style"), + P_("Whether default toolbars have text only, text and icons, icons only, etc."), + GTK_TYPE_TOOLBAR_STYLE, + GTK_TOOLBAR_BOTH, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_TOOLBAR_STYLE); + + /** + * GtkSettings:toolbar-icon-size: + * + * The size of icons in default toolbars. + * + * Since: 2.18 + */ + result = settings_install_property_parser (class, + g_param_spec_enum ("gtk-toolbar-icon-size", + P_("Toolbar Icon Size"), + P_("The size of icons in default toolbars."), + GTK_TYPE_ICON_SIZE, + GTK_ICON_SIZE_LARGE_TOOLBAR, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_TOOLBAR_ICON_SIZE); } static void @@ -1316,6 +1352,7 @@ settings_install_property_parser (GtkSettingsClass *class, case G_TYPE_FLOAT: case G_TYPE_DOUBLE: case G_TYPE_STRING: + case G_TYPE_ENUM: break; case G_TYPE_BOXED: if (strcmp (g_param_spec_get_name (pspec), "color-hash") == 0) diff --git a/gtk/gtktoolbar.c b/gtk/gtktoolbar.c index 6b1f05323c..70126ca2ab 100644 --- a/gtk/gtktoolbar.c +++ b/gtk/gtktoolbar.c @@ -625,20 +625,6 @@ gtk_toolbar_class_init (GtkToolbarClass *klass) GTK_TYPE_SHADOW_TYPE, GTK_SHADOW_OUT, GTK_PARAM_READABLE)); - - gtk_settings_install_property (g_param_spec_enum ("gtk-toolbar-style", - P_("Toolbar style"), - P_("Whether default toolbars have text only, text and icons, icons only, etc."), - GTK_TYPE_TOOLBAR_STYLE, - DEFAULT_TOOLBAR_STYLE, - GTK_PARAM_READWRITE)); - - gtk_settings_install_property (g_param_spec_enum ("gtk-toolbar-icon-size", - P_("Toolbar icon size"), - P_("Size of icons in default toolbars"), - GTK_TYPE_ICON_SIZE, - DEFAULT_ICON_SIZE, - GTK_PARAM_READWRITE)); binding_set = gtk_binding_set_by_class (klass); diff --git a/gtk/gtktoolbutton.c b/gtk/gtktoolbutton.c index 5975a51689..6eae655931 100644 --- a/gtk/gtktoolbutton.c +++ b/gtk/gtktoolbutton.c @@ -96,7 +96,9 @@ struct _GtkToolButtonPrivate gchar *label_text; GtkWidget *label_widget; GtkWidget *icon_widget; - + + GtkSizeGroup *text_size_group; + guint use_underline : 1; guint contents_invalid : 1; }; @@ -318,6 +320,8 @@ gtk_tool_button_construct_contents (GtkToolItem *tool_item) GtkIconSize icon_size; GtkWidget *box = NULL; guint icon_spacing; + GtkOrientation text_orientation = GTK_ORIENTATION_HORIZONTAL; + GtkSizeGroup *size_group = NULL; button->priv->contents_invalid = FALSE; @@ -355,7 +359,8 @@ gtk_tool_button_construct_contents (GtkToolItem *tool_item) if (style == GTK_TOOLBAR_BOTH_HORIZ && (gtk_tool_item_get_is_important (GTK_TOOL_ITEM (button)) || - gtk_tool_item_get_orientation (GTK_TOOL_ITEM (button)) == GTK_ORIENTATION_VERTICAL)) + gtk_tool_item_get_orientation (GTK_TOOL_ITEM (button)) == GTK_ORIENTATION_VERTICAL || + gtk_tool_item_get_text_orientation (GTK_TOOL_ITEM (button)) == GTK_ORIENTATION_VERTICAL)) { need_label = TRUE; } @@ -415,6 +420,28 @@ gtk_tool_button_construct_contents (GtkToolItem *tool_item) gtk_widget_show (label); } + + gtk_label_set_ellipsize (GTK_LABEL (label), + gtk_tool_item_get_ellipsize_mode (GTK_TOOL_ITEM (button))); + text_orientation = gtk_tool_item_get_text_orientation (GTK_TOOL_ITEM (button)); + if (text_orientation == GTK_ORIENTATION_HORIZONTAL) + { + gtk_label_set_angle (GTK_LABEL (label), 0); + gtk_misc_set_alignment (GTK_MISC (label), + gtk_tool_item_get_text_alignment (GTK_TOOL_ITEM (button)), + 0.5); + } + else + { + gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_NONE); + if (gtk_widget_get_direction (GTK_WIDGET (tool_item)) == GTK_TEXT_DIR_RTL) + gtk_label_set_angle (GTK_LABEL (label), -90); + else + gtk_label_set_angle (GTK_LABEL (label), 90); + gtk_misc_set_alignment (GTK_MISC (label), + 0.5, + 1 - gtk_tool_item_get_text_alignment (GTK_TOOL_ITEM (button))); + } } icon_size = gtk_tool_item_get_icon_size (GTK_TOOL_ITEM (button)); @@ -442,6 +469,22 @@ gtk_tool_button_construct_contents (GtkToolItem *tool_item) icon = gtk_image_new_from_icon_name (button->priv->icon_name, icon_size); gtk_widget_show (icon); } + + if (icon && text_orientation == GTK_ORIENTATION_HORIZONTAL) + gtk_misc_set_alignment (GTK_MISC (icon), + 1.0 - gtk_tool_item_get_text_alignment (GTK_TOOL_ITEM (button)), + 0.5); + else if (icon) + gtk_misc_set_alignment (GTK_MISC (icon), + 0.5, + gtk_tool_item_get_text_alignment (GTK_TOOL_ITEM (button))); + + if (icon) + { + size_group = gtk_tool_item_get_text_size_group (GTK_TOOL_ITEM (button)); + if (size_group != NULL) + gtk_size_group_add_widget (size_group, icon); + } } switch (style) @@ -452,7 +495,10 @@ gtk_tool_button_construct_contents (GtkToolItem *tool_item) break; case GTK_TOOLBAR_BOTH: - box = gtk_vbox_new (FALSE, icon_spacing); + if (text_orientation == GTK_ORIENTATION_HORIZONTAL) + box = gtk_vbox_new (FALSE, icon_spacing); + else + box = gtk_hbox_new (FALSE, icon_spacing); if (icon) gtk_box_pack_start (GTK_BOX (box), icon, TRUE, TRUE, 0); gtk_box_pack_end (GTK_BOX (box), label, FALSE, TRUE, 0); @@ -460,11 +506,22 @@ gtk_tool_button_construct_contents (GtkToolItem *tool_item) break; case GTK_TOOLBAR_BOTH_HORIZ: - box = gtk_hbox_new (FALSE, icon_spacing); - if (icon) - gtk_box_pack_start (GTK_BOX (box), icon, label? FALSE : TRUE, TRUE, 0); - if (label) - gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0); + if (text_orientation == GTK_ORIENTATION_HORIZONTAL) + { + box = gtk_hbox_new (FALSE, icon_spacing); + if (icon) + gtk_box_pack_start (GTK_BOX (box), icon, label? FALSE : TRUE, TRUE, 0); + if (label) + gtk_box_pack_end (GTK_BOX (box), label, TRUE, TRUE, 0); + } + else + { + box = gtk_vbox_new (FALSE, icon_spacing); + if (icon) + gtk_box_pack_end (GTK_BOX (box), icon, label ? FALSE : TRUE, TRUE, 0); + if (label) + gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0); + } gtk_container_add (GTK_CONTAINER (button->priv->button), box); break; diff --git a/gtk/gtktoolitem.c b/gtk/gtktoolitem.c index 7393f7e979..8f7406a9a9 100644 --- a/gtk/gtktoolitem.c +++ b/gtk/gtktoolitem.c @@ -704,6 +704,33 @@ gtk_tool_item_new (void) } /** + * gtk_tool_item_get_ellipsize_mode: + * @tool_item: a #GtkToolItem: + * + * Returns the ellipsize mode used for @tool_item. Custom subclasses of + * #GtkToolItem should call this function to find out how text should + * be ellipsized. + * + * Return value: a #PangoEllipsizeMode indicating how text in @tool_item + * should be ellipsized. + * + * Since: 2.14 + **/ +PangoEllipsizeMode +gtk_tool_item_get_ellipsize_mode (GtkToolItem *tool_item) +{ + GtkWidget *parent; + + g_return_val_if_fail (GTK_IS_TOOL_ITEM (tool_item), GTK_ORIENTATION_HORIZONTAL); + + parent = GTK_WIDGET (tool_item)->parent; + if (!parent || !GTK_IS_TOOL_SHELL (parent)) + return PANGO_ELLIPSIZE_NONE; + + return gtk_tool_shell_get_ellipsize_mode (GTK_TOOL_SHELL (parent)); +} + +/** * gtk_tool_item_get_icon_size: * @tool_item: a #GtkToolItem * @@ -828,6 +855,85 @@ gtk_tool_item_get_relief_style (GtkToolItem *tool_item) } /** + * gtk_tool_item_get_text_alignment: + * @tool_item: a #GtkToolItem: + * + * Returns the text alignment used for @tool_item. Custom subclasses of + * #GtkToolItem should call this function to find out how text should + * be aligned. + * + * Return value: a #gfloat indicating the horizontal text alignment + * used for @tool_item + * + * Since: 2.14 + **/ +gfloat +gtk_tool_item_get_text_alignment (GtkToolItem *tool_item) +{ + GtkWidget *parent; + + g_return_val_if_fail (GTK_IS_TOOL_ITEM (tool_item), GTK_ORIENTATION_HORIZONTAL); + + parent = GTK_WIDGET (tool_item)->parent; + if (!parent || !GTK_IS_TOOL_SHELL (parent)) + return 0.5; + + return gtk_tool_shell_get_text_alignment (GTK_TOOL_SHELL (parent)); +} + +/** + * gtk_tool_item_get_text_orientation: + * @tool_item: a #GtkToolItem: + * + * Returns the text orientation used for @tool_item. Custom subclasses of + * #GtkToolItem should call this function to find out how text should + * be orientated. + * + * Return value: a #GtkOrientation indicating the text orientation + * used for @tool_item + * + * Since: 2.14 + **/ +GtkOrientation +gtk_tool_item_get_text_orientation (GtkToolItem *tool_item) +{ + GtkWidget *parent; + + g_return_val_if_fail (GTK_IS_TOOL_ITEM (tool_item), GTK_ORIENTATION_HORIZONTAL); + + parent = GTK_WIDGET (tool_item)->parent; + if (!parent || !GTK_IS_TOOL_SHELL (parent)) + return GTK_ORIENTATION_HORIZONTAL; + + return gtk_tool_shell_get_text_orientation (GTK_TOOL_SHELL (parent)); +} + +/** + * gtk_tool_item_get_text_size_group: + * @tool_item: a #GtkToolItem: + * + * Returns the size group used for labels in @tool_item. Custom subclasses of + * #GtkToolItem should call this function and use the size group for labels. + * + * Return value: a #GtkSizeGroup + * + * Since: 2.14 + **/ +GtkSizeGroup * +gtk_tool_item_get_text_size_group (GtkToolItem *tool_item) +{ + GtkWidget *parent; + + g_return_val_if_fail (GTK_IS_TOOL_ITEM (tool_item), GTK_ORIENTATION_HORIZONTAL); + + parent = GTK_WIDGET (tool_item)->parent; + if (!parent || !GTK_IS_TOOL_SHELL (parent)) + return NULL; + + return gtk_tool_shell_get_text_size_group (GTK_TOOL_SHELL (parent)); +} + +/** * gtk_tool_item_set_expand: * @tool_item: a #GtkToolItem * @expand: Whether @tool_item is allocated extra space diff --git a/gtk/gtktoolitem.h b/gtk/gtktoolitem.h index a81b06bb80..7f8d9ada18 100644 --- a/gtk/gtktoolitem.h +++ b/gtk/gtktoolitem.h @@ -30,6 +30,7 @@ #include <gtk/gtkbin.h> #include <gtk/gtktooltips.h> #include <gtk/gtkmenuitem.h> +#include <gtk/gtksizegroup.h> G_BEGIN_DECLS @@ -113,10 +114,14 @@ gboolean gtk_tool_item_get_is_important (GtkToolItem *tool_item); void gtk_tool_item_set_is_important (GtkToolItem *tool_item, gboolean is_important); +PangoEllipsizeMode gtk_tool_item_get_ellipsize_mode (GtkToolItem *tool_item); GtkIconSize gtk_tool_item_get_icon_size (GtkToolItem *tool_item); GtkOrientation gtk_tool_item_get_orientation (GtkToolItem *tool_item); GtkToolbarStyle gtk_tool_item_get_toolbar_style (GtkToolItem *tool_item); GtkReliefStyle gtk_tool_item_get_relief_style (GtkToolItem *tool_item); +gfloat gtk_tool_item_get_text_alignment (GtkToolItem *tool_item); +GtkOrientation gtk_tool_item_get_text_orientation (GtkToolItem *tool_item); +GtkSizeGroup * gtk_tool_item_get_text_size_group (GtkToolItem *tool_item); GtkWidget * gtk_tool_item_retrieve_proxy_menu_item (GtkToolItem *tool_item); GtkWidget * gtk_tool_item_get_proxy_menu_item (GtkToolItem *tool_item, diff --git a/gtk/gtktoolitemgroup.c b/gtk/gtktoolitemgroup.c new file mode 100644 index 0000000000..3dffddf717 --- /dev/null +++ b/gtk/gtktoolitemgroup.c @@ -0,0 +1,2442 @@ +/* GtkToolPalette -- A tool palette with categories and DnD support + * Copyright (C) 2008 Openismus GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Mathias Hasselmann + * Jan Arne Petersen + */ + +#include "gtktoolpaletteprivate.h" + +#include <gtk/gtk.h> +#include <math.h> +#include <string.h> + +#define P_(msgid) (msgid) + +#define ANIMATION_TIMEOUT 50 +#define ANIMATION_DURATION (ANIMATION_TIMEOUT * 4) +#define DEFAULT_ANIMATION_STATE TRUE +#define DEFAULT_EXPANDER_SIZE 16 +#define DEFAULT_HEADER_SPACING 2 + +#define DEFAULT_LABEL NULL +#define DEFAULT_COLLAPSED FALSE +#define DEFAULT_ELLIPSIZE PANGO_ELLIPSIZE_NONE + +/** + * SECTION:GtkToolItemGroup + * @short_description: A sub container used in a tool palette + * @include: gtktoolitemgroup.h + * + * An #GtkToolItemGroup is used together with #GtkToolPalette to add #GtkToolItem<!-- -->s to a palette like container + * with different categories and drag and drop support. + * + * Since: 2.18 + */ + +enum +{ + PROP_NONE, + PROP_LABEL, + PROP_LABEL_WIDGET, + PROP_COLLAPSED, + PROP_ELLIPSIZE, + PROP_RELIEF +}; + +enum +{ + CHILD_PROP_NONE, + CHILD_PROP_HOMOGENEOUS, + CHILD_PROP_EXPAND, + CHILD_PROP_FILL, + CHILD_PROP_NEW_ROW, + CHILD_PROP_POSITION, +}; + +typedef struct _GtkToolItemGroupChild GtkToolItemGroupChild; + +struct _GtkToolItemGroupPrivate +{ + GtkWidget *header; + GtkWidget *label_widget; + + GList *children; + + gboolean animation; + gint64 animation_start; + GSource *animation_timeout; + GtkExpanderStyle expander_style; + gint expander_size; + gint header_spacing; + PangoEllipsizeMode ellipsize; + + gulong focus_set_id; + GtkWidget *toplevel; + + GtkSettings *settings; + gulong settings_connection; + + guint collapsed : 1; +}; + +struct _GtkToolItemGroupChild +{ + GtkToolItem *item; + + guint homogeneous : 1; + guint expand : 1; + guint fill : 1; + guint new_row : 1; +}; + +static void gtk_tool_item_group_tool_shell_init (GtkToolShellIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GtkToolItemGroup, gtk_tool_item_group, GTK_TYPE_CONTAINER, +G_IMPLEMENT_INTERFACE (GTK_TYPE_TOOL_SHELL, gtk_tool_item_group_tool_shell_init)); + +static GtkWidget* +gtk_tool_item_group_get_alignment (GtkToolItemGroup *group) +{ + return gtk_bin_get_child (GTK_BIN (group->priv->header)); +} + +static GtkOrientation +gtk_tool_item_group_get_orientation (GtkToolShell *shell) +{ + GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell)); + + if (GTK_IS_TOOL_PALETTE (parent)) + return gtk_orientable_get_orientation (GTK_ORIENTABLE (parent)); + + return GTK_ORIENTATION_VERTICAL; +} + +static GtkToolbarStyle +gtk_tool_item_group_get_style (GtkToolShell *shell) +{ + GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell)); + + if (GTK_IS_TOOL_PALETTE (parent)) + return gtk_tool_palette_get_style (GTK_TOOL_PALETTE (parent)); + + return GTK_TOOLBAR_ICONS; +} + +static GtkIconSize +gtk_tool_item_group_get_icon_size (GtkToolShell *shell) +{ + GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell)); + + if (GTK_IS_TOOL_PALETTE (parent)) + return gtk_tool_palette_get_icon_size (GTK_TOOL_PALETTE (parent)); + + return GTK_ICON_SIZE_SMALL_TOOLBAR; +} + +static PangoEllipsizeMode +gtk_tool_item_group_get_ellipsize_mode (GtkToolShell *shell) +{ + return GTK_TOOL_ITEM_GROUP (shell)->priv->ellipsize; +} + +static gfloat +gtk_tool_item_group_get_text_alignment (GtkToolShell *shell) +{ + if (GTK_TOOLBAR_TEXT == gtk_tool_item_group_get_style (shell) || + GTK_TOOLBAR_BOTH_HORIZ == gtk_tool_item_group_get_style (shell)) + return 0.0; + + return 0.5; +} + +static GtkOrientation +gtk_tool_item_group_get_text_orientation (GtkToolShell *shell) +{ + GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell)); + + if (GTK_IS_TOOL_PALETTE (parent)) + { + GtkOrientation orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (parent)); + if (GTK_ORIENTATION_HORIZONTAL == orientation && + (GTK_TOOLBAR_TEXT == gtk_tool_item_group_get_style (shell)/* || + GTK_TOOLBAR_BOTH_HORIZ == gtk_tool_item_group_get_style (shell)*/)) + return GTK_ORIENTATION_VERTICAL; + } + + return GTK_ORIENTATION_HORIZONTAL; +} + +static GtkSizeGroup * +gtk_tool_item_group_get_text_size_group (GtkToolShell *shell) +{ + GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (shell)); + + if (GTK_IS_TOOL_PALETTE (parent)) + return _gtk_tool_palette_get_size_group (GTK_TOOL_PALETTE (parent)); + + return NULL; +} + +static void +animation_change_notify (GtkToolItemGroup *group) +{ + GtkSettings *settings = group->priv->settings; + gboolean animation; + + if (settings) + g_object_get (settings, + "gtk-enable-animations", &animation, + NULL); + else + animation = DEFAULT_ANIMATION_STATE; + + group->priv->animation = animation; +} + +static void +gtk_tool_item_group_settings_change_notify (GtkSettings *settings, + const GParamSpec *pspec, + GtkToolItemGroup *group) +{ + if (! strcmp (pspec->name, "gtk-enable-animations")) + { + animation_change_notify (group); + } +} + +static void +gtk_tool_item_group_screen_changed (GtkWidget *widget, + GdkScreen *previous_screen) +{ + GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (widget); + GtkToolItemGroupPrivate* priv = group->priv; + GtkSettings *old_settings = priv->settings; + GtkSettings *settings; + + if (gtk_widget_has_screen (GTK_WIDGET (group))) + settings = gtk_widget_get_settings (GTK_WIDGET (group)); + else + settings = NULL; + + if (settings == old_settings) + return; + + if (old_settings) + { + g_signal_handler_disconnect (old_settings, priv->settings_connection); + g_object_unref (old_settings); + } + + if (settings) + { + priv->settings_connection = + g_signal_connect (settings, "notify", + G_CALLBACK (gtk_tool_item_group_settings_change_notify), + group); + priv->settings = g_object_ref (settings); + } + else + priv->settings = NULL; + + animation_change_notify (group); +} + +static void +gtk_tool_item_group_tool_shell_init (GtkToolShellIface *iface) +{ + iface->get_icon_size = gtk_tool_item_group_get_icon_size; + iface->get_orientation = gtk_tool_item_group_get_orientation; + iface->get_style = gtk_tool_item_group_get_style; + iface->get_text_alignment = gtk_tool_item_group_get_text_alignment; + iface->get_text_orientation = gtk_tool_item_group_get_text_orientation; + iface->get_text_size_group = gtk_tool_item_group_get_text_size_group; + iface->get_ellipsize_mode = gtk_tool_item_group_get_ellipsize_mode; +} + +static gboolean +gtk_tool_item_group_header_expose_event_cb (GtkWidget *widget, + GdkEventExpose *event, + gpointer data) +{ + GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (data); + GtkToolItemGroupPrivate* priv = group->priv; + GtkExpanderStyle expander_style; + GtkOrientation orientation; + gint x, y; + GtkTextDirection direction; + + orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group)); + expander_style = priv->expander_style; + direction = gtk_widget_get_direction (widget); + + if (GTK_ORIENTATION_VERTICAL == orientation) + { + if (GTK_TEXT_DIR_RTL == direction) + x = widget->allocation.x + widget->allocation.width - priv->expander_size / 2; + else + x = widget->allocation.x + priv->expander_size / 2; + y = widget->allocation.y + widget->allocation.height / 2; + } + else + { + x = widget->allocation.x + widget->allocation.width / 2; + y = widget->allocation.y + priv->expander_size / 2; + + /* Unfortunatly gtk_paint_expander() doesn't support rotated drawing + * modes. Luckily the following shady arithmetics produce the desired + * result. */ + expander_style = GTK_EXPANDER_EXPANDED - expander_style; /* XXX */ + } + + gtk_paint_expander (widget->style, widget->window, + priv->header->state, + &event->area, GTK_WIDGET (group), + "tool-palette-header", x, y, + expander_style); + + return FALSE; +} + +static void +gtk_tool_item_group_header_size_request_cb (GtkWidget *widget G_GNUC_UNUSED, + GtkRequisition *requisition, + gpointer data) +{ + GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (data); + requisition->height = MAX (requisition->height, group->priv->expander_size); +} + +static void +gtk_tool_item_group_header_clicked_cb (GtkButton *button G_GNUC_UNUSED, + gpointer data) +{ + GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (data); + GtkToolItemGroupPrivate* priv = group->priv; + GtkWidget *parent = gtk_widget_get_parent (data); + + if (priv->collapsed || + !GTK_IS_TOOL_PALETTE (parent) || + !gtk_tool_palette_get_exclusive (GTK_TOOL_PALETTE (parent), data)) + gtk_tool_item_group_set_collapsed (group, !priv->collapsed); +} + +static void +gtk_tool_item_group_header_adjust_style (GtkToolItemGroup *group) +{ + GtkWidget *alignment = gtk_tool_item_group_get_alignment (group); + GtkWidget *label_widget = gtk_bin_get_child (GTK_BIN (alignment)); + GtkWidget *widget = GTK_WIDGET (group); + GtkToolItemGroupPrivate* priv = group->priv; + gint dx = 0, dy = 0; + GtkTextDirection direction = gtk_widget_get_direction (widget); + + gtk_widget_style_get (widget, + "header-spacing", &(priv->header_spacing), + "expander-size", &(priv->expander_size), + NULL); + + switch (gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group))) + { + case GTK_ORIENTATION_HORIZONTAL: + dy = priv->header_spacing + priv->expander_size; + + if (GTK_IS_LABEL (label_widget)) + { + gtk_label_set_ellipsize (GTK_LABEL (label_widget), PANGO_ELLIPSIZE_NONE); + if (GTK_TEXT_DIR_RTL == direction) + gtk_label_set_angle (GTK_LABEL (label_widget), -90); + else + gtk_label_set_angle (GTK_LABEL (label_widget), 90); + } + break; + + case GTK_ORIENTATION_VERTICAL: + dx = priv->header_spacing + priv->expander_size; + + if (GTK_IS_LABEL (label_widget)) + { + gtk_label_set_ellipsize (GTK_LABEL (label_widget), priv->ellipsize); + gtk_label_set_angle (GTK_LABEL (label_widget), 0); + } + break; + } + + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), dy, 0, dx, 0); +} + +static void +gtk_tool_item_group_init (GtkToolItemGroup *group) +{ + GtkWidget *alignment; + GtkToolItemGroupPrivate* priv; + + gtk_widget_set_redraw_on_allocate (GTK_WIDGET (group), FALSE); + + group->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (group, + GTK_TYPE_TOOL_ITEM_GROUP, + GtkToolItemGroupPrivate); + + priv->children = NULL; + priv->header_spacing = DEFAULT_HEADER_SPACING; + priv->expander_size = DEFAULT_EXPANDER_SIZE; + priv->expander_style = GTK_EXPANDER_EXPANDED; + + priv->label_widget = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (priv->label_widget), 0.0, 0.5); + alignment = gtk_alignment_new (0.5, 0.5, 1.0, 1.0); + gtk_container_add (GTK_CONTAINER (alignment), priv->label_widget); + gtk_widget_show_all (alignment); + + gtk_widget_push_composite_child (); + priv->header = gtk_button_new (); + gtk_widget_set_composite_name (priv->header, "header"); + gtk_widget_pop_composite_child (); + + g_object_ref_sink (priv->header); + gtk_button_set_focus_on_click (GTK_BUTTON (priv->header), FALSE); + gtk_container_add (GTK_CONTAINER (priv->header), alignment); + gtk_widget_set_parent (priv->header, GTK_WIDGET (group)); + + gtk_tool_item_group_header_adjust_style (group); + + g_signal_connect_after (alignment, "expose-event", + G_CALLBACK (gtk_tool_item_group_header_expose_event_cb), + group); + g_signal_connect_after (alignment, "size-request", + G_CALLBACK (gtk_tool_item_group_header_size_request_cb), + group); + + g_signal_connect (priv->header, "clicked", + G_CALLBACK (gtk_tool_item_group_header_clicked_cb), + group); +} + +static void +gtk_tool_item_group_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (object); + + switch (prop_id) + { + case PROP_LABEL: + gtk_tool_item_group_set_label (group, g_value_get_string (value)); + break; + + case PROP_LABEL_WIDGET: + gtk_tool_item_group_set_label_widget (group, g_value_get_object (value)); + + case PROP_COLLAPSED: + gtk_tool_item_group_set_collapsed (group, g_value_get_boolean (value)); + break; + + case PROP_ELLIPSIZE: + gtk_tool_item_group_set_ellipsize (group, g_value_get_enum (value)); + break; + case PROP_RELIEF: + gtk_tool_item_group_set_header_relief (group, g_value_get_enum(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_tool_item_group_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (object); + + switch (prop_id) + { + case PROP_LABEL: + g_value_set_string (value, gtk_tool_item_group_get_label (group)); + break; + + case PROP_LABEL_WIDGET: + g_value_set_object (value, + gtk_tool_item_group_get_label_widget (group)); + break; + + case PROP_COLLAPSED: + g_value_set_boolean (value, gtk_tool_item_group_get_collapsed (group)); + break; + + case PROP_ELLIPSIZE: + g_value_set_enum (value, gtk_tool_item_group_get_ellipsize (group)); + break; + + case PROP_RELIEF: + g_value_set_enum (value, gtk_tool_item_group_get_header_relief (group)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_tool_item_group_finalize (GObject *object) +{ + GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (object); + + if (group->priv->children) + { + g_list_free (group->priv->children); + group->priv->children = NULL; + } + + G_OBJECT_CLASS (gtk_tool_item_group_parent_class)->finalize (object); +} + +static void +gtk_tool_item_group_dispose (GObject *object) +{ + GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (object); + GtkToolItemGroupPrivate* priv = group->priv; + + if (priv->toplevel) + { + /* disconnect focus tracking handler */ + g_signal_handler_disconnect (priv->toplevel, + priv->focus_set_id); + + priv->focus_set_id = 0; + priv->toplevel = NULL; + } + + G_OBJECT_CLASS (gtk_tool_item_group_parent_class)->dispose (object); +} + +static void +gtk_tool_item_group_get_item_size (GtkToolItemGroup *group, + GtkRequisition *item_size, + gboolean homogeneous_only, + gint *requested_rows) +{ + GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (group)); + + if (GTK_IS_TOOL_PALETTE (parent)) + _gtk_tool_palette_get_item_size (GTK_TOOL_PALETTE (parent), item_size, homogeneous_only, requested_rows); + else + _gtk_tool_item_group_item_size_request (group, item_size, homogeneous_only, requested_rows); +} + +static void +gtk_tool_item_group_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + const gint border_width = GTK_CONTAINER (widget)->border_width; + GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (widget); + GtkToolItemGroupPrivate* priv = group->priv; + GtkOrientation orientation; + GtkRequisition item_size; + gint requested_rows; + + if (priv->children && gtk_tool_item_group_get_label (group)) + { + gtk_widget_size_request (priv->header, requisition); + gtk_widget_show (priv->header); + } + else + { + requisition->width = requisition->height = 0; + gtk_widget_hide (priv->header); + } + + gtk_tool_item_group_get_item_size (group, &item_size, FALSE, &requested_rows); + + orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group)); + + if (GTK_ORIENTATION_VERTICAL == orientation) + requisition->width = MAX (requisition->width, item_size.width); + else + requisition->height = MAX (requisition->height, item_size.height * requested_rows); + + requisition->width += border_width * 2; + requisition->height += border_width * 2; +} + +static gboolean +gtk_tool_item_group_is_item_visible (GtkToolItemGroup *group, + GtkToolItemGroupChild *child) +{ + GtkToolbarStyle style; + GtkOrientation orientation; + + orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group)); + style = gtk_tool_shell_get_style (GTK_TOOL_SHELL (group)); + + /* horizontal tool palettes with text style support only homogeneous items */ + if (!child->homogeneous && + GTK_ORIENTATION_HORIZONTAL == orientation && + GTK_TOOLBAR_TEXT == style) + return FALSE; + + return + (GTK_WIDGET_VISIBLE (child->item)) && + (GTK_ORIENTATION_VERTICAL == orientation ? + gtk_tool_item_get_visible_vertical (child->item) : + gtk_tool_item_get_visible_horizontal (child->item)); +} + +static inline unsigned +udiv (unsigned x, + unsigned y) +{ + return (x + y - 1) / y; +} + +static void +gtk_tool_item_group_real_size_query (GtkWidget *widget, + GtkAllocation *allocation, + GtkRequisition *inquery) +{ + const gint border_width = GTK_CONTAINER (widget)->border_width; + GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (widget); + GtkToolItemGroupPrivate* priv = group->priv; + + GtkRequisition item_size; + GtkAllocation item_area; + + GtkOrientation orientation; + GtkToolbarStyle style; + + gint min_rows; + + orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group)); + style = gtk_tool_shell_get_style (GTK_TOOL_SHELL (group)); + + /* figure out the size of homogeneous items */ + gtk_tool_item_group_get_item_size (group, &item_size, TRUE, &min_rows); + + if (GTK_ORIENTATION_VERTICAL == orientation) + item_size.width = MIN (item_size.width, allocation->width); + else + item_size.height = MIN (item_size.height, allocation->height); + + item_area.width = 0; + item_area.height = 0; + + /* figure out the required columns (n_columns) and rows (n_rows) to place all items */ + if (!priv->collapsed || !priv->animation || priv->animation_timeout) + { + guint n_columns; + gint n_rows; + GList *it; + + if (GTK_ORIENTATION_VERTICAL == orientation) + { + gboolean new_row = FALSE; + gint row = -1; + guint col = 0; + + item_area.width = allocation->width - 2 * border_width; + n_columns = MAX (item_area.width / item_size.width, 1); + + /* calculate required rows for n_columns columns */ + for (it = priv->children; it != NULL; it = it->next) + { + GtkToolItemGroupChild *child = it->data; + + if (!gtk_tool_item_group_is_item_visible (group, child)) + continue; + + if (new_row || child->new_row) + { + new_row = FALSE; + row++; + col = 0; + } + + if (child->expand) + new_row = TRUE; + + if (child->homogeneous) + { + col++; + if (col >= n_columns) + new_row = TRUE; + } + else + { + GtkRequisition req = {0, 0}; + guint width; + + gtk_widget_size_request (GTK_WIDGET (child->item), &req); + + width = udiv (req.width, item_size.width); + col += width; + + if (col > n_columns) + row++; + + col = width; + + if (col >= n_columns) + new_row = TRUE; + } + } + n_rows = row + 2; + } + else + { + guint *row_min_width; + gint row = -1; + gboolean new_row = TRUE; + guint col = 0, min_col, max_col = 0, all_items = 0; + gint i; + + item_area.height = allocation->height - 2 * border_width; + n_rows = MAX (item_area.height / item_size.height, min_rows); + + row_min_width = g_new0 (guint, n_rows); + + /* calculate minimal and maximal required cols and minimal required rows */ + for (it = priv->children; it != NULL; it = it->next) + { + GtkToolItemGroupChild *child = it->data; + + if (!gtk_tool_item_group_is_item_visible (group, child)) + continue; + + if (new_row || child->new_row) + { + new_row = FALSE; + row++; + col = 0; + row_min_width[row] = 1; + } + + if (child->expand) + new_row = TRUE; + + if (child->homogeneous) + { + col++; + all_items++; + } + else + { + GtkRequisition req = {0, 0}; + guint width; + + gtk_widget_size_request (GTK_WIDGET (child->item), &req); + + width = udiv (req.width, item_size.width); + + col += width; + all_items += width; + + row_min_width[row] = MAX (row_min_width[row], width); + } + + max_col = MAX (max_col, col); + } + + /* calculate minimal required cols */ + min_col = udiv (all_items, n_rows); + + for (i = 0; i <= row; i++) + { + min_col = MAX (min_col, row_min_width[i]); + } + + /* simple linear search for minimal required columns for the given maximal number of rows (n_rows) */ + for (n_columns = min_col; n_columns < max_col; n_columns ++) + { + new_row = TRUE; + row = -1; + /* calculate required rows for n_columns columns */ + for (it = priv->children; it != NULL; it = it->next) + { + GtkToolItemGroupChild *child = it->data; + + if (!gtk_tool_item_group_is_item_visible (group, child)) + continue; + + if (new_row || child->new_row) + { + new_row = FALSE; + row++; + col = 0; + } + + if (child->expand) + new_row = TRUE; + + if (child->homogeneous) + { + col++; + if (col >= n_columns) + new_row = TRUE; + } + else + { + GtkRequisition req = {0, 0}; + guint width; + + gtk_widget_size_request (GTK_WIDGET (child->item), &req); + + width = udiv (req.width, item_size.width); + col += width; + + if (col > n_columns) + row++; + + col = width; + + if (col >= n_columns) + new_row = TRUE; + } + } + + if (row < n_rows) + break; + } + } + + item_area.width = item_size.width * n_columns; + item_area.height = item_size.height * n_rows; + } + + inquery->width = 0; + inquery->height = 0; + + /* figure out header widget size */ + if (GTK_WIDGET_VISIBLE (priv->header)) + { + GtkRequisition child_requisition; + + gtk_widget_size_request (priv->header, &child_requisition); + + if (GTK_ORIENTATION_VERTICAL == orientation) + inquery->height += child_requisition.height; + else + inquery->width += child_requisition.width; + } + + /* report effective widget size */ + inquery->width += item_area.width + 2 * border_width; + inquery->height += item_area.height + 2 * border_width; +} + +static void +gtk_tool_item_group_real_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + const gint border_width = GTK_CONTAINER (widget)->border_width; + GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (widget); + GtkToolItemGroupPrivate* priv = group->priv; + GtkRequisition child_requisition; + GtkAllocation child_allocation; + + GtkRequisition item_size; + GtkAllocation item_area; + + GtkOrientation orientation; + GtkToolbarStyle style; + + GList *it; + + gint n_columns, n_rows = 1; + gint min_rows; + + GtkTextDirection direction = gtk_widget_get_direction (widget); + + orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group)); + style = gtk_tool_shell_get_style (GTK_TOOL_SHELL (group)); + + /* chain up */ + GTK_WIDGET_CLASS (gtk_tool_item_group_parent_class)->size_allocate (widget, allocation); + + child_allocation.x = border_width; + child_allocation.y = border_width; + + /* place the header widget */ + if (GTK_WIDGET_VISIBLE (priv->header)) + { + gtk_widget_size_request (priv->header, &child_requisition); + + if (GTK_ORIENTATION_VERTICAL == orientation) + { + child_allocation.width = allocation->width; + child_allocation.height = child_requisition.height; + } + else + { + child_allocation.width = child_requisition.width; + child_allocation.height = allocation->height; + + if (GTK_TEXT_DIR_RTL == direction) + child_allocation.x = allocation->width - border_width - child_allocation.width; + } + + gtk_widget_size_allocate (priv->header, &child_allocation); + + if (GTK_ORIENTATION_VERTICAL == orientation) + child_allocation.y += child_allocation.height; + else if (GTK_TEXT_DIR_RTL != direction) + child_allocation.x += child_allocation.width; + else + child_allocation.x = border_width; + } + else + child_requisition.width = child_requisition.height = 0; + + /* figure out the size of homogeneous items */ + gtk_tool_item_group_get_item_size (group, &item_size, TRUE, &min_rows); + + /* figure out the available columns and size of item_area */ + if (GTK_ORIENTATION_VERTICAL == orientation) + { + item_size.width = MIN (item_size.width, allocation->width); + + item_area.width = allocation->width - 2 * border_width; + item_area.height = allocation->height - 2 * border_width - child_requisition.height; + + n_columns = MAX (item_area.width / item_size.width, 1); + + item_size.width = item_area.width / n_columns; + } + else + { + item_size.height = MIN (item_size.height, allocation->height); + + item_area.width = allocation->width - 2 * border_width - child_requisition.width; + item_area.height = allocation->height - 2 * border_width; + + n_columns = MAX (item_area.width / item_size.width, 1); + n_rows = MAX (item_area.height / item_size.height, min_rows); + + item_size.height = item_area.height / n_rows; + } + + item_area.x = child_allocation.x; + item_area.y = child_allocation.y; + + /* when expanded or in transition, place the tool items in a grid like layout */ + if (!priv->collapsed || !priv->animation || priv->animation_timeout) + { + gint col = 0, row = 0; + + for (it = priv->children; it != NULL; it = it->next) + { + GtkToolItemGroupChild *child = it->data; + gint col_child; + + if (!gtk_tool_item_group_is_item_visible (group, child)) + { + gtk_widget_set_child_visible (GTK_WIDGET (child->item), FALSE); + + continue; + } + + /* for non homogeneous widgets request the required size */ + child_requisition.width = 0; + + if (!child->homogeneous) + { + gtk_widget_size_request (GTK_WIDGET (child->item), &child_requisition); + child_requisition.width = MIN (child_requisition.width, item_area.width); + } + + /* select next row if at end of row */ + if (col > 0 && (child->new_row || (col * item_size.width) + MAX (child_requisition.width, item_size.width) > item_area.width)) + { + row++; + col = 0; + child_allocation.y += child_allocation.height; + } + + col_child = col; + + /* calculate the position and size of the item */ + if (!child->homogeneous) + { + gint col_width; + gint width; + + if (!child->expand) + col_width = udiv (child_requisition.width, item_size.width); + else + col_width = n_columns - col; + + width = col_width * item_size.width; + + if (GTK_TEXT_DIR_RTL == direction) + col_child = (n_columns - col - col_width); + + if (child->fill) + { + child_allocation.x = item_area.x + col_child * item_size.width; + child_allocation.width = width; + } + else + { + child_allocation.x = + (item_area.x + col_child * item_size.width + + (width - child_requisition.width) / 2); + child_allocation.width = child_requisition.width; + } + + col += col_width; + } + else + { + if (GTK_TEXT_DIR_RTL == direction) + col_child = (n_columns - col - 1); + + child_allocation.x = item_area.x + col_child * item_size.width; + child_allocation.width = item_size.width; + + col++; + } + + child_allocation.height = item_size.height; + + gtk_widget_size_allocate (GTK_WIDGET (child->item), &child_allocation); + gtk_widget_set_child_visible (GTK_WIDGET (child->item), TRUE); + } + + child_allocation.y += item_size.height; + } + + /* or just hide all items, when collapsed */ + + else + { + for (it = priv->children; it != NULL; it = it->next) + { + GtkToolItemGroupChild *child = it->data; + + gtk_widget_set_child_visible (GTK_WIDGET (child->item), FALSE); + } + } +} + +static void +gtk_tool_item_group_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + gtk_tool_item_group_real_size_allocate (widget, allocation); + + if (GTK_WIDGET_MAPPED (widget)) + gdk_window_invalidate_rect (widget->window, NULL, FALSE); +} + +static void +gtk_tool_item_group_set_focus_cb (GtkWidget *window G_GNUC_UNUSED, + GtkWidget *widget, + gpointer user_data) +{ + GtkAdjustment *adjustment; + GtkWidget *p; + + /* Find this group's parent widget in the focused widget's anchestry. */ + for (p = widget; p; p = gtk_widget_get_parent (p)) + if (p == user_data) + { + p = gtk_widget_get_parent (p); + break; + } + + if (GTK_IS_TOOL_PALETTE (p)) + { + /* Check that the focused widgets is fully visible within + * the group's parent widget and make it visible otherwise. */ + + adjustment = gtk_tool_palette_get_hadjustment (GTK_TOOL_PALETTE (p)); + adjustment = gtk_tool_palette_get_vadjustment (GTK_TOOL_PALETTE (p)); + + if (adjustment) + { + int y; + + /* Handle vertical adjustment. */ + if (gtk_widget_translate_coordinates + (widget, p, 0, 0, NULL, &y) && y < 0) + { + y += adjustment->value; + gtk_adjustment_clamp_page (adjustment, y, y + widget->allocation.height); + } + else if (gtk_widget_translate_coordinates + (widget, p, 0, widget->allocation.height, NULL, &y) && + y > p->allocation.height) + { + y += adjustment->value; + gtk_adjustment_clamp_page (adjustment, y - widget->allocation.height, y); + } + } + + adjustment = gtk_tool_palette_get_hadjustment (GTK_TOOL_PALETTE (p)); + + if (adjustment) + { + int x; + + /* Handle horizontal adjustment. */ + if (gtk_widget_translate_coordinates + (widget, p, 0, 0, &x, NULL) && x < 0) + { + x += adjustment->value; + gtk_adjustment_clamp_page (adjustment, x, x + widget->allocation.width); + } + else if (gtk_widget_translate_coordinates + (widget, p, widget->allocation.width, 0, &x, NULL) && + x > p->allocation.width) + { + x += adjustment->value; + gtk_adjustment_clamp_page (adjustment, x - widget->allocation.width, x); + } + + return; + } + } +} + +static void +gtk_tool_item_group_set_toplevel_window (GtkToolItemGroup *group, + GtkWidget *toplevel) +{ + GtkToolItemGroupPrivate* priv = group->priv; + + if (toplevel != priv->toplevel) + { + if (priv->toplevel) + { + /* Disconnect focus tracking handler. */ + g_signal_handler_disconnect (priv->toplevel, + priv->focus_set_id); + + priv->focus_set_id = 0; + priv->toplevel = NULL; + } + + if (toplevel) + { + /* Install focus tracking handler. We connect to the window's + * set-focus signal instead of connecting to the focus signal of + * each child to: + * + * 1) Reduce the number of signal handlers used. + * 2) Avoid special handling for group headers. + * 3) Catch focus grabs not only for direct children, + * but also for nested widgets. + */ + priv->focus_set_id = + g_signal_connect (toplevel, "set-focus", + G_CALLBACK (gtk_tool_item_group_set_focus_cb), + group); + + priv->toplevel = toplevel; + } + } +} + +static void +gtk_tool_item_group_realize (GtkWidget *widget) +{ + GtkWidget *toplevel_window; + const gint border_width = GTK_CONTAINER (widget)->border_width; + gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + GdkWindowAttr attributes; + GdkDisplay *display; + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = widget->allocation.x + border_width; + attributes.y = widget->allocation.y + border_width; + attributes.width = widget->allocation.width - border_width * 2; + attributes.height = widget->allocation.height - border_width * 2; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK + | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + | GDK_BUTTON_MOTION_MASK; + + widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), + &attributes, attributes_mask); + + display = gdk_drawable_get_display (widget->window); + + if (gdk_display_supports_composite (display)) + gdk_window_set_composited (widget->window, TRUE); + + gdk_window_set_user_data (widget->window, widget); + widget->style = gtk_style_attach (widget->style, widget->window); + gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + + gtk_container_forall (GTK_CONTAINER (widget), + (GtkCallback) gtk_widget_set_parent_window, + widget->window); + + gtk_widget_queue_resize_no_redraw (widget); + + toplevel_window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW); + gtk_tool_item_group_set_toplevel_window (GTK_TOOL_ITEM_GROUP (widget), + toplevel_window); +} + +static void +gtk_tool_item_group_unrealize (GtkWidget *widget) +{ + gtk_tool_item_group_set_toplevel_window (GTK_TOOL_ITEM_GROUP (widget), NULL); + GTK_WIDGET_CLASS (gtk_tool_item_group_parent_class)->unrealize (widget); +} + +static void +gtk_tool_item_group_style_set (GtkWidget *widget, + GtkStyle *previous_style) +{ + gtk_tool_item_group_header_adjust_style (GTK_TOOL_ITEM_GROUP (widget)); + GTK_WIDGET_CLASS (gtk_tool_item_group_parent_class)->style_set (widget, previous_style); +} + +static void +gtk_tool_item_group_add (GtkContainer *container, + GtkWidget *widget) +{ + g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (container)); + g_return_if_fail (GTK_IS_TOOL_ITEM (widget)); + + gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (container), + GTK_TOOL_ITEM (widget), -1); +} + +static void +gtk_tool_item_group_remove (GtkContainer *container, + GtkWidget *child) +{ + GtkToolItemGroup *group; + GtkToolItemGroupPrivate* priv; + GList *it; + + g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (container)); + group = GTK_TOOL_ITEM_GROUP (container); + priv = group->priv; + + for (it = priv->children; it != NULL; it = it->next) + { + GtkToolItemGroupChild *child_info = it->data; + + if ((GtkWidget *)child_info->item == child) + { + g_object_unref (child); + gtk_widget_unparent (child); + + g_free (child_info); + priv->children = g_list_delete_link (priv->children, it); + + gtk_widget_queue_resize (GTK_WIDGET (container)); + break; + } + } +} + +static void +gtk_tool_item_group_forall (GtkContainer *container, + gboolean internals, + GtkCallback callback, + gpointer callback_data) +{ + GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (container); + GtkToolItemGroupPrivate* priv = group->priv; + GList *children; + + if (internals && priv->header) + callback (priv->header, callback_data); + + children = priv->children; + while (children) + { + GtkToolItemGroupChild *child = children->data; + children = children->next; /* store pointer before call to callback + because the child pointer is invalid if the + child->item is removed from the item group + in callback */ + + callback (GTK_WIDGET (child->item), callback_data); + } +} + +static GType +gtk_tool_item_group_child_type (GtkContainer *container G_GNUC_UNUSED) +{ + return GTK_TYPE_TOOL_ITEM; +} + +static GtkToolItemGroupChild * +gtk_tool_item_group_get_child (GtkToolItemGroup *group, + GtkToolItem *item, + gint *position, + GList **link) +{ + guint i; + GList *it; + + g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), NULL); + g_return_val_if_fail (GTK_IS_TOOL_ITEM (item), NULL); + + for (it = group->priv->children, i = 0; it != NULL; it = it->next, ++i) + { + GtkToolItemGroupChild *child = it->data; + + if (child->item == item) + { + if (position) + *position = i; + + if (link) + *link = it; + + return child; + } + } + + return NULL; +} + +static void +gtk_tool_item_group_get_item_packing (GtkToolItemGroup *group, + GtkToolItem *item, + gboolean *homogeneous, + gboolean *expand, + gboolean *fill, + gboolean *new_row) +{ + GtkToolItemGroupChild *child; + + g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group)); + g_return_if_fail (GTK_IS_TOOL_ITEM (item)); + + child = gtk_tool_item_group_get_child (group, item, NULL, NULL); + if (!child) + return; + + if (expand) + *expand = child->expand; + + if (homogeneous) + *homogeneous = child->homogeneous; + + if (fill) + *fill = child->fill; + + if (new_row) + *new_row = child->new_row; +} + +static void +gtk_tool_item_group_set_item_packing (GtkToolItemGroup *group, + GtkToolItem *item, + gboolean homogeneous, + gboolean expand, + gboolean fill, + gboolean new_row) +{ + GtkToolItemGroupChild *child; + gboolean changed = FALSE; + + g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group)); + g_return_if_fail (GTK_IS_TOOL_ITEM (item)); + + child = gtk_tool_item_group_get_child (group, item, NULL, NULL); + if (!child) + return; + + gtk_widget_freeze_child_notify (GTK_WIDGET (item)); + + if (child->homogeneous != homogeneous) + { + child->homogeneous = homogeneous; + changed = TRUE; + gtk_widget_child_notify (GTK_WIDGET (item), "homogeneous"); + } + if (child->expand != expand) + { + child->expand = expand; + changed = TRUE; + gtk_widget_child_notify (GTK_WIDGET (item), "expand"); + } + if (child->fill != fill) + { + child->fill = fill; + changed = TRUE; + gtk_widget_child_notify (GTK_WIDGET (item), "fill"); + } + if (child->new_row != new_row) + { + child->new_row = new_row; + changed = TRUE; + gtk_widget_child_notify (GTK_WIDGET (item), "new-row"); + } + + gtk_widget_thaw_child_notify (GTK_WIDGET (item)); + + if (changed && GTK_WIDGET_VISIBLE (group) && GTK_WIDGET_VISIBLE (item)) + gtk_widget_queue_resize (GTK_WIDGET (group)); +} + +static void +gtk_tool_item_group_set_child_property (GtkContainer *container, + GtkWidget *child, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (container); + GtkToolItem *item = GTK_TOOL_ITEM (child); + gboolean homogeneous, expand, fill, new_row; + + if (prop_id != CHILD_PROP_POSITION) + gtk_tool_item_group_get_item_packing (group, item, + &homogeneous, + &expand, + &fill, + &new_row); + + switch (prop_id) + { + case CHILD_PROP_HOMOGENEOUS: + gtk_tool_item_group_set_item_packing (group, item, + g_value_get_boolean (value), + expand, + fill, + new_row); + break; + + case CHILD_PROP_EXPAND: + gtk_tool_item_group_set_item_packing (group, item, + homogeneous, + g_value_get_boolean (value), + fill, + new_row); + break; + + case CHILD_PROP_FILL: + gtk_tool_item_group_set_item_packing (group, item, + homogeneous, + expand, + g_value_get_boolean (value), + new_row); + break; + + case CHILD_PROP_NEW_ROW: + gtk_tool_item_group_set_item_packing (group, item, + homogeneous, + expand, + fill, + g_value_get_boolean (value)); + break; + + case CHILD_PROP_POSITION: + gtk_tool_item_group_set_item_position (group, item, g_value_get_int (value)); + break; + + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec); + break; + } +} + +static void +gtk_tool_item_group_get_child_property (GtkContainer *container, + GtkWidget *child, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (container); + GtkToolItem *item = GTK_TOOL_ITEM (child); + gboolean homogeneous, expand, fill, new_row; + + if (prop_id != CHILD_PROP_POSITION) + gtk_tool_item_group_get_item_packing (group, item, + &homogeneous, + &expand, + &fill, + &new_row); + + switch (prop_id) + { + case CHILD_PROP_HOMOGENEOUS: + g_value_set_boolean (value, homogeneous); + break; + + case CHILD_PROP_EXPAND: + g_value_set_boolean (value, expand); + break; + + case CHILD_PROP_FILL: + g_value_set_boolean (value, fill); + break; + + case CHILD_PROP_NEW_ROW: + g_value_set_boolean (value, new_row); + break; + + case CHILD_PROP_POSITION: + g_value_set_int (value, gtk_tool_item_group_get_item_position (group, item)); + break; + + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec); + break; + } +} + +static void +gtk_tool_item_group_class_init (GtkToolItemGroupClass *cls) +{ + GObjectClass *oclass = G_OBJECT_CLASS (cls); + GtkWidgetClass *wclass = GTK_WIDGET_CLASS (cls); + GtkContainerClass *cclass = GTK_CONTAINER_CLASS (cls); + + oclass->set_property = gtk_tool_item_group_set_property; + oclass->get_property = gtk_tool_item_group_get_property; + oclass->finalize = gtk_tool_item_group_finalize; + oclass->dispose = gtk_tool_item_group_dispose; + + wclass->size_request = gtk_tool_item_group_size_request; + wclass->size_allocate = gtk_tool_item_group_size_allocate; + wclass->realize = gtk_tool_item_group_realize; + wclass->unrealize = gtk_tool_item_group_unrealize; + wclass->style_set = gtk_tool_item_group_style_set; + wclass->screen_changed = gtk_tool_item_group_screen_changed; + + cclass->add = gtk_tool_item_group_add; + cclass->remove = gtk_tool_item_group_remove; + cclass->forall = gtk_tool_item_group_forall; + cclass->child_type = gtk_tool_item_group_child_type; + cclass->set_child_property = gtk_tool_item_group_set_child_property; + cclass->get_child_property = gtk_tool_item_group_get_child_property; + + g_object_class_install_property (oclass, PROP_LABEL, + g_param_spec_string ("label", + P_("Label"), + P_("The human-readable title of this item group"), + DEFAULT_LABEL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_object_class_install_property (oclass, PROP_LABEL_WIDGET, + g_param_spec_object ("label-widget", + P_("Label widget"), + P_("A widget to display in place of the usual label"), + GTK_TYPE_WIDGET, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_object_class_install_property (oclass, PROP_COLLAPSED, + g_param_spec_boolean ("collapsed", + P_("Collapsed"), + P_("Wether the group has been collapsed and items are hidden"), + DEFAULT_COLLAPSED, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_object_class_install_property (oclass, PROP_ELLIPSIZE, + g_param_spec_enum ("ellipsize", + P_("ellipsize"), + P_("Ellipsize for item group headers"), + PANGO_TYPE_ELLIPSIZE_MODE, DEFAULT_ELLIPSIZE, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_object_class_install_property (oclass, PROP_RELIEF, + g_param_spec_enum ("header-relief", + P_("Header Relief"), + P_("Relief of the group header button"), + GTK_TYPE_RELIEF_STYLE, GTK_RELIEF_NORMAL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + gtk_widget_class_install_style_property (wclass, + g_param_spec_int ("expander-size", + P_("Expander Size"), + P_("Size of the expander arrow"), + 0, + G_MAXINT, + DEFAULT_EXPANDER_SIZE, + G_PARAM_READABLE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + gtk_widget_class_install_style_property (wclass, + g_param_spec_int ("header-spacing", + P_("Header Spacing"), + P_("Spacing between expander arrow and caption"), + 0, + G_MAXINT, + DEFAULT_HEADER_SPACING, + G_PARAM_READABLE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + gtk_container_class_install_child_property (cclass, CHILD_PROP_HOMOGENEOUS, + g_param_spec_boolean ("homogeneous", + P_("Homogeneous"), + P_("Whether the item should be the same size as other homogeneous items"), + TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + gtk_container_class_install_child_property (cclass, CHILD_PROP_EXPAND, + g_param_spec_boolean ("expand", + P_("Expand"), + P_("Whether the item should receive extra space when the toolbar grows"), + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + gtk_container_class_install_child_property (cclass, CHILD_PROP_FILL, + g_param_spec_boolean ("fill", + P_("Fill"), + P_("Whether the item should fill the avaiable space"), + TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + gtk_container_class_install_child_property (cclass, CHILD_PROP_NEW_ROW, + g_param_spec_boolean ("new-row", + P_("New Row"), + P_("Whether the item should start a new row"), + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + gtk_container_class_install_child_property (cclass, CHILD_PROP_POSITION, + g_param_spec_int ("position", + P_("Position"), + P_("Position of the item within this group"), + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_type_class_add_private (cls, sizeof (GtkToolItemGroupPrivate)); +} + +/** + * gtk_tool_item_group_new: + * @label: the label of the new group. + * + * Creates a new tool item group with label @label. + * + * Returns: a new #GtkToolItemGroup. + * + * Since: 2.18 + */ +GtkWidget* +gtk_tool_item_group_new (const gchar *label) +{ + return g_object_new (GTK_TYPE_TOOL_ITEM_GROUP, "label", + label, NULL); +} + +/** + * gtk_tool_item_group_set_label: + * @group: an #GtkToolItemGroup. + * @label: the new human-readable label of of the group. + * + * Sets the label of the tool item group. The label is displayed in the header + * of the group. + * + * Since: 2.18 + */ +void +gtk_tool_item_group_set_label (GtkToolItemGroup *group, + const gchar *label) +{ + g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group)); + + if (!label) + { + gtk_tool_item_group_set_label_widget (group, NULL); + } + else + { + GtkWidget *child = gtk_label_new (label); + /* + gtk_label_set_use_underline (GTK_LABEL (child), group->priv->use_underline); + gtk_label_set_use_markup (GTK_LABEL (child), group->priv->use_markup); + */ + gtk_widget_show (child); + + gtk_tool_item_group_set_label_widget (group, child); + } + + g_object_notify (G_OBJECT (group), "label"); +} + +/** + * gtk_tool_item_group_set_label_widget: + * @group: an #GtkToolItemGroup. + * @widget: the new human-readable label of of the group. + * + * Sets the label of the tool item group. The label is displayed in the header + * of the group. + * + * Since: 2.18 + */ +void +gtk_tool_item_group_set_label_widget (GtkToolItemGroup *group, + GtkWidget *label_widget) +{ + GtkToolItemGroupPrivate* priv; + GtkWidget *alignment; + + g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group)); + g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget)); + g_return_if_fail (label_widget == NULL || label_widget->parent == NULL); + + priv = group->priv; + + if (priv->label_widget == label_widget) + return; + + alignment = gtk_tool_item_group_get_alignment (group); + + if (priv->label_widget) + { + gtk_widget_set_state (priv->label_widget, GTK_STATE_NORMAL); + gtk_container_remove (GTK_CONTAINER (alignment), priv->label_widget); + } + + + if (label_widget) + { + gtk_container_add (GTK_CONTAINER (alignment), label_widget); + + /* + if (priv->prelight) + gtk_widget_set_state (label_widget, GTK_STATE_PRELIGHT); + */ + } + + if (GTK_WIDGET_VISIBLE (group)) + gtk_widget_queue_resize (GTK_WIDGET (group)); + + /* Only show the header widget if the group has children: */ + if (label_widget && priv->children) + gtk_widget_show (priv->header); + else + gtk_widget_hide (priv->header); + + g_object_freeze_notify (G_OBJECT (group)); + g_object_notify (G_OBJECT (group), "label-widget"); + g_object_notify (G_OBJECT (group), "label"); + g_object_thaw_notify (G_OBJECT (group)); +} + +/** + * gtk_tool_item_group_header_relief: + * @group: an #GtkToolItemGroup. + * @style: The GtkReliefStyle + * + * Set the button relief of the group header. See #gtk_button_set_relief for + * details + * + * Since: 2.18 + */ +void +gtk_tool_item_group_set_header_relief (GtkToolItemGroup *group, + GtkReliefStyle style) +{ + g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group)); + gtk_button_set_relief (GTK_BUTTON(group->priv->header), style); +} + +static gint64 +gtk_tool_item_group_get_animation_timestamp (GtkToolItemGroup *group) +{ + GTimeVal now; + g_source_get_current_time (group->priv->animation_timeout, &now); + return (now.tv_sec * G_USEC_PER_SEC + now.tv_usec - group->priv->animation_start) / 1000; +} + +static void +gtk_tool_item_group_force_expose (GtkToolItemGroup *group) +{ + GtkToolItemGroupPrivate* priv = group->priv; + + if (GTK_WIDGET_REALIZED (priv->header)) + { + GtkWidget *alignment = gtk_tool_item_group_get_alignment (group); + GdkRectangle area; + + /* Find the header button's arrow area... */ + area.x = alignment->allocation.x; + area.y = alignment->allocation.y + (alignment->allocation.height - priv->expander_size) / 2; + area.height = priv->expander_size; + area.width = priv->expander_size; + + /* ... and invalidated it to get it animated. */ + gdk_window_invalidate_rect (priv->header->window, &area, TRUE); + } + + if (GTK_WIDGET_REALIZED (group)) + { + GtkWidget *widget = GTK_WIDGET (group); + GtkWidget *parent = gtk_widget_get_parent (widget); + int x, y, width, height; + + /* Find the tool item area button's arrow area... */ + width = widget->allocation.width; + height = widget->allocation.height; + + gtk_widget_translate_coordinates (widget, parent, 0, 0, &x, &y); + + if (GTK_WIDGET_VISIBLE (priv->header)) + { + height -= priv->header->allocation.height; + y += priv->header->allocation.height; + } + + /* ... and invalidated it to get it animated. */ + gtk_widget_queue_draw_area (parent, x, y, width, height); + } +} + +static gboolean +gtk_tool_item_group_animation_cb (gpointer data) +{ + GtkToolItemGroup *group = GTK_TOOL_ITEM_GROUP (data); + GtkToolItemGroupPrivate* priv = group->priv; + gint64 timestamp = gtk_tool_item_group_get_animation_timestamp (group); + + /* Enque this early to reduce number of expose events. */ + gtk_widget_queue_resize_no_redraw (GTK_WIDGET (group)); + + /* Figure out current style of the expander arrow. */ + if (priv->collapsed) + { + if (priv->expander_style == GTK_EXPANDER_EXPANDED) + priv->expander_style = GTK_EXPANDER_SEMI_COLLAPSED; + else + priv->expander_style = GTK_EXPANDER_COLLAPSED; + } + else + { + if (priv->expander_style == GTK_EXPANDER_COLLAPSED) + priv->expander_style = GTK_EXPANDER_SEMI_EXPANDED; + else + priv->expander_style = GTK_EXPANDER_EXPANDED; + } + + gtk_tool_item_group_force_expose (group); + + /* Finish animation when done. */ + if (timestamp >= ANIMATION_DURATION) + priv->animation_timeout = NULL; + + /* Ensure that all composited windows and child windows are repainted, before + * the parent widget gets its expose-event. This is needed to avoid heavy + * rendering artifacts. GTK+ should take care about this issue by itself I + * guess, but currently it doesn't. Also I don't understand the parameters + * of this issue well enough yet, to file a bug report. + */ + gdk_window_process_updates (GTK_WIDGET (group)->window, TRUE); + + return (priv->animation_timeout != NULL); +} + +/** + * gtk_tool_item_group_set_collapsed: + * @group: an #GtkToolItemGroup. + * @collapsed: whether the @group should be collapsed or expanded. + * + * Sets whether the @group should be collapsed or expanded. + * + * Since: 2.18 + */ +void +gtk_tool_item_group_set_collapsed (GtkToolItemGroup *group, + gboolean collapsed) +{ + GtkWidget *parent; + GtkToolItemGroupPrivate* priv; + + g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group)); + + priv = group->priv; + + parent = gtk_widget_get_parent (GTK_WIDGET (group)); + if (GTK_IS_TOOL_PALETTE (parent) && !collapsed) + _gtk_tool_palette_set_expanding_child (GTK_TOOL_PALETTE (parent), + GTK_WIDGET (group)); + if (collapsed != priv->collapsed) + { + if (priv->animation) + { + GTimeVal now; + + g_get_current_time (&now); + + if (priv->animation_timeout) + g_source_destroy (priv->animation_timeout); + + priv->animation_start = (now.tv_sec * G_USEC_PER_SEC + now.tv_usec); + priv->animation_timeout = g_timeout_source_new (ANIMATION_TIMEOUT); + + g_source_set_callback (priv->animation_timeout, + gtk_tool_item_group_animation_cb, + group, NULL); + + g_source_attach (priv->animation_timeout, NULL); + } + else + { + priv->expander_style = GTK_EXPANDER_COLLAPSED; + gtk_tool_item_group_force_expose (group); + } + + priv->collapsed = collapsed; + g_object_notify (G_OBJECT (group), "collapsed"); + } +} + +/** + * gtk_tool_item_group_set_ellipsize: + * @group: an #GtkToolItemGroup. + * @ellipsize: the #PangoEllipsizeMode labels in @group should use. + * + * Sets the ellipsization mode which should be used by labels in @group. + * + * Since: 2.18 + */ +void +gtk_tool_item_group_set_ellipsize (GtkToolItemGroup *group, + PangoEllipsizeMode ellipsize) +{ + g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group)); + + if (ellipsize != group->priv->ellipsize) + { + group->priv->ellipsize = ellipsize; + gtk_tool_item_group_header_adjust_style (group); + g_object_notify (G_OBJECT (group), "ellipsize"); + _gtk_tool_item_group_palette_reconfigured (group); + } +} + +/** + * gtk_tool_item_group_get_label: + * @group: an #GtkToolItemGroup. + * + * Gets the label of @group. + * + * Returns: the label of @group. The label is an internal string of @group and must not be modified. + * + * Since: 2.18 + */ +G_CONST_RETURN gchar* +gtk_tool_item_group_get_label (GtkToolItemGroup *group) +{ + GtkToolItemGroupPrivate *priv; + + g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), NULL); + + priv = group->priv; + + if (GTK_IS_LABEL (priv->label_widget)) + return gtk_label_get_label (GTK_LABEL (priv->label_widget)); + else + return NULL; +} + +/** + * gtk_tool_item_group_get_label_widget: + * @group: an #GtkToolItemGroup. + * + * Gets the label of @group. + * + * Returns: the label of @group. The label is an internal string of @group and must not be modified. + * + * Since: 2.18 + */ +GtkWidget* +gtk_tool_item_group_get_label_widget (GtkToolItemGroup *group) +{ + GtkWidget *alignment = gtk_tool_item_group_get_alignment (group); + return gtk_bin_get_child (GTK_BIN (alignment)); +} + +/** + * gtk_tool_item_group_get_collapsed: + * @group: an GtkToolItemGroup. + * + * Gets whether @group is collapsed or expanded. + * + * Returns: %TRUE if @group is collapsed, %FALSE if it is expanded. + * + * Since: 2.18 + */ +gboolean +gtk_tool_item_group_get_collapsed (GtkToolItemGroup *group) +{ + g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), DEFAULT_COLLAPSED); + return group->priv->collapsed; +} + +/** + * gtk_tool_item_group_get_ellipsize: + * @group: an #GtkToolItemGroup. + * + * Gets the ellipsization mode of @group. + * + * Returns: the #PangoEllipsizeMode of @group. + * + * Since: 2.18 + */ +PangoEllipsizeMode +gtk_tool_item_group_get_ellipsize (GtkToolItemGroup *group) +{ + g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), DEFAULT_ELLIPSIZE); + return group->priv->ellipsize; +} + +/** + * gtk_tool_item_group_get_header_relief: + * @group: an #GtkToolItemGroup. + * + * Gets the relief mode of the header button of @group. + * + * Returns: the #GtkReliefStyle + * + * Since: 2.18 + */ +GtkReliefStyle +gtk_tool_item_group_get_header_relief (GtkToolItemGroup *group) +{ + g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), GTK_RELIEF_NORMAL); + return gtk_button_get_relief (GTK_BUTTON (group->priv->header)); +} + +/** + * gtk_tool_item_group_insert: + * @group: an #GtkToolItemGroup. + * @item: the #GtkToolItem to insert into @group. + * @position: the position of @item in @group, starting with 0. The position -1 means end of list. + * + * Inserts @item at @position in the list of children of @group. + * + * Since: 2.18 + */ +void +gtk_tool_item_group_insert (GtkToolItemGroup *group, + GtkToolItem *item, + gint position) +{ + GtkWidget *parent, *child_widget; + GtkToolItemGroupChild *child; + + g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group)); + g_return_if_fail (GTK_IS_TOOL_ITEM (item)); + g_return_if_fail (position >= -1); + + parent = gtk_widget_get_parent (GTK_WIDGET (group)); + + child = g_new (GtkToolItemGroupChild, 1); + child->item = g_object_ref_sink (item); + child->homogeneous = TRUE; + child->expand = FALSE; + child->fill = TRUE; + child->new_row = FALSE; + + group->priv->children = g_list_insert (group->priv->children, child, position); + + if (GTK_IS_TOOL_PALETTE (parent)) + _gtk_tool_palette_child_set_drag_source (GTK_WIDGET (item), parent); + + child_widget = gtk_bin_get_child (GTK_BIN (item)); + + if (GTK_IS_BUTTON (child_widget)) + gtk_button_set_focus_on_click (GTK_BUTTON (child_widget), TRUE); + + gtk_widget_set_parent (GTK_WIDGET (item), GTK_WIDGET (group)); +} + +/** + * gtk_tool_item_group_set_item_position: + * @group: an #GtkToolItemGroup. + * @item: the #GtkToolItem to move to a new position, should be a child of @group. + * @position: the new position of @item in @group, starting with 0. The position -1 means end of list. + * + * Sets the position of @item in the list of children of @group. + * + * Since: 2.18 + */ +void +gtk_tool_item_group_set_item_position (GtkToolItemGroup *group, + GtkToolItem *item, + gint position) +{ + gint old_position; + GList *link; + GtkToolItemGroupChild *child; + GtkToolItemGroupPrivate* priv; + + g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group)); + g_return_if_fail (GTK_IS_TOOL_ITEM (item)); + + g_return_if_fail (position >= -1); + + child = gtk_tool_item_group_get_child (group, item, &old_position, &link); + priv = group->priv; + + g_return_if_fail (child != NULL); + + if (position == old_position) + return; + + priv->children = g_list_delete_link (priv->children, link); + priv->children = g_list_insert (priv->children, child, position); + + gtk_widget_child_notify (GTK_WIDGET (item), "position"); + if (GTK_WIDGET_VISIBLE (group) && GTK_WIDGET_VISIBLE (item)) + gtk_widget_queue_resize (GTK_WIDGET (group)); +} + +/** + * gtk_tool_item_group_get_item_position: + * @group: an #GtkToolItemGroup. + * @item: a #GtkToolItem. + * + * Gets the position of @item in @group as index. + * + * Returns: the index of @item in @group or -1 if @item is no child of @group. + * + * Since: 2.18 + */ +gint +gtk_tool_item_group_get_item_position (GtkToolItemGroup *group, + GtkToolItem *item) +{ + gint position; + + g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), -1); + g_return_val_if_fail (GTK_IS_TOOL_ITEM (item), -1); + + if (gtk_tool_item_group_get_child (group, item, &position, NULL)) + return position; + + return -1; +} + +/** + * gtk_tool_item_group_get_n_items: + * @group: an #GtkToolItemGroup. + * + * Gets the number of tool items in group. + * + * Returns: the number of tool items in group. + * + * Since: 2.18 + */ +guint +gtk_tool_item_group_get_n_items (GtkToolItemGroup *group) +{ + g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), 0); + + return g_list_length (group->priv->children); +} + +/** + * gtk_tool_item_group_get_nth_item: + * @group: an #GtkToolItemGroup. + * @index: the index. + * + * Gets the tool item at index in group. + * + * Returns: the #GtkToolItem at index. + * + * Since: 2.18 + */ +GtkToolItem* +gtk_tool_item_group_get_nth_item (GtkToolItemGroup *group, + guint index) +{ + GtkToolItemGroupChild *child; + + g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), NULL); + + child = g_list_nth_data (group->priv->children, index); + + return child != NULL ? child->item : NULL; +} + +/** + * gtk_tool_item_group_get_drop_item: + * @group: an #GtkToolItemGroup. + * @x: the x position. + * @y: the y position. + * + * Gets the tool item at position (x, y). + * + * Returns: the #GtkToolItem at position (x, y). + * + * Since: 2.18 + */ +GtkToolItem* +gtk_tool_item_group_get_drop_item (GtkToolItemGroup *group, + gint x, + gint y) +{ + GtkAllocation *allocation; + GtkOrientation orientation; + GList *it; + + g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), NULL); + + allocation = >K_WIDGET (group)->allocation; + orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group)); + + g_return_val_if_fail (x >= 0 && x < allocation->width, NULL); + g_return_val_if_fail (y >= 0 && y < allocation->height, NULL); + + for (it = group->priv->children; it != NULL; it = it->next) + { + GtkToolItemGroupChild *child = it->data; + GtkToolItem *item = child->item; + gint x0, y0; + + if (!item || !gtk_tool_item_group_is_item_visible (group, child)) + continue; + + allocation = >K_WIDGET (item)->allocation; + + x0 = x - allocation->x; + y0 = y - allocation->y; + + if (x0 >= 0 && x0 < allocation->width && + y0 >= 0 && y0 < allocation->height) + return item; + } + + return NULL; +} + +void +_gtk_tool_item_group_item_size_request (GtkToolItemGroup *group, + GtkRequisition *item_size, + gboolean homogeneous_only, + gint *requested_rows) +{ + GtkRequisition child_requisition; + GList *it; + gint rows = 0; + gboolean new_row = TRUE; + GtkOrientation orientation; + GtkToolbarStyle style; + + g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group)); + g_return_if_fail (NULL != item_size); + + orientation = gtk_tool_shell_get_orientation (GTK_TOOL_SHELL (group)); + style = gtk_tool_shell_get_style (GTK_TOOL_SHELL (group)); + + item_size->width = item_size->height = 0; + + for (it = group->priv->children; it != NULL; it = it->next) + { + GtkToolItemGroupChild *child = it->data; + + if (!gtk_tool_item_group_is_item_visible (group, child)) + continue; + + if (child->new_row || new_row) + { + rows++; + new_row = FALSE; + } + + if (!child->homogeneous && child->expand) + new_row = TRUE; + + gtk_widget_size_request (GTK_WIDGET (child->item), &child_requisition); + + if (!homogeneous_only || child->homogeneous) + item_size->width = MAX (item_size->width, child_requisition.width); + item_size->height = MAX (item_size->height, child_requisition.height); + } + + if (requested_rows) + *requested_rows = rows; +} + +void +_gtk_tool_item_group_paint (GtkToolItemGroup *group, + cairo_t *cr) +{ + GtkWidget *widget = GTK_WIDGET (group); + GtkToolItemGroupPrivate* priv = group->priv; + + gdk_cairo_set_source_pixmap (cr, widget->window, + widget->allocation.x, + widget->allocation.y); + + if (priv->animation_timeout) + { + GtkOrientation orientation = gtk_tool_item_group_get_orientation (GTK_TOOL_SHELL (group)); + cairo_pattern_t *mask; + gdouble v0, v1; + + if (GTK_ORIENTATION_VERTICAL == orientation) + v1 = widget->allocation.height; + else + v1 = widget->allocation.width; + + v0 = v1 - 256; + + if (!GTK_WIDGET_VISIBLE (priv->header)) + v0 = MAX (v0, 0); + else if (GTK_ORIENTATION_VERTICAL == orientation) + v0 = MAX (v0, priv->header->allocation.height); + else + v0 = MAX (v0, priv->header->allocation.width); + + v1 = MIN (v0 + 256, v1); + + if (GTK_ORIENTATION_VERTICAL == orientation) + { + v0 += widget->allocation.y; + v1 += widget->allocation.y; + + mask = cairo_pattern_create_linear (0.0, v0, 0.0, v1); + } + else + { + v0 += widget->allocation.x; + v1 += widget->allocation.x; + + mask = cairo_pattern_create_linear (v0, 0.0, v1, 0.0); + } + + cairo_pattern_add_color_stop_rgba (mask, 0.00, 0.0, 0.0, 0.0, 1.00); + cairo_pattern_add_color_stop_rgba (mask, 0.25, 0.0, 0.0, 0.0, 0.25); + cairo_pattern_add_color_stop_rgba (mask, 0.50, 0.0, 0.0, 0.0, 0.10); + cairo_pattern_add_color_stop_rgba (mask, 0.75, 0.0, 0.0, 0.0, 0.01); + cairo_pattern_add_color_stop_rgba (mask, 1.00, 0.0, 0.0, 0.0, 0.00); + + cairo_mask (cr, mask); + cairo_pattern_destroy (mask); + } + else + cairo_paint (cr); +} + +gint +_gtk_tool_item_group_get_size_for_limit (GtkToolItemGroup *group, + gint limit, + gboolean vertical, + gboolean animation) +{ + GtkRequisition requisition; + GtkToolItemGroupPrivate* priv = group->priv; + + gtk_widget_size_request (GTK_WIDGET (group), &requisition); + + if (!priv->collapsed || priv->animation_timeout) + { + GtkAllocation allocation = { 0, 0, requisition.width, requisition.height }; + GtkRequisition inquery; + + if (vertical) + allocation.width = limit; + else + allocation.height = limit; + + gtk_tool_item_group_real_size_query (GTK_WIDGET (group), + &allocation, &inquery); + + if (vertical) + inquery.height -= requisition.height; + else + inquery.width -= requisition.width; + + if (priv->animation_timeout && animation) + { + gint64 timestamp = gtk_tool_item_group_get_animation_timestamp (group); + + timestamp = MIN (timestamp, ANIMATION_DURATION); + + if (priv->collapsed) + timestamp = ANIMATION_DURATION - timestamp; + + if (vertical) + { + inquery.height *= timestamp; + inquery.height /= ANIMATION_DURATION; + } + else + { + inquery.width *= timestamp; + inquery.width /= ANIMATION_DURATION; + } + } + + if (vertical) + requisition.height += inquery.height; + else + requisition.width += inquery.width; + } + + return (vertical ? requisition.height : requisition.width); +} + +gint +_gtk_tool_item_group_get_height_for_width (GtkToolItemGroup *group, + gint width) +{ + return _gtk_tool_item_group_get_size_for_limit (group, width, TRUE, group->priv->animation); +} + +gint +_gtk_tool_item_group_get_width_for_height (GtkToolItemGroup *group, + gint height) +{ + return _gtk_tool_item_group_get_size_for_limit (group, height, FALSE, TRUE); +} + +static void +gtk_tool_palette_reconfigured_foreach_item (GtkWidget *child, + gpointer data G_GNUC_UNUSED) +{ + if (GTK_IS_TOOL_ITEM (child)) + gtk_tool_item_toolbar_reconfigured (GTK_TOOL_ITEM (child)); +} + + +void +_gtk_tool_item_group_palette_reconfigured (GtkToolItemGroup *group) +{ + gtk_container_foreach (GTK_CONTAINER (group), + gtk_tool_palette_reconfigured_foreach_item, + NULL); + + gtk_tool_item_group_header_adjust_style (group); +} diff --git a/gtk/gtktoolitemgroup.h b/gtk/gtktoolitemgroup.h new file mode 100644 index 0000000000..fef39316d6 --- /dev/null +++ b/gtk/gtktoolitemgroup.h @@ -0,0 +1,95 @@ +/* GtkToolPalette -- A tool palette with categories and DnD support + * Copyright (C) 2008 Openismus GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Mathias Hasselmann + */ + +#ifndef __GTK_TOOL_ITEM_GROUP_H__ +#define __GTK_TOOL_ITEM_GROUP_H__ + +#include <gtk/gtkcontainer.h> +#include <gtk/gtktoolitem.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_TOOL_ITEM_GROUP (gtk_tool_item_group_get_type()) +#define GTK_TOOL_ITEM_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, GTK_TYPE_TOOL_ITEM_GROUP, GtkToolItemGroup)) +#define GTK_TOOL_ITEM_GROUP_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST(cls, GTK_TYPE_TOOL_ITEM_GROUP, GtkToolItemGroupClass)) +#define GTK_IS_TOOL_ITEM_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, GTK_TYPE_TOOL_ITEM_GROUP)) +#define GTK_IS_TOOL_ITEM_GROUP_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE(obj, GTK_TYPE_TOOL_ITEM_GROUP)) +#define GTK_TOOL_ITEM_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_TOOL_ITEM_GROUP, GtkToolItemGroupClass)) + +typedef struct _GtkToolItemGroup GtkToolItemGroup; +typedef struct _GtkToolItemGroupClass GtkToolItemGroupClass; +typedef struct _GtkToolItemGroupPrivate GtkToolItemGroupPrivate; + +/** + * GtkToolItemGroup: + * + * This should not be accessed directly. Use the accessor functions below. + */ +struct _GtkToolItemGroup +{ + GtkContainer parent_instance; + GtkToolItemGroupPrivate *priv; +}; + +struct _GtkToolItemGroupClass +{ + GtkContainerClass parent_class; +}; + +GType gtk_tool_item_group_get_type (void) G_GNUC_CONST; +GtkWidget* gtk_tool_item_group_new (const gchar *label); + +void gtk_tool_item_group_set_label (GtkToolItemGroup *group, + const gchar *label); +void gtk_tool_item_group_set_label_widget (GtkToolItemGroup *group, + GtkWidget *label_widget); +void gtk_tool_item_group_set_collapsed (GtkToolItemGroup *group, + gboolean collapsed); +void gtk_tool_item_group_set_ellipsize (GtkToolItemGroup *group, + PangoEllipsizeMode ellipsize); +void gtk_tool_item_group_set_header_relief (GtkToolItemGroup *group, + GtkReliefStyle style); + +G_CONST_RETURN gchar* gtk_tool_item_group_get_label (GtkToolItemGroup *group); +GtkWidget *gtk_tool_item_group_get_label_widget (GtkToolItemGroup *group); +gboolean gtk_tool_item_group_get_collapsed (GtkToolItemGroup *group); +PangoEllipsizeMode gtk_tool_item_group_get_ellipsize (GtkToolItemGroup *group); +GtkReliefStyle gtk_tool_item_group_get_header_relief (GtkToolItemGroup *group); + +void gtk_tool_item_group_insert (GtkToolItemGroup *group, + GtkToolItem *item, + gint position); +void gtk_tool_item_group_set_item_position (GtkToolItemGroup *group, + GtkToolItem *item, + gint position); +gint gtk_tool_item_group_get_item_position (GtkToolItemGroup *group, + GtkToolItem *item); + +guint gtk_tool_item_group_get_n_items (GtkToolItemGroup *group); +GtkToolItem* gtk_tool_item_group_get_nth_item (GtkToolItemGroup *group, + guint index); +GtkToolItem* gtk_tool_item_group_get_drop_item (GtkToolItemGroup *group, + gint x, + gint y); + +G_END_DECLS + +#endif /* __GTK_TOOL_ITEM_GROUP_H__ */ diff --git a/gtk/gtktoolpalette.c b/gtk/gtktoolpalette.c new file mode 100644 index 0000000000..ca255bd82f --- /dev/null +++ b/gtk/gtktoolpalette.c @@ -0,0 +1,1889 @@ +/* GtkToolPalette -- A tool palette with categories and DnD support + * Copyright (C) 2008 Openismus GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Mathias Hasselmann + */ + +#include "gtktoolpaletteprivate.h" +#include "gtkmarshalers.h" + +#include <gtk/gtk.h> +#include <string.h> + +#define DEFAULT_ICON_SIZE GTK_ICON_SIZE_SMALL_TOOLBAR +#define DEFAULT_ORIENTATION GTK_ORIENTATION_VERTICAL +#define DEFAULT_TOOLBAR_STYLE GTK_TOOLBAR_ICONS + +#define DEFAULT_CHILD_EXCLUSIVE FALSE +#define DEFAULT_CHILD_EXPAND FALSE + +#define P_(msgid) (msgid) + +/** + * SECTION:GtkToolPalette + * @short_description: A tool palette with categories + * @include: gtktoolpalette.h + * + * An #GtkToolPalette allows it to add #GtkToolItem<!-- -->s to a palette like container + * with different categories and drag and drop support. + * + * An #GtkToolPalette is created with a call to gtk_tool_palette_new(). + * + * #GtkToolItem<!-- -->s cannot be added directly to an #GtkToolPalette, instead they + * are added to an #GtkToolItemGroup which can than be added to an #GtkToolPalette. To add + * an #GtkToolItemGroup to an #GtkToolPalette use gtk_container_add(). + * + * |[ + * GtkWidget *palette, *group; + * GtkToolItem *item; + * + * palette = gtk_tool_palette_new (); + * group = gtk_tool_item_group_new (_("Test Category")); + * gtk_container_add (GTK_CONTAINER (palette), group); + * + * item = gtk_tool_button_new_from_stock (GTK_STOCK_OK); + * gtk_tool_item_group_insert (GTK_TOOL_ITEM_GROUP (group), item, -1); + * ]| + * + * The easiest way to use drag and drop with GtkToolPalette is to call gtk_tool_palette_add_drag_dest() + * with the desired drag source @palette and the desired drag target @widget. Than gtk_tool_palette_get_drag_item() + * can be used to get the dragged item in the #GtkWidget::drag-data-received signal handler of the drag target. + * + * |[ + * static void + * passive_canvas_drag_data_received (GtkWidget *widget, + * GdkDragContext *context, + * gint x, + * gint y, + * GtkSelectionData *selection, + * guint info, + * guint time, + * gpointer data) + * { + * GtkWidget *palette; + * GtkWidget *item; + * + * /<!-- -->* Get the dragged item *<!-- -->/ + * palette = gtk_widget_get_ancestor (gtk_drag_get_source_widget (context), GTK_TYPE_TOOL_PALETTE); + * if (palette != NULL) + * item = gtk_tool_palette_get_drag_item (GTK_TOOL_PALETTE (palette), selection); + * + * /<!-- -->* Do something with item *<!-- -->/ + * } + * + * GtkWidget *target, palette; + * + * palette = gtk_tool_palette_new (); + * target = gtk_drawing_area_new (); + * + * g_signal_connect (G_OBJECT (target), "drag-data-received", + * G_CALLBACK (passive_canvas_drag_data_received), NULL); + * gtk_tool_palette_add_drag_dest (GTK_TOOL_PALETTE (palette), target, + * GTK_DEST_DEFAULT_ALL, + * GTK_TOOL_PALETTE_DRAG_ITEMS, + * GDK_ACTION_COPY); + * ]| + * + * Since: 2.18 + */ + +typedef struct _GtkToolItemGroupInfo GtkToolItemGroupInfo; +typedef struct _GtkToolPaletteDragData GtkToolPaletteDragData; + +enum +{ + PROP_NONE, + PROP_ICON_SIZE, + PROP_ICON_SIZE_SET, + PROP_ORIENTATION, + PROP_TOOLBAR_STYLE, +}; + +enum +{ + CHILD_PROP_NONE, + CHILD_PROP_EXCLUSIVE, + CHILD_PROP_EXPAND, +}; + +struct _GtkToolItemGroupInfo +{ + GtkToolItemGroup *widget; + + guint notify_collapsed; + guint exclusive : 1; + guint expand : 1; +}; + +struct _GtkToolPalettePrivate +{ + GtkToolItemGroupInfo *groups; + gsize groups_size; + gsize groups_length; + + GtkAdjustment *hadjustment; + GtkAdjustment *vadjustment; + + GtkIconSize icon_size; + gboolean icon_size_set; + GtkOrientation orientation; + GtkToolbarStyle style; + gboolean style_set; + + GtkWidget *expanding_child; + + GtkSizeGroup *text_size_group; + + GtkSettings *settings; + gulong settings_connection; + + guint sparse_groups : 1; + guint drag_source : 2; +}; + +struct _GtkToolPaletteDragData +{ + GtkToolPalette *palette; + GtkWidget *item; +}; + +static GdkAtom dnd_target_atom_item = GDK_NONE; +static GdkAtom dnd_target_atom_group = GDK_NONE; + +static const GtkTargetEntry dnd_targets[] = +{ + { "application/x-GTK-tool-palette-item", GTK_TARGET_SAME_APP, 0 }, + { "application/x-GTK-tool-palette-group", GTK_TARGET_SAME_APP, 0 }, +}; + +G_DEFINE_TYPE_WITH_CODE (GtkToolPalette, + gtk_tool_palette, + GTK_TYPE_CONTAINER, + G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL)); + +static void +gtk_tool_palette_init (GtkToolPalette *palette) +{ + palette->priv = G_TYPE_INSTANCE_GET_PRIVATE (palette, + GTK_TYPE_TOOL_PALETTE, + GtkToolPalettePrivate); + + palette->priv->groups_size = 4; + palette->priv->groups_length = 0; + palette->priv->groups = g_new0 (GtkToolItemGroupInfo, palette->priv->groups_size); + + palette->priv->icon_size = DEFAULT_ICON_SIZE; + palette->priv->icon_size_set = FALSE; + palette->priv->orientation = DEFAULT_ORIENTATION; + palette->priv->style = DEFAULT_TOOLBAR_STYLE; + palette->priv->style_set = FALSE; + + palette->priv->text_size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH); +} + +static void +gtk_tool_palette_reconfigured (GtkToolPalette *palette) +{ + guint i; + + for (i = 0; i < palette->priv->groups_length; ++i) + { + if (palette->priv->groups[i].widget) + _gtk_tool_item_group_palette_reconfigured (palette->priv->groups[i].widget); + } + + gtk_widget_queue_resize_no_redraw (GTK_WIDGET (palette)); +} + +static void +gtk_tool_palette_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkToolPalette *palette = GTK_TOOL_PALETTE (object); + + switch (prop_id) + { + case PROP_ICON_SIZE: + if ((guint) g_value_get_enum (value) != palette->priv->icon_size) + { + palette->priv->icon_size = g_value_get_enum (value); + gtk_tool_palette_reconfigured (palette); + } + break; + + case PROP_ICON_SIZE_SET: + if ((guint) g_value_get_enum (value) != palette->priv->icon_size) + { + palette->priv->icon_size_set = g_value_get_enum (value); + gtk_tool_palette_reconfigured (palette); + } + break; + + case PROP_ORIENTATION: + if ((guint) g_value_get_enum (value) != palette->priv->orientation) + { + palette->priv->orientation = g_value_get_enum (value); + gtk_tool_palette_reconfigured (palette); + } + break; + + case PROP_TOOLBAR_STYLE: + if ((guint) g_value_get_enum (value) != palette->priv->style) + { + palette->priv->style = g_value_get_enum (value); + gtk_tool_palette_reconfigured (palette); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_tool_palette_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkToolPalette *palette = GTK_TOOL_PALETTE (object); + + switch (prop_id) + { + case PROP_ICON_SIZE: + g_value_set_enum (value, gtk_tool_palette_get_icon_size (palette)); + break; + + case PROP_ICON_SIZE_SET: + g_value_set_boolean (value, palette->priv->icon_size_set); + break; + + case PROP_ORIENTATION: + g_value_set_enum (value, palette->priv->orientation); + break; + + case PROP_TOOLBAR_STYLE: + g_value_set_enum (value, gtk_tool_palette_get_style (palette)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_tool_palette_dispose (GObject *object) +{ + GtkToolPalette *palette = GTK_TOOL_PALETTE (object); + guint i; + + if (palette->priv->hadjustment) + { + g_object_unref (palette->priv->hadjustment); + palette->priv->hadjustment = NULL; + } + + if (palette->priv->vadjustment) + { + g_object_unref (palette->priv->vadjustment); + palette->priv->vadjustment = NULL; + } + + for (i = 0; i < palette->priv->groups_size; ++i) + { + GtkToolItemGroupInfo *group = &palette->priv->groups[i]; + + if (group->notify_collapsed) + { + g_signal_handler_disconnect (group->widget, group->notify_collapsed); + group->notify_collapsed = 0; + } + } + + if (palette->priv->text_size_group) + { + g_object_unref (palette->priv->text_size_group); + palette->priv->text_size_group = NULL; + } + + G_OBJECT_CLASS (gtk_tool_palette_parent_class)->dispose (object); +} + +static void +gtk_tool_palette_finalize (GObject *object) +{ + GtkToolPalette *palette = GTK_TOOL_PALETTE (object); + + if (palette->priv->groups) + { + palette->priv->groups_length = 0; + g_free (palette->priv->groups); + palette->priv->groups = NULL; + } + + G_OBJECT_CLASS (gtk_tool_palette_parent_class)->finalize (object); +} + +static void +gtk_tool_palette_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + const gint border_width = GTK_CONTAINER (widget)->border_width; + GtkToolPalette *palette = GTK_TOOL_PALETTE (widget); + GtkRequisition child_requisition; + guint i; + + requisition->width = 0; + requisition->height = 0; + + for (i = 0; i < palette->priv->groups_length; ++i) + { + GtkToolItemGroupInfo *group = &palette->priv->groups[i]; + + if (!group->widget) + continue; + + gtk_widget_size_request (GTK_WIDGET (group->widget), &child_requisition); + + if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation) + { + requisition->width = MAX (requisition->width, child_requisition.width); + requisition->height += child_requisition.height; + } + else + { + requisition->width += child_requisition.width; + requisition->height = MAX (requisition->height, child_requisition.height); + } + } + + requisition->width += border_width * 2; + requisition->height += border_width * 2; +} + +static void +gtk_tool_palette_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + const gint border_width = GTK_CONTAINER (widget)->border_width; + GtkToolPalette *palette = GTK_TOOL_PALETTE (widget); + GtkAdjustment *adjustment = NULL; + GtkAllocation child_allocation; + + gint n_expand_groups = 0; + gint remaining_space = 0; + gint expand_space = 0; + + gint page_start, page_size = 0; + gint offset = 0; + guint i; + + gint min_offset = -1, max_offset = -1; + + gint x; + + gint *group_sizes = g_newa(gint, palette->priv->groups_length); + + GtkTextDirection direction = gtk_widget_get_direction (widget); + + GTK_WIDGET_CLASS (gtk_tool_palette_parent_class)->size_allocate (widget, allocation); + + if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation) + { + adjustment = palette->priv->vadjustment; + page_size = allocation->height; + } + else + { + adjustment = palette->priv->hadjustment; + page_size = allocation->width; + } + + if (adjustment) + offset = gtk_adjustment_get_value (adjustment); + if (GTK_ORIENTATION_HORIZONTAL == palette->priv->orientation && + GTK_TEXT_DIR_RTL == direction) + offset = -offset; + + if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation) + child_allocation.width = allocation->width - border_width * 2; + else + child_allocation.height = allocation->height - border_width * 2; + + if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation) + remaining_space = allocation->height; + else + remaining_space = allocation->width; + + /* figure out the required size of all groups to be able to distribute the + * remaining space on allocation */ + for (i = 0; i < palette->priv->groups_length; ++i) + { + GtkToolItemGroupInfo *group = &palette->priv->groups[i]; + gint size; + + if (!group->widget) + continue; + + widget = GTK_WIDGET (group->widget); + + if (gtk_tool_item_group_get_n_items (group->widget)) + { + if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation) + size = _gtk_tool_item_group_get_height_for_width (group->widget, child_allocation.width); + else + size = _gtk_tool_item_group_get_width_for_height (group->widget, child_allocation.height); + + if (group->expand && !gtk_tool_item_group_get_collapsed (group->widget)) + n_expand_groups += 1; + } + else + size = 0; + + remaining_space -= size; + group_sizes[i] = size; + + /* if the widget is currently expanding an offset which allows to display as much of the + * widget as possible is calculated */ + if (widget == palette->priv->expanding_child) + { + gint limit = + GTK_ORIENTATION_VERTICAL == palette->priv->orientation ? + child_allocation.width : child_allocation.height; + + gint real_size; + guint j; + + min_offset = 0; + + for (j = 0; j < i; ++j) + min_offset += group_sizes[j]; + + max_offset = min_offset + group_sizes[i]; + + real_size = _gtk_tool_item_group_get_size_for_limit + (GTK_TOOL_ITEM_GROUP (widget), limit, + GTK_ORIENTATION_VERTICAL == palette->priv->orientation, + FALSE); + + if (size == real_size) + palette->priv->expanding_child = NULL; + } + } + + if (n_expand_groups > 0) + { + remaining_space = MAX (0, remaining_space); + expand_space = remaining_space / n_expand_groups; + } + + if (max_offset != -1) + { + gint limit = + GTK_ORIENTATION_VERTICAL == palette->priv->orientation ? + allocation->height : allocation->width; + + offset = MIN (MAX (offset, max_offset - limit), min_offset); + } + + if (remaining_space > 0) + offset = 0; + + x = border_width; + child_allocation.y = border_width; + + if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation) + child_allocation.y -= offset; + else + x -= offset; + + /* allocate all groups at the calculated positions */ + for (i = 0; i < palette->priv->groups_length; ++i) + { + GtkToolItemGroupInfo *group = &palette->priv->groups[i]; + GtkWidget *widget; + + if (!group->widget) + continue; + + widget = GTK_WIDGET (group->widget); + + if (gtk_tool_item_group_get_n_items (group->widget)) + { + gint size = group_sizes[i]; + + if (group->expand && !gtk_tool_item_group_get_collapsed (group->widget)) + { + size += MIN (expand_space, remaining_space); + remaining_space -= expand_space; + } + + if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation) + child_allocation.height = size; + else + child_allocation.width = size; + + if (GTK_ORIENTATION_HORIZONTAL == palette->priv->orientation && + GTK_TEXT_DIR_RTL == direction) + child_allocation.x = allocation->width - x - child_allocation.width; + else + child_allocation.x = x; + + gtk_widget_size_allocate (widget, &child_allocation); + gtk_widget_show (widget); + + if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation) + child_allocation.y += child_allocation.height; + else + x += child_allocation.width; + } + else + gtk_widget_hide (widget); + } + + if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation) + { + child_allocation.y += border_width; + child_allocation.y += offset; + + page_start = child_allocation.y; + } + else + { + x += border_width; + x += offset; + + page_start = x; + } + + /* update the scrollbar to match the displayed adjustment */ + if (adjustment) + { + gdouble value; + + adjustment->page_increment = page_size * 0.9; + adjustment->step_increment = page_size * 0.1; + adjustment->page_size = page_size; + + if (GTK_ORIENTATION_VERTICAL == palette->priv->orientation || + GTK_TEXT_DIR_LTR == direction) + { + adjustment->lower = 0; + adjustment->upper = MAX (0, page_start); + + value = MIN (offset, adjustment->upper - adjustment->page_size); + gtk_adjustment_clamp_page (adjustment, value, offset + page_size); + } + else + { + adjustment->lower = page_size - MAX (0, page_start); + adjustment->upper = page_size; + + offset = -offset; + + value = MAX (offset, adjustment->lower); + gtk_adjustment_clamp_page (adjustment, offset, value + page_size); + } + + gtk_adjustment_changed (adjustment); + } +} + +static gboolean +gtk_tool_palette_expose_event (GtkWidget *widget, + GdkEventExpose *event) +{ + GtkToolPalette *palette = GTK_TOOL_PALETTE (widget); + GdkDisplay *display; + cairo_t *cr; + guint i; + + display = gdk_drawable_get_display (widget->window); + + if (!gdk_display_supports_composite (display)) + return FALSE; + + cr = gdk_cairo_create (widget->window); + gdk_cairo_region (cr, event->region); + cairo_clip (cr); + + cairo_push_group (cr); + + for (i = 0; i < palette->priv->groups_length; ++i) + if (palette->priv->groups[i].widget) + _gtk_tool_item_group_paint (palette->priv->groups[i].widget, cr); + + cairo_pop_group_to_source (cr); + + cairo_paint (cr); + cairo_destroy (cr); + + return FALSE; +} + +static void +gtk_tool_palette_realize (GtkWidget *widget) +{ + const gint border_width = GTK_CONTAINER (widget)->border_width; + gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + GdkWindowAttr attributes; + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = widget->allocation.x + border_width; + attributes.y = widget->allocation.y + border_width; + attributes.width = widget->allocation.width - border_width * 2; + attributes.height = widget->allocation.height - border_width * 2; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK + | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + | GDK_BUTTON_MOTION_MASK; + + widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), + &attributes, attributes_mask); + + gdk_window_set_user_data (widget->window, widget); + widget->style = gtk_style_attach (widget->style, widget->window); + gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + + gtk_container_forall (GTK_CONTAINER (widget), + (GtkCallback) gtk_widget_set_parent_window, + widget->window); + + gtk_widget_queue_resize_no_redraw (widget); +} + +static void +gtk_tool_palette_adjustment_value_changed (GtkAdjustment *adjustment G_GNUC_UNUSED, + gpointer data) +{ + GtkWidget *widget = GTK_WIDGET (data); + gtk_tool_palette_size_allocate (widget, &widget->allocation); +} + +static void +gtk_tool_palette_set_scroll_adjustments (GtkWidget *widget, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment) +{ + GtkToolPalette *palette = GTK_TOOL_PALETTE (widget); + + if (palette->priv->hadjustment) + g_object_unref (palette->priv->hadjustment); + if (palette->priv->vadjustment) + g_object_unref (palette->priv->vadjustment); + + if (hadjustment) + g_object_ref_sink (hadjustment); + if (vadjustment) + g_object_ref_sink (vadjustment); + + palette->priv->hadjustment = hadjustment; + palette->priv->vadjustment = vadjustment; + + if (palette->priv->hadjustment) + g_signal_connect (palette->priv->hadjustment, "value-changed", + G_CALLBACK (gtk_tool_palette_adjustment_value_changed), + palette); + if (palette->priv->vadjustment) + g_signal_connect (palette->priv->vadjustment, "value-changed", + G_CALLBACK (gtk_tool_palette_adjustment_value_changed), + palette); +} + +static void +gtk_tool_palette_repack (GtkToolPalette *palette) +{ + guint si, di; + + for (si = di = 0; di < palette->priv->groups_length; ++si) + { + if (palette->priv->groups[si].widget) + { + palette->priv->groups[di] = palette->priv->groups[si]; + ++di; + } + else + --palette->priv->groups_length; + } + + palette->priv->sparse_groups = FALSE; +} + +static void +gtk_tool_palette_add (GtkContainer *container, + GtkWidget *child) +{ + GtkToolPalette *palette; + + g_return_if_fail (GTK_IS_TOOL_PALETTE (container)); + g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (child)); + + palette = GTK_TOOL_PALETTE (container); + + if (palette->priv->groups_length == palette->priv->groups_size) + gtk_tool_palette_repack (palette); + + if (palette->priv->groups_length == palette->priv->groups_size) + { + gsize old_size = palette->priv->groups_size; + gsize new_size = old_size * 2; + + palette->priv->groups = g_renew (GtkToolItemGroupInfo, + palette->priv->groups, + new_size); + + memset (palette->priv->groups + old_size, 0, + sizeof (GtkToolItemGroupInfo) * old_size); + + palette->priv->groups_size = new_size; + } + + palette->priv->groups[palette->priv->groups_length].widget = g_object_ref_sink (child); + palette->priv->groups_length += 1; + + gtk_widget_set_parent (child, GTK_WIDGET (palette)); +} + +static void +gtk_tool_palette_remove (GtkContainer *container, + GtkWidget *child) +{ + GtkToolPalette *palette; + guint i; + + g_return_if_fail (GTK_IS_TOOL_PALETTE (container)); + palette = GTK_TOOL_PALETTE (container); + + for (i = 0; i < palette->priv->groups_length; ++i) + if ((GtkWidget*) palette->priv->groups[i].widget == child) + { + g_object_unref (child); + gtk_widget_unparent (child); + + memset (&palette->priv->groups[i], 0, sizeof (GtkToolItemGroupInfo)); + palette->priv->sparse_groups = TRUE; + } +} + +static void +gtk_tool_palette_forall (GtkContainer *container, + gboolean internals G_GNUC_UNUSED, + GtkCallback callback, + gpointer callback_data) +{ + GtkToolPalette *palette = GTK_TOOL_PALETTE (container); + guint i; + + if (palette->priv->groups) + { + for (i = 0; i < palette->priv->groups_length; ++i) + if (palette->priv->groups[i].widget) + callback (GTK_WIDGET (palette->priv->groups[i].widget), + callback_data); + } +} + +static GType +gtk_tool_palette_child_type (GtkContainer *container G_GNUC_UNUSED) +{ + return GTK_TYPE_TOOL_ITEM_GROUP; +} + +static void +gtk_tool_palette_set_child_property (GtkContainer *container, + GtkWidget *child, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkToolPalette *palette = GTK_TOOL_PALETTE (container); + + switch (prop_id) + { + case CHILD_PROP_EXCLUSIVE: + gtk_tool_palette_set_exclusive (palette, child, g_value_get_boolean (value)); + break; + + case CHILD_PROP_EXPAND: + gtk_tool_palette_set_expand (palette, child, g_value_get_boolean (value)); + break; + + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec); + break; + } +} + +static void +gtk_tool_palette_get_child_property (GtkContainer *container, + GtkWidget *child, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkToolPalette *palette = GTK_TOOL_PALETTE (container); + + switch (prop_id) + { + case CHILD_PROP_EXCLUSIVE: + g_value_set_boolean (value, gtk_tool_palette_get_exclusive (palette, child)); + break; + + case CHILD_PROP_EXPAND: + g_value_set_boolean (value, gtk_tool_palette_get_expand (palette, child)); + break; + + default: + GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, prop_id, pspec); + break; + } +} + +static void +style_change_notify (GtkToolPalette *palette) +{ + GtkToolPalettePrivate* priv = palette->priv; + + if (!priv->style_set) + { + /* pretend it was set, then unset, thus reverting to new default */ + priv->style_set = TRUE; + gtk_tool_palette_unset_style (palette); + } +} + +static void +icon_size_change_notify (GtkToolPalette *palette) +{ + GtkToolPalettePrivate* priv = palette->priv; + + if (!priv->icon_size_set) + { + /* pretend it was set, then unset, thus reverting to new default */ + priv->icon_size_set = TRUE; + gtk_tool_palette_unset_icon_size (palette); + } +} + +static void +gtk_tool_palette_settings_change_notify (GtkSettings *settings, + const GParamSpec *pspec, + GtkToolPalette *palette) +{ + if (! strcmp (pspec->name, "gtk-toolbar-style")) + style_change_notify (palette); + else if (! strcmp (pspec->name, "gtk-toolbar-icon-size")) + icon_size_change_notify (palette); +} + +static void +gtk_tool_palette_screen_changed (GtkWidget *widget, + GdkScreen *previous_screen) +{ + GtkToolPalette *palette = GTK_TOOL_PALETTE (widget); + GtkToolPalettePrivate* priv = palette->priv; + GtkSettings *old_settings = priv->settings; + GtkSettings *settings; + + if (gtk_widget_has_screen (GTK_WIDGET (palette))) + settings = gtk_widget_get_settings (GTK_WIDGET (palette)); + else + settings = NULL; + + if (settings == old_settings) + return; + + if (old_settings) + { + g_signal_handler_disconnect (old_settings, priv->settings_connection); + g_object_unref (old_settings); + } + + if (settings) + { + priv->settings_connection = + g_signal_connect (settings, "notify", + G_CALLBACK (gtk_tool_palette_settings_change_notify), + palette); + priv->settings = g_object_ref (settings); + } + else + priv->settings = NULL; + + gtk_tool_palette_reconfigured (palette); +} + + +static void +gtk_tool_palette_class_init (GtkToolPaletteClass *cls) +{ + GObjectClass *oclass = G_OBJECT_CLASS (cls); + GtkWidgetClass *wclass = GTK_WIDGET_CLASS (cls); + GtkContainerClass *cclass = GTK_CONTAINER_CLASS (cls); + + oclass->set_property = gtk_tool_palette_set_property; + oclass->get_property = gtk_tool_palette_get_property; + oclass->dispose = gtk_tool_palette_dispose; + oclass->finalize = gtk_tool_palette_finalize; + + wclass->size_request = gtk_tool_palette_size_request; + wclass->size_allocate = gtk_tool_palette_size_allocate; + wclass->expose_event = gtk_tool_palette_expose_event; + wclass->realize = gtk_tool_palette_realize; + + cclass->add = gtk_tool_palette_add; + cclass->remove = gtk_tool_palette_remove; + cclass->forall = gtk_tool_palette_forall; + cclass->child_type = gtk_tool_palette_child_type; + cclass->set_child_property = gtk_tool_palette_set_child_property; + cclass->get_child_property = gtk_tool_palette_get_child_property; + + cls->set_scroll_adjustments = gtk_tool_palette_set_scroll_adjustments; + + /* Handle screen-changed so we can update our GtkSettings. + */ + wclass->screen_changed = gtk_tool_palette_screen_changed; + + /** + * GtkToolPalette::set-scroll-adjustments: + * @widget: the GtkToolPalette that received the signal + * @hadjustment: The horizontal adjustment + * @vadjustment: The vertical adjustment + * + * The ::set-scroll-adjustments when FIXME + * + * Since: 2.18 + */ + wclass->set_scroll_adjustments_signal = + g_signal_new ("set-scroll-adjustments", + G_TYPE_FROM_CLASS (oclass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkToolPaletteClass, set_scroll_adjustments), + NULL, NULL, + _gtk_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, 2, + GTK_TYPE_ADJUSTMENT, + GTK_TYPE_ADJUSTMENT); + + g_object_class_override_property (oclass, PROP_ORIENTATION, + "orientation"); + + /** + * GtkToolPalette:icon-size: + * + * The size of the icons in a tool palette is normally determined by + * the toolbar-icon-size setting. When this property is set, it + * overrides the setting. + * + * This should only be used for special-purpose toolbars, normal + * application toolbars should respect the user preferences for the + * size of icons. + * + * Since: 2.18 + */ + g_object_class_install_property (oclass, + PROP_ICON_SIZE, + g_param_spec_enum ("icon-size", + P_("Icon size"), + P_("Size of icons in this tool palette"), + GTK_TYPE_ICON_SIZE, + DEFAULT_ICON_SIZE, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + /** + * GtkToolbar:icon-size-set: + * + * Is %TRUE if the icon-size property has been set. + * + * Since: 2.10 + */ + g_object_class_install_property (oclass, + PROP_ICON_SIZE_SET, + g_param_spec_boolean ("icon-size-set", + P_("Icon size set"), + P_("Whether the icon-size property has been set"), + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_object_class_install_property (oclass, PROP_TOOLBAR_STYLE, + g_param_spec_enum ("toolbar-style", + P_("Toolbar Style"), + P_("Style of items in the tool palette"), + GTK_TYPE_TOOLBAR_STYLE, + DEFAULT_TOOLBAR_STYLE, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + + gtk_container_class_install_child_property (cclass, CHILD_PROP_EXCLUSIVE, + g_param_spec_boolean ("exclusive", + P_("Exclusive"), + P_("Whether the item group should be the only expanded at a given time"), + DEFAULT_CHILD_EXCLUSIVE, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + gtk_container_class_install_child_property (cclass, CHILD_PROP_EXPAND, + g_param_spec_boolean ("expand", + P_("Expand"), + P_("Whether the item group should receive extra space when the palette grows"), + DEFAULT_CHILD_EXPAND, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME | + G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_type_class_add_private (cls, sizeof (GtkToolPalettePrivate)); + + dnd_target_atom_item = gdk_atom_intern_static_string (dnd_targets[0].target); + dnd_target_atom_group = gdk_atom_intern_static_string (dnd_targets[1].target); +} + +/** + * gtk_tool_palette_new: + * + * Creates a new tool palette. + * + * Returns: a new #GtkToolPalette. + * + * Since: 2.18 + */ +GtkWidget* +gtk_tool_palette_new (void) +{ + return g_object_new (GTK_TYPE_TOOL_PALETTE, NULL); +} + +/** + * gtk_tool_palette_set_icon_size: + * @palette: an #GtkToolPalette. + * @icon_size: the #GtkIconSize that icons in the tool palette shall have. + * + * Sets the size of icons in the tool palette. + * + * Since: 2.18 + */ +void +gtk_tool_palette_set_icon_size (GtkToolPalette *palette, + GtkIconSize icon_size) +{ + GtkToolPalettePrivate *priv; + + g_return_if_fail (GTK_IS_TOOL_PALETTE (palette)); + g_return_if_fail (icon_size != GTK_ICON_SIZE_INVALID); + + priv = palette->priv; + + if (!priv->icon_size_set) + { + priv->icon_size_set = TRUE; + g_object_notify (G_OBJECT (palette), "icon-size-set"); + } + + if (priv->icon_size == icon_size) + return; + + priv->icon_size = icon_size; + g_object_notify (G_OBJECT (palette), "icon-size"); + + gtk_tool_palette_reconfigured (palette); + + gtk_widget_queue_resize (GTK_WIDGET (palette)); +} + +static GtkSettings * +toolpalette_get_settings (GtkToolPalette *palette) +{ + GtkToolPalettePrivate *priv = palette->priv; + return priv->settings; +} + +/** + * gtk_tool_palette_unset_icon_size: + * @palette: an #GtkToolPalette. + * + * Unsets the tool palette icon size set with gtk_tool_palette_set_icon_size(), so that + * user preferences will be used to determine the icon size. + * + * Since: 2.18 + */ +void +gtk_tool_palette_unset_icon_size (GtkToolPalette *palette) +{ + GtkToolPalettePrivate* priv = palette->priv; + GtkIconSize size; + + g_return_if_fail (GTK_IS_TOOL_PALETTE (palette)); + + if (palette->priv->icon_size_set) + { + GtkSettings *settings = toolpalette_get_settings (palette); + + if (settings) + { + g_object_get (settings, + "gtk-toolbar-icon-size", &size, + NULL); + } + else + size = DEFAULT_ICON_SIZE; + + if (size != palette->priv->icon_size) + { + gtk_tool_palette_set_icon_size (palette, size); + g_object_notify (G_OBJECT (palette), "icon-size"); + } + + priv->icon_size_set = FALSE; + g_object_notify (G_OBJECT (palette), "icon-size-set"); + } +} + +/* Set the "toolbar-style" property and do appropriate things. + * GtkToolbar does this by emiting a signal instead of just calling a function, + * but I don't see how that is useful. murrayc. + */ +static void +gtk_tool_palette_change_style (GtkToolPalette *palette, + GtkToolbarStyle style) +{ + GtkToolPalettePrivate* priv = palette->priv; + + if (priv->style != style) + { + priv->style = style; + + gtk_tool_palette_reconfigured (palette); + + gtk_widget_queue_resize (GTK_WIDGET (palette)); + g_object_notify (G_OBJECT (palette), "toolbar-style"); + } +} + + +/** + * gtk_tool_palette_set_style: + * @palette: a #GtkToolPalette. + * @style: the #GtkToolbarStyle that items in the tool palette shall have. + * + * Sets the style (text, icons or both) of items in the tool palette. + * + * Since: 2.18 + */ +void +gtk_tool_palette_set_style (GtkToolPalette *palette, + GtkToolbarStyle style) +{ + g_return_if_fail (GTK_IS_TOOL_PALETTE (palette)); + + palette->priv->style_set = TRUE; + gtk_tool_palette_change_style (palette, style); +} + + +/** + * gtk_tool_palette_unset_style: + * @palette: a #GtkToolPalette. + * + * Unsets a toolbar style set with gtk_tool_palette_set_style(), so that + * user preferences will be used to determine the toolbar style. + **/ +void +gtk_tool_palette_unset_style (GtkToolPalette *palette) +{ + GtkToolPalettePrivate* priv = palette->priv; + GtkToolbarStyle style; + + g_return_if_fail (GTK_IS_TOOL_PALETTE (palette)); + + if (priv->style_set) + { + GtkSettings *settings = toolpalette_get_settings (palette); + + if (settings) + g_object_get (settings, + "gtk-toolbar-style", &style, + NULL); + else + style = DEFAULT_TOOLBAR_STYLE; + + if (style != priv->style) + gtk_tool_palette_change_style (palette, style); + + priv->style_set = FALSE; + } +} + +/** + * gtk_tool_palette_get_icon_size: + * @palette: an #GtkToolPalette. + * + * Gets the size of icons in the tool palette. See gtk_tool_palette_set_icon_size(). + * + * Returns: the #GtkIconSize of icons in the tool palette. + * + * Since: 2.18 + */ +GtkIconSize +gtk_tool_palette_get_icon_size (GtkToolPalette *palette) +{ + g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_ICON_SIZE); + return palette->priv->icon_size; +} + +/** + * gtk_tool_palette_get_style: + * @palette: an #GtkToolPalette. + * + * Gets the style (icons, text or both) of items in the tool palette. + * + * Returns: the #GtkToolbarStyle of items in the tool palette. + * + * Since: 2.18 + */ +GtkToolbarStyle +gtk_tool_palette_get_style (GtkToolPalette *palette) +{ + g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_TOOLBAR_STYLE); + return palette->priv->style; +} + +/** + * gtk_tool_palette_set_group_position: + * @palette: an #GtkToolPalette. + * @group: an #GtkToolItemGroup which is a child of palette. + * @position: a new index for group. + * + * Sets the position of the group as an index of the tool palette. + * If position is 0 the group will become the first child, if position is + * -1 it will become the last child. + * + * Since: 2.18 + */ +void +gtk_tool_palette_set_group_position (GtkToolPalette *palette, + GtkWidget *group, + gint position) +{ + GtkToolItemGroupInfo group_info; + gint old_position; + gpointer src, dst; + gsize len; + + g_return_if_fail (GTK_IS_TOOL_PALETTE (palette)); + g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group)); + + gtk_tool_palette_repack (palette); + + g_return_if_fail (position >= -1); + + if (-1 == position) + position = palette->priv->groups_length - 1; + + g_return_if_fail ((guint) position < palette->priv->groups_length); + + if (GTK_TOOL_ITEM_GROUP (group) == palette->priv->groups[position].widget) + return; + + old_position = gtk_tool_palette_get_group_position (palette, group); + g_return_if_fail (old_position >= 0); + + group_info = palette->priv->groups[old_position]; + + if (position < old_position) + { + dst = palette->priv->groups + position + 1; + src = palette->priv->groups + position; + len = old_position - position; + } + else + { + dst = palette->priv->groups + old_position; + src = palette->priv->groups + old_position + 1; + len = position - old_position; + } + + memmove (dst, src, len * sizeof (*palette->priv->groups)); + palette->priv->groups[position] = group_info; + + gtk_widget_queue_resize (GTK_WIDGET (palette)); +} + +static void +gtk_tool_palette_group_notify_collapsed (GtkToolItemGroup *group, + GParamSpec *pspec G_GNUC_UNUSED, + gpointer data) +{ + GtkToolPalette *palette = GTK_TOOL_PALETTE (data); + guint i; + + if (gtk_tool_item_group_get_collapsed (group)) + return; + + for (i = 0; i < palette->priv->groups_size; ++i) + { + GtkToolItemGroup *current_group = palette->priv->groups[i].widget; + + if (current_group && current_group != group) + gtk_tool_item_group_set_collapsed (palette->priv->groups[i].widget, TRUE); + } +} + +/** + * gtk_tool_palette_set_exclusive: + * @palette: an #GtkToolPalette. + * @group: an #GtkToolItemGroup which is a child of palette. + * @exclusive: whether the group should be exclusive or not. + * + * Sets whether the group should be exclusive or not. If an exclusive group is expanded + * all other groups are collapsed. + * + * Since: 2.18 + */ +void +gtk_tool_palette_set_exclusive (GtkToolPalette *palette, + GtkWidget *group, + gboolean exclusive) +{ + GtkToolItemGroupInfo *group_info; + gint position; + + g_return_if_fail (GTK_IS_TOOL_PALETTE (palette)); + g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group)); + + position = gtk_tool_palette_get_group_position (palette, group); + g_return_if_fail (position >= 0); + + group_info = &palette->priv->groups[position]; + + if (exclusive == group_info->exclusive) + return; + + group_info->exclusive = exclusive; + + if (group_info->exclusive != (0 != group_info->notify_collapsed)) + { + if (group_info->exclusive) + { + group_info->notify_collapsed = + g_signal_connect (group, "notify::collapsed", + G_CALLBACK (gtk_tool_palette_group_notify_collapsed), + palette); + } + else + { + g_signal_handler_disconnect (group, group_info->notify_collapsed); + group_info->notify_collapsed = 0; + } + } + + gtk_tool_palette_group_notify_collapsed (group_info->widget, NULL, palette); + gtk_widget_child_notify (group, "exclusive"); +} + +/** + * gtk_tool_palette_set_expand: + * @palette: an #GtkToolPalette. + * @group: an #GtkToolItemGroup which is a child of palette. + * @expand: whether the group should be given extra space. + * + * Sets whether the group should be given extra space. + * + * Since: 2.18 + */ +void +gtk_tool_palette_set_expand (GtkToolPalette *palette, + GtkWidget *group, + gboolean expand) +{ + GtkToolItemGroupInfo *group_info; + gint position; + + g_return_if_fail (GTK_IS_TOOL_PALETTE (palette)); + g_return_if_fail (GTK_IS_TOOL_ITEM_GROUP (group)); + + position = gtk_tool_palette_get_group_position (palette, group); + g_return_if_fail (position >= 0); + + group_info = &palette->priv->groups[position]; + + if (expand != group_info->expand) + { + group_info->expand = expand; + gtk_widget_queue_resize (GTK_WIDGET (palette)); + gtk_widget_child_notify (group, "expand"); + } +} + +/** + * gtk_tool_palette_get_group_position: + * @palette: an #GtkToolPalette. + * @group: an #GtkToolItemGroup. + * + * Gets the position of @group in @palette as index. see gtk_tool_palette_set_group_position(). + * + * Returns: the index of group or -1 if @group is not a child of @palette. + * + * Since: 2.18 + */ +gint +gtk_tool_palette_get_group_position (GtkToolPalette *palette, + GtkWidget *group) +{ + guint i; + + g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), -1); + g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), -1); + + for (i = 0; i < palette->priv->groups_length; ++i) + if ((gpointer) group == palette->priv->groups[i].widget) + return i; + + return -1; +} + +/** + * gtk_tool_palette_get_exclusive: + * @palette: an #GtkToolPalette. + * @group: an #GtkToolItemGroup which is a child of palette. + * + * Gets whether group is exclusive or not. See gtk_tool_palette_set_exclusive(). + * + * Returns: %TRUE if group is exclusive. + * + * Since: 2.18 + */ +gboolean +gtk_tool_palette_get_exclusive (GtkToolPalette *palette, + GtkWidget *group) +{ + gint position; + + g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_CHILD_EXCLUSIVE); + g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), DEFAULT_CHILD_EXCLUSIVE); + + position = gtk_tool_palette_get_group_position (palette, group); + g_return_val_if_fail (position >= 0, DEFAULT_CHILD_EXCLUSIVE); + + return palette->priv->groups[position].exclusive; +} + +/** + * gtk_tool_palette_get_expand: + * @palette: an #GtkToolPalette. + * @group: an #GtkToolItemGroup which is a child of palette. + * + * Gets whether group should be given extra space. See gtk_tool_palette_set_expand(). + * + * Returns: %TRUE if group should be given extra space, %FALSE otherwise. + * + * Since: 2.18 + */ +gboolean +gtk_tool_palette_get_expand (GtkToolPalette *palette, + GtkWidget *group) +{ + gint position; + + g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), DEFAULT_CHILD_EXPAND); + g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (group), DEFAULT_CHILD_EXPAND); + + position = gtk_tool_palette_get_group_position (palette, group); + g_return_val_if_fail (position >= 0, DEFAULT_CHILD_EXPAND); + + return palette->priv->groups[position].expand; +} + +/** + * gtk_tool_palette_get_drop_item: + * @palette: an #GtkToolPalette. + * @x: the x position. + * @y: the y position. + * + * Gets the item at position (x, y). See gtk_tool_palette_get_drop_group(). + * + * Returns: the #GtkToolItem at position or %NULL if there is no such item. + * + * Since: 2.18 + */ +GtkToolItem* +gtk_tool_palette_get_drop_item (GtkToolPalette *palette, + gint x, + gint y) +{ + GtkWidget *group = gtk_tool_palette_get_drop_group (palette, x, y); + + if (group) + return gtk_tool_item_group_get_drop_item (GTK_TOOL_ITEM_GROUP (group), + x - group->allocation.x, + y - group->allocation.y); + + return NULL; +} + +/** + * gtk_tool_palette_get_drop_group: + * @palette: an #GtkToolPalette. + * @x: the x position. + * @y: the y position. + * + * Gets the group at position (x, y). + * + * Returns: the #GtkToolItemGroup at position or %NULL if there is no such group. + * + * Since: 2.18 + */ +GtkWidget* +gtk_tool_palette_get_drop_group (GtkToolPalette *palette, + gint x, + gint y) +{ + GtkAllocation *allocation; + guint i; + + g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), NULL); + + allocation = >K_WIDGET (palette)->allocation; + + g_return_val_if_fail (x >= 0 && x < allocation->width, NULL); + g_return_val_if_fail (y >= 0 && y < allocation->height, NULL); + + for (i = 0; i < palette->priv->groups_length; ++i) + { + GtkToolItemGroupInfo *group = &palette->priv->groups[i]; + GtkWidget *widget; + gint x0, y0; + + if (!group->widget) + continue; + + widget = GTK_WIDGET (group->widget); + + x0 = x - widget->allocation.x; + y0 = y - widget->allocation.y; + + if (x0 >= 0 && x0 < widget->allocation.width && + y0 >= 0 && y0 < widget->allocation.height) + return widget; + } + + return NULL; +} + +/** + * gtk_tool_palette_get_drag_item: + * @palette: an #GtkToolPalette. + * @selection: a #GtkSelectionData. + * + * Get the dragged item from the selection. This could be a #GtkToolItem or + * an #GtkToolItemGroup. + * + * Returns: the dragged item in selection. + * + * Since: 2.18 + */ +GtkWidget* +gtk_tool_palette_get_drag_item (GtkToolPalette *palette, + const GtkSelectionData *selection) +{ + GtkToolPaletteDragData *data; + + g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), NULL); + g_return_val_if_fail (NULL != selection, NULL); + + g_return_val_if_fail (selection->format == 8, NULL); + g_return_val_if_fail (selection->length == sizeof (GtkToolPaletteDragData), NULL); + g_return_val_if_fail (selection->target == dnd_target_atom_item || + selection->target == dnd_target_atom_group, + NULL); + + data = (GtkToolPaletteDragData*) selection->data; + + g_return_val_if_fail (data->palette == palette, NULL); + + if (dnd_target_atom_item == selection->target) + g_return_val_if_fail (GTK_IS_TOOL_ITEM (data->item), NULL); + else if (dnd_target_atom_group == selection->target) + g_return_val_if_fail (GTK_IS_TOOL_ITEM_GROUP (data->item), NULL); + + return data->item; +} + +/** + * gtk_tool_palette_set_drag_source: + * @palette: an #GtkToolPalette. + * @targets: the #GtkToolPaletteDragTargets which the widget should support. + * + * Sets the tool palette as a drag source. Enables all groups and items in + * the tool palette as drag sources on button 1 and button 3 press with copy + * and move actions. + * + * See gtk_drag_source_set(). + * + * Since: 2.18 + * + */ +void +gtk_tool_palette_set_drag_source (GtkToolPalette *palette, + GtkToolPaletteDragTargets targets) +{ + guint i; + + g_return_if_fail (GTK_IS_TOOL_PALETTE (palette)); + + if ((palette->priv->drag_source & targets) == targets) + return; + + palette->priv->drag_source |= targets; + + for (i = 0; i < palette->priv->groups_length; ++i) + { + if (palette->priv->groups[i].widget) + gtk_container_forall (GTK_CONTAINER (palette->priv->groups[i].widget), + _gtk_tool_palette_child_set_drag_source, + palette); + } +} + +/** + * gtk_tool_palette_add_drag_dest: + * @palette: an #GtkToolPalette. + * @widget: a #GtkWidget which should be a drag destination for palette. + * @flags: the flags that specify what actions GTK+ should take for drops on that widget. + * @targets: the #GtkToolPaletteDragTargets which the widget should support. + * @actions: the #GdkDragAction<!-- -->s which the widget should suppport. + * + * Sets the tool palette as drag source (see gtk_tool_palette_set_drag_source) and + * sets widget as a drag destination for drags from palette. With flags the actions + * (like highlighting and target checking) which should be performed by GTK+ for + * drops on widget can be specified. With targets the supported drag targets + * (groups and/or items) can be specified. With actions the supported drag actions + * (copy and move) can be specified. + * + * See gtk_drag_dest_set(). + * + * Since: 2.18 + */ +void +gtk_tool_palette_add_drag_dest (GtkToolPalette *palette, + GtkWidget *widget, + GtkDestDefaults flags, + GtkToolPaletteDragTargets targets, + GdkDragAction actions) +{ + GtkTargetEntry entries[G_N_ELEMENTS (dnd_targets)]; + gint n_entries = 0; + + g_return_if_fail (GTK_IS_TOOL_PALETTE (palette)); + g_return_if_fail (GTK_IS_WIDGET (widget)); + + gtk_tool_palette_set_drag_source (palette, + targets); + + if (targets & GTK_TOOL_PALETTE_DRAG_ITEMS) + entries[n_entries++] = dnd_targets[0]; + if (targets & GTK_TOOL_PALETTE_DRAG_GROUPS) + entries[n_entries++] = dnd_targets[1]; + + gtk_drag_dest_set (widget, flags, entries, n_entries, actions); +} + +void +_gtk_tool_palette_get_item_size (GtkToolPalette *palette, + GtkRequisition *item_size, + gboolean homogeneous_only, + gint *requested_rows) +{ + GtkRequisition max_requisition; + gint max_rows; + guint i; + + g_return_if_fail (GTK_IS_TOOL_PALETTE (palette)); + g_return_if_fail (NULL != item_size); + + max_requisition.width = 0; + max_requisition.height = 0; + max_rows = 0; + + /* iterate over all groups and calculate the max item_size and max row request */ + for (i = 0; i < palette->priv->groups_length; ++i) + { + GtkRequisition requisition; + gint rows; + GtkToolItemGroupInfo *group = &palette->priv->groups[i]; + + if (!group->widget) + continue; + + _gtk_tool_item_group_item_size_request (group->widget, &requisition, homogeneous_only, &rows); + + max_requisition.width = MAX (max_requisition.width, requisition.width); + max_requisition.height = MAX (max_requisition.height, requisition.height); + max_rows = MAX (max_rows, rows); + } + + *item_size = max_requisition; + if (requested_rows) + *requested_rows = max_rows; +} + +static void +gtk_tool_palette_item_drag_data_get (GtkWidget *widget, + GdkDragContext *context G_GNUC_UNUSED, + GtkSelectionData *selection, + guint info G_GNUC_UNUSED, + guint time G_GNUC_UNUSED, + gpointer data) +{ + GtkToolPaletteDragData drag_data = { GTK_TOOL_PALETTE (data), NULL }; + + if (selection->target == dnd_target_atom_item) + drag_data.item = gtk_widget_get_ancestor (widget, GTK_TYPE_TOOL_ITEM); + + if (drag_data.item) + gtk_selection_data_set (selection, selection->target, 8, + (guchar*) &drag_data, sizeof (drag_data)); +} + +static void +gtk_tool_palette_child_drag_data_get (GtkWidget *widget, + GdkDragContext *context G_GNUC_UNUSED, + GtkSelectionData *selection, + guint info G_GNUC_UNUSED, + guint time G_GNUC_UNUSED, + gpointer data) +{ + GtkToolPaletteDragData drag_data = { GTK_TOOL_PALETTE (data), NULL }; + + if (selection->target == dnd_target_atom_group) + drag_data.item = gtk_widget_get_ancestor (widget, GTK_TYPE_TOOL_ITEM_GROUP); + + if (drag_data.item) + gtk_selection_data_set (selection, selection->target, 8, + (guchar*) &drag_data, sizeof (drag_data)); +} + +void +_gtk_tool_palette_child_set_drag_source (GtkWidget *child, + gpointer data) +{ + GtkToolPalette *palette = GTK_TOOL_PALETTE (data); + + /* Check drag_source, + * to work properly when called from gtk_tool_item_group_insert(). + */ + if (!palette->priv->drag_source) + return; + + if (GTK_IS_TOOL_ITEM (child) && + (palette->priv->drag_source & GTK_TOOL_PALETTE_DRAG_ITEMS)) + { + /* Connect to child instead of the item itself, + * to work arround bug 510377. + */ + if (GTK_IS_TOOL_BUTTON (child)) + child = gtk_bin_get_child (GTK_BIN (child)); + + if (!child) + return; + + gtk_drag_source_set (child, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK, + &dnd_targets[0], 1, GDK_ACTION_COPY | GDK_ACTION_MOVE); + + g_signal_connect (child, "drag-data-get", + G_CALLBACK (gtk_tool_palette_item_drag_data_get), + palette); + } + else if (GTK_IS_BUTTON (child) && + (palette->priv->drag_source & GTK_TOOL_PALETTE_DRAG_GROUPS)) + { + gtk_drag_source_set (child, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK, + &dnd_targets[1], 1, GDK_ACTION_COPY | GDK_ACTION_MOVE); + + g_signal_connect (child, "drag-data-get", + G_CALLBACK (gtk_tool_palette_child_drag_data_get), + palette); + } +} + +/** + * gtk_tool_palette_get_drag_target_item: + * + * Get the target entry for a dragged #GtkToolItem. + * + * Returns: the #GtkTargetEntry for a dragged item. + * + * Since: 2.18 + */ +G_CONST_RETURN GtkTargetEntry* +gtk_tool_palette_get_drag_target_item (void) +{ + return &dnd_targets[0]; +} + +/** + * gtk_tool_palette_get_drag_target_group: + * + * Get the target entry for a dragged #GtkToolItemGroup. + * + * Returns: the #GtkTargetEntry for a dragged group. + * + * Since: 2.18 + */ +G_CONST_RETURN GtkTargetEntry* +gtk_tool_palette_get_drag_target_group (void) +{ + return &dnd_targets[1]; +} + +void +_gtk_tool_palette_set_expanding_child (GtkToolPalette *palette, + GtkWidget *widget) +{ + g_return_if_fail (GTK_IS_TOOL_PALETTE (palette)); + palette->priv->expanding_child = widget; +} + +GtkAdjustment* +gtk_tool_palette_get_hadjustment (GtkToolPalette *palette) +{ + g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), NULL); + return palette->priv->hadjustment; +} + +GtkAdjustment* +gtk_tool_palette_get_vadjustment (GtkToolPalette *palette) +{ + g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), NULL); + return palette->priv->vadjustment; +} + +GtkSizeGroup * +_gtk_tool_palette_get_size_group (GtkToolPalette *palette) +{ + g_return_val_if_fail (GTK_IS_TOOL_PALETTE (palette), NULL); + + return palette->priv->text_size_group; +} diff --git a/gtk/gtktoolpalette.h b/gtk/gtktoolpalette.h new file mode 100644 index 0000000000..a7b32507fd --- /dev/null +++ b/gtk/gtktoolpalette.h @@ -0,0 +1,140 @@ +/* GtkToolPalette -- A tool palette with categories and DnD support + * Copyright (C) 2008 Openismus GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Mathias Hasselmann + */ + +#ifndef __GTK_TOOL_PALETTE_H__ +#define __GTK_TOOL_PALETTE_H__ + +#include <gtk/gtkcontainer.h> +#include <gtk/gtkdnd.h> +#include <gtk/gtktoolitem.h> + +G_BEGIN_DECLS + +#define GTK_TYPE_TOOL_PALETTE (gtk_tool_palette_get_type()) +#define GTK_TOOL_PALETTE(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, GTK_TYPE_TOOL_PALETTE, GtkToolPalette)) +#define GTK_TOOL_PALETTE_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST(cls, GTK_TYPE_TOOL_PALETTE, GtkToolPaletteClass)) +#define GTK_IS_TOOL_PALETTE(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, GTK_TYPE_TOOL_PALETTE)) +#define GTK_IS_TOOL_PALETTE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE(obj, GTK_TYPE_TOOL_PALETTE)) +#define GTK_TOOL_PALETTE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_TOOL_PALETTE, GtkToolPaletteClass)) + +typedef struct _GtkToolPalette GtkToolPalette; +typedef struct _GtkToolPaletteClass GtkToolPaletteClass; +typedef struct _GtkToolPalettePrivate GtkToolPalettePrivate; + +/** + * GtkToolPaletteDragTargets: + * @GTK_TOOL_PALETTE_DRAG_ITEMS: Support drag of items. + * @GTK_TOOL_PALETTE_DRAG_GROUPS: Support drag of groups. + * + * Flags used to specify the supported drag targets. + */ +typedef enum /*< flags >*/ +{ + GTK_TOOL_PALETTE_DRAG_ITEMS = (1 << 0), + GTK_TOOL_PALETTE_DRAG_GROUPS = (1 << 1) +} +GtkToolPaletteDragTargets; + +/** + * GtkToolPalette: + * + * This should not be accessed directly. Use the accessor functions below. + */ +struct _GtkToolPalette +{ + GtkContainer parent_instance; + GtkToolPalettePrivate *priv; +}; + +struct _GtkToolPaletteClass +{ + GtkContainerClass parent_class; + + void (*set_scroll_adjustments) (GtkWidget *widget, + GtkAdjustment *hadjustment, + GtkAdjustment *vadjustment); + + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); + void (*_gtk_reserved5) (void); + void (*_gtk_reserved6) (void); +}; + +GType gtk_tool_palette_get_type (void) G_GNUC_CONST; +GtkWidget* gtk_tool_palette_new (void); + +void gtk_tool_palette_set_group_position (GtkToolPalette *palette, + GtkWidget *group, + gint position); +void gtk_tool_palette_set_exclusive (GtkToolPalette *palette, + GtkWidget *group, + gboolean exclusive); +void gtk_tool_palette_set_expand (GtkToolPalette *palette, + GtkWidget *group, + gboolean expand); + +gint gtk_tool_palette_get_group_position (GtkToolPalette *palette, + GtkWidget *group); +gboolean gtk_tool_palette_get_exclusive (GtkToolPalette *palette, + GtkWidget *group); +gboolean gtk_tool_palette_get_expand (GtkToolPalette *palette, + GtkWidget *group); + +void gtk_tool_palette_set_icon_size (GtkToolPalette *palette, + GtkIconSize icon_size); +void gtk_tool_palette_unset_icon_size (GtkToolPalette *palette); +void gtk_tool_palette_set_style (GtkToolPalette *palette, + GtkToolbarStyle style); +void gtk_tool_palette_unset_style (GtkToolPalette *palette); + +GtkIconSize gtk_tool_palette_get_icon_size (GtkToolPalette *palette); +GtkToolbarStyle gtk_tool_palette_get_style (GtkToolPalette *palette); + +GtkToolItem* gtk_tool_palette_get_drop_item (GtkToolPalette *palette, + gint x, + gint y); +GtkWidget* gtk_tool_palette_get_drop_group (GtkToolPalette *palette, + gint x, + gint y); +GtkWidget* gtk_tool_palette_get_drag_item (GtkToolPalette *palette, + const GtkSelectionData *selection); + +void gtk_tool_palette_set_drag_source (GtkToolPalette *palette, + GtkToolPaletteDragTargets targets); +void gtk_tool_palette_add_drag_dest (GtkToolPalette *palette, + GtkWidget *widget, + GtkDestDefaults flags, + GtkToolPaletteDragTargets targets, + GdkDragAction actions); + +GtkAdjustment* gtk_tool_palette_get_hadjustment (GtkToolPalette *palette); +GtkAdjustment* gtk_tool_palette_get_vadjustment (GtkToolPalette *palette); + +G_CONST_RETURN GtkTargetEntry* gtk_tool_palette_get_drag_target_item (void) G_GNUC_CONST; +G_CONST_RETURN GtkTargetEntry* gtk_tool_palette_get_drag_target_group (void) G_GNUC_CONST; + + +G_END_DECLS + +#endif /* __GTK_TOOL_PALETTE_H__ */ diff --git a/gtk/gtktoolpaletteprivate.h b/gtk/gtktoolpaletteprivate.h new file mode 100644 index 0000000000..6771d287c7 --- /dev/null +++ b/gtk/gtktoolpaletteprivate.h @@ -0,0 +1,55 @@ +/* GtkToolPalette -- A tool palette with categories and DnD support + * Copyright (C) 2008 Openismus GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Mathias Hasselmann + */ + +#ifndef __GTK_TOOL_PALETTE_PRIVATE_H__ +#define __GTK_TOOL_PALETTE_PRIVATE_H__ + +#include <gtk/gtk.h> + +void _gtk_tool_palette_get_item_size (GtkToolPalette *palette, + GtkRequisition *item_size, + gboolean homogeneous_only, + gint *requested_rows); +void _gtk_tool_palette_child_set_drag_source (GtkWidget *widget, + gpointer data); +void _gtk_tool_palette_set_expanding_child (GtkToolPalette *palette, + GtkWidget *widget); + +void _gtk_tool_item_group_palette_reconfigured (GtkToolItemGroup *group); +void _gtk_tool_item_group_item_size_request (GtkToolItemGroup *group, + GtkRequisition *item_size, + gboolean homogeneous_only, + gint *requested_rows); +gint _gtk_tool_item_group_get_height_for_width (GtkToolItemGroup *group, + gint width); +gint _gtk_tool_item_group_get_width_for_height (GtkToolItemGroup *group, + gint height); +void _gtk_tool_item_group_paint (GtkToolItemGroup *group, + cairo_t *cr); +gint _gtk_tool_item_group_get_size_for_limit (GtkToolItemGroup *group, + gint limit, + gboolean vertical, + gboolean animation); + + +GtkSizeGroup *_gtk_tool_palette_get_size_group (GtkToolPalette *palette); + +#endif /* __GTK_TOOL_PALETTE_PRIVATE_H__ */ diff --git a/gtk/gtktoolshell.c b/gtk/gtktoolshell.c index 955d84bfc4..ec1c10b5ca 100644 --- a/gtk/gtktoolshell.c +++ b/gtk/gtktoolshell.c @@ -156,5 +156,97 @@ gtk_tool_shell_rebuild_menu (GtkToolShell *shell) iface->rebuild_menu (shell); } +/** + * gtk_tool_shell_get_text_orientation: + * @shell: a #GtkToolShell + * + * Retrieves the current text orientation for the tool shell. Tool items must not + * call this function directly, but rely on gtk_tool_item_get_text_orientation() + * instead. + * + * Return value: the current text orientation of @shell + * + * Since: 2.14 + **/ +GtkOrientation +gtk_tool_shell_get_text_orientation (GtkToolShell *shell) +{ + GtkToolShellIface *iface = GTK_TOOL_SHELL_GET_IFACE (shell); + + if (iface->get_text_orientation) + return GTK_TOOL_SHELL_GET_IFACE (shell)->get_text_orientation (shell); + + return GTK_ORIENTATION_HORIZONTAL; +} + +/** + * gtk_tool_shell_get_text_alignment: + * @shell: a #GtkToolShell + * + * Retrieves the current text alignment for the tool shell. Tool items must not + * call this function directly, but rely on gtk_tool_item_get_text_alignment() + * instead. + * + * Return value: the current text alignment of @shell + * + * Since: 2.14 + **/ +gfloat +gtk_tool_shell_get_text_alignment (GtkToolShell *shell) +{ + GtkToolShellIface *iface = GTK_TOOL_SHELL_GET_IFACE (shell); + + if (iface->get_text_alignment) + return GTK_TOOL_SHELL_GET_IFACE (shell)->get_text_alignment (shell); + + return 0.5f; +} + +/** + * gtk_tool_shell_get_ellipsize_mode + * @shell: a #GtkToolShell + * + * Retrieves the current ellipsize mode for the tool shell. Tool items must not + * call this function directly, but rely on gtk_tool_item_get_ellipsize_mode() + * instead. + * + * Return value: the current ellipsize mode of @shell + * + * Since: 2.14 + **/ +PangoEllipsizeMode +gtk_tool_shell_get_ellipsize_mode (GtkToolShell *shell) +{ + GtkToolShellIface *iface = GTK_TOOL_SHELL_GET_IFACE (shell); + + if (iface->get_ellipsize_mode) + return GTK_TOOL_SHELL_GET_IFACE (shell)->get_ellipsize_mode (shell); + + return PANGO_ELLIPSIZE_NONE; +} + +/** + * gtk_tool_shell_get_text_size_group: + * @shell: a #GtkToolShell + * + * Retrieves the current text size group for the tool shell. Tool items must not + * call this function directly, but rely on gtk_tool_item_get_text_size_group() + * instead. + * + * Return value: the current text size group of @shell + * + * Since: 2.14 + **/ +GtkSizeGroup * +gtk_tool_shell_get_text_size_group (GtkToolShell *shell) +{ + GtkToolShellIface *iface = GTK_TOOL_SHELL_GET_IFACE (shell); + + if (iface->get_text_size_group) + return GTK_TOOL_SHELL_GET_IFACE (shell)->get_text_size_group (shell); + + return NULL; +} + #define __GTK_TOOL_SHELL_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtktoolshell.h b/gtk/gtktoolshell.h index be49304445..0605a4006b 100644 --- a/gtk/gtktoolshell.h +++ b/gtk/gtktoolshell.h @@ -29,6 +29,8 @@ #include <gtk/gtkenums.h> +#include <pango/pango.h> +#include <gtk/gtksizegroup.h> G_BEGIN_DECLS @@ -43,11 +45,15 @@ typedef struct _GtkToolShellIface GtkToolShellIface; /** * GtkToolShellIface: - * @get_icon_size: mandatory implementation of gtk_tool_shell_get_icon_size(). - * @get_orientation: mandatory implementation of gtk_tool_shell_get_orientation(). - * @get_style: mandatory implementation of gtk_tool_shell_get_style(). - * @get_relief_style: optional implementation of gtk_tool_shell_get_relief_style(). - * @rebuild_menu: optional implementation of gtk_tool_shell_rebuild_menu(). + * @get_icon_size: mandatory implementation of gtk_tool_shell_get_icon_size(). + * @get_orientation: mandatory implementation of gtk_tool_shell_get_orientation(). + * @get_style: mandatory implementation of gtk_tool_shell_get_style(). + * @get_relief_style: optional implementation of gtk_tool_shell_get_relief_style(). + * @rebuild_menu: optional implementation of gtk_tool_shell_rebuild_menu(). + * @get_text_orientation: optional implementation of gtk_tool_shell_get_text_orientation(). + * @get_text_alignment: optional implementation of gtk_tool_shell_get_text_alignment(). + * @get_ellipsize_mode: optional implementation of gtk_tool_shell_get_ellipsize_mode(). + * @get_text_size_group: optional implementation of gtk_tool_shell_get_text_size_group(). * * Virtual function table for the #GtkToolShell interface. */ @@ -57,20 +63,28 @@ struct _GtkToolShellIface GTypeInterface g_iface; /*< public >*/ - GtkIconSize (*get_icon_size) (GtkToolShell *shell); - GtkOrientation (*get_orientation) (GtkToolShell *shell); - GtkToolbarStyle (*get_style) (GtkToolShell *shell); - GtkReliefStyle (*get_relief_style) (GtkToolShell *shell); - void (*rebuild_menu) (GtkToolShell *shell); + GtkIconSize (*get_icon_size) (GtkToolShell *shell); + GtkOrientation (*get_orientation) (GtkToolShell *shell); + GtkToolbarStyle (*get_style) (GtkToolShell *shell); + GtkReliefStyle (*get_relief_style) (GtkToolShell *shell); + void (*rebuild_menu) (GtkToolShell *shell); + GtkOrientation (*get_text_orientation) (GtkToolShell *shell); + gfloat (*get_text_alignment) (GtkToolShell *shell); + PangoEllipsizeMode (*get_ellipsize_mode) (GtkToolShell *shell); + GtkSizeGroup * (*get_text_size_group) (GtkToolShell *shell); }; -GType gtk_tool_shell_get_type (void) G_GNUC_CONST; +GType gtk_tool_shell_get_type (void) G_GNUC_CONST; -GtkIconSize gtk_tool_shell_get_icon_size (GtkToolShell *shell); -GtkOrientation gtk_tool_shell_get_orientation (GtkToolShell *shell); -GtkToolbarStyle gtk_tool_shell_get_style (GtkToolShell *shell); -GtkReliefStyle gtk_tool_shell_get_relief_style (GtkToolShell *shell); -void gtk_tool_shell_rebuild_menu (GtkToolShell *shell); +GtkIconSize gtk_tool_shell_get_icon_size (GtkToolShell *shell); +GtkOrientation gtk_tool_shell_get_orientation (GtkToolShell *shell); +GtkToolbarStyle gtk_tool_shell_get_style (GtkToolShell *shell); +GtkReliefStyle gtk_tool_shell_get_relief_style (GtkToolShell *shell); +void gtk_tool_shell_rebuild_menu (GtkToolShell *shell); +GtkOrientation gtk_tool_shell_get_text_orientation (GtkToolShell *shell); +gfloat gtk_tool_shell_get_text_alignment (GtkToolShell *shell); +PangoEllipsizeMode gtk_tool_shell_get_ellipsize_mode (GtkToolShell *shell); +GtkSizeGroup * gtk_tool_shell_get_text_size_group (GtkToolShell *shell); G_END_DECLS |