Files
evolution/shell/e-shell-switcher.c
Matthew Barnes d2fb5ee1a8 Avoid using GdkEventButton directly in certain places.
Prefer dealing with GdkEvent pointers and using accessor functions like
gdk_event_get_button().

This is complicated by the fact that some GtkWidget method declarations
still use GdkEventButton pointers, and synthesizing button events pretty
much requires direct GdkEventButton access.  But GDK seems to be nudging
itself toward sealing the GdkEvent union.  Likely to happen in GDK4.

Mainly clean up signal handlers and leave method overrides alone for now.
2012-11-29 13:24:24 -05:00

793 lines
19 KiB
C

/*
* 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)
*
*/
/**
* SECTION: e-shell-switcher
* @short_description: buttons for switching views
* @include: shell/e-shell-switcher.h
**/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "e-shell-switcher.h"
#include <glib/gi18n.h>
#include <libebackend/libebackend.h>
#define E_SHELL_SWITCHER_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_SHELL_SWITCHER, EShellSwitcherPrivate))
#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 guint signals[LAST_SIGNAL];
/* Forward Declarations */
static void shell_switcher_tool_shell_iface_init (GtkToolShellIface *iface);
G_DEFINE_TYPE_WITH_CODE (
EShellSwitcher,
e_shell_switcher,
GTK_TYPE_BIN,
G_IMPLEMENT_INTERFACE (
E_TYPE_EXTENSIBLE, NULL)
G_IMPLEMENT_INTERFACE (
GTK_TYPE_TOOL_SHELL,
shell_switcher_tool_shell_iface_init))
static gint
shell_switcher_layout_actions (EShellSwitcher *switcher)
{
GtkAllocation allocation;
gint 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;
gtk_widget_get_allocation (GTK_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_get_preferred_size (widget, &requisition, NULL);
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 (btns_per_row > 0 && 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--) {
gint 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 (e_shell_switcher_parent_class)->dispose (object);
}
static void
shell_switcher_get_preferred_width (GtkWidget *widget,
gint *minimum,
gint *natural)
{
EShellSwitcherPrivate *priv;
GtkWidget *child;
GList *iter;
priv = E_SHELL_SWITCHER_GET_PRIVATE (widget);
*minimum = *natural = 0;
child = gtk_bin_get_child (GTK_BIN (widget));
if (child != NULL)
gtk_widget_get_preferred_width (child, minimum, natural);
if (!priv->toolbar_visible)
return;
for (iter = priv->proxies; iter != NULL; iter = iter->next) {
GtkWidget *widget_proxy = iter->data;
gint child_min, child_nat;
gtk_widget_get_preferred_width (
widget_proxy, &child_min, &child_nat);
child_min += H_PADDING;
child_nat += H_PADDING;
*minimum = MAX (*minimum, child_min);
*natural = MAX (*natural, child_nat);
}
}
static void
shell_switcher_get_preferred_height (GtkWidget *widget,
gint *minimum,
gint *natural)
{
EShellSwitcherPrivate *priv;
GtkWidget *child;
GList *iter;
priv = E_SHELL_SWITCHER_GET_PRIVATE (widget);
*minimum = *natural = 0;
child = gtk_bin_get_child (GTK_BIN (widget));
if (child != NULL)
gtk_widget_get_preferred_height (child, minimum, natural);
if (!priv->toolbar_visible)
return;
for (iter = priv->proxies; iter != NULL; iter = iter->next) {
GtkWidget *widget = iter->data;
gint child_min, child_nat;
gtk_widget_get_preferred_height (
widget, &child_min, &child_nat);
child_min += V_PADDING;
child_nat += V_PADDING;
*minimum += child_min;
*natural += child_nat;
}
}
static void
shell_switcher_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
EShellSwitcher *switcher;
GtkAllocation child_allocation;
GtkWidget *child;
gint height;
switcher = E_SHELL_SWITCHER (widget);
gtk_widget_set_allocation (widget, 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 (e_shell_switcher_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 (e_shell_switcher_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 gfloat
shell_switcher_get_text_alignment (GtkToolShell *shell)
{
return 0.0;
}
static void
e_shell_switcher_class_init (EShellSwitcherClass *class)
{
GObjectClass *object_class;
GtkWidgetClass *widget_class;
GtkContainerClass *container_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->get_preferred_width = shell_switcher_get_preferred_width;
widget_class->get_preferred_height = shell_switcher_get_preferred_height;
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
e_shell_switcher_init (EShellSwitcher *switcher)
{
switcher->priv = E_SHELL_SWITCHER_GET_PRIVATE (switcher);
gtk_widget_set_has_window (GTK_WIDGET (switcher), FALSE);
e_extensible_load_extensions (E_EXTENSIBLE (switcher));
}
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;
iface->get_text_alignment = shell_switcher_get_text_alignment;
}
/**
* 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);
}
/*
* gtk+ doesn't give us what we want - a middle click,
* option on toolbar items, so we have to get it by force.
*/
static GtkButton *
tool_item_get_button (GtkWidget *widget)
{
GtkWidget *child;
g_return_val_if_fail (GTK_IS_TOOL_ITEM (widget), NULL);
child = gtk_bin_get_child (GTK_BIN (widget));
if (child != NULL && GTK_IS_BUTTON (child))
return GTK_BUTTON (child);
else
return NULL;
}
static gboolean
tool_item_button_cb (GtkWidget *internal_widget,
GdkEvent *button_event,
GtkAction *action)
{
guint event_button = 0;
g_return_val_if_fail (GTK_IS_ACTION (action), FALSE);
gdk_event_get_button (button_event, &event_button);
if (event_button == 2) {
gtk_action_activate (action);
return TRUE;
}
return FALSE;
}
/**
* e_shell_switcher_add_action:
* @switcher: an #EShellSwitcher
* @switch_action: a #GtkAction
* @new_window_action: a #GtkAction
*
* Adds a button to @switcher that proxies for @switcher_action.
* Switcher buttons appear in the order they were added. A middle
* click opens a new window of this type.
*
* #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 *switch_action,
GtkAction *new_window_action)
{
GtkWidget *widget;
GtkButton *button;
g_return_if_fail (E_IS_SHELL_SWITCHER (switcher));
g_return_if_fail (GTK_IS_ACTION (switch_action));
g_return_if_fail (GTK_IS_ACTION (new_window_action));
g_object_ref (switch_action);
widget = gtk_action_create_tool_item (switch_action);
gtk_tool_item_set_is_important (GTK_TOOL_ITEM (widget), TRUE);
gtk_widget_show (widget);
button = tool_item_get_button (widget);
if (button != NULL)
g_signal_connect (
button, "button-release-event",
G_CALLBACK (tool_item_button_cb),
new_window_action);
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));
if (switcher->priv->toolbar_visible == visible)
return;
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");
}