4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 * SECTION: e-shell-switcher
23 * @short_description: buttons for switching views
24 * @include: shell/e-shell-switcher.h
27 #include "evolution-config.h"
29 #include "e-shell-switcher.h"
31 #include <glib/gi18n.h>
32 #include <libebackend/libebackend.h>
34 #include <e-util/e-util.h>
36 #include "e-shell-window-private.h"
38 #define E_SHELL_SWITCHER_GET_PRIVATE(obj) \
39 (G_TYPE_INSTANCE_GET_PRIVATE \
40 ((obj), E_TYPE_SHELL_SWITCHER, EShellSwitcherPrivate))
42 #define E_SHELL_SWITCHER_GET_PRIVATE(obj) \
43 (G_TYPE_INSTANCE_GET_PRIVATE \
44 ((obj), E_TYPE_SHELL_SWITCHER, EShellSwitcherPrivate))
49 struct _EShellSwitcherPrivate
{
52 GtkToolbarStyle style
;
53 GtkSettings
*settings
;
54 gulong settings_handler_id
;
55 gboolean toolbar_visible
;
69 static guint signals
[LAST_SIGNAL
];
71 /* Forward Declarations */
72 static void shell_switcher_tool_shell_iface_init (GtkToolShellIface
*iface
);
74 G_DEFINE_TYPE_WITH_CODE (
78 G_IMPLEMENT_INTERFACE (
79 E_TYPE_EXTENSIBLE
, NULL
)
80 G_IMPLEMENT_INTERFACE (
82 shell_switcher_tool_shell_iface_init
))
85 shell_switcher_layout_actions (EShellSwitcher
*switcher
)
87 GtkAllocation allocation
;
88 gint num_btns
= g_list_length (switcher
->priv
->proxies
), btns_per_row
;
98 gtk_widget_get_allocation (GTK_WIDGET (switcher
), &allocation
);
100 y
= allocation
.y
+ allocation
.height
;
103 return allocation
.height
;
105 icons_only
= (switcher
->priv
->style
== GTK_TOOLBAR_ICONS
);
107 /* Figure out the max width and height. */
108 for (p
= switcher
->priv
->proxies
; p
!= NULL
; p
= p
->next
) {
109 GtkWidget
*widget
= p
->data
;
110 GtkRequisition requisition
;
112 gtk_widget_get_preferred_size (widget
, &requisition
, NULL
);
113 max_height
= MAX (max_height
, requisition
.height
);
114 max_width
= MAX (max_width
, requisition
.width
);
117 /* Figure out how many rows and columns we'll use. */
118 btns_per_row
= MAX (1, allocation
.width
/ (max_width
+ H_PADDING
));
120 /* If using text buttons, we want to try to have a
121 * completely filled-in grid, but if we can't, we want
122 * the odd row to have just a single button. */
123 while (btns_per_row
> 0 && num_btns
% btns_per_row
> 1)
127 if (btns_per_row
<= 0)
130 /* Assign buttons to rows. */
131 rows
= g_new0 (GList
*, num_btns
/ btns_per_row
+ 1);
133 if (!icons_only
&& num_btns
% btns_per_row
!= 0 && switcher
->priv
->proxies
) {
134 rows
[0] = g_list_append (rows
[0], switcher
->priv
->proxies
->data
);
136 p
= switcher
->priv
->proxies
->next
;
137 row_number
= p
? 1 : 0;
139 p
= switcher
->priv
->proxies
;
143 for (; p
!= NULL
; p
= p
->next
) {
144 GtkWidget
*widget
= p
->data
;
146 if (g_list_length (rows
[row_number
]) == btns_per_row
)
149 rows
[row_number
] = g_list_append (rows
[row_number
], widget
);
152 row_last
= row_number
;
154 /* Layout the buttons. */
155 for (i
= row_last
; i
>= 0; i
--) {
156 gint len
, extra_width
;
158 x
= H_PADDING
+ allocation
.x
;
159 y
-= max_height
+ V_PADDING
;
160 len
= g_list_length (rows
[i
]);
163 (allocation
.width
- (len
* max_width
) -
164 (len
* H_PADDING
+ H_PADDING
)) / len
;
167 for (p
= rows
[i
]; p
!= NULL
; p
= p
->next
) {
168 GtkAllocation child_allocation
;
170 child_allocation
.x
= x
;
171 child_allocation
.y
= y
;
172 child_allocation
.width
= max_width
+ extra_width
;
173 child_allocation
.height
= max_height
;
175 gtk_widget_size_allocate (
176 GTK_WIDGET (p
->data
), &child_allocation
);
178 x
+= child_allocation
.width
+ H_PADDING
;
182 for (i
= 0; i
<= row_last
; i
++)
183 g_list_free (rows
[i
]);
186 return y
- allocation
.y
- V_PADDING
;
190 shell_switcher_toolbar_style_changed_cb (EShellSwitcher
*switcher
)
192 if (!switcher
->priv
->style_set
) {
193 switcher
->priv
->style_set
= TRUE
;
194 e_shell_switcher_unset_style (switcher
);
199 shell_switcher_set_property (GObject
*object
,
204 switch (property_id
) {
205 case PROP_TOOLBAR_STYLE
:
206 e_shell_switcher_set_style (
207 E_SHELL_SWITCHER (object
),
208 g_value_get_enum (value
));
211 case PROP_TOOLBAR_VISIBLE
:
212 e_shell_switcher_set_visible (
213 E_SHELL_SWITCHER (object
),
214 g_value_get_boolean (value
));
218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
222 shell_switcher_get_property (GObject
*object
,
227 switch (property_id
) {
228 case PROP_TOOLBAR_STYLE
:
230 value
, e_shell_switcher_get_style (
231 E_SHELL_SWITCHER (object
)));
234 case PROP_TOOLBAR_VISIBLE
:
235 g_value_set_boolean (
236 value
, e_shell_switcher_get_visible (
237 E_SHELL_SWITCHER (object
)));
241 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
245 shell_switcher_dispose (GObject
*object
)
247 EShellSwitcherPrivate
*priv
;
249 priv
= E_SHELL_SWITCHER_GET_PRIVATE (object
);
251 while (priv
->proxies
!= NULL
) {
252 GtkWidget
*widget
= priv
->proxies
->data
;
253 gtk_container_remove (GTK_CONTAINER (object
), widget
);
256 /* Chain up to parent's dispose() method. */
257 G_OBJECT_CLASS (e_shell_switcher_parent_class
)->dispose (object
);
261 shell_switcher_get_preferred_width (GtkWidget
*widget
,
265 EShellSwitcherPrivate
*priv
;
269 priv
= E_SHELL_SWITCHER_GET_PRIVATE (widget
);
271 *minimum
= *natural
= 0;
273 child
= gtk_bin_get_child (GTK_BIN (widget
));
275 gtk_widget_get_preferred_width (child
, minimum
, natural
);
277 if (!priv
->toolbar_visible
)
280 for (iter
= priv
->proxies
; iter
!= NULL
; iter
= iter
->next
) {
281 GtkWidget
*widget_proxy
= iter
->data
;
282 gint child_min
, child_nat
;
284 gtk_widget_get_preferred_width (
285 widget_proxy
, &child_min
, &child_nat
);
287 child_min
+= H_PADDING
;
288 child_nat
+= H_PADDING
;
290 *minimum
= MAX (*minimum
, child_min
);
291 *natural
= MAX (*natural
, child_nat
);
296 shell_switcher_get_preferred_height (GtkWidget
*switcher_widget
,
300 EShellSwitcherPrivate
*priv
;
304 priv
= E_SHELL_SWITCHER_GET_PRIVATE (switcher_widget
);
306 *minimum
= *natural
= 0;
308 child
= gtk_bin_get_child (GTK_BIN (switcher_widget
));
310 gtk_widget_get_preferred_height (child
, minimum
, natural
);
312 if (!priv
->toolbar_visible
)
315 for (iter
= priv
->proxies
; iter
!= NULL
; iter
= iter
->next
) {
316 GtkWidget
*widget
= iter
->data
;
317 gint child_min
, child_nat
;
319 gtk_widget_get_preferred_height (
320 widget
, &child_min
, &child_nat
);
322 child_min
+= V_PADDING
;
323 child_nat
+= V_PADDING
;
325 *minimum
+= child_min
;
326 *natural
+= child_nat
;
331 shell_switcher_size_allocate (GtkWidget
*widget
,
332 GtkAllocation
*allocation
)
334 EShellSwitcher
*switcher
;
335 GtkAllocation child_allocation
;
339 switcher
= E_SHELL_SWITCHER (widget
);
341 gtk_widget_set_allocation (widget
, allocation
);
343 if (switcher
->priv
->toolbar_visible
)
344 height
= shell_switcher_layout_actions (switcher
);
346 height
= allocation
->height
;
348 child_allocation
.x
= allocation
->x
;
349 child_allocation
.y
= allocation
->y
;
350 child_allocation
.width
= allocation
->width
;
351 child_allocation
.height
= height
;
353 child
= gtk_bin_get_child (GTK_BIN (widget
));
355 gtk_widget_size_allocate (child
, &child_allocation
);
359 shell_switcher_screen_changed (GtkWidget
*widget
,
360 GdkScreen
*previous_screen
)
362 EShellSwitcherPrivate
*priv
;
363 GtkSettings
*settings
;
365 priv
= E_SHELL_SWITCHER_GET_PRIVATE (widget
);
367 if (gtk_widget_has_screen (widget
))
368 settings
= gtk_widget_get_settings (widget
);
372 if (settings
== priv
->settings
)
375 if (priv
->settings
!= NULL
) {
376 g_signal_handler_disconnect (
377 priv
->settings
, priv
->settings_handler_id
);
378 g_object_unref (priv
->settings
);
381 if (settings
!= NULL
) {
382 priv
->settings
= g_object_ref (settings
);
383 priv
->settings_handler_id
= e_signal_connect_notify_swapped (
384 settings
, "notify::gtk-toolbar-style",
385 G_CALLBACK (shell_switcher_toolbar_style_changed_cb
),
388 priv
->settings
= NULL
;
390 shell_switcher_toolbar_style_changed_cb (E_SHELL_SWITCHER (widget
));
394 shell_switcher_remove (GtkContainer
*container
,
395 GtkWidget
*remove_widget
)
397 EShellSwitcherPrivate
*priv
;
400 priv
= E_SHELL_SWITCHER_GET_PRIVATE (container
);
402 /* Look in the internal widgets first. */
404 link
= g_list_find (priv
->proxies
, remove_widget
);
406 GtkWidget
*widget
= link
->data
;
408 gtk_widget_unparent (widget
);
409 priv
->proxies
= g_list_delete_link (priv
->proxies
, link
);
410 gtk_widget_queue_resize (GTK_WIDGET (container
));
414 /* Chain up to parent's remove() method. */
415 GTK_CONTAINER_CLASS (e_shell_switcher_parent_class
)->remove (
416 container
, remove_widget
);
420 shell_switcher_forall (GtkContainer
*container
,
421 gboolean include_internals
,
422 GtkCallback callback
,
423 gpointer callback_data
)
425 EShellSwitcherPrivate
*priv
;
427 priv
= E_SHELL_SWITCHER_GET_PRIVATE (container
);
429 if (include_internals
)
431 priv
->proxies
, (GFunc
) callback
, callback_data
);
433 /* Chain up to parent's forall() method. */
434 GTK_CONTAINER_CLASS (e_shell_switcher_parent_class
)->forall (
435 container
, include_internals
, callback
, callback_data
);
439 shell_switcher_style_changed (EShellSwitcher
*switcher
,
440 GtkToolbarStyle style
)
442 if (switcher
->priv
->style
== style
)
445 switcher
->priv
->style
= style
;
448 switcher
->priv
->proxies
,
449 (GFunc
) gtk_tool_item_toolbar_reconfigured
, NULL
);
451 gtk_widget_queue_resize (GTK_WIDGET (switcher
));
452 g_object_notify (G_OBJECT (switcher
), "toolbar-style");
456 shell_switcher_get_icon_size (GtkToolShell
*shell
)
458 return GTK_ICON_SIZE_LARGE_TOOLBAR
;
461 static GtkOrientation
462 shell_switcher_get_orientation (GtkToolShell
*shell
)
464 return GTK_ORIENTATION_HORIZONTAL
;
467 static GtkToolbarStyle
468 shell_switcher_get_style (GtkToolShell
*shell
)
470 return e_shell_switcher_get_style (E_SHELL_SWITCHER (shell
));
473 static GtkReliefStyle
474 shell_switcher_get_relief_style (GtkToolShell
*shell
)
476 return GTK_RELIEF_NORMAL
;
480 shell_switcher_get_text_alignment (GtkToolShell
*shell
)
486 e_shell_switcher_class_init (EShellSwitcherClass
*class)
488 GObjectClass
*object_class
;
489 GtkWidgetClass
*widget_class
;
490 GtkContainerClass
*container_class
;
492 g_type_class_add_private (class, sizeof (EShellSwitcherPrivate
));
494 object_class
= G_OBJECT_CLASS (class);
495 object_class
->set_property
= shell_switcher_set_property
;
496 object_class
->get_property
= shell_switcher_get_property
;
497 object_class
->dispose
= shell_switcher_dispose
;
499 widget_class
= GTK_WIDGET_CLASS (class);
500 widget_class
->get_preferred_width
= shell_switcher_get_preferred_width
;
501 widget_class
->get_preferred_height
= shell_switcher_get_preferred_height
;
502 widget_class
->size_allocate
= shell_switcher_size_allocate
;
503 widget_class
->screen_changed
= shell_switcher_screen_changed
;
505 container_class
= GTK_CONTAINER_CLASS (class);
506 container_class
->remove
= shell_switcher_remove
;
507 container_class
->forall
= shell_switcher_forall
;
509 class->style_changed
= shell_switcher_style_changed
;
512 * EShellSwitcher:toolbar-style
514 * The switcher's toolbar style.
516 g_object_class_install_property (
522 "The switcher's toolbar style",
523 GTK_TYPE_TOOLBAR_STYLE
,
524 E_SHELL_SWITCHER_DEFAULT_TOOLBAR_STYLE
,
527 G_PARAM_STATIC_STRINGS
));
530 * EShellSwitcher:toolbar-visible
532 * Whether the switcher is visible.
534 g_object_class_install_property (
536 PROP_TOOLBAR_VISIBLE
,
537 g_param_spec_boolean (
540 "Whether the switcher is visible",
544 G_PARAM_STATIC_STRINGS
));
547 * EShellSwitcher::style-changed
548 * @switcher: the #EShellSwitcher which emitted the signal
549 * @style: the new #GtkToolbarStyle of the switcher
551 * Emitted when the style of the switcher changes.
553 signals
[STYLE_CHANGED
] = g_signal_new (
555 G_OBJECT_CLASS_TYPE (class),
557 G_STRUCT_OFFSET (EShellSwitcherClass
, style_changed
),
559 g_cclosure_marshal_VOID__ENUM
,
561 GTK_TYPE_TOOLBAR_STYLE
);
565 e_shell_switcher_init (EShellSwitcher
*switcher
)
567 switcher
->priv
= E_SHELL_SWITCHER_GET_PRIVATE (switcher
);
569 gtk_widget_set_has_window (GTK_WIDGET (switcher
), FALSE
);
571 e_extensible_load_extensions (E_EXTENSIBLE (switcher
));
575 shell_switcher_tool_shell_iface_init (GtkToolShellIface
*iface
)
577 iface
->get_icon_size
= shell_switcher_get_icon_size
;
578 iface
->get_orientation
= shell_switcher_get_orientation
;
579 iface
->get_style
= shell_switcher_get_style
;
580 iface
->get_relief_style
= shell_switcher_get_relief_style
;
581 iface
->get_text_alignment
= shell_switcher_get_text_alignment
;
585 * e_shell_switcher_new:
587 * Creates a new #EShellSwitcher instance.
589 * Returns: a new #EShellSwitcher instance
592 e_shell_switcher_new (void)
594 return g_object_new (E_TYPE_SHELL_SWITCHER
, NULL
);
598 * gtk+ doesn't give us what we want - a middle click,
599 * option on toolbar items, so we have to get it by force.
602 tool_item_get_button (GtkWidget
*widget
)
606 g_return_val_if_fail (GTK_IS_TOOL_ITEM (widget
), NULL
);
608 child
= gtk_bin_get_child (GTK_BIN (widget
));
609 if (child
!= NULL
&& GTK_IS_BUTTON (child
))
610 return GTK_BUTTON (child
);
616 tool_item_button_cb (GtkWidget
*internal_widget
,
617 GdkEvent
*button_event
,
620 guint32 my_mods
= GDK_SHIFT_MASK
| GDK_CONTROL_MASK
| GDK_MOD1_MASK
|
621 GDK_SUPER_MASK
| GDK_HYPER_MASK
| GDK_META_MASK
;
622 GdkModifierType event_state
= 0;
623 guint event_button
= 0;
625 g_return_val_if_fail (GTK_IS_ACTION (action
), FALSE
);
627 gdk_event_get_button (button_event
, &event_button
);
628 gdk_event_get_state (button_event
, &event_state
);
630 if (event_button
== 2 || (event_button
== 1 && (event_state
& my_mods
) == GDK_SHIFT_MASK
)) {
631 gtk_action_activate (action
);
639 * e_shell_switcher_add_action:
640 * @switcher: an #EShellSwitcher
641 * @switch_action: a #GtkAction
642 * @new_window_action: a #GtkAction
644 * Adds a button to @switcher that proxies for @switcher_action.
645 * Switcher buttons appear in the order they were added. A middle
646 * click opens a new window of this type.
648 * #EShellWindow adds switcher actions in the order given by the
649 * <structfield>sort_order</structfield> field in #EShellBackendClass.
652 e_shell_switcher_add_action (EShellSwitcher
*switcher
,
653 GtkAction
*switch_action
,
654 GtkAction
*new_window_action
)
661 gboolean skip
= FALSE
;
663 g_return_if_fail (E_IS_SHELL_SWITCHER (switcher
));
664 g_return_if_fail (GTK_IS_ACTION (switch_action
));
665 g_return_if_fail (GTK_IS_ACTION (new_window_action
));
667 settings
= e_util_ref_settings ("org.gnome.evolution.shell");
668 strv
= g_settings_get_strv (settings
, "buttons-hide");
669 g_clear_object (&settings
);
671 for (ii
= 0; strv
&& strv
[ii
] && !skip
; ii
++) {
674 name
= g_strdup_printf (E_SHELL_SWITCHER_FORMAT
, strv
[ii
]);
675 skip
= g_strcmp0 (name
, gtk_action_get_name (switch_action
)) == 0;
684 g_object_ref (switch_action
);
685 widget
= gtk_action_create_tool_item (switch_action
);
686 gtk_tool_item_set_is_important (GTK_TOOL_ITEM (widget
), TRUE
);
687 gtk_widget_show (widget
);
689 button
= tool_item_get_button (widget
);
692 button
, "button-release-event",
693 G_CALLBACK (tool_item_button_cb
),
696 switcher
->priv
->proxies
= g_list_append (
697 switcher
->priv
->proxies
, widget
);
699 gtk_widget_set_parent (widget
, GTK_WIDGET (switcher
));
700 gtk_widget_queue_resize (GTK_WIDGET (switcher
));
704 * e_shell_switcher_get_style:
705 * @switcher: an #EShellSwitcher
707 * Returns whether @switcher has text, icons or both.
709 * Returns: the current style of @shell
712 e_shell_switcher_get_style (EShellSwitcher
*switcher
)
714 g_return_val_if_fail (
715 E_IS_SHELL_SWITCHER (switcher
),
716 E_SHELL_SWITCHER_DEFAULT_TOOLBAR_STYLE
);
718 return switcher
->priv
->style
;
722 * e_shell_switcher_set_style:
723 * @switcher: an #EShellSwitcher
724 * @style: the new style for @switcher
726 * Alters the view of @switcher to display either icons only, text only,
730 e_shell_switcher_set_style (EShellSwitcher
*switcher
,
731 GtkToolbarStyle style
)
733 g_return_if_fail (E_IS_SHELL_SWITCHER (switcher
));
735 switcher
->priv
->style_set
= TRUE
;
736 g_signal_emit (switcher
, signals
[STYLE_CHANGED
], 0, style
);
740 * e_shell_switcher_unset_style:
741 * @switcher: an #EShellSwitcher
743 * Unsets a switcher style set with e_shell_switcher_set_style(), so
744 * that user preferences will be used to determine the switcher style.
747 e_shell_switcher_unset_style (EShellSwitcher
*switcher
)
749 GtkSettings
*settings
;
750 GtkToolbarStyle style
;
752 g_return_if_fail (E_IS_SHELL_SWITCHER (switcher
));
754 if (!switcher
->priv
->style_set
)
757 settings
= switcher
->priv
->settings
;
758 if (settings
!= NULL
)
759 g_object_get (settings
, "gtk-toolbar-style", &style
, NULL
);
761 style
= E_SHELL_SWITCHER_DEFAULT_TOOLBAR_STYLE
;
763 if (style
== GTK_TOOLBAR_BOTH
)
764 style
= GTK_TOOLBAR_BOTH_HORIZ
;
766 if (style
!= switcher
->priv
->style
)
767 g_signal_emit (switcher
, signals
[STYLE_CHANGED
], 0, style
);
769 switcher
->priv
->style_set
= FALSE
;
773 * e_shell_switcher_get_visible:
774 * @switcher: an #EShellSwitcher
776 * Returns %TRUE if the switcher buttons are visible.
778 * Note that switcher button visibility is different than
779 * @switcher<!-- -->'s GTK_VISIBLE flag, since #EShellSwitcher
780 * is actually a container widget for #EShellSidebar.
782 * Returns: %TRUE if the switcher buttons are visible
785 e_shell_switcher_get_visible (EShellSwitcher
*switcher
)
787 g_return_val_if_fail (E_IS_SHELL_SWITCHER (switcher
), FALSE
);
789 return switcher
->priv
->toolbar_visible
;
793 * e_shell_switcher_set_visible:
794 * @switcher: an #EShellSwitcher
795 * @visible: whether the switcher buttons should be visible
797 * Sets the switcher button visiblity to @visible.
799 * Note that switcher button visibility is different than
800 * @switcher<!-- -->'s GTK_VISIBLE flag, since #EShellSwitcher
801 * is actually a container widget for #EShellSidebar.
804 e_shell_switcher_set_visible (EShellSwitcher
*switcher
,
809 g_return_if_fail (E_IS_SHELL_SWITCHER (switcher
));
811 if (switcher
->priv
->toolbar_visible
== visible
)
814 switcher
->priv
->toolbar_visible
= visible
;
816 for (iter
= switcher
->priv
->proxies
; iter
!= NULL
; iter
= iter
->next
)
817 g_object_set (iter
->data
, "visible", visible
, NULL
);
819 gtk_widget_queue_resize (GTK_WIDGET (switcher
));
821 g_object_notify (G_OBJECT (switcher
), "toolbar-visible");