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