No issues found
1 /*
2 * e-shell-sidebar.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 *
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
19 *
20 */
21
22 /**
23 * SECTION: e-shell-sidebar
24 * @short_description: the left side of the main window
25 * @include: shell/e-shell-sidebar.h
26 **/
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31
32 #include "e-shell-sidebar.h"
33
34 #include <libebackend/libebackend.h>
35
36 #include <libevolution-utils/e-alert-sink.h>
37 #include <e-util/e-unicode.h>
38 #include <shell/e-shell-view.h>
39
40 #define E_SHELL_SIDEBAR_GET_PRIVATE(obj) \
41 (G_TYPE_INSTANCE_GET_PRIVATE \
42 ((obj), E_TYPE_SHELL_SIDEBAR, EShellSidebarPrivate))
43
44 struct _EShellSidebarPrivate {
45
46 gpointer shell_view; /* weak pointer */
47
48 GtkWidget *event_box;
49
50 gchar *icon_name;
51 gchar *primary_text;
52 gchar *secondary_text;
53 };
54
55 enum {
56 PROP_0,
57 PROP_ICON_NAME,
58 PROP_PRIMARY_TEXT,
59 PROP_SECONDARY_TEXT,
60 PROP_SHELL_VIEW
61 };
62
63 /* Forward Declarations */
64 static void e_shell_sidebar_alert_sink_init
65 (EAlertSinkInterface *interface);
66
67 G_DEFINE_TYPE_WITH_CODE (
68 EShellSidebar,
69 e_shell_sidebar,
70 GTK_TYPE_BIN,
71 G_IMPLEMENT_INTERFACE (
72 E_TYPE_ALERT_SINK, e_shell_sidebar_alert_sink_init)
73 G_IMPLEMENT_INTERFACE (
74 E_TYPE_EXTENSIBLE, NULL))
75
76 static void
77 shell_sidebar_set_shell_view (EShellSidebar *shell_sidebar,
78 EShellView *shell_view)
79 {
80 g_return_if_fail (shell_sidebar->priv->shell_view == NULL);
81
82 shell_sidebar->priv->shell_view = shell_view;
83
84 g_object_add_weak_pointer (
85 G_OBJECT (shell_view),
86 &shell_sidebar->priv->shell_view);
87 }
88
89 static void
90 shell_sidebar_set_property (GObject *object,
91 guint property_id,
92 const GValue *value,
93 GParamSpec *pspec)
94 {
95 switch (property_id) {
96 case PROP_ICON_NAME:
97 e_shell_sidebar_set_icon_name (
98 E_SHELL_SIDEBAR (object),
99 g_value_get_string (value));
100 return;
101
102 case PROP_PRIMARY_TEXT:
103 e_shell_sidebar_set_primary_text (
104 E_SHELL_SIDEBAR (object),
105 g_value_get_string (value));
106 return;
107
108 case PROP_SECONDARY_TEXT:
109 e_shell_sidebar_set_secondary_text (
110 E_SHELL_SIDEBAR (object),
111 g_value_get_string (value));
112 return;
113
114 case PROP_SHELL_VIEW:
115 shell_sidebar_set_shell_view (
116 E_SHELL_SIDEBAR (object),
117 g_value_get_object (value));
118 return;
119 }
120
121 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
122 }
123
124 static void
125 shell_sidebar_get_property (GObject *object,
126 guint property_id,
127 GValue *value,
128 GParamSpec *pspec)
129 {
130 switch (property_id) {
131 case PROP_ICON_NAME:
132 g_value_set_string (
133 value, e_shell_sidebar_get_icon_name (
134 E_SHELL_SIDEBAR (object)));
135 return;
136
137 case PROP_PRIMARY_TEXT:
138 g_value_set_string (
139 value, e_shell_sidebar_get_primary_text (
140 E_SHELL_SIDEBAR (object)));
141 return;
142
143 case PROP_SECONDARY_TEXT:
144 g_value_set_string (
145 value, e_shell_sidebar_get_secondary_text (
146 E_SHELL_SIDEBAR (object)));
147 return;
148
149 case PROP_SHELL_VIEW:
150 g_value_set_object (
151 value, e_shell_sidebar_get_shell_view (
152 E_SHELL_SIDEBAR (object)));
153 return;
154 }
155
156 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
157 }
158
159 static void
160 shell_sidebar_dispose (GObject *object)
161 {
162 EShellSidebarPrivate *priv;
163
164 priv = E_SHELL_SIDEBAR_GET_PRIVATE (object);
165
166 if (priv->shell_view != NULL) {
167 g_object_remove_weak_pointer (
168 G_OBJECT (priv->shell_view), &priv->shell_view);
169 priv->shell_view = NULL;
170 }
171
172 /* Unparent the widget before destroying it to avoid
173 * writing a custom GtkContainer::remove() method. */
174
175 if (priv->event_box != NULL) {
176 gtk_widget_unparent (priv->event_box);
177 gtk_widget_destroy (priv->event_box);
178 g_object_unref (priv->event_box);
179 priv->event_box = NULL;
180 }
181
182 /* Chain up to parent's dispose() method. */
183 G_OBJECT_CLASS (e_shell_sidebar_parent_class)->dispose (object);
184 }
185
186 static void
187 shell_sidebar_finalize (GObject *object)
188 {
189 EShellSidebarPrivate *priv;
190
191 priv = E_SHELL_SIDEBAR_GET_PRIVATE (object);
192
193 g_free (priv->icon_name);
194 g_free (priv->primary_text);
195 g_free (priv->secondary_text);
196
197 /* Chain up to parent's finalize() method. */
198 G_OBJECT_CLASS (e_shell_sidebar_parent_class)->finalize (object);
199 }
200
201 static void
202 shell_sidebar_constructed (GObject *object)
203 {
204 EShellView *shell_view;
205 EShellSidebar *shell_sidebar;
206 GtkSizeGroup *size_group;
207 GtkAction *action;
208 GtkWidget *widget;
209 gchar *label;
210 gchar *icon_name;
211
212 shell_sidebar = E_SHELL_SIDEBAR (object);
213 shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
214 size_group = e_shell_view_get_size_group (shell_view);
215 action = e_shell_view_get_action (shell_view);
216
217 widget = shell_sidebar->priv->event_box;
218 gtk_size_group_add_widget (size_group, widget);
219
220 g_object_get (action, "icon-name", &icon_name, NULL);
221 e_shell_sidebar_set_icon_name (shell_sidebar, icon_name);
222 g_free (icon_name);
223
224 g_object_get (action, "label", &label, NULL);
225 e_shell_sidebar_set_primary_text (shell_sidebar, label);
226 g_free (label);
227
228 e_extensible_load_extensions (E_EXTENSIBLE (object));
229
230 /* Chain up to parent's constructed() method. */
231 G_OBJECT_CLASS (e_shell_sidebar_parent_class)->constructed (object);
232 }
233
234 static void
235 shell_sidebar_get_preferred_width (GtkWidget *widget,
236 gint *minimum,
237 gint *natural)
238 {
239 GtkWidget *child;
240
241 child = gtk_bin_get_child (GTK_BIN (widget));
242 gtk_widget_get_preferred_width (child, minimum, natural);
243
244 /* Do not use priv->event_box here, otherwise it won't ellipsize. */
245 }
246
247 static void
248 shell_sidebar_get_preferred_height (GtkWidget *widget,
249 gint *minimum,
250 gint *natural)
251 {
252 EShellSidebarPrivate *priv;
253 gint child_min, child_nat;
254 GtkWidget *child;
255
256 priv = E_SHELL_SIDEBAR_GET_PRIVATE (widget);
257
258 child = gtk_bin_get_child (GTK_BIN (widget));
259 gtk_widget_get_preferred_height (child, minimum, natural);
260
261 child = priv->event_box;
262 gtk_widget_get_preferred_height (child, &child_min, &child_nat);
263
264 *minimum += child_min;
265 *natural += child_nat;
266 }
267
268 static void
269 shell_sidebar_size_allocate (GtkWidget *widget,
270 GtkAllocation *allocation)
271 {
272 EShellSidebarPrivate *priv;
273 GtkAllocation child_allocation;
274 GtkRequisition child_requisition;
275 GtkWidget *child;
276
277 priv = E_SHELL_SIDEBAR_GET_PRIVATE (widget);
278
279 gtk_widget_set_allocation (widget, allocation);
280
281 child = priv->event_box;
282 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
283
284 child_allocation.x = allocation->x;
285 child_allocation.y = allocation->y;
286 child_allocation.width = allocation->width;
287 child_allocation.height = child_requisition.height;
288
289 gtk_widget_size_allocate (child, &child_allocation);
290
291 child_allocation.y += child_requisition.height;
292 child_allocation.height =
293 allocation->height - child_requisition.height;
294
295 child = gtk_bin_get_child (GTK_BIN (widget));
296 if (child != NULL)
297 gtk_widget_size_allocate (child, &child_allocation);
298 }
299
300 static void
301 shell_sidebar_forall (GtkContainer *container,
302 gboolean include_internals,
303 GtkCallback callback,
304 gpointer callback_data)
305 {
306 EShellSidebarPrivate *priv;
307
308 priv = E_SHELL_SIDEBAR_GET_PRIVATE (container);
309
310 if (include_internals && callback && priv->event_box)
311 callback (priv->event_box, callback_data);
312
313 /* Chain up to parent's forall() method. */
314 GTK_CONTAINER_CLASS (e_shell_sidebar_parent_class)->forall (
315 container, include_internals, callback, callback_data);
316 }
317
318 static void
319 shell_sidebar_submit_alert (EAlertSink *alert_sink,
320 EAlert *alert)
321 {
322 EShellView *shell_view;
323 EShellContent *shell_content;
324 EShellSidebar *shell_sidebar;
325
326 /* EShellSidebar is a proxy alert sink. Forward the alert
327 * to the EShellContent widget for display to the user. */
328
329 shell_sidebar = E_SHELL_SIDEBAR (alert_sink);
330 shell_view = e_shell_sidebar_get_shell_view (shell_sidebar);
331 shell_content = e_shell_view_get_shell_content (shell_view);
332
333 alert_sink = E_ALERT_SINK (shell_content);
334 e_alert_sink_submit_alert (alert_sink, alert);
335 }
336
337 static void
338 e_shell_sidebar_class_init (EShellSidebarClass *class)
339 {
340 GObjectClass *object_class;
341 GtkWidgetClass *widget_class;
342 GtkContainerClass *container_class;
343
344 g_type_class_add_private (class, sizeof (EShellSidebarPrivate));
345
346 object_class = G_OBJECT_CLASS (class);
347 object_class->set_property = shell_sidebar_set_property;
348 object_class->get_property = shell_sidebar_get_property;
349 object_class->dispose = shell_sidebar_dispose;
350 object_class->finalize = shell_sidebar_finalize;
351 object_class->constructed = shell_sidebar_constructed;
352
353 widget_class = GTK_WIDGET_CLASS (class);
354 widget_class->get_preferred_width = shell_sidebar_get_preferred_width;
355 widget_class->get_preferred_height = shell_sidebar_get_preferred_height;
356 widget_class->size_allocate = shell_sidebar_size_allocate;
357
358 container_class = GTK_CONTAINER_CLASS (class);
359 container_class->forall = shell_sidebar_forall;
360
361 /**
362 * EShellSidebar:icon-name
363 *
364 * The named icon is displayed at the top of the sidebar.
365 **/
366 g_object_class_install_property (
367 object_class,
368 PROP_ICON_NAME,
369 g_param_spec_string (
370 "icon-name",
371 "Icon Name",
372 NULL,
373 NULL,
374 G_PARAM_READWRITE));
375
376 /**
377 * EShellSidebar:primary-text
378 *
379 * The primary text is displayed in bold at the top of the sidebar.
380 **/
381 g_object_class_install_property (
382 object_class,
383 PROP_PRIMARY_TEXT,
384 g_param_spec_string (
385 "primary-text",
386 "Primary Text",
387 NULL,
388 NULL,
389 G_PARAM_READWRITE));
390
391 /**
392 * EShellSidebar:secondary-text
393 *
394 * The secondary text is displayed in a smaller font at the top of
395 * the sidebar.
396 **/
397 g_object_class_install_property (
398 object_class,
399 PROP_SECONDARY_TEXT,
400 g_param_spec_string (
401 "secondary-text",
402 "Secondary Text",
403 NULL,
404 NULL,
405 G_PARAM_READWRITE));
406
407 /**
408 * EShellSidebar:shell-view
409 *
410 * The #EShellView to which the sidebar widget belongs.
411 **/
412 g_object_class_install_property (
413 object_class,
414 PROP_SHELL_VIEW,
415 g_param_spec_object (
416 "shell-view",
417 "Shell View",
418 NULL,
419 E_TYPE_SHELL_VIEW,
420 G_PARAM_READWRITE |
421 G_PARAM_CONSTRUCT_ONLY));
422 }
423
424 static void
425 e_shell_sidebar_alert_sink_init (EAlertSinkInterface *interface)
426 {
427 interface->submit_alert = shell_sidebar_submit_alert;
428 }
429
430 static void
431 e_shell_sidebar_init (EShellSidebar *shell_sidebar)
432 {
433 GtkStyle *style;
434 GtkWidget *widget;
435 GtkWidget *container;
436 PangoAttribute *attribute;
437 PangoAttrList *attribute_list;
438 const GdkColor *color;
439 const gchar *icon_name;
440
441 shell_sidebar->priv = E_SHELL_SIDEBAR_GET_PRIVATE (shell_sidebar);
442
443 gtk_widget_set_has_window (GTK_WIDGET (shell_sidebar), FALSE);
444
445 widget = gtk_event_box_new ();
446 style = gtk_widget_get_style (widget);
447 color = &style->bg[GTK_STATE_ACTIVE];
448 gtk_widget_modify_bg (widget, GTK_STATE_NORMAL, color);
449 gtk_widget_set_parent (widget, GTK_WIDGET (shell_sidebar));
450 shell_sidebar->priv->event_box = g_object_ref (widget);
451 gtk_widget_show (widget);
452
453 container = widget;
454
455 widget = gtk_hbox_new (FALSE, 6);
456 gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
457 gtk_container_add (GTK_CONTAINER (container), widget);
458 gtk_widget_show (widget);
459
460 container = widget;
461
462 /* Pick a bogus icon name just to get the storage type set. */
463 icon_name = "evolution";
464 e_shell_sidebar_set_icon_name (shell_sidebar, icon_name);
465 widget = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
466 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
467 gtk_widget_show (widget);
468
469 g_object_bind_property (
470 shell_sidebar, "icon-name",
471 widget, "icon-name",
472 G_BINDING_SYNC_CREATE);
473
474 widget = gtk_label_new (NULL);
475 gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
476 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
477 gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
478 gtk_widget_show (widget);
479
480 attribute_list = pango_attr_list_new ();
481 attribute = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
482 pango_attr_list_insert (attribute_list, attribute);
483 gtk_label_set_attributes (GTK_LABEL (widget), attribute_list);
484 pango_attr_list_unref (attribute_list);
485
486 g_object_bind_property (
487 shell_sidebar, "primary-text",
488 widget, "label",
489 G_BINDING_SYNC_CREATE);
490
491 widget = gtk_label_new (NULL);
492 gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
493 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
494 gtk_widget_show (widget);
495
496 attribute_list = pango_attr_list_new ();
497 attribute = pango_attr_scale_new (PANGO_SCALE_SMALL);
498 pango_attr_list_insert (attribute_list, attribute);
499 gtk_label_set_attributes (GTK_LABEL (widget), attribute_list);
500 pango_attr_list_unref (attribute_list);
501
502 g_object_bind_property (
503 shell_sidebar, "secondary-text",
504 widget, "label",
505 G_BINDING_SYNC_CREATE);
506 }
507
508 /**
509 * e_shell_sidebar_new:
510 * @shell_view: an #EShellView
511 *
512 * Creates a new #EShellSidebar instance belonging to @shell_view.
513 *
514 * Returns: a new #EShellSidebar instance
515 **/
516 GtkWidget *
517 e_shell_sidebar_new (EShellView *shell_view)
518 {
519 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
520
521 return g_object_new (
522 E_TYPE_SHELL_SIDEBAR,
523 "shell-view", shell_view, NULL);
524 }
525
526 /**
527 * e_shell_sidebar_check_state:
528 * @shell_sidebar: an #EShellSidebar
529 *
530 * #EShellSidebar subclasses should implement the
531 * <structfield>check_state</structfield> method in #EShellSidebarClass
532 * to return a set of flags describing the current sidebar selection.
533 * Subclasses are responsible for defining their own flags. This is
534 * primarily used to assist shell views with updating actions (see
535 * e_shell_view_update_actions()).
536 *
537 * Returns: a set of flags describing the current @shell_sidebar selection
538 **/
539 guint32
540 e_shell_sidebar_check_state (EShellSidebar *shell_sidebar)
541 {
542 EShellSidebarClass *shell_sidebar_class;
543
544 g_return_val_if_fail (E_IS_SHELL_SIDEBAR (shell_sidebar), 0);
545
546 shell_sidebar_class = E_SHELL_SIDEBAR_GET_CLASS (shell_sidebar);
547 g_return_val_if_fail (shell_sidebar_class->check_state != NULL, 0);
548
549 return shell_sidebar_class->check_state (shell_sidebar);
550 }
551
552 /**
553 * e_shell_sidebar_get_shell_view:
554 * @shell_sidebar: an #EShellSidebar
555 *
556 * Returns the #EShellView that was passed to e_shell_sidebar_new().
557 *
558 * Returns: the #EShellView to which @shell_sidebar belongs
559 **/
560 EShellView *
561 e_shell_sidebar_get_shell_view (EShellSidebar *shell_sidebar)
562 {
563 g_return_val_if_fail (E_IS_SHELL_SIDEBAR (shell_sidebar), NULL);
564
565 return E_SHELL_VIEW (shell_sidebar->priv->shell_view);
566 }
567
568 /**
569 * e_shell_sidebar_get_icon_name:
570 * @shell_sidebar: an #EShellSidebar
571 *
572 * Returns the icon name displayed at the top of the sidebar.
573 *
574 * Returns: the icon name for @shell_sidebar
575 **/
576 const gchar *
577 e_shell_sidebar_get_icon_name (EShellSidebar *shell_sidebar)
578 {
579 g_return_val_if_fail (E_IS_SHELL_SIDEBAR (shell_sidebar), NULL);
580
581 return shell_sidebar->priv->icon_name;
582 }
583
584 /**
585 * e_shell_sidebar_set_icon_name:
586 * @shell_sidebar: an #EShellSidebar
587 * @icon_name: a themed icon name
588 *
589 * Sets the icon name displayed at the top of the sidebar.
590 **/
591 void
592 e_shell_sidebar_set_icon_name (EShellSidebar *shell_sidebar,
593 const gchar *icon_name)
594 {
595 g_return_if_fail (E_IS_SHELL_SIDEBAR (shell_sidebar));
596
597 if (g_strcmp0 (shell_sidebar->priv->icon_name, icon_name) == 0)
598 return;
599
600 g_free (shell_sidebar->priv->icon_name);
601 shell_sidebar->priv->icon_name = g_strdup (icon_name);
602
603 g_object_notify (G_OBJECT (shell_sidebar), "icon-name");
604 }
605
606 /**
607 * e_shell_sidebar_get_primary_text:
608 * @shell_sidebar: an #EShellSidebar
609 *
610 * Returns the primary text for @shell_sidebar.
611 *
612 * The primary text is displayed in bold at the top of the sidebar. It
613 * defaults to the shell view's label (as seen on the switcher button),
614 * but typically shows the name of the selected item in the sidebar.
615 *
616 * Returns: the primary text for @shell_sidebar
617 **/
618 const gchar *
619 e_shell_sidebar_get_primary_text (EShellSidebar *shell_sidebar)
620 {
621 g_return_val_if_fail (E_IS_SHELL_SIDEBAR (shell_sidebar), NULL);
622
623 return shell_sidebar->priv->primary_text;
624 }
625
626 /**
627 * e_shell_sidebar_set_primary_text:
628 * @shell_sidebar: an #EShellSidebar
629 * @primary_text: text to be displayed in a bold font
630 *
631 * Sets the primary text for @shell_sidebar.
632 *
633 * The primary text is displayed in bold at the top of the sidebar. It
634 * defaults to the shell view's label (as seen on the switcher button),
635 * but typically shows the name of the selected item in the sidebar.
636 **/
637 void
638 e_shell_sidebar_set_primary_text (EShellSidebar *shell_sidebar,
639 const gchar *primary_text)
640 {
641 g_return_if_fail (E_IS_SHELL_SIDEBAR (shell_sidebar));
642
643 if (g_strcmp0 (shell_sidebar->priv->primary_text, primary_text) == 0)
644 return;
645
646 g_free (shell_sidebar->priv->primary_text);
647 shell_sidebar->priv->primary_text = e_utf8_ensure_valid (primary_text);
648
649 gtk_widget_queue_resize (GTK_WIDGET (shell_sidebar));
650 g_object_notify (G_OBJECT (shell_sidebar), "primary-text");
651 }
652
653 /**
654 * e_shell_sidebar_get_secondary_text:
655 * @shell_sidebar: an #EShellSidebar
656 *
657 * Returns the secondary text for @shell_sidebar.
658 *
659 * The secondary text is displayed in a smaller font at the top of the
660 * sidebar. It typically shows information about the contents of the
661 * selected sidebar item, such as total number of items, number of
662 * selected items, etc.
663 *
664 * Returns: the secondary text for @shell_sidebar
665 **/
666 const gchar *
667 e_shell_sidebar_get_secondary_text (EShellSidebar *shell_sidebar)
668 {
669 g_return_val_if_fail (E_IS_SHELL_SIDEBAR (shell_sidebar), NULL);
670
671 return shell_sidebar->priv->secondary_text;
672 }
673
674 /**
675 * e_shell_sidebar_set_secondary_text:
676 * @shell_sidebar: an #EShellSidebar
677 * @secondary_text: text to be displayed in a smaller font
678 *
679 * Sets the secondary text for @shell_sidebar.
680 *
681 * The secondary text is displayed in a smaller font at the top of the
682 * sidebar. It typically shows information about the contents of the
683 * selected sidebar item, such as total number of items, number of
684 * selected items, etc.
685 **/
686 void
687 e_shell_sidebar_set_secondary_text (EShellSidebar *shell_sidebar,
688 const gchar *secondary_text)
689 {
690 g_return_if_fail (E_IS_SHELL_SIDEBAR (shell_sidebar));
691
692 if (g_strcmp0 (shell_sidebar->priv->secondary_text, secondary_text) == 0)
693 return;
694
695 g_free (shell_sidebar->priv->secondary_text);
696 shell_sidebar->priv->secondary_text = e_utf8_ensure_valid (secondary_text);
697
698 gtk_widget_queue_resize (GTK_WIDGET (shell_sidebar));
699 g_object_notify (G_OBJECT (shell_sidebar), "secondary-text");
700 }