No issues found
1 /*
2 * e-shell-view.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-view
24 * @short_description: views within the main window
25 * @include: shell/e-shell-view.h
26 **/
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31
32 #include "e-shell-view.h"
33
34 #include <string.h>
35 #include <glib/gi18n.h>
36 #include <libebackend/libebackend.h>
37
38 #include "e-util/e-file-utils.h"
39 #include "e-util/e-plugin-ui.h"
40 #include "e-util/e-source-util.h"
41 #include "e-util/e-ui-manager.h"
42 #include "e-util/e-util-private.h"
43 #include "e-util/e-util.h"
44 #include "filter/e-rule-context.h"
45
46 #include "e-shell-searchbar.h"
47 #include "e-shell-window-actions.h"
48
49 #define E_SHELL_VIEW_GET_PRIVATE(obj) \
50 (G_TYPE_INSTANCE_GET_PRIVATE \
51 ((obj), E_TYPE_SHELL_VIEW, EShellViewPrivate))
52
53 #define SIMPLE_SEARCHBAR_WIDTH 300
54 #define STATE_SAVE_TIMEOUT_SECONDS 3
55
56 struct _EShellViewPrivate {
57
58 gpointer shell_window; /* weak pointer */
59
60 GKeyFile *state_key_file;
61 gpointer state_save_activity; /* weak pointer */
62 guint state_save_timeout_id;
63
64 gchar *title;
65 gchar *view_id;
66 gint page_num;
67 guint merge_id;
68
69 GtkAction *action;
70 GtkSizeGroup *size_group;
71 GtkWidget *shell_content;
72 GtkWidget *shell_sidebar;
73 GtkWidget *shell_taskbar;
74 GtkWidget *searchbar;
75
76 EFilterRule *search_rule;
77 guint execute_search_blocked;
78
79 guint update_actions_blocked;
80 gboolean update_actions_called;
81
82 GtkWidget *preferences_window;
83 gulong preferences_hide_id;
84 };
85
86 enum {
87 PROP_0,
88 PROP_ACTION,
89 PROP_PAGE_NUM,
90 PROP_SEARCHBAR,
91 PROP_SEARCH_RULE,
92 PROP_SHELL_BACKEND,
93 PROP_SHELL_CONTENT,
94 PROP_SHELL_SIDEBAR,
95 PROP_SHELL_TASKBAR,
96 PROP_SHELL_WINDOW,
97 PROP_STATE_KEY_FILE,
98 PROP_TITLE,
99 PROP_VIEW_ID
100 };
101
102 enum {
103 TOGGLED,
104 CLEAR_SEARCH,
105 CUSTOM_SEARCH,
106 EXECUTE_SEARCH,
107 UPDATE_ACTIONS,
108 LAST_SIGNAL
109 };
110
111 static gpointer parent_class;
112 static gulong signals[LAST_SIGNAL];
113
114 static void
115 shell_view_init_search_context (EShellViewClass *class)
116 {
117 EShellBackend *shell_backend;
118 ERuleContext *search_context;
119 const gchar *config_dir;
120 gchar *system_filename;
121 gchar *user_filename;
122
123 shell_backend = class->shell_backend;
124
125 /* Sanity check the class fields we need. */
126 g_return_if_fail (class->search_rules != NULL);
127 g_return_if_fail (E_IS_SHELL_BACKEND (shell_backend));
128
129 /* The basename for built-in searches is specified in the
130 * shell view class. All built-in search rules live in the
131 * same directory. */
132 system_filename = g_build_filename (
133 EVOLUTION_RULEDIR, class->search_rules, NULL);
134
135 /* The filename for custom saved searches is always of
136 * the form "$(shell_backend_config_dir)/searches.xml". */
137 config_dir = e_shell_backend_get_config_dir (shell_backend);
138 user_filename = g_build_filename (config_dir, "searches.xml", NULL);
139
140 /* Create the search context instance. Subclasses may override
141 * the GType so check that it's really an ERuleContext instance. */
142 search_context = g_object_new (class->search_context_type, NULL);
143 g_return_if_fail (E_IS_RULE_CONTEXT (search_context));
144 class->search_context = search_context;
145
146 e_rule_context_add_part_set (
147 search_context, "partset", E_TYPE_FILTER_PART,
148 e_rule_context_add_part, e_rule_context_next_part);
149 e_rule_context_add_rule_set (
150 search_context, "ruleset", E_TYPE_FILTER_RULE,
151 e_rule_context_add_rule, e_rule_context_next_rule);
152 e_rule_context_load (search_context, system_filename, user_filename);
153
154 g_free (system_filename);
155 g_free (user_filename);
156 }
157
158 static void
159 shell_view_init_view_collection (EShellViewClass *class)
160 {
161 EShellBackend *shell_backend;
162 const gchar *base_dir;
163 const gchar *backend_name;
164 gchar *system_dir;
165 gchar *local_dir;
166
167 shell_backend = class->shell_backend;
168 g_return_if_fail (E_IS_SHELL_BACKEND (shell_backend));
169 backend_name = E_SHELL_BACKEND_GET_CLASS (shell_backend)->name;
170
171 base_dir = EVOLUTION_GALVIEWSDIR;
172 system_dir = g_build_filename (base_dir, backend_name, NULL);
173
174 base_dir = e_shell_backend_get_config_dir (shell_backend);
175 local_dir = g_build_filename (base_dir, "views", NULL);
176
177 /* The view collection is never destroyed. */
178 class->view_collection = gal_view_collection_new ();
179
180 gal_view_collection_set_title (
181 class->view_collection, class->label);
182
183 gal_view_collection_set_storage_directories (
184 class->view_collection, system_dir, local_dir);
185
186 g_free (system_dir);
187 g_free (local_dir);
188
189 /* This is all we can do. It's up to the subclasses to
190 * add the appropriate factories to the view collection. */
191 }
192
193 static void
194 shell_view_update_view_id (EShellView *shell_view,
195 GalViewInstance *view_instance)
196 {
197 gchar *view_id;
198
199 view_id = gal_view_instance_get_current_view_id (view_instance);
200 e_shell_view_set_view_id (shell_view, view_id);
201 g_free (view_id);
202 }
203
204 static void
205 shell_view_load_state (EShellView *shell_view)
206 {
207 EShellBackend *shell_backend;
208 GKeyFile *key_file;
209 const gchar *config_dir;
210 gchar *filename;
211 GError *error = NULL;
212
213 shell_backend = e_shell_view_get_shell_backend (shell_view);
214 config_dir = e_shell_backend_get_config_dir (shell_backend);
215 filename = g_build_filename (config_dir, "state.ini", NULL);
216
217 /* XXX Should do this asynchronously. */
218 key_file = shell_view->priv->state_key_file;
219 g_key_file_load_from_file (key_file, filename, 0, &error);
220
221 if (error == NULL)
222 goto exit;
223
224 if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
225 g_warning ("%s", error->message);
226
227 g_error_free (error);
228
229 exit:
230 g_free (filename);
231 }
232
233 typedef struct {
234 EShellView *shell_view;
235 gchar *contents;
236 } SaveStateData;
237
238 static void
239 shell_view_save_state_done_cb (GFile *file,
240 GAsyncResult *result,
241 SaveStateData *data)
242 {
243 GError *error = NULL;
244
245 e_file_replace_contents_finish (file, result, NULL, &error);
246
247 if (error != NULL) {
248 g_warning ("%s", error->message);
249 g_error_free (error);
250 }
251
252 g_object_unref (data->shell_view);
253 g_free (data->contents);
254 g_slice_free (SaveStateData, data);
255 }
256
257 static EActivity *
258 shell_view_save_state (EShellView *shell_view,
259 gboolean immediately)
260 {
261 EShellBackend *shell_backend;
262 SaveStateData *data;
263 EActivity *activity;
264 GKeyFile *key_file;
265 GFile *file;
266 const gchar *config_dir;
267 gchar *contents;
268 gchar *path;
269
270 shell_backend = e_shell_view_get_shell_backend (shell_view);
271 config_dir = e_shell_backend_get_config_dir (shell_backend);
272 key_file = shell_view->priv->state_key_file;
273
274 contents = g_key_file_to_data (key_file, NULL, NULL);
275 g_return_val_if_fail (contents != NULL, NULL);
276
277 path = g_build_filename (config_dir, "state.ini", NULL);
278 if (immediately) {
279 g_file_set_contents (path, contents, -1, NULL);
280
281 g_free (path);
282 g_free (contents);
283
284 return NULL;
285 }
286
287 file = g_file_new_for_path (path);
288 g_free (path);
289
290 /* GIO does not copy the contents string, so we need to keep
291 * it in memory until saving is complete. We reference the
292 * shell view to keep it from being finalized while saving. */
293 data = g_slice_new (SaveStateData);
294 data->shell_view = g_object_ref (shell_view);
295 data->contents = contents;
296
297 /* The returned activity is a borrowed reference. */
298 activity = e_file_replace_contents_async (
299 file, contents, strlen (contents), NULL,
300 FALSE, G_FILE_CREATE_PRIVATE, (GAsyncReadyCallback)
301 shell_view_save_state_done_cb, data);
302
303 e_activity_set_text (
304 activity, (_("Saving user interface state")));
305
306 e_shell_backend_add_activity (shell_backend, activity);
307
308 g_object_unref (file);
309
310 return activity;
311 }
312
313 static gboolean
314 shell_view_state_timeout_cb (EShellView *shell_view)
315 {
316 EActivity *activity;
317
318 /* If a save is still in progress, check back later. */
319 if (shell_view->priv->state_save_activity != NULL)
320 return TRUE;
321
322 activity = shell_view_save_state (shell_view, FALSE);
323
324 /* Set up a weak pointer that gets set to NULL when the
325 * activity finishes. This will tell us if we're still
326 * busy saving state data to disk on the next timeout. */
327 shell_view->priv->state_save_activity = activity;
328 g_object_add_weak_pointer (
329 G_OBJECT (shell_view->priv->state_save_activity),
330 &shell_view->priv->state_save_activity);
331
332 shell_view->priv->state_save_timeout_id = 0;
333
334 return FALSE;
335 }
336
337 static void
338 shell_view_emit_toggled (EShellView *shell_view)
339 {
340 g_signal_emit (shell_view, signals[TOGGLED], 0);
341 }
342
343 static void
344 shell_view_set_action (EShellView *shell_view,
345 GtkAction *action)
346 {
347 gchar *label;
348
349 g_return_if_fail (shell_view->priv->action == NULL);
350
351 shell_view->priv->action = g_object_ref (action);
352
353 g_object_get (action, "label", &label, NULL);
354 e_shell_view_set_title (shell_view, label);
355 g_free (label);
356
357 g_signal_connect_swapped (
358 action, "toggled",
359 G_CALLBACK (shell_view_emit_toggled), shell_view);
360 }
361
362 static void
363 shell_view_set_shell_window (EShellView *shell_view,
364 EShellWindow *shell_window)
365 {
366 g_return_if_fail (E_IS_SHELL_WINDOW (shell_window));
367 g_return_if_fail (shell_view->priv->shell_window == NULL);
368
369 shell_view->priv->shell_window = shell_window;
370
371 g_object_add_weak_pointer (
372 G_OBJECT (shell_window),
373 &shell_view->priv->shell_window);
374 }
375
376 static void
377 shell_view_set_property (GObject *object,
378 guint property_id,
379 const GValue *value,
380 GParamSpec *pspec)
381 {
382 switch (property_id) {
383 case PROP_ACTION:
384 shell_view_set_action (
385 E_SHELL_VIEW (object),
386 g_value_get_object (value));
387 return;
388
389 case PROP_PAGE_NUM:
390 e_shell_view_set_page_num (
391 E_SHELL_VIEW (object),
392 g_value_get_int (value));
393 return;
394
395 case PROP_SEARCH_RULE:
396 e_shell_view_set_search_rule (
397 E_SHELL_VIEW (object),
398 g_value_get_object (value));
399 return;
400
401 case PROP_SHELL_WINDOW:
402 shell_view_set_shell_window (
403 E_SHELL_VIEW (object),
404 g_value_get_object (value));
405 return;
406
407 case PROP_TITLE:
408 e_shell_view_set_title (
409 E_SHELL_VIEW (object),
410 g_value_get_string (value));
411 return;
412
413 case PROP_VIEW_ID:
414 e_shell_view_set_view_id (
415 E_SHELL_VIEW (object),
416 g_value_get_string (value));
417 return;
418 }
419
420 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
421 }
422
423 static void
424 shell_view_get_property (GObject *object,
425 guint property_id,
426 GValue *value,
427 GParamSpec *pspec)
428 {
429 switch (property_id) {
430 case PROP_ACTION:
431 g_value_set_object (
432 value, e_shell_view_get_action (
433 E_SHELL_VIEW (object)));
434 return;
435
436 case PROP_PAGE_NUM:
437 g_value_set_int (
438 value, e_shell_view_get_page_num (
439 E_SHELL_VIEW (object)));
440 return;
441
442 case PROP_SEARCHBAR:
443 g_value_set_object (
444 value, e_shell_view_get_searchbar (
445 E_SHELL_VIEW (object)));
446 return;
447
448 case PROP_SEARCH_RULE:
449 g_value_set_object (
450 value, e_shell_view_get_search_rule (
451 E_SHELL_VIEW (object)));
452 return;
453
454 case PROP_SHELL_BACKEND:
455 g_value_set_object (
456 value, e_shell_view_get_shell_backend (
457 E_SHELL_VIEW (object)));
458
459 case PROP_SHELL_CONTENT:
460 g_value_set_object (
461 value, e_shell_view_get_shell_content (
462 E_SHELL_VIEW (object)));
463 return;
464
465 case PROP_SHELL_SIDEBAR:
466 g_value_set_object (
467 value, e_shell_view_get_shell_sidebar (
468 E_SHELL_VIEW (object)));
469 return;
470
471 case PROP_SHELL_TASKBAR:
472 g_value_set_object (
473 value, e_shell_view_get_shell_taskbar (
474 E_SHELL_VIEW (object)));
475 return;
476
477 case PROP_SHELL_WINDOW:
478 g_value_set_object (
479 value, e_shell_view_get_shell_window (
480 E_SHELL_VIEW (object)));
481 return;
482
483 case PROP_STATE_KEY_FILE:
484 g_value_set_pointer (
485 value, e_shell_view_get_state_key_file (
486 E_SHELL_VIEW (object)));
487 return;
488
489 case PROP_TITLE:
490 g_value_set_string (
491 value, e_shell_view_get_title (
492 E_SHELL_VIEW (object)));
493 return;
494
495 case PROP_VIEW_ID:
496 g_value_set_string (
497 value, e_shell_view_get_view_id (
498 E_SHELL_VIEW (object)));
499 return;
500 }
501
502 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
503 }
504
505 static void
506 shell_view_dispose (GObject *object)
507 {
508 EShellViewPrivate *priv;
509
510 priv = E_SHELL_VIEW_GET_PRIVATE (object);
511
512 /* Expedite any pending state saves. */
513 if (priv->state_save_timeout_id > 0) {
514 g_source_remove (priv->state_save_timeout_id);
515 priv->state_save_timeout_id = 0;
516 if (priv->state_save_activity == NULL)
517 shell_view_save_state (E_SHELL_VIEW (object), TRUE);
518 }
519
520 if (priv->state_save_activity != NULL) {
521 g_object_remove_weak_pointer (
522 G_OBJECT (priv->state_save_activity),
523 &priv->state_save_activity);
524 priv->state_save_activity = NULL;
525 }
526
527 if (priv->shell_window != NULL) {
528 g_object_remove_weak_pointer (
529 G_OBJECT (priv->shell_window), &priv->shell_window);
530 priv->shell_window = NULL;
531 }
532
533 if (priv->shell_content != NULL) {
534 g_object_unref (priv->shell_content);
535 priv->shell_content = NULL;
536 }
537
538 if (priv->shell_sidebar != NULL) {
539 g_object_unref (priv->shell_sidebar);
540 priv->shell_sidebar = NULL;
541 }
542
543 if (priv->shell_taskbar != NULL) {
544 g_object_unref (priv->shell_taskbar);
545 priv->shell_taskbar = NULL;
546 }
547
548 if (priv->searchbar != NULL) {
549 g_object_unref (priv->searchbar);
550 priv->searchbar = NULL;
551 }
552
553 if (priv->search_rule != NULL) {
554 g_object_unref (priv->search_rule);
555 priv->search_rule = NULL;
556 }
557
558 if (priv->preferences_window != NULL) {
559 g_signal_handler_disconnect (
560 priv->preferences_window,
561 priv->preferences_hide_id);
562 g_object_unref (priv->preferences_window);
563 priv->preferences_window = NULL;
564 priv->preferences_hide_id = 0;
565 }
566
567 /* Chain up to parent's dispose() method. */
568 G_OBJECT_CLASS (parent_class)->dispose (object);
569 }
570
571 static void
572 shell_view_finalize (GObject *object)
573 {
574 EShellViewPrivate *priv;
575
576 priv = E_SHELL_VIEW_GET_PRIVATE (object);
577
578 g_key_file_free (priv->state_key_file);
579
580 g_free (priv->title);
581 g_free (priv->view_id);
582
583 /* Chain up to parent's finalize() method. */
584 G_OBJECT_CLASS (parent_class)->finalize (object);
585 }
586
587 static void
588 shell_view_constructed (GObject *object)
589 {
590 EShell *shell;
591 EShellView *shell_view;
592 EShellBackend *shell_backend;
593 EShellViewClass *shell_view_class;
594 GtkWidget *widget;
595
596 shell_view = E_SHELL_VIEW (object);
597 shell_view_class = E_SHELL_VIEW_GET_CLASS (shell_view);
598
599 shell_backend = e_shell_view_get_shell_backend (shell_view);
600 shell = e_shell_backend_get_shell (shell_backend);
601
602 shell_view_load_state (shell_view);
603
604 /* Invoke factory methods. */
605
606 /* Create the taskbar widget first so the content and
607 * sidebar widgets can access it during construction. */
608 widget = shell_view_class->new_shell_taskbar (shell_view);
609 shell_view->priv->shell_taskbar = g_object_ref_sink (widget);
610 gtk_widget_show (widget);
611
612 widget = shell_view_class->new_shell_content (shell_view);
613 shell_view->priv->shell_content = g_object_ref_sink (widget);
614 gtk_widget_show (widget);
615
616 widget = shell_view_class->new_shell_sidebar (shell_view);
617 shell_view->priv->shell_sidebar = g_object_ref_sink (widget);
618 gtk_widget_show (widget);
619
620 if (shell_view_class->construct_searchbar != NULL) {
621 widget = shell_view_class->construct_searchbar (shell_view);
622 shell_view->priv->searchbar = g_object_ref_sink (widget);
623 }
624
625 /* Size group should be safe to unreference now. */
626 g_object_unref (shell_view->priv->size_group);
627 shell_view->priv->size_group = NULL;
628
629 /* Update actions whenever the Preferences window is closed. */
630 widget = e_shell_get_preferences_window (shell);
631 shell_view->priv->preferences_window = g_object_ref (widget);
632 shell_view->priv->preferences_hide_id = g_signal_connect_swapped (
633 shell_view->priv->preferences_window, "hide",
634 G_CALLBACK (e_shell_view_update_actions), shell_view);
635
636 e_extensible_load_extensions (E_EXTENSIBLE (object));
637
638 /* Chain up to parent's constructed() method. */
639 G_OBJECT_CLASS (parent_class)->constructed (object);
640 }
641
642 static GtkWidget *
643 shell_view_construct_searchbar (EShellView *shell_view)
644 {
645 EShell *shell;
646 EShellWindow *shell_window;
647 EShellContent *shell_content;
648 EShellSearchbar *shell_searchbar;
649 EShellViewClass *shell_view_class;
650 GtkToolItem *item;
651 GtkAction *action;
652 GtkWidget *main_toolbar;
653 GtkWidget *widget;
654
655 shell_content = e_shell_view_get_shell_content (shell_view);
656 shell_window = e_shell_view_get_shell_window (shell_view);
657 shell = e_shell_window_get_shell (shell_window);
658
659 shell_view_class = E_SHELL_VIEW_GET_CLASS (shell_view);
660 widget = shell_view_class->new_shell_searchbar (shell_view);
661
662 /* In normal mode, we hand the searchbar off to EShellContent. */
663 if (!e_shell_get_express_mode (shell)) {
664 e_shell_content_set_searchbar (shell_content, widget);
665 gtk_widget_show (widget);
666 return widget;
667 }
668
669 /* Express mode is more complicated. We append a heavily simplified
670 * version of it to the main toolbar, but only show it when this shell
671 * view is active. So each view still gets its own searchbar. */
672
673 shell_searchbar = E_SHELL_SEARCHBAR (widget);
674 e_shell_searchbar_set_express_mode (shell_searchbar, TRUE);
675
676 /* XXX Hardcoded sizes are evil, but what should the width be
677 * relative to. Window width? The other toolbar width? */
678 gtk_widget_set_size_request (widget, SIMPLE_SEARCHBAR_WIDTH, -1);
679
680 main_toolbar = e_shell_window_get_managed_widget (
681 shell_window, "/search-toolbar");
682
683 item = gtk_tool_item_new ();
684 gtk_container_add (GTK_CONTAINER (item), widget);
685 gtk_widget_show (GTK_WIDGET (item));
686
687 action = e_shell_view_get_action (shell_view);
688 g_object_bind_property (
689 action, "active",
690 widget, "visible",
691 G_BINDING_SYNC_CREATE);
692
693 gtk_toolbar_insert (GTK_TOOLBAR (main_toolbar), item, -1);
694
695 if (e_shell_get_small_screen_mode (shell)) {
696 GtkWidget *image;
697 GtkWidget *container;
698 GtkAction *action;
699 GtkToolItem *item;
700
701 action = e_shell_window_get_action (
702 shell_window, "close-window");
703 image = gtk_image_new_from_icon_name (
704 "window-close-hover", GTK_ICON_SIZE_DIALOG);
705 item = gtk_tool_button_new (
706 image, gtk_action_get_label (action));
707 gtk_widget_set_name (GTK_WIDGET (item), "MeeGoCloseButton");
708 gtk_activatable_set_related_action (
709 GTK_ACTIVATABLE (item), action);
710 container = e_shell_window_get_menu_bar_box (shell_window);
711 gtk_box_pack_start (
712 GTK_BOX (container),
713 GTK_WIDGET (item), FALSE, FALSE, 0);
714 gtk_widget_show_all (GTK_WIDGET (item));
715 }
716
717 return widget;
718 }
719
720 static gchar *
721 shell_view_get_search_name (EShellView *shell_view)
722 {
723 EShellSearchbar *searchbar;
724 EFilterRule *rule;
725 const gchar *search_text;
726
727 rule = e_shell_view_get_search_rule (shell_view);
728 g_return_val_if_fail (E_IS_FILTER_RULE (rule), NULL);
729
730 searchbar = E_SHELL_SEARCHBAR (shell_view->priv->searchbar);
731 search_text = e_shell_searchbar_get_search_text (searchbar);
732
733 if (search_text == NULL || *search_text == '\0')
734 search_text = "''";
735
736 return g_strdup_printf ("%s %s", rule->name, search_text);
737 }
738
739 static void
740 shell_view_toggled (EShellView *shell_view)
741 {
742 EShellViewPrivate *priv = shell_view->priv;
743 EShellViewClass *shell_view_class;
744 EShellWindow *shell_window;
745 GtkUIManager *ui_manager;
746 const gchar *basename, *id;
747 gboolean view_is_active;
748
749 shell_view_class = E_SHELL_VIEW_GET_CLASS (shell_view);
750 shell_window = e_shell_view_get_shell_window (shell_view);
751 ui_manager = e_shell_window_get_ui_manager (shell_window);
752 view_is_active = e_shell_view_is_active (shell_view);
753 basename = shell_view_class->ui_definition;
754 id = shell_view_class->ui_manager_id;
755
756 if (view_is_active && priv->merge_id == 0) {
757 priv->merge_id = e_ui_manager_add_ui_from_file (
758 E_UI_MANAGER (ui_manager), basename);
759 e_plugin_ui_enable_manager (ui_manager, id);
760 } else if (!view_is_active && priv->merge_id != 0) {
761 e_plugin_ui_disable_manager (ui_manager, id);
762 gtk_ui_manager_remove_ui (ui_manager, priv->merge_id);
763 gtk_ui_manager_ensure_update (ui_manager);
764 priv->merge_id = 0;
765 }
766
767 gtk_ui_manager_ensure_update (ui_manager);
768 }
769
770 static void
771 shell_view_clear_search (EShellView *shell_view)
772 {
773 e_shell_view_set_search_rule (shell_view, NULL);
774 e_shell_view_execute_search (shell_view);
775 }
776
777 static void
778 shell_view_custom_search (EShellView *shell_view,
779 EFilterRule *custom_rule)
780 {
781 e_shell_view_set_search_rule (shell_view, custom_rule);
782 e_shell_view_execute_search (shell_view);
783 }
784
785 static void
786 shell_view_update_actions (EShellView *shell_view)
787 {
788 EShellWindow *shell_window;
789 EFocusTracker *focus_tracker;
790
791 g_return_if_fail (!shell_view->priv->update_actions_blocked);
792 g_return_if_fail (e_shell_view_is_active (shell_view));
793
794 shell_window = e_shell_view_get_shell_window (shell_view);
795 focus_tracker = e_shell_window_get_focus_tracker (shell_window);
796
797 e_focus_tracker_update_actions (focus_tracker);
798 }
799
800 static void
801 e_shell_view_class_init (EShellViewClass *class)
802 {
803 GObjectClass *object_class;
804
805 parent_class = g_type_class_peek_parent (class);
806 g_type_class_add_private (class, sizeof (EShellViewPrivate));
807
808 object_class = G_OBJECT_CLASS (class);
809 object_class->set_property = shell_view_set_property;
810 object_class->get_property = shell_view_get_property;
811 object_class->dispose = shell_view_dispose;
812 object_class->finalize = shell_view_finalize;
813 object_class->constructed = shell_view_constructed;
814
815 class->search_context_type = E_TYPE_RULE_CONTEXT;
816
817 /* Default Factories */
818 class->new_shell_content = e_shell_content_new;
819 class->new_shell_sidebar = e_shell_sidebar_new;
820 class->new_shell_taskbar = e_shell_taskbar_new;
821 class->new_shell_searchbar = e_shell_searchbar_new;
822
823 class->construct_searchbar = shell_view_construct_searchbar;
824 class->get_search_name = shell_view_get_search_name;
825
826 class->toggled = shell_view_toggled;
827 class->clear_search = shell_view_clear_search;
828 class->custom_search = shell_view_custom_search;
829 class->update_actions = shell_view_update_actions;
830
831 /**
832 * EShellView:action:
833 *
834 * The #GtkRadioAction registered with #EShellSwitcher.
835 **/
836 g_object_class_install_property (
837 object_class,
838 PROP_ACTION,
839 g_param_spec_object (
840 "action",
841 "Switcher Action",
842 "The switcher action for this shell view",
843 GTK_TYPE_RADIO_ACTION,
844 G_PARAM_READWRITE |
845 G_PARAM_CONSTRUCT_ONLY));
846
847 /**
848 * EShellView:page-num
849 *
850 * The #GtkNotebook page number of the shell view.
851 **/
852 g_object_class_install_property (
853 object_class,
854 PROP_PAGE_NUM,
855 g_param_spec_int (
856 "page-num",
857 "Page Number",
858 "The notebook page number of the shell view",
859 -1,
860 G_MAXINT,
861 -1,
862 G_PARAM_READWRITE));
863
864 /**
865 * EShellView:search-rule
866 *
867 * Criteria for the current search results.
868 **/
869 g_object_class_install_property (
870 object_class,
871 PROP_SEARCH_RULE,
872 g_param_spec_object (
873 "search-rule",
874 "Search Rule",
875 "Criteria for the current search results",
876 E_TYPE_FILTER_RULE,
877 G_PARAM_READWRITE));
878
879 /**
880 * EShellView:shell-backend
881 *
882 * The #EShellBackend for this shell view.
883 **/
884 g_object_class_install_property (
885 object_class,
886 PROP_SHELL_BACKEND,
887 g_param_spec_object (
888 "shell-backend",
889 "Shell Backend",
890 "The EShellBackend for this shell view",
891 E_TYPE_SHELL_BACKEND,
892 G_PARAM_READABLE));
893
894 /**
895 * EShellView:shell-content
896 *
897 * The content widget appears in an #EShellWindow<!-- -->'s
898 * right pane.
899 **/
900 g_object_class_install_property (
901 object_class,
902 PROP_SHELL_CONTENT,
903 g_param_spec_object (
904 "shell-content",
905 "Shell Content Widget",
906 "The content widget appears in "
907 "a shell window's right pane",
908 E_TYPE_SHELL_CONTENT,
909 G_PARAM_READABLE));
910
911 /**
912 * EShellView:shell-sidebar
913 *
914 * The sidebar widget appears in an #EShellWindow<!-- -->'s
915 * left pane.
916 **/
917 g_object_class_install_property (
918 object_class,
919 PROP_SHELL_SIDEBAR,
920 g_param_spec_object (
921 "shell-sidebar",
922 "Shell Sidebar Widget",
923 "The sidebar widget appears in "
924 "a shell window's left pane",
925 E_TYPE_SHELL_SIDEBAR,
926 G_PARAM_READABLE));
927
928 /**
929 * EShellView:shell-taskbar
930 *
931 * The taskbar widget appears at the bottom of an #EShellWindow.
932 **/
933 g_object_class_install_property (
934 object_class,
935 PROP_SHELL_TASKBAR,
936 g_param_spec_object (
937 "shell-taskbar",
938 "Shell Taskbar Widget",
939 "The taskbar widget appears at "
940 "the bottom of a shell window",
941 E_TYPE_SHELL_TASKBAR,
942 G_PARAM_READABLE));
943
944 /**
945 * EShellView:shell-window
946 *
947 * The #EShellWindow to which the shell view belongs.
948 **/
949 g_object_class_install_property (
950 object_class,
951 PROP_SHELL_WINDOW,
952 g_param_spec_object (
953 "shell-window",
954 "Shell Window",
955 "The window to which the shell view belongs",
956 E_TYPE_SHELL_WINDOW,
957 G_PARAM_READWRITE |
958 G_PARAM_CONSTRUCT_ONLY));
959
960 /**
961 * EShellView:state-key-file
962 *
963 * The #GKeyFile holding widget state data.
964 **/
965 g_object_class_install_property (
966 object_class,
967 PROP_STATE_KEY_FILE,
968 g_param_spec_pointer (
969 "state-key-file",
970 "State Key File",
971 "The key file holding widget state data",
972 G_PARAM_READABLE));
973
974 /**
975 * EShellView:title
976 *
977 * The title of the shell view. Also serves as the #EShellWindow
978 * title when the shell view is active.
979 **/
980 g_object_class_install_property (
981 object_class,
982 PROP_TITLE,
983 g_param_spec_string (
984 "title",
985 "Title",
986 "The title of the shell view",
987 NULL,
988 G_PARAM_READWRITE));
989
990 /**
991 * EShellView:view-id
992 *
993 * The current #GalView ID.
994 **/
995 g_object_class_install_property (
996 object_class,
997 PROP_VIEW_ID,
998 g_param_spec_string (
999 "view-id",
1000 "Current View ID",
1001 "The current GAL view ID",
1002 NULL,
1003 G_PARAM_READWRITE));
1004
1005 /**
1006 * EShellView::toggled
1007 * @shell_view: the #EShellView which emitted the signal
1008 *
1009 * Emitted when @shell_view is activated or deactivated.
1010 * Use e_shell_view_is_active() to find out which event has
1011 * occurred. The shell view being deactivated is always
1012 * notified before the shell view being activated.
1013 *
1014 * By default, #EShellView adds the UI definition file
1015 * given in the <structfield>ui_definition</structfield>
1016 * field of #EShellViewClass on activation, and removes the
1017 * UI definition on deactivation.
1018 **/
1019 signals[TOGGLED] = g_signal_new (
1020 "toggled",
1021 G_OBJECT_CLASS_TYPE (object_class),
1022 G_SIGNAL_RUN_FIRST,
1023 G_STRUCT_OFFSET (EShellViewClass, toggled),
1024 NULL, NULL,
1025 g_cclosure_marshal_VOID__VOID,
1026 G_TYPE_NONE, 0);
1027
1028 /**
1029 * EShellView::clear-search
1030 * @shell_view: the #EShellView which emitted the signal
1031 *
1032 * Clears the current search. See e_shell_view_clear_search() for
1033 * details.
1034 **/
1035 signals[CLEAR_SEARCH] = g_signal_new (
1036 "clear-search",
1037 G_OBJECT_CLASS_TYPE (object_class),
1038 G_SIGNAL_RUN_LAST,
1039 G_STRUCT_OFFSET (EShellViewClass, clear_search),
1040 NULL, NULL,
1041 g_cclosure_marshal_VOID__VOID,
1042 G_TYPE_NONE, 0);
1043
1044 /**
1045 * EShellView::custom-search
1046 * @shell_view: the #EShellView which emitted the signal
1047 * @custom_rule: criteria for the custom search
1048 *
1049 * Emitted when an advanced or saved search is about to be executed.
1050 * See e_shell_view_custom_search() for details.
1051 **/
1052 signals[CUSTOM_SEARCH] = g_signal_new (
1053 "custom-search",
1054 G_OBJECT_CLASS_TYPE (object_class),
1055 G_SIGNAL_RUN_LAST,
1056 G_STRUCT_OFFSET (EShellViewClass, custom_search),
1057 NULL, NULL,
1058 g_cclosure_marshal_VOID__OBJECT,
1059 G_TYPE_NONE, 1,
1060 E_TYPE_FILTER_RULE);
1061
1062 /**
1063 * EShellView::execute-search
1064 * @shell_view: the #EShellView which emitted the signal
1065 *
1066 * #EShellView subclasses should override the
1067 * <structfield>execute_search</structfield> method in
1068 * #EShellViewClass to execute the current search conditions.
1069 **/
1070 signals[EXECUTE_SEARCH] = g_signal_new (
1071 "execute-search",
1072 G_OBJECT_CLASS_TYPE (object_class),
1073 G_SIGNAL_RUN_FIRST,
1074 G_STRUCT_OFFSET (EShellViewClass, execute_search),
1075 NULL, NULL,
1076 g_cclosure_marshal_VOID__VOID,
1077 G_TYPE_NONE, 0);
1078
1079 /**
1080 * EShellView::update-actions
1081 * @shell_view: the #EShellView which emitted the signal
1082 *
1083 * #EShellView subclasses should override the
1084 * <structfield>update_actions</structfield> method in
1085 * #EShellViewClass to update sensitivities, labels, or any
1086 * other aspect of the #GtkAction<!-- -->s they have registered.
1087 *
1088 * Plugins can also connect to this signal to be notified
1089 * when to update their own #GtkAction<!-- -->s.
1090 **/
1091 signals[UPDATE_ACTIONS] = g_signal_new (
1092 "update-actions",
1093 G_OBJECT_CLASS_TYPE (object_class),
1094 G_SIGNAL_RUN_FIRST,
1095 G_STRUCT_OFFSET (EShellViewClass, update_actions),
1096 NULL, NULL,
1097 g_cclosure_marshal_VOID__VOID,
1098 G_TYPE_NONE, 0);
1099 }
1100
1101 static void
1102 e_shell_view_init (EShellView *shell_view,
1103 EShellViewClass *class)
1104 {
1105 GtkSizeGroup *size_group;
1106
1107 /* XXX Our use of GInstanceInitFunc's 'class' parameter
1108 * prevents us from using G_DEFINE_ABSTRACT_TYPE. */
1109
1110 if (class->search_context == NULL)
1111 shell_view_init_search_context (class);
1112
1113 if (class->view_collection == NULL)
1114 shell_view_init_view_collection (class);
1115
1116 size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
1117
1118 shell_view->priv = E_SHELL_VIEW_GET_PRIVATE (shell_view);
1119 shell_view->priv->state_key_file = g_key_file_new ();
1120 shell_view->priv->size_group = size_group;
1121 }
1122
1123 GType
1124 e_shell_view_get_type (void)
1125 {
1126 static GType type = 0;
1127
1128 if (G_UNLIKELY (type == 0)) {
1129 const GTypeInfo type_info = {
1130 sizeof (EShellViewClass),
1131 (GBaseInitFunc) NULL,
1132 (GBaseFinalizeFunc) NULL,
1133 (GClassInitFunc) e_shell_view_class_init,
1134 (GClassFinalizeFunc) NULL,
1135 NULL, /* class_data */
1136 sizeof (EShellView),
1137 0, /* n_preallocs */
1138 (GInstanceInitFunc) e_shell_view_init,
1139 NULL /* value_table */
1140 };
1141
1142 const GInterfaceInfo extensible_info = {
1143 (GInterfaceInitFunc) NULL,
1144 (GInterfaceFinalizeFunc) NULL,
1145 NULL /* interface_data */
1146 };
1147
1148 type = g_type_register_static (
1149 G_TYPE_OBJECT, "EShellView",
1150 &type_info, G_TYPE_FLAG_ABSTRACT);
1151
1152 g_type_add_interface_static (
1153 type, E_TYPE_EXTENSIBLE, &extensible_info);
1154 }
1155
1156 return type;
1157 }
1158
1159 /**
1160 * e_shell_view_get_name:
1161 * @shell_view: an #EShellView
1162 *
1163 * Returns the view name for @shell_view, which is also the name of
1164 * the corresponding #EShellBackend (see the <structfield>name</structfield>
1165 * field in #EShellBackendInfo).
1166 *
1167 * Returns: the view name for @shell_view
1168 **/
1169 const gchar *
1170 e_shell_view_get_name (EShellView *shell_view)
1171 {
1172 GtkAction *action;
1173
1174 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1175
1176 action = e_shell_view_get_action (shell_view);
1177
1178 /* Switcher actions have a secret "view-name" data value.
1179 * This gets set in e_shell_window_create_switcher_actions(). */
1180 return g_object_get_data (G_OBJECT (action), "view-name");
1181 }
1182
1183 /**
1184 * e_shell_view_get_action:
1185 * @shell_view: an #EShellView
1186 *
1187 * Returns the switcher action for @shell_view.
1188 *
1189 * An #EShellWindow creates a #GtkRadioAction for each registered subclass
1190 * of #EShellView. This action gets passed to the #EShellSwitcher, which
1191 * displays a button that proxies the action. The icon at the top of the
1192 * sidebar also proxies the action. When @shell_view is active, the
1193 * action's icon becomes the #EShellWindow icon.
1194 *
1195 * Returns: the switcher action for @shell_view
1196 **/
1197 GtkAction *
1198 e_shell_view_get_action (EShellView *shell_view)
1199 {
1200 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1201
1202 return shell_view->priv->action;
1203 }
1204
1205 /**
1206 * e_shell_view_get_title:
1207 * @shell_view: an #EShellView
1208 *
1209 * Returns the title for @shell_view. When @shell_view is active, the
1210 * shell view's title becomes the #EShellWindow title.
1211 *
1212 * Returns: the title for @shell_view
1213 **/
1214 const gchar *
1215 e_shell_view_get_title (EShellView *shell_view)
1216 {
1217 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1218
1219 return shell_view->priv->title;
1220 }
1221
1222 /**
1223 * e_shell_view_set_title:
1224 * @shell_view: an #EShellView
1225 * @title: a title for @shell_view
1226 *
1227 * Sets the title for @shell_view. When @shell_view is active, the
1228 * shell view's title becomes the #EShellWindow title.
1229 **/
1230 void
1231 e_shell_view_set_title (EShellView *shell_view,
1232 const gchar *title)
1233 {
1234 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1235
1236 if (title == NULL)
1237 title = E_SHELL_VIEW_GET_CLASS (shell_view)->label;
1238
1239 if (g_strcmp0 (shell_view->priv->title, title) == 0)
1240 return;
1241
1242 g_free (shell_view->priv->title);
1243 shell_view->priv->title = g_strdup (title);
1244
1245 g_object_notify (G_OBJECT (shell_view), "title");
1246 }
1247
1248 /**
1249 * e_shell_view_get_view_id:
1250 * @shell_view: an #EShellView
1251 *
1252 * Returns the ID of the currently selected #GalView.
1253 *
1254 * #EShellView subclasses are responsible for keeping this property in
1255 * sync with their #GalViewInstance. #EShellView itself just provides
1256 * a place to store the view ID, and emits a #GObject::notify signal
1257 * when the property changes.
1258 *
1259 * Returns: the ID of the current #GalView
1260 **/
1261 const gchar *
1262 e_shell_view_get_view_id (EShellView *shell_view)
1263 {
1264 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1265
1266 return shell_view->priv->view_id;
1267 }
1268
1269 /**
1270 * e_shell_view_set_view_id:
1271 * @shell_view: an #EShellView
1272 * @view_id: a #GalView ID
1273 *
1274 * Selects the #GalView whose ID is equal to @view_id.
1275 *
1276 * #EShellView subclasses are responsible for keeping this property in
1277 * sync with their #GalViewInstance. #EShellView itself just provides
1278 * a place to store the view ID, and emits a #GObject::notify signal
1279 * when the property changes.
1280 **/
1281 void
1282 e_shell_view_set_view_id (EShellView *shell_view,
1283 const gchar *view_id)
1284 {
1285 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1286
1287 if (g_strcmp0 (shell_view->priv->view_id, view_id) == 0)
1288 return;
1289
1290 g_free (shell_view->priv->view_id);
1291 shell_view->priv->view_id = g_strdup (view_id);
1292
1293 g_object_notify (G_OBJECT (shell_view), "view-id");
1294 }
1295
1296 /**
1297 * e_shell_view_get_shell_window:
1298 * @shell_view: an #EShellView
1299 *
1300 * Returns the #EShellWindow to which @shell_view belongs.
1301 *
1302 * Returns: the #EShellWindow to which @shell_view belongs
1303 **/
1304 EShellWindow *
1305 e_shell_view_get_shell_window (EShellView *shell_view)
1306 {
1307 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1308
1309 return E_SHELL_WINDOW (shell_view->priv->shell_window);
1310 }
1311
1312 /**
1313 * e_shell_view_is_active:
1314 * @shell_view: an #EShellView
1315 *
1316 * Returns %TRUE if @shell_view is active. That is, if it's currently
1317 * visible in its #EShellWindow. An #EShellWindow can only display one
1318 * shell view at a time.
1319 *
1320 * Technically this just checks the #GtkToggleAction:active property of
1321 * the shell view's switcher action. See e_shell_view_get_action().
1322 *
1323 * Returns: %TRUE if @shell_view is active
1324 **/
1325 gboolean
1326 e_shell_view_is_active (EShellView *shell_view)
1327 {
1328 GtkAction *action;
1329
1330 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), FALSE);
1331
1332 action = e_shell_view_get_action (shell_view);
1333
1334 return gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action));
1335 }
1336
1337 /**
1338 * e_shell_view_get_page_num:
1339 * @shell_view: an #EShellView
1340 *
1341 * This function is only interesting to #EShellWindow. It returns the
1342 * #GtkNotebook page number for @shell_view. The rest of the application
1343 * should have no need for this.
1344 *
1345 * Returns: the notebook page number for @shell_view
1346 **/
1347 gint
1348 e_shell_view_get_page_num (EShellView *shell_view)
1349 {
1350 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), -1);
1351
1352 return shell_view->priv->page_num;
1353 }
1354
1355 /**
1356 * e_shell_view_set_page_num:
1357 * @shell_view: an #EShellView
1358 * @page_num: a notebook page number
1359 *
1360 * This function is only interesting to #EShellWindow. It sets the
1361 * #GtkNotebook page number for @shell_view. The rest of the application
1362 * must never call this because it could mess up shell view switching.
1363 **/
1364 void
1365 e_shell_view_set_page_num (EShellView *shell_view,
1366 gint page_num)
1367 {
1368 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1369
1370 if (shell_view->priv->page_num == page_num)
1371 return;
1372
1373 shell_view->priv->page_num = page_num;
1374
1375 g_object_notify (G_OBJECT (shell_view), "page-num");
1376 }
1377
1378 /**
1379 * e_shell_view_get_search_name:
1380 * @shell_view: an #EShellView
1381 *
1382 * Returns a newly-allocated string containing a suitable name for the
1383 * current search criteria. This is used as the suggested name in the
1384 * Save Search dialog. Free the returned string with g_free().
1385 *
1386 * Returns: a name for the current search criteria
1387 **/
1388 gchar *
1389 e_shell_view_get_search_name (EShellView *shell_view)
1390 {
1391 EShellViewClass *class;
1392
1393 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1394
1395 class = E_SHELL_VIEW_GET_CLASS (shell_view);
1396 g_return_val_if_fail (class->get_search_name != NULL, NULL);
1397
1398 return class->get_search_name (shell_view);
1399 }
1400
1401 /**
1402 * e_shell_view_get_search_rule:
1403 * @shell_view: an #EShellView
1404 *
1405 * Returns the search criteria used to generate the current search results.
1406 *
1407 * Returns: the current search criteria
1408 **/
1409 EFilterRule *
1410 e_shell_view_get_search_rule (EShellView *shell_view)
1411 {
1412 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1413
1414 return shell_view->priv->search_rule;
1415 }
1416
1417 /**
1418 * e_shell_view_get_searchbar:
1419 * @shell_view: an #EShellView
1420 *
1421 * Returns the searchbar widget for @shell_view.
1422 **/
1423 GtkWidget *
1424 e_shell_view_get_searchbar (EShellView *shell_view)
1425 {
1426 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1427
1428 return shell_view->priv->searchbar;
1429 }
1430
1431 /**
1432 * e_shell_view_set_search_rule:
1433 * @shell_view: an #EShellView
1434 * @search_rule: an #EFilterRule
1435 *
1436 * Sets the search criteria used to generate the current search results.
1437 * Note that this will not trigger a search. e_shell_view_execute_search()
1438 * must be called explicitly.
1439 **/
1440 void
1441 e_shell_view_set_search_rule (EShellView *shell_view,
1442 EFilterRule *search_rule)
1443 {
1444 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1445
1446 if (shell_view->priv->search_rule == search_rule)
1447 return;
1448
1449 if (search_rule != NULL) {
1450 g_return_if_fail (E_IS_FILTER_RULE (search_rule));
1451 g_object_ref (search_rule);
1452 }
1453
1454 if (shell_view->priv->search_rule != NULL)
1455 g_object_unref (shell_view->priv->search_rule);
1456
1457 shell_view->priv->search_rule = search_rule;
1458
1459 g_object_notify (G_OBJECT (shell_view), "search-rule");
1460 }
1461
1462 /**
1463 * e_shell_view_get_search_query:
1464 * @shell_view: an #EShellView
1465 *
1466 * Converts the #EShellView:search-rule property to a newly-allocated
1467 * S-expression string. If the #EShellView:search-rule property is %NULL
1468 * the function returns %NULL.
1469 *
1470 * Returns: an S-expression string, or %NULL
1471 **/
1472 gchar *
1473 e_shell_view_get_search_query (EShellView *shell_view)
1474 {
1475 EFilterRule *rule;
1476 GString *string;
1477
1478 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1479
1480 rule = e_shell_view_get_search_rule (shell_view);
1481 if (rule == NULL)
1482 return NULL;
1483
1484 string = g_string_sized_new (1024);
1485 e_filter_rule_build_code (rule, string);
1486
1487 return g_string_free (string, FALSE);
1488 }
1489
1490 /**
1491 * e_shell_view_get_size_group:
1492 * @shell_view: an #EShellView
1493 *
1494 * Returns a #GtkSizeGroup that #EShellContent and #EShellSidebar use
1495 * to keep the search bar and sidebar banner vertically aligned. The
1496 * rest of the application should have no need for this.
1497 *
1498 * Note, this is only available during #EShellView construction.
1499 *
1500 * Returns: a #GtkSizeGroup for internal use
1501 **/
1502 GtkSizeGroup *
1503 e_shell_view_get_size_group (EShellView *shell_view)
1504 {
1505 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1506
1507 return shell_view->priv->size_group;
1508 }
1509
1510 /**
1511 * e_shell_view_get_shell_backend:
1512 * @shell_view: an #EShellView
1513 *
1514 * Returns the corresponding #EShellBackend for @shell_view.
1515 *
1516 * Returns: the corresponding #EShellBackend for @shell_view
1517 **/
1518 EShellBackend *
1519 e_shell_view_get_shell_backend (EShellView *shell_view)
1520 {
1521 EShellViewClass *class;
1522
1523 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1524
1525 class = E_SHELL_VIEW_GET_CLASS (shell_view);
1526 g_return_val_if_fail (class->shell_backend != NULL, NULL);
1527
1528 return class->shell_backend;
1529 }
1530
1531 /**
1532 * e_shell_view_get_shell_content:
1533 * @shell_view: an #EShellView
1534 *
1535 * Returns the #EShellContent instance for @shell_view.
1536 *
1537 * By default, #EShellView creates a plain #EShellContent during
1538 * initialization. But #EShellView subclasses can override the
1539 * <structfield>new_shell_content</structfield> factory method
1540 * in #EShellViewClass to create a custom #EShellContent.
1541 *
1542 * Returns: the #EShellContent instance for @shell_view
1543 **/
1544 EShellContent *
1545 e_shell_view_get_shell_content (EShellView *shell_view)
1546 {
1547 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1548
1549 return E_SHELL_CONTENT (shell_view->priv->shell_content);
1550 }
1551
1552 /**
1553 * e_shell_view_get_shell_sidebar:
1554 * @shell_view: an #EShellView
1555 *
1556 * Returns the #EShellSidebar instance for @shell_view.
1557 *
1558 * By default, #EShellView creates a plain #EShellSidebar during
1559 * initialization. But #EShellView subclasses can override the
1560 * <structfield>new_shell_sidebar</structfield> factory method
1561 * in #EShellViewClass to create a custom #EShellSidebar.
1562 *
1563 * Returns: the #EShellSidebar instance for @shell_view
1564 **/
1565 EShellSidebar *
1566 e_shell_view_get_shell_sidebar (EShellView *shell_view)
1567 {
1568 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1569
1570 return E_SHELL_SIDEBAR (shell_view->priv->shell_sidebar);
1571 }
1572
1573 /**
1574 * e_shell_view_get_shell_taskbar:
1575 * @shell_view: an #EShellView
1576 *
1577 * Returns the #EShellTaskbar instance for @shell_view.
1578 *
1579 * By default, #EShellView creates a plain #EShellTaskbar during
1580 * initialization. But #EShellView subclasses can override the
1581 * <structfield>new_shell_taskbar</structfield> factory method
1582 * in #EShellViewClass to create a custom #EShellTaskbar.
1583 *
1584 * Returns: the #EShellTaskbar instance for @shell_view
1585 **/
1586 EShellTaskbar *
1587 e_shell_view_get_shell_taskbar (EShellView *shell_view)
1588 {
1589 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1590
1591 return E_SHELL_TASKBAR (shell_view->priv->shell_taskbar);
1592 }
1593
1594 /**
1595 * e_shell_view_get_state_key_file:
1596 * @shell_view: an #EShellView
1597 *
1598 * Returns the #GKeyFile holding widget state data for @shell_view.
1599 *
1600 * Returns: the #GKeyFile for @shell_view
1601 **/
1602 GKeyFile *
1603 e_shell_view_get_state_key_file (EShellView *shell_view)
1604 {
1605 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1606
1607 return shell_view->priv->state_key_file;
1608 }
1609
1610 /**
1611 * e_shell_view_set_state_dirty:
1612 * @shell_view: an #EShellView
1613 *
1614 * Marks the widget state data as modified (or "dirty") and schedules it
1615 * to be saved to disk after a short delay. The delay caps the frequency
1616 * of saving to disk.
1617 **/
1618 void
1619 e_shell_view_set_state_dirty (EShellView *shell_view)
1620 {
1621 guint source_id;
1622
1623 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1624
1625 /* If a timeout is already scheduled, do nothing. */
1626 if (shell_view->priv->state_save_timeout_id > 0)
1627 return;
1628
1629 source_id = g_timeout_add_seconds (
1630 STATE_SAVE_TIMEOUT_SECONDS, (GSourceFunc)
1631 shell_view_state_timeout_cb, shell_view);
1632
1633 shell_view->priv->state_save_timeout_id = source_id;
1634 }
1635
1636 /**
1637 * e_shell_view_clear_search:
1638 * @shell_view: an #EShellView
1639 *
1640 * Emits the #EShellView::clear-search signal.
1641 *
1642 * The default method sets the #EShellView:search-rule property to
1643 * %NULL and then emits the #EShellView::execute-search signal.
1644 **/
1645 void
1646 e_shell_view_clear_search (EShellView *shell_view)
1647 {
1648 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1649
1650 g_signal_emit (shell_view, signals[CLEAR_SEARCH], 0);
1651 }
1652
1653 /**
1654 * e_shell_view_custom_search:
1655 * @shell_view: an #EShellView
1656 * @custom_rule: an #EFilterRule
1657 *
1658 * Emits the #EShellView::custom-search signal to indicate an advanced
1659 * or saved search is about to be executed.
1660 *
1661 * The default method sets the #EShellView:search-rule property to
1662 * @custom_rule and then emits the #EShellView::execute-search signal.
1663 **/
1664 void
1665 e_shell_view_custom_search (EShellView *shell_view,
1666 EFilterRule *custom_rule)
1667 {
1668 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1669 g_return_if_fail (E_IS_FILTER_RULE (custom_rule));
1670
1671 g_signal_emit (shell_view, signals[CUSTOM_SEARCH], 0, custom_rule);
1672 }
1673
1674 /**
1675 * e_shell_view_execute_search:
1676 * @shell_view: an #EShellView
1677 *
1678 * Emits the #EShellView::execute-search signal.
1679 *
1680 * #EShellView subclasses should implement the
1681 * <structfield>execute_search</structfield> method in #EShellViewClass
1682 * to execute a search based on the current search conditions.
1683 **/
1684 void
1685 e_shell_view_execute_search (EShellView *shell_view)
1686 {
1687 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1688
1689 if (!e_shell_view_is_execute_search_blocked (shell_view))
1690 g_signal_emit (shell_view, signals[EXECUTE_SEARCH], 0);
1691 }
1692
1693 /**
1694 * e_shell_view_block_execute_search:
1695 * @shell_view: an #EShellView
1696 *
1697 * Blocks e_shell_view_execute_search() in a way it does nothing.
1698 * Pair function for this is e_shell_view_unblock_execute_search().
1699 **/
1700 void
1701 e_shell_view_block_execute_search (EShellView *shell_view)
1702 {
1703 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1704 g_return_if_fail (shell_view->priv->execute_search_blocked + 1 != 0);
1705
1706 shell_view->priv->execute_search_blocked++;
1707 }
1708
1709 /**
1710 * e_shell_view_unblock_execute_search:
1711 * @shell_view: an #EShellView
1712 *
1713 * Unblocks previously blocked e_shell_view_execute_search() with
1714 * function e_shell_view_block_execute_search().
1715 **/
1716 void
1717 e_shell_view_unblock_execute_search (EShellView *shell_view)
1718 {
1719 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1720 g_return_if_fail (shell_view->priv->execute_search_blocked > 0);
1721
1722 shell_view->priv->execute_search_blocked--;
1723 }
1724
1725 /**
1726 * e_shell_view_is_execute_search_blocked:
1727 * @shell_view: an #EShellView
1728 *
1729 * Returns whether e_shell_view_execute_search() is blocked.
1730 **/
1731 gboolean
1732 e_shell_view_is_execute_search_blocked (EShellView *shell_view)
1733 {
1734 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), FALSE);
1735
1736 return shell_view->priv->execute_search_blocked > 0;
1737 }
1738
1739 /**
1740 * e_shell_view_update_actions:
1741 * @shell_view: an #EShellView
1742 *
1743 * Emits the #EShellView::update-actions signal.
1744 *
1745 * #EShellView subclasses should implement the
1746 * <structfield>update_actions</structfield> method in #EShellViewClass
1747 * to update the various #GtkAction<!-- -->s based on the current
1748 * #EShellSidebar and #EShellContent selections. The
1749 * #EShellView::update-actions signal is typically emitted just before
1750 * showing a popup menu or just after the user selects an item in the
1751 * shell view.
1752 *
1753 * Emission can be blocked by e_shell_view_block_update_actions().
1754 **/
1755 void
1756 e_shell_view_update_actions (EShellView *shell_view)
1757 {
1758 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1759
1760 if (!e_shell_view_is_active (shell_view))
1761 return;
1762
1763 if (shell_view->priv->update_actions_blocked > 0) {
1764 shell_view->priv->update_actions_called = TRUE;
1765 } else {
1766 shell_view->priv->update_actions_called = FALSE;
1767 g_signal_emit (shell_view, signals[UPDATE_ACTIONS], 0);
1768 }
1769 }
1770
1771 /**
1772 * e_shell_view_block_update_actions:
1773 * @shell_view: an #EShellView
1774 *
1775 * Block emission of #EShellView::update-actions signal through
1776 * e_shell_view_update_actions(). The emission si blocked until
1777 * e_shell_view_unblock_update_actions() is called same times as
1778 * this function.
1779 **/
1780 void
1781 e_shell_view_block_update_actions (EShellView *shell_view)
1782 {
1783 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1784 g_return_if_fail (shell_view->priv->update_actions_blocked + 1 != 0);
1785
1786 shell_view->priv->update_actions_blocked++;
1787 if (shell_view->priv->update_actions_blocked == 1)
1788 shell_view->priv->update_actions_called = FALSE;
1789 }
1790
1791 /**
1792 * e_shell_view_unblock_update_actions:
1793 * @shell_view: an #EShellView
1794 *
1795 * Unblock emission of #EShellView::update-actions signal through
1796 * e_shell_view_update_actions(), previously blocked by function
1797 * e_shell_view_block_update_actions().
1798 **/
1799 void
1800 e_shell_view_unblock_update_actions (EShellView *shell_view)
1801 {
1802 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1803 g_return_if_fail (shell_view->priv->update_actions_blocked > 0);
1804
1805 shell_view->priv->update_actions_blocked--;
1806
1807 if (!shell_view->priv->update_actions_blocked &&
1808 shell_view->priv->update_actions_called) {
1809
1810 shell_view->priv->update_actions_called = FALSE;
1811 e_shell_view_update_actions (shell_view);
1812 }
1813 }
1814
1815 /**
1816 * e_shell_view_show_popup_menu:
1817 * @shell_view: an #EShellView
1818 * @widget_path: path in the UI definition
1819 * @event: a #GdkEventButton
1820 *
1821 * Displays a context-sensitive (or "popup") menu that is described in
1822 * the UI definition loaded into @shell_view<!-- -->'s user interface
1823 * manager. The menu will be shown at the current mouse cursor position.
1824 *
1825 * The #EShellView::update-actions signal is emitted just prior to
1826 * showing the menu to give @shell_view and any plugins that extend
1827 * @shell_view a chance to update the menu's actions.
1828 *
1829 * Returns: the popup menu being displayed
1830 **/
1831 GtkWidget *
1832 e_shell_view_show_popup_menu (EShellView *shell_view,
1833 const gchar *widget_path,
1834 GdkEventButton *event)
1835 {
1836 EShellWindow *shell_window;
1837 GtkWidget *menu;
1838
1839 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1840
1841 e_shell_view_update_actions (shell_view);
1842
1843 shell_window = e_shell_view_get_shell_window (shell_view);
1844 menu = e_shell_window_get_managed_widget (shell_window, widget_path);
1845 g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1846
1847 if (event != NULL)
1848 gtk_menu_popup (
1849 GTK_MENU (menu), NULL, NULL, NULL, NULL,
1850 event->button, event->time);
1851 else
1852 gtk_menu_popup (
1853 GTK_MENU (menu), NULL, NULL, NULL, NULL,
1854 0, gtk_get_current_event_time ());
1855
1856 return menu;
1857 }
1858
1859 /**
1860 * e_shell_view_new_view_instance:
1861 * @shell_view: an #EShellView
1862 * @instance_id: a name for the #GalViewInstance
1863 *
1864 * Creates a new #GalViewInstance and configures it to keep
1865 * @shell_view<!-- -->'s #EShellView:view-id property up-to-date.
1866 *
1867 * Returns: a new #GalViewInstance
1868 **/
1869 GalViewInstance *
1870 e_shell_view_new_view_instance (EShellView *shell_view,
1871 const gchar *instance_id)
1872 {
1873 EShellViewClass *class;
1874 GalViewCollection *view_collection;
1875 GalViewInstance *view_instance;
1876
1877 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1878
1879 class = E_SHELL_VIEW_GET_CLASS (shell_view);
1880
1881 view_collection = class->view_collection;
1882 view_instance = gal_view_instance_new (view_collection, instance_id);
1883
1884 g_signal_connect_swapped (
1885 view_instance, "changed",
1886 G_CALLBACK (shell_view_update_view_id), shell_view);
1887
1888 g_signal_connect_swapped (
1889 view_instance, "loaded",
1890 G_CALLBACK (shell_view_update_view_id), shell_view);
1891
1892 return view_instance;
1893 }
1894
1895 /**
1896 * e_shell_view_write_source:
1897 * @shell_view: an #EShellView
1898 * @source: an #ESource
1899 *
1900 * Submits the current contents of @source to the D-Bus service to be
1901 * written to disk and broadcast to other clients.
1902 *
1903 * This function does not block: @shell_view will dispatch the operation
1904 * asynchronously and handle any errors.
1905 **/
1906 void
1907 e_shell_view_write_source (EShellView *shell_view,
1908 ESource *source)
1909 {
1910 EActivity *activity;
1911 EAlertSink *alert_sink;
1912 EShellBackend *shell_backend;
1913 EShellContent *shell_content;
1914
1915 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1916 g_return_if_fail (E_IS_SOURCE (source));
1917
1918 shell_backend = e_shell_view_get_shell_backend (shell_view);
1919 shell_content = e_shell_view_get_shell_content (shell_view);
1920
1921 alert_sink = E_ALERT_SINK (shell_content);
1922 activity = e_source_util_write (source, alert_sink);
1923 e_shell_backend_add_activity (shell_backend, activity);
1924 }
1925
1926 /**
1927 * e_shell_view_remove_source:
1928 * @shell_view: an #EShellView
1929 * @source: the #ESource to be removed
1930 *
1931 * Requests the D-Bus service to delete the key files for @source and all of
1932 * its descendants and broadcast their removal to all clients.
1933 *
1934 * This function does not block: @shell_view will dispatch the operation
1935 * asynchronously and handle any errors.
1936 **/
1937 void
1938 e_shell_view_remove_source (EShellView *shell_view,
1939 ESource *source)
1940 {
1941 EActivity *activity;
1942 EAlertSink *alert_sink;
1943 EShellBackend *shell_backend;
1944 EShellContent *shell_content;
1945
1946 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1947 g_return_if_fail (E_IS_SOURCE (source));
1948
1949 shell_backend = e_shell_view_get_shell_backend (shell_view);
1950 shell_content = e_shell_view_get_shell_content (shell_view);
1951
1952 alert_sink = E_ALERT_SINK (shell_content);
1953 activity = e_source_util_remove (source, alert_sink);
1954 e_shell_backend_add_activity (shell_backend, activity);
1955 }
1956
1957 /**
1958 * e_shell_view_remote_delete_source:
1959 * @shell_view: an #EShellView
1960 * @source: an #ESource
1961 *
1962 * Deletes the resource represented by @source from a remote server.
1963 * The @source must be #ESource:remote-deletable. This will also delete
1964 * the key file for @source and broadcast its removal to all clients,
1965 * similar to e_shell_view_remove_source().
1966 *
1967 * This function does not block; @shell_view will dispatch the operation
1968 * asynchronously and handle any errors.
1969 **/
1970 void
1971 e_shell_view_remote_delete_source (EShellView *shell_view,
1972 ESource *source)
1973 {
1974 EActivity *activity;
1975 EAlertSink *alert_sink;
1976 EShellBackend *shell_backend;
1977 EShellContent *shell_content;
1978
1979 g_return_if_fail (E_IS_SHELL_VIEW (shell_view));
1980 g_return_if_fail (E_IS_SOURCE (source));
1981
1982 shell_backend = e_shell_view_get_shell_backend (shell_view);
1983 shell_content = e_shell_view_get_shell_content (shell_view);
1984
1985 alert_sink = E_ALERT_SINK (shell_content);
1986 activity = e_source_util_remote_delete (source, alert_sink);
1987 e_shell_backend_add_activity (shell_backend, activity);
1988 }