Bug 795870 - Add a way to initiate refresh of account sources
[evolution.git] / src / shell / e-shell-searchbar.c
blobbbd0b513be97740a736e82fe919f77a0dd60d0ac
1 /*
2 * e-shell-searchbar.c
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 /**
22 * SECTION: e-shell-searchbar
23 * @short_description: quick search interface
24 * @include: shell/e-shell-searchbar.h
25 **/
27 #include "evolution-config.h"
29 #include "e-shell-searchbar.h"
31 #include <glib/gi18n-lib.h>
32 #include <libebackend/libebackend.h>
34 #include "e-shell-window-actions.h"
36 #define E_SHELL_SEARCHBAR_GET_PRIVATE(obj) \
37 (G_TYPE_INSTANCE_GET_PRIVATE \
38 ((obj), E_TYPE_SHELL_SEARCHBAR, EShellSearchbarPrivate))
40 #define SEARCH_OPTION_ADVANCED (-1)
42 /* Default "state key file" group: [Search Bar] */
43 #define STATE_GROUP_DEFAULT "Search Bar"
45 #define STATE_KEY_SEARCH_FILTER "SearchFilter"
46 #define STATE_KEY_SEARCH_OPTION "SearchOption"
47 #define STATE_KEY_SEARCH_SCOPE "SearchScope"
48 #define STATE_KEY_SEARCH_TEXT "SearchText"
50 struct _EShellSearchbarPrivate {
52 gpointer shell_view; /* weak pointer */
54 GtkRadioAction *search_option;
55 EFilterRule *search_rule;
56 GtkCssProvider *css_provider;
58 /* Child Widgets (not referenced) */
59 GtkWidget *filter_combo_box;
60 GtkWidget *search_entry;
61 GtkWidget *scope_combo_box;
63 /* State Key File */
64 gchar *state_group;
66 gboolean filter_visible;
67 gboolean scope_visible;
68 gboolean state_dirty;
71 enum {
72 PROP_0,
73 PROP_FILTER_COMBO_BOX,
74 PROP_FILTER_VISIBLE,
75 PROP_SEARCH_HINT,
76 PROP_SEARCH_OPTION,
77 PROP_SEARCH_TEXT,
78 PROP_SCOPE_COMBO_BOX,
79 PROP_SCOPE_VISIBLE,
80 PROP_SHELL_VIEW,
81 PROP_STATE_GROUP
84 G_DEFINE_TYPE_WITH_CODE (
85 EShellSearchbar,
86 e_shell_searchbar,
87 GTK_TYPE_BOX,
88 G_IMPLEMENT_INTERFACE (
89 E_TYPE_EXTENSIBLE, NULL))
91 static void
92 shell_searchbar_save_search_filter (EShellSearchbar *searchbar)
94 EShellView *shell_view;
95 EActionComboBox *action_combo_box;
96 GtkRadioAction *radio_action;
97 GKeyFile *key_file;
98 const gchar *action_name;
99 const gchar *state_group;
100 const gchar *key;
102 shell_view = e_shell_searchbar_get_shell_view (searchbar);
103 state_group = e_shell_searchbar_get_state_group (searchbar);
104 g_return_if_fail (state_group != NULL);
106 key = STATE_KEY_SEARCH_FILTER;
107 key_file = e_shell_view_get_state_key_file (shell_view);
109 action_combo_box = e_shell_searchbar_get_filter_combo_box (searchbar);
110 radio_action = e_action_combo_box_get_action (action_combo_box);
112 if (radio_action != NULL)
113 radio_action = e_radio_action_get_current_action (radio_action);
115 if (radio_action != NULL) {
116 action_name = gtk_action_get_name (GTK_ACTION (radio_action));
117 g_key_file_set_string (key_file, state_group, key, action_name);
118 } else
119 g_key_file_remove_key (key_file, state_group, key, NULL);
121 e_shell_view_set_state_dirty (shell_view);
124 static void
125 shell_searchbar_save_search_option (EShellSearchbar *searchbar)
127 EShellView *shell_view;
128 GtkRadioAction *radio_action;
129 GKeyFile *key_file;
130 const gchar *action_name;
131 const gchar *state_group;
132 const gchar *key;
134 shell_view = e_shell_searchbar_get_shell_view (searchbar);
135 state_group = e_shell_searchbar_get_state_group (searchbar);
136 g_return_if_fail (state_group != NULL);
138 key = STATE_KEY_SEARCH_OPTION;
139 key_file = e_shell_view_get_state_key_file (shell_view);
141 radio_action = e_shell_searchbar_get_search_option (searchbar);
143 if (radio_action != NULL)
144 radio_action = e_radio_action_get_current_action (radio_action);
146 if (radio_action != NULL) {
147 action_name = gtk_action_get_name (GTK_ACTION (radio_action));
148 g_key_file_set_string (key_file, state_group, key, action_name);
149 } else
150 g_key_file_remove_key (key_file, state_group, key, NULL);
152 e_shell_view_set_state_dirty (shell_view);
155 static void
156 shell_searchbar_save_search_text (EShellSearchbar *searchbar)
158 EShellView *shell_view;
159 GKeyFile *key_file;
160 const gchar *search_text;
161 const gchar *state_group;
162 const gchar *key;
164 shell_view = e_shell_searchbar_get_shell_view (searchbar);
165 state_group = e_shell_searchbar_get_state_group (searchbar);
166 g_return_if_fail (state_group != NULL);
168 key = STATE_KEY_SEARCH_TEXT;
169 key_file = e_shell_view_get_state_key_file (shell_view);
171 search_text = e_shell_searchbar_get_search_text (searchbar);
173 if (search_text != NULL && *search_text != '\0')
174 g_key_file_set_string (key_file, state_group, key, search_text);
175 else
176 g_key_file_remove_key (key_file, state_group, key, NULL);
178 e_shell_view_set_state_dirty (shell_view);
181 static void
182 shell_searchbar_save_search_scope (EShellSearchbar *searchbar)
184 EShellView *shell_view;
185 EActionComboBox *action_combo_box;
186 GtkRadioAction *radio_action;
187 GKeyFile *key_file;
188 const gchar *action_name;
189 const gchar *state_group;
190 const gchar *key;
192 shell_view = e_shell_searchbar_get_shell_view (searchbar);
194 /* Search scope is hard-coded to the default state group. */
195 state_group = STATE_GROUP_DEFAULT;
197 key = STATE_KEY_SEARCH_SCOPE;
198 key_file = e_shell_view_get_state_key_file (shell_view);
200 action_combo_box = e_shell_searchbar_get_scope_combo_box (searchbar);
201 radio_action = e_action_combo_box_get_action (action_combo_box);
203 if (radio_action != NULL)
204 radio_action = e_radio_action_get_current_action (radio_action);
206 if (radio_action != NULL) {
207 action_name = gtk_action_get_name (GTK_ACTION (radio_action));
208 g_key_file_set_string (key_file, state_group, key, action_name);
209 } else
210 g_key_file_remove_key (key_file, state_group, key, NULL);
212 e_shell_view_set_state_dirty (shell_view);
215 static void
216 shell_searchbar_update_search_widgets (EShellSearchbar *searchbar)
218 EShellView *shell_view;
219 EShellWindow *shell_window;
220 GtkAction *action;
221 GtkWidget *widget;
222 const gchar *search_text;
223 gboolean sensitive;
225 /* EShellView subclasses are responsible for actually
226 * executing the search. This is all cosmetic stuff. */
228 widget = searchbar->priv->search_entry;
229 shell_view = e_shell_searchbar_get_shell_view (searchbar);
230 shell_window = e_shell_view_get_shell_window (shell_view);
231 search_text = e_shell_searchbar_get_search_text (searchbar);
233 sensitive =
234 (search_text != NULL && *search_text != '\0') ||
235 (e_shell_view_get_search_rule (shell_view) != NULL);
237 if (sensitive) {
238 GdkRGBA bg, fg;
239 gchar *css;
241 e_utils_get_theme_color (widget, "theme_selected_bg_color", E_UTILS_DEFAULT_THEME_SELECTED_BG_COLOR, &bg);
242 e_utils_get_theme_color (widget, "theme_selected_fg_color", E_UTILS_DEFAULT_THEME_SELECTED_FG_COLOR, &fg);
244 css = g_strdup_printf (
245 "GtkEntry#searchbar_searchentry_active { "
246 " background:none; "
247 " background-color:#%06x; "
248 " color:#%06x; "
249 "}",
250 e_rgba_to_value (&bg),
251 e_rgba_to_value (&fg));
252 gtk_css_provider_load_from_data (
253 searchbar->priv->css_provider, css, -1, NULL);
254 g_free (css);
256 gtk_widget_set_name (widget, "searchbar_searchentry_active");
257 } else {
258 gtk_widget_set_name (widget, "searchbar_searchentry");
261 action = E_SHELL_WINDOW_ACTION_SEARCH_CLEAR (shell_window);
262 gtk_action_set_sensitive (action, sensitive);
264 action = E_SHELL_WINDOW_ACTION_SEARCH_SAVE (shell_window);
265 gtk_action_set_visible (action, sensitive && e_shell_view_get_search_rule (shell_view) != NULL);
268 static void
269 shell_searchbar_clear_search_cb (EShellView *shell_view,
270 EShellSearchbar *searchbar)
272 GtkRadioAction *search_option;
273 gint current_value;
275 e_shell_searchbar_set_search_text (searchbar, NULL);
277 search_option = e_shell_searchbar_get_search_option (searchbar);
278 if (search_option == NULL)
279 return;
281 /* Reset the search option if it's set to advanced search. */
282 current_value = gtk_radio_action_get_current_value (search_option);
283 if (current_value == SEARCH_OPTION_ADVANCED)
284 gtk_radio_action_set_current_value (search_option, 0);
287 static void
288 shell_searchbar_custom_search_cb (EShellView *shell_view,
289 EFilterRule *custom_rule,
290 EShellSearchbar *searchbar)
292 GtkRadioAction *search_option;
293 gint value = SEARCH_OPTION_ADVANCED;
295 e_shell_searchbar_set_search_text (searchbar, NULL);
297 search_option = e_shell_searchbar_get_search_option (searchbar);
298 if (search_option != NULL)
299 gtk_radio_action_set_current_value (search_option, value);
302 static void
303 shell_searchbar_execute_search_cb (EShellView *shell_view,
304 EShellSearchbar *searchbar)
306 EShellContent *shell_content;
308 shell_searchbar_update_search_widgets (searchbar);
310 e_shell_searchbar_save_state (searchbar);
312 if (!e_shell_view_is_active (shell_view))
313 return;
315 /* Direct the focus away from the search entry, so that a
316 * focus-in event is required before the text can be changed.
317 * This will reset the entry to the appropriate visual state. */
318 if (gtk_widget_is_focus (searchbar->priv->search_entry)) {
319 shell_content = e_shell_view_get_shell_content (shell_view);
320 e_shell_content_focus_search_results (shell_content);
324 static void
325 shell_searchbar_filter_changed_cb (GtkComboBox *filter_combo_box,
326 EShellSearchbar *searchbar)
328 EShellView *shell_view;
330 shell_view = e_shell_searchbar_get_shell_view (searchbar);
332 e_shell_view_execute_search (shell_view);
335 static void
336 shell_searchbar_entry_activate_cb (EShellSearchbar *searchbar)
338 EShellView *shell_view;
339 EShellWindow *shell_window;
340 GtkAction *action;
341 const gchar *search_text;
343 shell_view = e_shell_searchbar_get_shell_view (searchbar);
344 shell_window = e_shell_view_get_shell_window (shell_view);
346 search_text = e_shell_searchbar_get_search_text (searchbar);
347 if (search_text != NULL && *search_text != '\0')
348 action = E_SHELL_WINDOW_ACTION_SEARCH_QUICK (shell_window);
349 else
350 action = E_SHELL_WINDOW_ACTION_SEARCH_CLEAR (shell_window);
352 gtk_action_activate (action);
355 static void
356 shell_searchbar_entry_changed_cb (EShellSearchbar *searchbar)
358 EShellView *shell_view;
359 EShellWindow *shell_window;
360 GtkAction *action;
361 const gchar *search_text;
362 gboolean sensitive;
364 shell_view = e_shell_searchbar_get_shell_view (searchbar);
365 shell_window = e_shell_view_get_shell_window (shell_view);
367 search_text = e_shell_searchbar_get_search_text (searchbar);
368 sensitive = (search_text != NULL && *search_text != '\0');
370 action = E_SHELL_WINDOW_ACTION_SEARCH_QUICK (shell_window);
371 gtk_action_set_sensitive (action, sensitive);
374 static void
375 shell_searchbar_entry_icon_press_cb (EShellSearchbar *searchbar,
376 GtkEntryIconPosition icon_pos,
377 GdkEvent *event)
379 EShellView *shell_view;
380 EShellWindow *shell_window;
381 GtkAction *action;
383 /* Show the search options menu when the icon is pressed. */
385 if (icon_pos != GTK_ENTRY_ICON_PRIMARY)
386 return;
388 shell_view = e_shell_searchbar_get_shell_view (searchbar);
389 shell_window = e_shell_view_get_shell_window (shell_view);
391 action = E_SHELL_WINDOW_ACTION_SEARCH_OPTIONS (shell_window);
392 gtk_action_activate (action);
395 static void
396 shell_searchbar_entry_icon_release_cb (EShellSearchbar *searchbar,
397 GtkEntryIconPosition icon_pos,
398 GdkEvent *event)
400 EShellView *shell_view;
401 EShellWindow *shell_window;
402 GtkAction *action;
404 /* Clear the search when the icon is pressed and released. */
406 if (icon_pos != GTK_ENTRY_ICON_SECONDARY)
407 return;
409 shell_view = e_shell_searchbar_get_shell_view (searchbar);
410 shell_window = e_shell_view_get_shell_window (shell_view);
412 action = E_SHELL_WINDOW_ACTION_SEARCH_CLEAR (shell_window);
413 gtk_action_activate (action);
416 static gboolean
417 shell_searchbar_entry_key_press_cb (EShellSearchbar *searchbar,
418 GdkEventKey *key_event,
419 GtkWindow *entry)
421 EShellView *shell_view;
422 EShellWindow *shell_window;
423 GtkAction *action;
424 guint mask;
426 mask = gtk_accelerator_get_default_mod_mask ();
427 if ((key_event->state & mask) != GDK_MOD1_MASK)
428 return FALSE;
430 if (key_event->keyval != GDK_KEY_Down)
431 return FALSE;
433 shell_view = e_shell_searchbar_get_shell_view (searchbar);
434 shell_window = e_shell_view_get_shell_window (shell_view);
436 action = E_SHELL_WINDOW_ACTION_SEARCH_OPTIONS (shell_window);
437 gtk_action_activate (action);
439 return TRUE;
442 static void
443 shell_searchbar_option_changed_cb (GtkRadioAction *action,
444 GtkRadioAction *current,
445 EShellSearchbar *searchbar)
447 EShellView *shell_view;
448 const gchar *search_text;
449 const gchar *label;
450 gint current_value;
452 shell_view = e_shell_searchbar_get_shell_view (searchbar);
454 label = gtk_action_get_label (GTK_ACTION (current));
455 e_shell_searchbar_set_search_hint (searchbar, label);
457 current_value = gtk_radio_action_get_current_value (current);
458 search_text = e_shell_searchbar_get_search_text (searchbar);
460 if (current_value != SEARCH_OPTION_ADVANCED) {
461 e_shell_view_set_search_rule (shell_view, NULL);
462 e_shell_searchbar_set_search_text (searchbar, search_text);
463 if (search_text != NULL && *search_text != '\0') {
464 e_shell_view_execute_search (shell_view);
465 } else {
466 shell_searchbar_save_search_option (searchbar);
469 } else if (search_text != NULL)
470 e_shell_searchbar_set_search_text (searchbar, NULL);
473 static gboolean
474 shell_searchbar_entry_focus_in_cb (GtkWidget *entry,
475 GdkEvent *event,
476 EShellSearchbar *searchbar)
478 /* to not change background when user changes search entry content */
479 gtk_widget_set_name (entry, "searchbar_searchentry");
481 return FALSE;
484 static gboolean
485 shell_searchbar_entry_focus_out_cb (GtkWidget *entry,
486 GdkEvent *event,
487 EShellSearchbar *searchbar)
489 shell_searchbar_update_search_widgets (searchbar);
491 return FALSE;
494 static void
495 shell_searchbar_set_shell_view (EShellSearchbar *searchbar,
496 EShellView *shell_view)
498 g_return_if_fail (searchbar->priv->shell_view == NULL);
500 searchbar->priv->shell_view = shell_view;
502 g_object_add_weak_pointer (
503 G_OBJECT (shell_view),
504 &searchbar->priv->shell_view);
507 static void
508 shell_searchbar_set_property (GObject *object,
509 guint property_id,
510 const GValue *value,
511 GParamSpec *pspec)
513 switch (property_id) {
514 case PROP_FILTER_VISIBLE:
515 e_shell_searchbar_set_filter_visible (
516 E_SHELL_SEARCHBAR (object),
517 g_value_get_boolean (value));
518 return;
520 case PROP_SEARCH_HINT:
521 e_shell_searchbar_set_search_hint (
522 E_SHELL_SEARCHBAR (object),
523 g_value_get_string (value));
524 return;
526 case PROP_SEARCH_OPTION:
527 e_shell_searchbar_set_search_option (
528 E_SHELL_SEARCHBAR (object),
529 g_value_get_object (value));
530 return;
532 case PROP_SEARCH_TEXT:
533 e_shell_searchbar_set_search_text (
534 E_SHELL_SEARCHBAR (object),
535 g_value_get_string (value));
536 return;
538 case PROP_SCOPE_VISIBLE:
539 e_shell_searchbar_set_scope_visible (
540 E_SHELL_SEARCHBAR (object),
541 g_value_get_boolean (value));
542 return;
544 case PROP_SHELL_VIEW:
545 shell_searchbar_set_shell_view (
546 E_SHELL_SEARCHBAR (object),
547 g_value_get_object (value));
548 return;
550 case PROP_STATE_GROUP:
551 e_shell_searchbar_set_state_group (
552 E_SHELL_SEARCHBAR (object),
553 g_value_get_string (value));
554 return;
557 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
560 static void
561 shell_searchbar_get_property (GObject *object,
562 guint property_id,
563 GValue *value,
564 GParamSpec *pspec)
566 switch (property_id) {
567 case PROP_FILTER_COMBO_BOX:
568 g_value_set_object (
569 value, e_shell_searchbar_get_filter_combo_box (
570 E_SHELL_SEARCHBAR (object)));
571 return;
573 case PROP_FILTER_VISIBLE:
574 g_value_set_boolean (
575 value, e_shell_searchbar_get_filter_visible (
576 E_SHELL_SEARCHBAR (object)));
577 return;
579 case PROP_SEARCH_HINT:
580 g_value_set_string (
581 value, e_shell_searchbar_get_search_hint (
582 E_SHELL_SEARCHBAR (object)));
583 return;
585 case PROP_SEARCH_OPTION:
586 g_value_set_object (
587 value, e_shell_searchbar_get_search_option (
588 E_SHELL_SEARCHBAR (object)));
589 return;
591 case PROP_SEARCH_TEXT:
592 g_value_set_string (
593 value, e_shell_searchbar_get_search_text (
594 E_SHELL_SEARCHBAR (object)));
595 return;
597 case PROP_SCOPE_COMBO_BOX:
598 g_value_set_object (
599 value, e_shell_searchbar_get_scope_combo_box (
600 E_SHELL_SEARCHBAR (object)));
601 return;
603 case PROP_SCOPE_VISIBLE:
604 g_value_set_boolean (
605 value, e_shell_searchbar_get_scope_visible (
606 E_SHELL_SEARCHBAR (object)));
607 return;
609 case PROP_SHELL_VIEW:
610 g_value_set_object (
611 value, e_shell_searchbar_get_shell_view (
612 E_SHELL_SEARCHBAR (object)));
613 return;
615 case PROP_STATE_GROUP:
616 g_value_set_string (
617 value, e_shell_searchbar_get_state_group (
618 E_SHELL_SEARCHBAR (object)));
619 return;
622 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
625 static void
626 shell_searchbar_dispose (GObject *object)
628 EShellSearchbarPrivate *priv;
630 priv = E_SHELL_SEARCHBAR_GET_PRIVATE (object);
632 if (priv->shell_view != NULL) {
633 g_object_remove_weak_pointer (
634 G_OBJECT (priv->shell_view), &priv->shell_view);
635 priv->shell_view = NULL;
638 if (priv->search_option != NULL) {
639 g_signal_handlers_disconnect_matched (
640 priv->search_option, G_SIGNAL_MATCH_DATA,
641 0, 0, NULL, NULL, object);
642 g_clear_object (&priv->search_option);
645 g_clear_object (&priv->css_provider);
647 /* Chain up to parent's dispose() method. */
648 G_OBJECT_CLASS (e_shell_searchbar_parent_class)->dispose (object);
651 static void
652 shell_searchbar_finalize (GObject *object)
654 EShellSearchbarPrivate *priv;
656 priv = E_SHELL_SEARCHBAR_GET_PRIVATE (object);
658 g_free (priv->state_group);
660 /* Chain up to parent's finalize() method. */
661 G_OBJECT_CLASS (e_shell_searchbar_parent_class)->finalize (object);
664 static void
665 shell_searchbar_constructed (GObject *object)
667 EShellView *shell_view;
668 EShellWindow *shell_window;
669 EShellSearchbar *searchbar;
670 GtkSizeGroup *size_group;
671 GtkAction *action;
672 GtkWidget *widget;
674 searchbar = E_SHELL_SEARCHBAR (object);
675 shell_view = e_shell_searchbar_get_shell_view (searchbar);
676 shell_window = e_shell_view_get_shell_window (shell_view);
677 size_group = e_shell_view_get_size_group (shell_view);
679 g_signal_connect (
680 shell_view, "clear-search",
681 G_CALLBACK (shell_searchbar_clear_search_cb), searchbar);
683 g_signal_connect (
684 shell_view, "custom-search",
685 G_CALLBACK (shell_searchbar_custom_search_cb), searchbar);
687 g_signal_connect_after (
688 shell_view, "execute-search",
689 G_CALLBACK (shell_searchbar_execute_search_cb), searchbar);
691 widget = searchbar->priv->filter_combo_box;
693 g_signal_connect_swapped (
694 widget, "changed",
695 G_CALLBACK (e_shell_searchbar_set_state_dirty), searchbar);
697 /* Use G_CONNECT_AFTER here so the EActionComboBox has a
698 * chance to update its radio actions before we go sifting
699 * through the radio group for the current action. */
700 g_signal_connect_after (
701 widget, "changed",
702 G_CALLBACK (shell_searchbar_filter_changed_cb), searchbar);
704 searchbar->priv->css_provider = gtk_css_provider_new ();
705 widget = searchbar->priv->search_entry;
706 gtk_style_context_add_provider (
707 gtk_widget_get_style_context (widget),
708 GTK_STYLE_PROVIDER (searchbar->priv->css_provider),
709 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
711 action = E_SHELL_WINDOW_ACTION_SEARCH_CLEAR (shell_window);
713 e_binding_bind_property (
714 action, "sensitive",
715 widget, "secondary-icon-sensitive",
716 G_BINDING_SYNC_CREATE);
717 e_binding_bind_property (
718 action, "icon-name",
719 widget, "secondary-icon-name",
720 G_BINDING_SYNC_CREATE);
721 e_binding_bind_property (
722 action, "tooltip",
723 widget, "secondary-icon-tooltip-text",
724 G_BINDING_SYNC_CREATE);
726 action = E_SHELL_WINDOW_ACTION_SEARCH_OPTIONS (shell_window);
728 e_binding_bind_property (
729 action, "sensitive",
730 widget, "primary-icon-sensitive",
731 G_BINDING_SYNC_CREATE);
732 e_binding_bind_property (
733 action, "icon-name",
734 widget, "primary-icon-name",
735 G_BINDING_SYNC_CREATE);
736 e_binding_bind_property (
737 action, "tooltip",
738 widget, "primary-icon-tooltip-text",
739 G_BINDING_SYNC_CREATE);
741 widget = GTK_WIDGET (searchbar);
742 gtk_size_group_add_widget (size_group, widget);
744 e_extensible_load_extensions (E_EXTENSIBLE (object));
746 /* Chain up to parent's constructed() method. */
747 G_OBJECT_CLASS (e_shell_searchbar_parent_class)->constructed (object);
750 static void
751 shell_searchbar_map (GtkWidget *widget)
753 /* Chain up to parent's map() method. */
754 GTK_WIDGET_CLASS (e_shell_searchbar_parent_class)->map (widget);
756 /* Load state after constructed() so we don't derail
757 * subclass initialization. We wait until map() so we
758 * have usable style colors for the entry box. */
759 e_shell_searchbar_load_state (E_SHELL_SEARCHBAR (widget));
762 static void
763 e_shell_searchbar_class_init (EShellSearchbarClass *class)
765 GObjectClass *object_class;
766 GtkWidgetClass *widget_class;
768 g_type_class_add_private (class, sizeof (EShellSearchbarPrivate));
770 object_class = G_OBJECT_CLASS (class);
771 object_class->set_property = shell_searchbar_set_property;
772 object_class->get_property = shell_searchbar_get_property;
773 object_class->dispose = shell_searchbar_dispose;
774 object_class->finalize = shell_searchbar_finalize;
775 object_class->constructed = shell_searchbar_constructed;
777 widget_class = GTK_WIDGET_CLASS (class);
778 widget_class->map = shell_searchbar_map;
780 g_object_class_install_property (
781 object_class,
782 PROP_FILTER_COMBO_BOX,
783 g_param_spec_object (
784 "filter-combo-box",
785 NULL,
786 NULL,
787 E_TYPE_ACTION_COMBO_BOX,
788 G_PARAM_READABLE |
789 G_PARAM_STATIC_STRINGS));
791 g_object_class_install_property (
792 object_class,
793 PROP_FILTER_VISIBLE,
794 g_param_spec_boolean (
795 "filter-visible",
796 NULL,
797 NULL,
798 TRUE,
799 G_PARAM_READWRITE |
800 G_PARAM_CONSTRUCT |
801 G_PARAM_STATIC_STRINGS));
803 g_object_class_install_property (
804 object_class,
805 PROP_SEARCH_HINT,
806 g_param_spec_string (
807 "search-hint",
808 NULL,
809 NULL,
810 NULL,
811 G_PARAM_READWRITE |
812 G_PARAM_STATIC_STRINGS));
814 g_object_class_install_property (
815 object_class,
816 PROP_SEARCH_OPTION,
817 g_param_spec_object (
818 "search-option",
819 NULL,
820 NULL,
821 GTK_TYPE_RADIO_ACTION,
822 G_PARAM_READWRITE |
823 G_PARAM_STATIC_STRINGS));
825 g_object_class_install_property (
826 object_class,
827 PROP_SEARCH_TEXT,
828 g_param_spec_string (
829 "search-text",
830 NULL,
831 NULL,
832 NULL,
833 G_PARAM_READWRITE |
834 G_PARAM_STATIC_STRINGS));
836 g_object_class_install_property (
837 object_class,
838 PROP_SCOPE_COMBO_BOX,
839 g_param_spec_object (
840 "scope-combo-box",
841 NULL,
842 NULL,
843 E_TYPE_ACTION_COMBO_BOX,
844 G_PARAM_READABLE |
845 G_PARAM_STATIC_STRINGS));
847 g_object_class_install_property (
848 object_class,
849 PROP_SCOPE_VISIBLE,
850 g_param_spec_boolean (
851 "scope-visible",
852 NULL,
853 NULL,
854 FALSE,
855 G_PARAM_READWRITE |
856 G_PARAM_CONSTRUCT |
857 G_PARAM_STATIC_STRINGS));
860 * EShellSearchbar:shell-view
862 * The #EShellView to which the searchbar widget belongs.
864 g_object_class_install_property (
865 object_class,
866 PROP_SHELL_VIEW,
867 g_param_spec_object (
868 "shell-view",
869 NULL,
870 NULL,
871 E_TYPE_SHELL_VIEW,
872 G_PARAM_READWRITE |
873 G_PARAM_CONSTRUCT_ONLY |
874 G_PARAM_STATIC_STRINGS));
877 * EShellSearchbar:state-group
879 * Key file group name to read and write search bar state.
881 g_object_class_install_property (
882 object_class,
883 PROP_STATE_GROUP,
884 g_param_spec_string (
885 "state-group",
886 NULL,
887 NULL,
888 STATE_GROUP_DEFAULT,
889 G_PARAM_READWRITE |
890 G_PARAM_CONSTRUCT |
891 G_PARAM_STATIC_STRINGS));
894 static void
895 e_shell_searchbar_init (EShellSearchbar *searchbar)
897 GtkBox *box;
898 GtkLabel *label;
899 GtkWidget *widget;
901 searchbar->priv = E_SHELL_SEARCHBAR_GET_PRIVATE (searchbar);
903 gtk_box_set_spacing (GTK_BOX (searchbar), 6);
904 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (searchbar)),
905 "header-box");
907 /* Filter Combo Widgets */
909 box = GTK_BOX (searchbar);
911 widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
912 gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
914 e_binding_bind_property (
915 searchbar, "filter-visible",
916 widget, "visible",
917 G_BINDING_SYNC_CREATE);
919 box = GTK_BOX (widget);
921 /* Translators: The "Show:" label precedes a combo box that
922 * allows the user to filter the current view. Examples of
923 * items that appear in the combo box are "Unread Messages",
924 * "Important Messages", or "Active Appointments". */
925 widget = gtk_label_new_with_mnemonic (_("Sho_w:"));
926 gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
927 gtk_widget_show (widget);
929 label = GTK_LABEL (widget);
931 widget = e_action_combo_box_new ();
932 gtk_label_set_mnemonic_widget (label, widget);
933 gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
934 searchbar->priv->filter_combo_box = widget;
935 gtk_widget_show (widget);
937 /* Search Entry Widgets */
939 box = GTK_BOX (searchbar);
941 widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
942 gtk_widget_set_margin_left (widget, 12);
943 gtk_box_pack_start (box, widget, TRUE, TRUE, 0);
944 gtk_widget_show (widget);
946 box = GTK_BOX (widget);
948 /* Translators: This is part of the quick search interface.
949 * example: Search: [_______________] in [ Current Folder ] */
950 widget = gtk_label_new_with_mnemonic (_("Sear_ch:"));
951 gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
952 gtk_widget_show (widget);
954 label = GTK_LABEL (widget);
956 widget = gtk_entry_new ();
957 gtk_label_set_mnemonic_widget (label, widget);
958 gtk_box_pack_start (box, widget, TRUE, TRUE, 0);
959 searchbar->priv->search_entry = widget;
960 gtk_widget_show (widget);
962 g_signal_connect_swapped (
963 widget, "activate",
964 G_CALLBACK (shell_searchbar_entry_activate_cb),
965 searchbar);
967 g_signal_connect_swapped (
968 widget, "changed",
969 G_CALLBACK (shell_searchbar_entry_changed_cb),
970 searchbar);
972 g_signal_connect_swapped (
973 widget, "changed",
974 G_CALLBACK (e_shell_searchbar_set_state_dirty),
975 searchbar);
977 g_signal_connect_swapped (
978 widget, "icon-press",
979 G_CALLBACK (shell_searchbar_entry_icon_press_cb),
980 searchbar);
982 g_signal_connect_swapped (
983 widget, "icon-release",
984 G_CALLBACK (shell_searchbar_entry_icon_release_cb),
985 searchbar);
987 g_signal_connect_swapped (
988 widget, "key-press-event",
989 G_CALLBACK (shell_searchbar_entry_key_press_cb),
990 searchbar);
992 g_signal_connect (
993 widget, "focus-in-event",
994 G_CALLBACK (shell_searchbar_entry_focus_in_cb),
995 searchbar);
997 g_signal_connect (
998 widget, "focus-out-event",
999 G_CALLBACK (shell_searchbar_entry_focus_out_cb),
1000 searchbar);
1002 /* Scope Combo Widgets */
1004 box = GTK_BOX (searchbar);
1006 widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
1007 gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
1009 e_binding_bind_property (
1010 searchbar, "scope-visible",
1011 widget, "visible",
1012 G_BINDING_SYNC_CREATE);
1014 box = GTK_BOX (widget);
1016 /* Translators: This is part of the quick search interface.
1017 * example: Search: [_______________] in [ Current Folder ] */
1018 widget = gtk_label_new_with_mnemonic (_("i_n"));
1019 gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
1020 gtk_widget_show (widget);
1022 label = GTK_LABEL (widget);
1024 widget = e_action_combo_box_new ();
1025 gtk_label_set_mnemonic_widget (label, widget);
1026 gtk_box_pack_start (box, widget, FALSE, FALSE, 0);
1027 searchbar->priv->scope_combo_box = widget;
1028 gtk_widget_show (widget);
1030 /* Use G_CONNECT_AFTER here so the EActionComboBox has a
1031 * chance to update its radio actions before we go sifting
1032 * through the radio group for the current action. */
1033 g_signal_connect_data (
1034 widget, "changed",
1035 G_CALLBACK (shell_searchbar_save_search_scope),
1036 searchbar, (GClosureNotify) NULL,
1037 G_CONNECT_AFTER | G_CONNECT_SWAPPED);
1041 * e_shell_searchbar_new:
1042 * @shell_view: an #EShellView
1044 * Creates a new #EShellSearchbar instance.
1046 * Returns: a new #EShellSearchbar instance
1048 GtkWidget *
1049 e_shell_searchbar_new (EShellView *shell_view)
1051 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
1053 return g_object_new (
1054 E_TYPE_SHELL_SEARCHBAR,
1055 "shell-view", shell_view,
1056 "orientation", GTK_ORIENTATION_HORIZONTAL,
1057 NULL);
1061 * e_shell_searchbar_get_shell_view:
1062 * @searchbar: an #EShellSearchbar
1064 * Returns the #EShellView that was passed to e_shell_searchbar_new().
1066 * Returns: the #EShellView to which @searchbar belongs
1068 EShellView *
1069 e_shell_searchbar_get_shell_view (EShellSearchbar *searchbar)
1071 g_return_val_if_fail (E_IS_SHELL_SEARCHBAR (searchbar), NULL);
1073 return E_SHELL_VIEW (searchbar->priv->shell_view);
1076 EActionComboBox *
1077 e_shell_searchbar_get_filter_combo_box (EShellSearchbar *searchbar)
1079 g_return_val_if_fail (E_IS_SHELL_SEARCHBAR (searchbar), NULL);
1081 return E_ACTION_COMBO_BOX (searchbar->priv->filter_combo_box);
1084 gboolean
1085 e_shell_searchbar_get_filter_visible (EShellSearchbar *searchbar)
1087 g_return_val_if_fail (E_IS_SHELL_SEARCHBAR (searchbar), FALSE);
1089 return searchbar->priv->filter_visible;
1092 void
1093 e_shell_searchbar_set_filter_visible (EShellSearchbar *searchbar,
1094 gboolean filter_visible)
1096 g_return_if_fail (E_IS_SHELL_SEARCHBAR (searchbar));
1098 if (searchbar->priv->filter_visible == filter_visible)
1099 return;
1101 searchbar->priv->filter_visible = filter_visible;
1103 /* If we're hiding the filter combo box, reset it to its
1104 * first item so that no content gets permanently hidden. */
1105 if (!filter_visible) {
1106 EActionComboBox *combo_box;
1108 combo_box = e_shell_searchbar_get_filter_combo_box (searchbar);
1109 gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0);
1112 g_object_notify (G_OBJECT (searchbar), "filter-visible");
1115 const gchar *
1116 e_shell_searchbar_get_search_hint (EShellSearchbar *searchbar)
1118 GtkEntry *entry;
1120 g_return_val_if_fail (E_IS_SHELL_SEARCHBAR (searchbar), NULL);
1122 entry = GTK_ENTRY (searchbar->priv->search_entry);
1124 return gtk_entry_get_placeholder_text (entry);
1127 void
1128 e_shell_searchbar_set_search_hint (EShellSearchbar *searchbar,
1129 const gchar *search_hint)
1131 GtkEntry *entry;
1133 g_return_if_fail (E_IS_SHELL_SEARCHBAR (searchbar));
1135 entry = GTK_ENTRY (searchbar->priv->search_entry);
1137 if (g_strcmp0 (gtk_entry_get_placeholder_text (entry), search_hint) == 0)
1138 return;
1140 gtk_entry_set_placeholder_text (entry, search_hint);
1142 g_object_notify (G_OBJECT (searchbar), "search-hint");
1145 GtkRadioAction *
1146 e_shell_searchbar_get_search_option (EShellSearchbar *searchbar)
1148 g_return_val_if_fail (E_IS_SHELL_SEARCHBAR (searchbar), NULL);
1150 return searchbar->priv->search_option;
1153 void
1154 e_shell_searchbar_set_search_option (EShellSearchbar *searchbar,
1155 GtkRadioAction *search_option)
1157 g_return_if_fail (E_IS_SHELL_SEARCHBAR (searchbar));
1159 if (searchbar->priv->search_option == search_option)
1160 return;
1162 if (search_option != NULL) {
1163 g_return_if_fail (GTK_IS_RADIO_ACTION (search_option));
1164 g_object_ref (search_option);
1167 if (searchbar->priv->search_option != NULL) {
1168 g_signal_handlers_disconnect_matched (
1169 searchbar->priv->search_option,
1170 G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
1171 searchbar);
1172 g_object_unref (searchbar->priv->search_option);
1175 searchbar->priv->search_option = search_option;
1177 if (search_option != NULL)
1178 g_signal_connect (
1179 search_option, "changed",
1180 G_CALLBACK (shell_searchbar_option_changed_cb),
1181 searchbar);
1183 g_object_notify (G_OBJECT (searchbar), "search-option");
1186 const gchar *
1187 e_shell_searchbar_get_search_text (EShellSearchbar *searchbar)
1189 GtkEntry *entry;
1191 g_return_val_if_fail (E_IS_SHELL_SEARCHBAR (searchbar), NULL);
1193 entry = GTK_ENTRY (searchbar->priv->search_entry);
1195 return gtk_entry_get_text (entry);
1198 void
1199 e_shell_searchbar_set_search_text (EShellSearchbar *searchbar,
1200 const gchar *search_text)
1202 GtkEntry *entry;
1204 g_return_if_fail (E_IS_SHELL_SEARCHBAR (searchbar));
1206 entry = GTK_ENTRY (searchbar->priv->search_entry);
1208 /* XXX Really wish gtk_entry_set_text()
1209 * would just learn to accept NULL. */
1210 if (search_text == NULL)
1211 search_text = "";
1213 if (g_strcmp0 (gtk_entry_get_text (entry), search_text) == 0)
1214 return;
1216 gtk_entry_set_text (entry, search_text);
1218 shell_searchbar_update_search_widgets (searchbar);
1220 g_object_notify (G_OBJECT (searchbar), "search-text");
1223 GtkWidget *
1224 e_shell_searchbar_get_search_box (EShellSearchbar *searchbar)
1226 g_return_val_if_fail (searchbar != NULL, NULL);
1227 g_return_val_if_fail (searchbar->priv != NULL, NULL);
1228 g_return_val_if_fail (searchbar->priv->search_entry != NULL, NULL);
1230 return gtk_widget_get_parent (searchbar->priv->search_entry);
1233 EActionComboBox *
1234 e_shell_searchbar_get_scope_combo_box (EShellSearchbar *searchbar)
1236 g_return_val_if_fail (E_IS_SHELL_SEARCHBAR (searchbar), NULL);
1238 return E_ACTION_COMBO_BOX (searchbar->priv->scope_combo_box);
1241 gboolean
1242 e_shell_searchbar_get_scope_visible (EShellSearchbar *searchbar)
1244 g_return_val_if_fail (E_IS_SHELL_SEARCHBAR (searchbar), FALSE);
1246 return searchbar->priv->scope_visible;
1249 void
1250 e_shell_searchbar_set_scope_visible (EShellSearchbar *searchbar,
1251 gboolean scope_visible)
1253 g_return_if_fail (E_IS_SHELL_SEARCHBAR (searchbar));
1255 if (searchbar->priv->scope_visible == scope_visible)
1256 return;
1258 searchbar->priv->scope_visible = scope_visible;
1260 g_object_notify (G_OBJECT (searchbar), "scope-visible");
1263 void
1264 e_shell_searchbar_set_state_dirty (EShellSearchbar *searchbar)
1266 g_return_if_fail (E_IS_SHELL_SEARCHBAR (searchbar));
1268 searchbar->priv->state_dirty = TRUE;
1271 const gchar *
1272 e_shell_searchbar_get_state_group (EShellSearchbar *searchbar)
1274 g_return_val_if_fail (E_IS_SHELL_SEARCHBAR (searchbar), NULL);
1276 return searchbar->priv->state_group;
1279 void
1280 e_shell_searchbar_set_state_group (EShellSearchbar *searchbar,
1281 const gchar *state_group)
1283 g_return_if_fail (E_IS_SHELL_SEARCHBAR (searchbar));
1285 if (state_group == NULL)
1286 state_group = STATE_GROUP_DEFAULT;
1288 if (g_strcmp0 (searchbar->priv->state_group, state_group) == 0)
1289 return;
1291 g_free (searchbar->priv->state_group);
1292 searchbar->priv->state_group = g_strdup (state_group);
1294 g_object_notify (G_OBJECT (searchbar), "state-group");
1297 static gboolean
1298 idle_execute_search (gpointer shell_view)
1300 e_shell_view_execute_search (shell_view);
1301 g_object_unref (shell_view);
1302 return FALSE;
1305 void
1306 e_shell_searchbar_load_state (EShellSearchbar *searchbar)
1308 EShellView *shell_view;
1309 EShellWindow *shell_window;
1310 GKeyFile *key_file;
1311 GtkAction *action;
1312 GtkWidget *widget;
1313 const gchar *search_text;
1314 const gchar *state_group;
1315 const gchar *key;
1316 gchar *string;
1317 gint value = 0;
1319 g_return_if_fail (E_IS_SHELL_SEARCHBAR (searchbar));
1321 shell_view = e_shell_searchbar_get_shell_view (searchbar);
1322 state_group = e_shell_searchbar_get_state_group (searchbar);
1323 g_return_if_fail (state_group != NULL);
1325 key_file = e_shell_view_get_state_key_file (shell_view);
1326 shell_window = e_shell_view_get_shell_window (shell_view);
1328 /* Changing the combo boxes triggers searches, so block
1329 * the search action until the state is fully restored. */
1330 action = E_SHELL_WINDOW_ACTION_SEARCH_QUICK (shell_window);
1331 gtk_action_block_activate (action);
1333 e_shell_view_block_execute_search (shell_view);
1335 e_shell_view_set_search_rule (shell_view, NULL);
1337 key = STATE_KEY_SEARCH_FILTER;
1338 string = g_key_file_get_string (key_file, state_group, key, NULL);
1339 if (string != NULL && *string != '\0')
1340 action = e_shell_window_get_action (shell_window, string);
1341 else
1342 action = NULL;
1343 if (GTK_IS_RADIO_ACTION (action))
1344 gtk_action_activate (action);
1345 else {
1346 /* Pick the first combo box item. */
1347 widget = searchbar->priv->filter_combo_box;
1348 gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
1350 g_free (string);
1352 /* Avoid restoring to the "Advanced Search" option, since we
1353 * don't currently save the search rule (TODO but we should). */
1354 key = STATE_KEY_SEARCH_OPTION;
1355 string = g_key_file_get_string (key_file, state_group, key, NULL);
1356 if (string != NULL && *string != '\0')
1357 action = e_shell_window_get_action (shell_window, string);
1358 else
1359 action = NULL;
1360 if (GTK_IS_RADIO_ACTION (action))
1361 g_object_get (action, "value", &value, NULL);
1362 else
1363 value = SEARCH_OPTION_ADVANCED;
1364 if (value != SEARCH_OPTION_ADVANCED)
1365 gtk_action_activate (action);
1366 else if (searchbar->priv->search_option != NULL)
1367 gtk_radio_action_set_current_value (
1368 searchbar->priv->search_option, 0);
1369 g_free (string);
1371 key = STATE_KEY_SEARCH_TEXT;
1372 string = g_key_file_get_string (key_file, state_group, key, NULL);
1373 search_text = e_shell_searchbar_get_search_text (searchbar);
1374 if (search_text != NULL && *search_text == '\0')
1375 search_text = NULL;
1376 if (g_strcmp0 (string, search_text) != 0)
1377 e_shell_searchbar_set_search_text (searchbar, string);
1378 g_free (string);
1380 /* Search scope is hard-coded to the default state group. */
1381 state_group = STATE_GROUP_DEFAULT;
1383 key = STATE_KEY_SEARCH_SCOPE;
1384 string = g_key_file_get_string (key_file, state_group, key, NULL);
1385 if (string != NULL && *string != '\0')
1386 action = e_shell_window_get_action (shell_window, string);
1387 else
1388 action = NULL;
1389 if (GTK_IS_RADIO_ACTION (action))
1390 gtk_action_activate (action);
1391 else {
1392 /* Pick the first combo box item. */
1393 widget = searchbar->priv->scope_combo_box;
1394 gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
1396 g_free (string);
1398 e_shell_view_unblock_execute_search (shell_view);
1400 action = E_SHELL_WINDOW_ACTION_SEARCH_QUICK (shell_window);
1401 gtk_action_unblock_activate (action);
1403 /* Execute the search when we have time. */
1405 g_object_ref (shell_view);
1406 searchbar->priv->state_dirty = FALSE;
1408 /* Prioritize ahead of GTK+ redraws. */
1409 g_idle_add_full (
1410 G_PRIORITY_HIGH_IDLE,
1411 idle_execute_search, shell_view, NULL);
1414 void
1415 e_shell_searchbar_save_state (EShellSearchbar *searchbar)
1417 g_return_if_fail (E_IS_SHELL_SEARCHBAR (searchbar));
1419 /* Skip saving state if it hasn't changed since it was loaded. */
1420 if (!searchbar->priv->state_dirty)
1421 return;
1423 shell_searchbar_save_search_filter (searchbar);
1425 shell_searchbar_save_search_option (searchbar);
1427 shell_searchbar_save_search_text (searchbar);
1429 shell_searchbar_save_search_scope (searchbar);
1431 searchbar->priv->state_dirty = FALSE;
1434 void
1435 e_shell_searchbar_search_entry_grab_focus (EShellSearchbar *searchbar)
1437 g_return_if_fail (E_IS_SHELL_SEARCHBAR (searchbar));
1438 g_return_if_fail (searchbar->priv->search_entry);
1440 gtk_widget_grab_focus (searchbar->priv->search_entry);