diff options
Diffstat (limited to 'shell/e-shell-switcher.c')
-rw-r--r-- | shell/e-shell-switcher.c | 700 |
1 files changed, 700 insertions, 0 deletions
diff --git a/shell/e-shell-switcher.c b/shell/e-shell-switcher.c new file mode 100644 index 0000000000..2980bd983d --- /dev/null +++ b/shell/e-shell-switcher.c @@ -0,0 +1,700 @@ +/* + * e-shell-switcher.c + * + * This program 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 of the License, or (at your option) version 3. + * + * This program 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 the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-shell-switcher.h" + +#include <glib/gi18n.h> + +#define E_SHELL_SWITCHER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SHELL_SWITCHER, EShellSwitcherPrivate)) + +#define H_PADDING 6 +#define V_PADDING 6 + +struct _EShellSwitcherPrivate { + GList *proxies; + gboolean style_set; + GtkToolbarStyle style; + GtkSettings *settings; + gulong settings_handler_id; + gboolean toolbar_visible; +}; + +enum { + PROP_0, + PROP_TOOLBAR_STYLE, + PROP_TOOLBAR_VISIBLE +}; + +enum { + STYLE_CHANGED, + LAST_SIGNAL +}; + +static gpointer parent_class; +static guint signals[LAST_SIGNAL]; + +static gint +shell_switcher_layout_actions (EShellSwitcher *switcher) +{ + GtkAllocation *allocation; + int num_btns = g_list_length (switcher->priv->proxies), btns_per_row; + GList **rows, *p; + gboolean icons_only; + gint row_number; + gint max_width = 0; + gint max_height = 0; + gint row_last; + gint x, y; + gint i; + + allocation = >K_WIDGET (switcher)->allocation; + y = allocation->y + allocation->height; + + if (num_btns == 0) + return allocation->height; + + icons_only = (switcher->priv->style == GTK_TOOLBAR_ICONS); + + /* Figure out the max width and height. */ + for (p = switcher->priv->proxies; p != NULL; p = p->next) { + GtkWidget *widget = p->data; + GtkRequisition requisition; + + gtk_widget_size_request (widget, &requisition); + max_height = MAX (max_height, requisition.height); + max_width = MAX (max_width, requisition.width); + } + + /* Figure out how many rows and columns we'll use. */ + btns_per_row = MAX (1, allocation->width / (max_width + H_PADDING)); + if (!icons_only) { + /* If using text buttons, we want to try to have a + * completely filled-in grid, but if we can't, we want + * the odd row to have just a single button. */ + while (num_btns % btns_per_row > 1) + btns_per_row--; + } + + /* Assign buttons to rows. */ + rows = g_new0 (GList *, num_btns / btns_per_row + 1); + + if (!icons_only && num_btns % btns_per_row != 0) { + rows[0] = g_list_append (rows[0], switcher->priv->proxies->data); + + p = switcher->priv->proxies->next; + row_number = p ? 1 : 0; + } else { + p = switcher->priv->proxies; + row_number = 0; + } + + for (; p != NULL; p = p->next) { + GtkWidget *widget = p->data; + + if (g_list_length (rows[row_number]) == btns_per_row) + row_number++; + + rows[row_number] = g_list_append (rows[row_number], widget); + } + + row_last = row_number; + + /* Layout the buttons. */ + for (i = row_last; i >= 0; i--) { + int len, extra_width; + + x = H_PADDING + allocation->x; + y -= max_height; + len = g_list_length (rows[i]); + if (!icons_only) + extra_width = (allocation->width - (len * max_width ) - (len * H_PADDING)) / len; + else + extra_width = 0; + for (p = rows [i]; p != NULL; p = p->next) { + GtkAllocation child_allocation; + + child_allocation.x = x; + child_allocation.y = y; + child_allocation.width = max_width + extra_width; + child_allocation.height = max_height; + + gtk_widget_size_allocate (GTK_WIDGET (p->data), &child_allocation); + + x += child_allocation.width + H_PADDING; + } + + y -= V_PADDING; + } + + for (i = 0; i <= row_last; i ++) + g_list_free (rows [i]); + g_free (rows); + + return y - allocation->y; +} + +static void +shell_switcher_toolbar_style_changed_cb (EShellSwitcher *switcher) +{ + if (!switcher->priv->style_set) { + switcher->priv->style_set = TRUE; + e_shell_switcher_unset_style (switcher); + } +} + +static void +shell_switcher_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_TOOLBAR_STYLE: + e_shell_switcher_set_style ( + E_SHELL_SWITCHER (object), + g_value_get_enum (value)); + return; + + case PROP_TOOLBAR_VISIBLE: + e_shell_switcher_set_visible ( + E_SHELL_SWITCHER (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +shell_switcher_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_TOOLBAR_STYLE: + g_value_set_enum ( + value, e_shell_switcher_get_style ( + E_SHELL_SWITCHER (object))); + return; + + case PROP_TOOLBAR_VISIBLE: + g_value_set_boolean ( + value, e_shell_switcher_get_visible ( + E_SHELL_SWITCHER (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +shell_switcher_dispose (GObject *object) +{ + EShellSwitcherPrivate *priv; + + priv = E_SHELL_SWITCHER_GET_PRIVATE (object); + + while (priv->proxies != NULL) { + GtkWidget *widget = priv->proxies->data; + gtk_container_remove (GTK_CONTAINER (object), widget); + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +shell_switcher_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + EShellSwitcherPrivate *priv; + GtkWidget *child; + GList *iter; + + priv = E_SHELL_SWITCHER_GET_PRIVATE (widget); + + requisition->width = 0; + requisition->height = 0; + + child = gtk_bin_get_child (GTK_BIN (widget)); + if (child != NULL) + gtk_widget_size_request (child, requisition); + + if (!priv->toolbar_visible) + return; + + for (iter = priv->proxies; iter != NULL; iter = iter->next) { + GtkWidget *widget = iter->data; + GtkRequisition child_requisition; + + gtk_widget_size_request (widget, &child_requisition); + + child_requisition.width += H_PADDING; + child_requisition.height += V_PADDING; + + requisition->width = MAX ( + requisition->width, child_requisition.width); + requisition->height += child_requisition.height; + } +} + +static void +shell_switcher_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + EShellSwitcher *switcher; + GtkAllocation child_allocation; + GtkWidget *child; + gint height; + + switcher = E_SHELL_SWITCHER (widget); + + widget->allocation = *allocation; + + if (switcher->priv->toolbar_visible) + height = shell_switcher_layout_actions (switcher); + else + height = allocation->height; + + child_allocation.x = allocation->x; + child_allocation.y = allocation->y; + child_allocation.width = allocation->width; + child_allocation.height = height; + + child = gtk_bin_get_child (GTK_BIN (widget)); + if (child != NULL) + gtk_widget_size_allocate (child, &child_allocation); +} + +static void +shell_switcher_screen_changed (GtkWidget *widget, + GdkScreen *previous_screen) +{ + EShellSwitcherPrivate *priv; + GtkSettings *settings; + + priv = E_SHELL_SWITCHER_GET_PRIVATE (widget); + + if (gtk_widget_has_screen (widget)) + settings = gtk_widget_get_settings (widget); + else + settings = NULL; + + if (settings == priv->settings) + return; + + if (priv->settings != NULL) { + g_signal_handler_disconnect ( + priv->settings, priv->settings_handler_id); + g_object_unref (priv->settings); + } + + if (settings != NULL) { + priv->settings = g_object_ref (settings); + priv->settings_handler_id = g_signal_connect_swapped ( + settings, "notify::gtk-toolbar-style", + G_CALLBACK (shell_switcher_toolbar_style_changed_cb), + widget); + } else + priv->settings = NULL; + + shell_switcher_toolbar_style_changed_cb (E_SHELL_SWITCHER (widget)); +} + +static void +shell_switcher_remove (GtkContainer *container, + GtkWidget *widget) +{ + EShellSwitcherPrivate *priv; + GList *link; + + priv = E_SHELL_SWITCHER_GET_PRIVATE (container); + + /* Look in the internal widgets first. */ + + link = g_list_find (priv->proxies, widget); + if (link != NULL) { + GtkWidget *widget = link->data; + + gtk_widget_unparent (widget); + priv->proxies = g_list_delete_link (priv->proxies, link); + gtk_widget_queue_resize (GTK_WIDGET (container)); + return; + } + + /* Chain up to parent's remove() method. */ + GTK_CONTAINER_CLASS (parent_class)->remove (container, widget); +} + +static void +shell_switcher_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + EShellSwitcherPrivate *priv; + + priv = E_SHELL_SWITCHER_GET_PRIVATE (container); + + if (include_internals) + g_list_foreach ( + priv->proxies, (GFunc) callback, callback_data); + + /* Chain up to parent's forall() method. */ + GTK_CONTAINER_CLASS (parent_class)->forall ( + container, include_internals, callback, callback_data); +} + +static void +shell_switcher_style_changed (EShellSwitcher *switcher, + GtkToolbarStyle style) +{ + if (switcher->priv->style == style) + return; + + switcher->priv->style = style; + + g_list_foreach ( + switcher->priv->proxies, + (GFunc) gtk_tool_item_toolbar_reconfigured, NULL); + + gtk_widget_queue_resize (GTK_WIDGET (switcher)); + g_object_notify (G_OBJECT (switcher), "toolbar-style"); +} + +static GtkIconSize +shell_switcher_get_icon_size (GtkToolShell *shell) +{ + return GTK_ICON_SIZE_LARGE_TOOLBAR; +} + +static GtkOrientation +shell_switcher_get_orientation (GtkToolShell *shell) +{ + return GTK_ORIENTATION_HORIZONTAL; +} + +static GtkToolbarStyle +shell_switcher_get_style (GtkToolShell *shell) +{ + return e_shell_switcher_get_style (E_SHELL_SWITCHER (shell)); +} + +static GtkReliefStyle +shell_switcher_get_relief_style (GtkToolShell *shell) +{ + return GTK_RELIEF_NORMAL; +} + +static void +shell_switcher_class_init (EShellSwitcherClass *class) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EShellSwitcherPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = shell_switcher_set_property; + object_class->get_property = shell_switcher_get_property; + object_class->dispose = shell_switcher_dispose; + + widget_class = GTK_WIDGET_CLASS (class); + widget_class->size_request = shell_switcher_size_request; + widget_class->size_allocate = shell_switcher_size_allocate; + widget_class->screen_changed = shell_switcher_screen_changed; + + container_class = GTK_CONTAINER_CLASS (class); + container_class->remove = shell_switcher_remove; + container_class->forall = shell_switcher_forall; + + class->style_changed = shell_switcher_style_changed; + + /** + * EShellSwitcher:toolbar-style + * + * The switcher's toolbar style. + **/ + g_object_class_install_property ( + object_class, + PROP_TOOLBAR_STYLE, + g_param_spec_enum ( + "toolbar-style", + _("Toolbar Style"), + _("The switcher's toolbar style"), + GTK_TYPE_TOOLBAR_STYLE, + E_SHELL_SWITCHER_DEFAULT_TOOLBAR_STYLE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + /** + * EShellSwitcher:toolbar-visible + * + * Whether the switcher is visible. + **/ + g_object_class_install_property ( + object_class, + PROP_TOOLBAR_VISIBLE, + g_param_spec_boolean ( + "toolbar-visible", + _("Toolbar Visible"), + _("Whether the switcher is visible"), + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + /** + * EShellSwitcher::style-changed + * @switcher: the #EShellSwitcher which emitted the signal + * @style: the new #GtkToolbarStyle of the switcher + * + * Emitted when the style of the switcher changes. + **/ + signals[STYLE_CHANGED] = g_signal_new ( + "style-changed", + G_OBJECT_CLASS_TYPE (class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EShellSwitcherClass, style_changed), + NULL, NULL, + g_cclosure_marshal_VOID__ENUM, + G_TYPE_NONE, 1, + GTK_TYPE_TOOLBAR_STYLE); +} + +static void +shell_switcher_init (EShellSwitcher *switcher) +{ + switcher->priv = E_SHELL_SWITCHER_GET_PRIVATE (switcher); + + GTK_WIDGET_SET_FLAGS (switcher, GTK_NO_WINDOW); +} + +static void +shell_switcher_tool_shell_iface_init (GtkToolShellIface *iface) +{ + iface->get_icon_size = shell_switcher_get_icon_size; + iface->get_orientation = shell_switcher_get_orientation; + iface->get_style = shell_switcher_get_style; + iface->get_relief_style = shell_switcher_get_relief_style; +} + +GType +e_shell_switcher_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { + sizeof (EShellSwitcherClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) shell_switcher_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EShellSwitcher), + 0, /* n_preallocs */ + (GInstanceInitFunc) shell_switcher_init, + NULL /* value_table */ + }; + + static const GInterfaceInfo tool_shell_info = { + (GInterfaceInitFunc) shell_switcher_tool_shell_iface_init, + (GInterfaceFinalizeFunc) NULL, + NULL /* interface_data */ + }; + + type = g_type_register_static ( + GTK_TYPE_BIN, "EShellSwitcher", &type_info, 0); + + g_type_add_interface_static ( + type, GTK_TYPE_TOOL_SHELL, &tool_shell_info); + } + + return type; +} + +/** + * e_shell_switcher_new: + * + * Creates a new #EShellSwitcher instance. + * + * Returns: a new #EShellSwitcher instance + **/ +GtkWidget * +e_shell_switcher_new (void) +{ + return g_object_new (E_TYPE_SHELL_SWITCHER, NULL); +} + +/** + * e_shell_switcher_add_action: + * @switcher: an #EShellSwitcher + * @action: a #GtkAction + * + * Adds a button to @switcher that proxies for @action. Switcher buttons + * appear in the order they were added. + * + * #EShellWindow adds switcher actions in the order given by the + * <structfield>sort_order</structfield> field in #EShellBackendClass. + **/ +void +e_shell_switcher_add_action (EShellSwitcher *switcher, + GtkAction *action) +{ + GtkWidget *widget; + + g_return_if_fail (E_IS_SHELL_SWITCHER (switcher)); + g_return_if_fail (GTK_IS_ACTION (action)); + + g_object_ref (action); + widget = gtk_action_create_tool_item (action); + gtk_tool_item_set_is_important (GTK_TOOL_ITEM (widget), TRUE); + gtk_widget_show (widget); + + switcher->priv->proxies = g_list_append ( + switcher->priv->proxies, widget); + + gtk_widget_set_parent (widget, GTK_WIDGET (switcher)); + gtk_widget_queue_resize (GTK_WIDGET (switcher)); +} + +/** + * e_shell_switcher_get_style: + * @switcher: an #EShellSwitcher + * + * Returns whether @switcher has text, icons or both. + * + * Returns: the current style of @shell + **/ +GtkToolbarStyle +e_shell_switcher_get_style (EShellSwitcher *switcher) +{ + g_return_val_if_fail ( + E_IS_SHELL_SWITCHER (switcher), + E_SHELL_SWITCHER_DEFAULT_TOOLBAR_STYLE); + + return switcher->priv->style; +} + +/** + * e_shell_switcher_set_style: + * @switcher: an #EShellSwitcher + * @style: the new style for @switcher + * + * Alters the view of @switcher to display either icons only, text only, + * or both. + **/ +void +e_shell_switcher_set_style (EShellSwitcher *switcher, + GtkToolbarStyle style) +{ + g_return_if_fail (E_IS_SHELL_SWITCHER (switcher)); + + switcher->priv->style_set = TRUE; + g_signal_emit (switcher, signals[STYLE_CHANGED], 0, style); +} + +/** + * e_shell_switcher_unset_style: + * @switcher: an #EShellSwitcher + * + * Unsets a switcher style set with e_shell_switcher_set_style(), so + * that user preferences will be used to determine the switcher style. + **/ +void +e_shell_switcher_unset_style (EShellSwitcher *switcher) +{ + GtkSettings *settings; + GtkToolbarStyle style; + + g_return_if_fail (E_IS_SHELL_SWITCHER (switcher)); + + if (!switcher->priv->style_set) + return; + + settings = switcher->priv->settings; + if (settings != NULL) + g_object_get (settings, "gtk-toolbar-style", &style, NULL); + else + style = E_SHELL_SWITCHER_DEFAULT_TOOLBAR_STYLE; + + if (style == GTK_TOOLBAR_BOTH) + style = GTK_TOOLBAR_BOTH_HORIZ; + + if (style != switcher->priv->style) + g_signal_emit (switcher, signals[STYLE_CHANGED], 0, style); + + switcher->priv->style_set = FALSE; +} + +/** + * e_shell_switcher_get_visible: + * @switcher: an #EShellSwitcher + * + * Returns %TRUE if the switcher buttons are visible. + * + * Note that switcher button visibility is different than + * @switcher<!-- -->'s GTK_VISIBLE flag, since #EShellSwitcher + * is actually a container widget for #EShellSidebar. + * + * Returns: %TRUE if the switcher buttons are visible + **/ +gboolean +e_shell_switcher_get_visible (EShellSwitcher *switcher) +{ + g_return_val_if_fail (E_IS_SHELL_SWITCHER (switcher), FALSE); + + return switcher->priv->toolbar_visible; +} + +/** + * e_shell_switcher_set_visible: + * @switcher: an #EShellSwitcher + * @visible: whether the switcher buttons should be visible + * + * Sets the switcher button visiblity to @visible. + * + * Note that switcher button visibility is different than + * @switcher<!-- -->'s GTK_VISIBLE flag, since #EShellSwitcher + * is actually a container widget for #EShellSidebar. + **/ +void +e_shell_switcher_set_visible (EShellSwitcher *switcher, + gboolean visible) +{ + GList *iter; + + g_return_if_fail (E_IS_SHELL_SWITCHER (switcher)); + + switcher->priv->toolbar_visible = visible; + + for (iter = switcher->priv->proxies; iter != NULL; iter = iter->next) + g_object_set (iter->data, "visible", visible, NULL); + + gtk_widget_queue_resize (GTK_WIDGET (switcher)); + + g_object_notify (G_OBJECT (switcher), "toolbar-visible"); +} |