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