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-content
23 * @short_description: the right side of the main window
24 * @include: shell/e-shell-content.h
27 #include "evolution-config.h"
29 #include "e-shell-content.h"
31 #include <glib/gi18n.h>
32 #include <libebackend/libebackend.h>
34 #include "e-shell-backend.h"
35 #include "e-shell-searchbar.h"
36 #include "e-shell-view.h"
37 #include "e-shell-window-actions.h"
39 #define E_SHELL_CONTENT_GET_PRIVATE(obj) \
40 (G_TYPE_INSTANCE_GET_PRIVATE \
41 ((obj), E_TYPE_SHELL_CONTENT, EShellContentPrivate))
43 struct _EShellContentPrivate
{
45 gpointer shell_view
; /* weak pointer */
48 GtkWidget
*searchbar
; /* not referenced */
50 /* Custom search rules. */
60 /* Forward Declarations */
61 static void e_shell_content_alert_sink_init
62 (EAlertSinkInterface
*iface
);
64 G_DEFINE_TYPE_WITH_CODE (
68 G_IMPLEMENT_INTERFACE (
69 E_TYPE_ALERT_SINK
, e_shell_content_alert_sink_init
)
70 G_IMPLEMENT_INTERFACE (
71 E_TYPE_EXTENSIBLE
, NULL
));
74 shell_content_dialog_rule_changed (GtkWidget
*dialog
,
79 sensitive
= (rule
!= NULL
) && (rule
->parts
!= NULL
);
81 gtk_dialog_set_response_sensitive (
82 GTK_DIALOG (dialog
), GTK_RESPONSE_OK
, sensitive
);
83 gtk_dialog_set_response_sensitive (
84 GTK_DIALOG (dialog
), GTK_RESPONSE_APPLY
, sensitive
);
88 shell_content_set_shell_view (EShellContent
*shell_content
,
89 EShellView
*shell_view
)
91 g_return_if_fail (shell_content
->priv
->shell_view
== NULL
);
93 shell_content
->priv
->shell_view
= shell_view
;
95 g_object_add_weak_pointer (
96 G_OBJECT (shell_view
),
97 &shell_content
->priv
->shell_view
);
101 shell_content_set_property (GObject
*object
,
106 switch (property_id
) {
107 case PROP_SHELL_VIEW
:
108 shell_content_set_shell_view (
109 E_SHELL_CONTENT (object
),
110 g_value_get_object (value
));
114 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
118 shell_content_get_property (GObject
*object
,
123 switch (property_id
) {
126 value
, e_shell_content_get_alert_bar (
127 E_SHELL_CONTENT (object
)));
130 case PROP_SHELL_VIEW
:
132 value
, e_shell_content_get_shell_view (
133 E_SHELL_CONTENT (object
)));
137 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
141 shell_content_dispose (GObject
*object
)
143 EShellContentPrivate
*priv
;
145 priv
= E_SHELL_CONTENT_GET_PRIVATE (object
);
147 if (priv
->shell_view
!= NULL
) {
148 g_object_remove_weak_pointer (
149 G_OBJECT (priv
->shell_view
), &priv
->shell_view
);
150 priv
->shell_view
= NULL
;
153 if (priv
->alert_bar
) {
154 gtk_widget_unparent (priv
->alert_bar
);
155 g_clear_object (&priv
->alert_bar
);
158 /* Chain up to parent's dispose() method. */
159 G_OBJECT_CLASS (e_shell_content_parent_class
)->dispose (object
);
163 shell_content_finalize (GObject
*object
)
165 EShellContentPrivate
*priv
;
167 priv
= E_SHELL_CONTENT_GET_PRIVATE (object
);
169 g_free (priv
->user_filename
);
171 /* Chain up to parent's finalize() method. */
172 G_OBJECT_CLASS (e_shell_content_parent_class
)->finalize (object
);
176 shell_content_constructed (GObject
*object
)
178 EShellContent
*shell_content
;
179 EShellBackend
*shell_backend
;
180 EShellView
*shell_view
;
182 const gchar
*config_dir
;
184 shell_content
= E_SHELL_CONTENT (object
);
185 shell_view
= e_shell_content_get_shell_view (shell_content
);
186 shell_backend
= e_shell_view_get_shell_backend (shell_view
);
188 widget
= e_alert_bar_new ();
189 gtk_widget_set_parent (widget
, GTK_WIDGET (shell_content
));
190 shell_content
->priv
->alert_bar
= g_object_ref_sink (widget
);
191 /* EAlertBar controls its own visibility. */
193 /* XXX Regenerate the filename for custom saved search as done
194 * in shell_view_init_search_context(). ERuleContext ought
195 * to remember the filename when loading rules so you don't
196 * have to keep passing it in when saving rules. */
197 config_dir
= e_shell_backend_get_config_dir (shell_backend
);
198 shell_content
->priv
->user_filename
=
199 g_build_filename (config_dir
, "searches.xml", NULL
);
201 e_extensible_load_extensions (E_EXTENSIBLE (object
));
203 /* Chain up to parent's constructed() method. */
204 G_OBJECT_CLASS (e_shell_content_parent_class
)->constructed (object
);
208 shell_content_get_preferred_width (GtkWidget
*widget
,
212 EShellContentPrivate
*priv
;
215 priv
= E_SHELL_CONTENT_GET_PRIVATE (widget
);
217 *minimum
= *natural
= 0;
219 child
= gtk_bin_get_child (GTK_BIN (widget
));
220 gtk_widget_get_preferred_width (child
, minimum
, natural
);
222 if (gtk_widget_get_visible (priv
->alert_bar
)) {
226 gtk_widget_get_preferred_width (
227 priv
->alert_bar
, &child_minimum
, &child_natural
);
229 *minimum
= MAX (*minimum
, child_minimum
);
230 *natural
= MAX (*natural
, child_natural
);
233 if (priv
->searchbar
!= NULL
) {
237 gtk_widget_get_preferred_width (
238 priv
->searchbar
, &child_minimum
, &child_natural
);
240 *minimum
= MAX (*minimum
, child_minimum
);
241 *natural
= MAX (*natural
, child_natural
);
246 shell_content_get_preferred_height (GtkWidget
*widget
,
250 EShellContentPrivate
*priv
;
253 priv
= E_SHELL_CONTENT_GET_PRIVATE (widget
);
255 child
= gtk_bin_get_child (GTK_BIN (widget
));
256 gtk_widget_get_preferred_height (child
, minimum
, natural
);
258 if (gtk_widget_get_visible (priv
->alert_bar
)) {
262 gtk_widget_get_preferred_height (
263 priv
->alert_bar
, &child_minimum
, &child_natural
);
265 *minimum
+= child_minimum
;
266 *natural
+= child_natural
;
269 if (priv
->searchbar
!= NULL
) {
273 gtk_widget_get_preferred_height (
274 priv
->searchbar
, &child_minimum
, &child_natural
);
276 *minimum
+= child_minimum
;
277 *natural
+= child_natural
;
282 shell_content_size_allocate (GtkWidget
*widget
,
283 GtkAllocation
*allocation
)
285 EShellContentPrivate
*priv
;
286 GtkAllocation child_allocation
;
287 GtkRequisition child_requisition
;
289 gint remaining_height
;
291 priv
= E_SHELL_CONTENT_GET_PRIVATE (widget
);
293 remaining_height
= allocation
->height
;
294 gtk_widget_set_allocation (widget
, allocation
);
296 child_allocation
.x
= allocation
->x
;
297 child_allocation
.y
= allocation
->y
;
298 child_allocation
.width
= allocation
->width
;
300 child_requisition
.height
= 0;
302 /* Alert bar gets to be as tall as it wants (if visible). */
304 child
= priv
->alert_bar
;
305 child_allocation
.y
+= child_requisition
.height
;
307 if (gtk_widget_get_visible (child
))
308 gtk_widget_get_preferred_height_for_width (
309 child
, child_allocation
.width
,
310 &child_requisition
.height
, NULL
);
312 child_requisition
.height
= 0;
314 remaining_height
-= child_requisition
.height
;
315 child_allocation
.height
= child_requisition
.height
;
317 if (child_allocation
.height
> 0)
318 gtk_widget_size_allocate (child
, &child_allocation
);
320 /* Search bar gets to be as tall as it wants (if we have one). */
322 child
= priv
->searchbar
;
323 child_allocation
.y
+= child_requisition
.height
;
326 gtk_widget_get_preferred_size (child
, &child_requisition
, NULL
);
328 child_requisition
.height
= 0;
330 remaining_height
-= child_requisition
.height
;
331 child_allocation
.height
= child_requisition
.height
;
334 gtk_widget_size_allocate (child
, &child_allocation
);
336 /* The GtkBin child gets whatever vertical space is left. */
338 child_allocation
.y
+= child_requisition
.height
;
339 child_allocation
.height
= remaining_height
;
341 child
= gtk_bin_get_child (GTK_BIN (widget
));
343 gtk_widget_size_allocate (child
, &child_allocation
);
347 shell_content_remove (GtkContainer
*container
,
350 GtkContainerClass
*container_class
;
351 EShellContentPrivate
*priv
;
353 priv
= E_SHELL_CONTENT_GET_PRIVATE (container
);
355 if (widget
== priv
->alert_bar
) {
356 gtk_widget_unparent (priv
->alert_bar
);
357 g_clear_object (&priv
->alert_bar
);
361 if (widget
== priv
->searchbar
) {
362 gtk_widget_unparent (priv
->searchbar
);
363 priv
->searchbar
= NULL
;
367 /* Chain up to parent's remove() method. */
368 container_class
= GTK_CONTAINER_CLASS (e_shell_content_parent_class
);
369 container_class
->remove (container
, widget
);
373 shell_content_forall (GtkContainer
*container
,
374 gboolean include_internals
,
375 GtkCallback callback
,
376 gpointer callback_data
)
378 EShellContentPrivate
*priv
;
380 priv
= E_SHELL_CONTENT_GET_PRIVATE (container
);
382 if (priv
->alert_bar
!= NULL
)
383 callback (priv
->alert_bar
, callback_data
);
385 if (priv
->searchbar
!= NULL
)
386 callback (priv
->searchbar
, callback_data
);
388 /* Chain up to parent's forall() method. */
389 GTK_CONTAINER_CLASS (e_shell_content_parent_class
)->forall (
390 container
, include_internals
, callback
, callback_data
);
394 shell_content_submit_alert (EAlertSink
*alert_sink
,
397 GtkWidget
*alert_bar
;
399 alert_bar
= e_shell_content_get_alert_bar (E_SHELL_CONTENT (alert_sink
));
401 e_alert_bar_submit_alert (E_ALERT_BAR (alert_bar
), alert
);
405 e_shell_content_class_init (EShellContentClass
*class)
407 GObjectClass
*object_class
;
408 GtkWidgetClass
*widget_class
;
409 GtkContainerClass
*container_class
;
411 g_type_class_add_private (class, sizeof (EShellContentPrivate
));
413 object_class
= G_OBJECT_CLASS (class);
414 object_class
->set_property
= shell_content_set_property
;
415 object_class
->get_property
= shell_content_get_property
;
416 object_class
->dispose
= shell_content_dispose
;
417 object_class
->finalize
= shell_content_finalize
;
418 object_class
->constructed
= shell_content_constructed
;
420 widget_class
= GTK_WIDGET_CLASS (class);
421 widget_class
->get_preferred_width
= shell_content_get_preferred_width
;
422 widget_class
->get_preferred_height
= shell_content_get_preferred_height
;
423 widget_class
->size_allocate
= shell_content_size_allocate
;
425 container_class
= GTK_CONTAINER_CLASS (class);
426 container_class
->remove
= shell_content_remove
;
427 container_class
->forall
= shell_content_forall
;
430 * EShellContent:alert-bar
432 * Displays informational and error messages.
434 g_object_class_install_property (
437 g_param_spec_object (
440 "Displays informational and error messages",
443 G_PARAM_STATIC_STRINGS
));
446 * EShellContent:shell-view
448 * The #EShellView to which the content widget belongs.
450 g_object_class_install_property (
453 g_param_spec_object (
459 G_PARAM_CONSTRUCT_ONLY
|
460 G_PARAM_STATIC_STRINGS
));
464 e_shell_content_alert_sink_init (EAlertSinkInterface
*iface
)
466 iface
->submit_alert
= shell_content_submit_alert
;
470 e_shell_content_init (EShellContent
*shell_content
)
472 shell_content
->priv
= E_SHELL_CONTENT_GET_PRIVATE (shell_content
);
474 gtk_widget_set_has_window (GTK_WIDGET (shell_content
), FALSE
);
478 * e_shell_content_new:
479 * @shell_view: an #EShellView
481 * Creates a new #EShellContent instance belonging to @shell_view.
483 * Returns: a new #EShellContent instance
486 e_shell_content_new (EShellView
*shell_view
)
488 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view
), NULL
);
490 return g_object_new (
491 E_TYPE_SHELL_CONTENT
, "shell-view", shell_view
, NULL
);
495 * e_shell_content_set_searchbar:
496 * @shell_content: an #EShellContent
497 * @searchbar: a #GtkWidget, or %NULL
499 * Packs @searchbar at the top of @shell_content.
502 e_shell_content_set_searchbar (EShellContent
*shell_content
,
503 GtkWidget
*searchbar
)
505 g_return_if_fail (E_IS_SHELL_CONTENT (shell_content
));
507 if (searchbar
!= NULL
)
508 g_return_if_fail (GTK_IS_WIDGET (searchbar
));
510 if (shell_content
->priv
->searchbar
!= NULL
)
511 gtk_container_remove (
512 GTK_CONTAINER (shell_content
),
513 shell_content
->priv
->searchbar
);
515 shell_content
->priv
->searchbar
= searchbar
;
517 if (searchbar
!= NULL
)
518 gtk_widget_set_parent (searchbar
, GTK_WIDGET (shell_content
));
520 gtk_widget_queue_resize (GTK_WIDGET (shell_content
));
524 * e_shell_content_check_state:
525 * @shell_content: an #EShellContent
527 * #EShellContent subclasses should implement the
528 * <structfield>check_state</structfield> method in #EShellContentClass
529 * to return a set of flags describing the current content selection.
530 * Subclasses are responsible for defining their own flags. This is
531 * primarily used to assist shell views with updating actions (see
532 * e_shell_view_update_actions()).
534 * Returns: a set of flags describing the current @shell_content selection
537 e_shell_content_check_state (EShellContent
*shell_content
)
539 EShellContentClass
*shell_content_class
;
541 g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content
), 0);
543 shell_content_class
= E_SHELL_CONTENT_GET_CLASS (shell_content
);
544 g_return_val_if_fail (shell_content_class
!= NULL
, 0);
545 g_return_val_if_fail (shell_content_class
->check_state
!= NULL
, 0);
547 return shell_content_class
->check_state (shell_content
);
551 * e_shell_content_focus_search_results:
552 * @shell_content: an #EShellContent
554 * #EShellContent subclasses should implement the
555 * <structfield>focus_search_results</structfield> method in
556 * #EShellContentClass to direct input focus to the widget
557 * displaying search results. This is usually called during
558 * e_shell_view_execute_search().
561 e_shell_content_focus_search_results (EShellContent
*shell_content
)
563 EShellContentClass
*shell_content_class
;
565 g_return_if_fail (E_IS_SHELL_CONTENT (shell_content
));
567 shell_content_class
= E_SHELL_CONTENT_GET_CLASS (shell_content
);
568 g_return_if_fail (shell_content_class
!= NULL
);
570 if (shell_content_class
->focus_search_results
!= NULL
)
571 shell_content_class
->focus_search_results (shell_content
);
575 * e_shell_content_get_alert_bar:
576 * @shell_content: an #EShellContent
578 * Returns the #EAlertBar used to display informational and error messages.
580 * Returns: the #EAlertBar for @shell_content
583 e_shell_content_get_alert_bar (EShellContent
*shell_content
)
585 g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content
), NULL
);
587 return shell_content
->priv
->alert_bar
;
591 * e_shell_content_get_shell_view:
592 * @shell_content: an #EShellContent
594 * Returns the #EShellView that was passed to e_shell_content_new().
596 * Returns: the #EShellView to which @shell_content belongs
599 e_shell_content_get_shell_view (EShellContent
*shell_content
)
601 g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content
), NULL
);
603 return E_SHELL_VIEW (shell_content
->priv
->shell_view
);
607 e_shell_content_run_advanced_search_dialog (EShellContent
*shell_content
)
609 EShellView
*shell_view
;
610 EShellWindow
*shell_window
;
611 GtkWidget
*content_area
;
615 ERuleContext
*context
;
616 const gchar
*user_filename
;
619 EAlert
*alert
= NULL
;
621 g_return_if_fail (E_IS_SHELL_CONTENT (shell_content
));
623 shell_view
= e_shell_content_get_shell_view (shell_content
);
624 shell_window
= e_shell_view_get_shell_window (shell_view
);
625 user_filename
= shell_content
->priv
->user_filename
;
627 rule
= e_shell_view_get_search_rule (shell_view
);
630 rule
= e_filter_rule_new ();
632 rule
= e_filter_rule_clone (rule
);
634 context
= E_SHELL_VIEW_GET_CLASS (shell_view
)->search_context
;
635 widget
= e_filter_rule_get_widget (rule
, context
);
636 e_filter_rule_set_source (rule
, E_FILTER_SOURCE_INCOMING
);
638 dialog
= gtk_dialog_new_with_buttons (
639 _("Advanced Search"), GTK_WINDOW (shell_window
),
640 GTK_DIALOG_DESTROY_WITH_PARENT
,
641 _("_Cancel"), GTK_RESPONSE_CANCEL
,
642 _("_Save"), GTK_RESPONSE_APPLY
,
643 _("_OK"), GTK_RESPONSE_OK
, NULL
);
645 gtk_container_set_border_width (GTK_CONTAINER (dialog
), 7);
646 gtk_container_set_border_width (GTK_CONTAINER (widget
), 3);
647 gtk_window_set_default_size (GTK_WINDOW (dialog
), 600, 300);
649 content_area
= gtk_dialog_get_content_area (GTK_DIALOG (dialog
));
650 gtk_box_pack_start (GTK_BOX (content_area
), widget
, TRUE
, TRUE
, 0);
652 handler_id
= g_signal_connect_swapped (
653 rule
, "changed", G_CALLBACK (
654 shell_content_dialog_rule_changed
), dialog
);
656 shell_content_dialog_rule_changed (dialog
, rule
);
659 response
= gtk_dialog_run (GTK_DIALOG (dialog
));
661 if (response
!= GTK_RESPONSE_OK
&& response
!= GTK_RESPONSE_APPLY
)
664 if (!e_filter_rule_validate (rule
, &alert
)) {
665 e_alert_run_dialog (GTK_WINDOW (dialog
), alert
);
666 g_object_unref (alert
);
671 e_shell_view_custom_search (shell_view
, rule
);
673 if (response
== GTK_RESPONSE_APPLY
) {
674 if (!e_rule_context_find_rule (context
, rule
->name
, rule
->source
))
675 e_rule_context_add_rule (context
, rule
);
676 e_rule_context_save (context
, user_filename
);
681 g_signal_handler_disconnect (rule
, handler_id
);
683 g_object_unref (rule
);
684 gtk_widget_destroy (dialog
);
688 e_shell_content_run_edit_searches_dialog (EShellContent
*shell_content
)
690 EShellView
*shell_view
;
691 ERuleContext
*context
;
693 const gchar
*user_filename
;
695 g_return_if_fail (E_IS_SHELL_CONTENT (shell_content
));
697 shell_view
= e_shell_content_get_shell_view (shell_content
);
698 context
= E_SHELL_VIEW_GET_CLASS (shell_view
)->search_context
;
699 user_filename
= shell_content
->priv
->user_filename
;
701 editor
= e_rule_editor_new (
702 context
, E_FILTER_SOURCE_INCOMING
, _("Searches"));
703 gtk_window_set_title (GTK_WINDOW (editor
), _("Searches"));
705 if (gtk_dialog_run (GTK_DIALOG (editor
)) == GTK_RESPONSE_OK
)
706 e_rule_context_save (context
, user_filename
);
708 gtk_widget_destroy (GTK_WIDGET (editor
));
712 e_shell_content_run_save_search_dialog (EShellContent
*shell_content
)
714 EShellView
*shell_view
;
715 EShellWindow
*shell_window
;
716 GtkWidget
*content_area
;
720 ERuleContext
*context
;
721 const gchar
*user_filename
;
725 EAlert
*alert
= NULL
;
727 g_return_if_fail (E_IS_SHELL_CONTENT (shell_content
));
729 shell_view
= e_shell_content_get_shell_view (shell_content
);
730 shell_window
= e_shell_view_get_shell_window (shell_view
);
731 user_filename
= shell_content
->priv
->user_filename
;
733 rule
= e_shell_view_get_search_rule (shell_view
);
734 g_return_if_fail (E_IS_FILTER_RULE (rule
));
735 rule
= e_filter_rule_clone (rule
);
737 search_name
= e_shell_view_get_search_name (shell_view
);
738 e_filter_rule_set_name (rule
, search_name
);
739 g_free (search_name
);
741 context
= E_SHELL_VIEW_GET_CLASS (shell_view
)->search_context
;
742 widget
= e_filter_rule_get_widget (rule
, context
);
743 e_filter_rule_set_source (rule
, E_FILTER_SOURCE_INCOMING
);
745 dialog
= gtk_dialog_new_with_buttons (
746 _("Save Search"), GTK_WINDOW (shell_window
),
747 GTK_DIALOG_DESTROY_WITH_PARENT
,
748 _("_Cancel"), GTK_RESPONSE_CANCEL
,
749 _("_OK"), GTK_RESPONSE_OK
, NULL
);
751 gtk_container_set_border_width (GTK_CONTAINER (dialog
), 7);
752 gtk_container_set_border_width (GTK_CONTAINER (widget
), 3);
753 gtk_window_set_default_size (GTK_WINDOW (dialog
), 500, 300);
755 content_area
= gtk_dialog_get_content_area (GTK_DIALOG (dialog
));
756 gtk_box_pack_start (GTK_BOX (content_area
), widget
, TRUE
, TRUE
, 0);
758 handler_id
= g_signal_connect_swapped (
759 rule
, "changed", G_CALLBACK (
760 shell_content_dialog_rule_changed
), dialog
);
762 shell_content_dialog_rule_changed (dialog
, rule
);
765 response
= gtk_dialog_run (GTK_DIALOG (dialog
));
767 if (response
!= GTK_RESPONSE_OK
)
770 if (!e_filter_rule_validate (rule
, &alert
)) {
771 e_alert_run_dialog (GTK_WINDOW (dialog
), alert
);
772 g_object_unref (alert
);
777 /* XXX This function steals the rule reference, so
778 * counteract that by referencing it again. */
779 e_rule_context_add_rule (context
, g_object_ref (rule
));
781 e_rule_context_save (context
, user_filename
);
784 g_signal_handler_disconnect (rule
, handler_id
);
786 g_object_unref (rule
);
787 gtk_widget_destroy (dialog
);