1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2013 Intel Corporation
4  *
5  * This library is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Tristan Van Berkom <tristanvb@openismus.com>
18  */
19 
20 #include <libebook/libebook.h>
21 
22 #include "cursor-search.h"
23 
24 /* GObjectClass */
25 static void  cursor_search_finalize       (GObject           *object);
26 static void  cursor_search_get_property   (GObject           *object,
27 					   guint              property_id,
28 					   GValue            *value,
29 					   GParamSpec        *pspec);
30 
31 /* UI Callbacks */
32 static void  cursor_search_option_toggled (CursorSearch         *search,
33 					   GtkWidget            *item);
34 static void  cursor_search_entry_changed  (CursorSearch         *search,
35 					   GtkEditable          *entry);
36 static void  cursor_search_icon_press     (CursorSearch         *search,
37 					   GtkEntryIconPosition  icon_pos,
38 					   GdkEvent             *event,
39 					   GtkEntry             *entry);
40 
41 typedef enum {
42 	SEARCH_NAME,
43 	SEARCH_PHONE,
44 	SEARCH_EMAIL
45 } SearchType;
46 
47 struct _CursorSearchPrivate {
48 	GtkWidget   *popup;
49 	GtkWidget   *name_radio;
50 	GtkWidget   *phone_radio;
51 	GtkWidget   *email_radio;
52 
53 	SearchType   type;
54 	gchar       *sexp;
55 };
56 
57 enum {
58 	PROP_0,
59 	PROP_SEXP,
60 };
61 
62 G_DEFINE_TYPE_WITH_PRIVATE (CursorSearch, cursor_search, GTK_TYPE_SEARCH_ENTRY);
63 
64 /************************************************************************
65  *                          GObjectClass                                *
66  ************************************************************************/
67 static void
cursor_search_class_init(CursorSearchClass * klass)68 cursor_search_class_init (CursorSearchClass *klass)
69 {
70 	GObjectClass *object_class;
71 	GtkWidgetClass *widget_class;
72 
73 	object_class = G_OBJECT_CLASS (klass);
74 	object_class->finalize = cursor_search_finalize;
75 	object_class->get_property = cursor_search_get_property;
76 
77 	g_object_class_install_property (
78 		object_class,
79 		PROP_SEXP,
80 		g_param_spec_string (
81 			"sexp",
82 			"Search Expression",
83 			"The active search expression",
84 			NULL,
85 			G_PARAM_READABLE |
86 			G_PARAM_STATIC_STRINGS));
87 
88 	/* Bind to template */
89 	widget_class = GTK_WIDGET_CLASS (klass);
90 	gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/evolution/cursor-example/cursor-search.ui");
91 	gtk_widget_class_bind_template_child_private (widget_class, CursorSearch, popup);
92 	gtk_widget_class_bind_template_child_private (widget_class, CursorSearch, name_radio);
93 	gtk_widget_class_bind_template_child_private (widget_class, CursorSearch, phone_radio);
94 	gtk_widget_class_bind_template_child_private (widget_class, CursorSearch, email_radio);
95 	gtk_widget_class_bind_template_callback (widget_class, cursor_search_option_toggled);
96 	gtk_widget_class_bind_template_callback (widget_class, cursor_search_entry_changed);
97 	gtk_widget_class_bind_template_callback (widget_class, cursor_search_icon_press);
98 }
99 
100 static void
cursor_search_init(CursorSearch * search)101 cursor_search_init (CursorSearch *search)
102 {
103 	search->priv = cursor_search_get_instance_private (search);
104 
105 	gtk_widget_init_template (GTK_WIDGET (search));
106 
107 	g_object_set (
108 		search,
109 		"primary-icon-activatable", TRUE,
110 		"primary-icon-sensitive", TRUE,
111 		NULL);
112 }
113 
114 static void
cursor_search_finalize(GObject * object)115 cursor_search_finalize (GObject *object)
116 {
117 	CursorSearch        *search = CURSOR_SEARCH (object);
118 	CursorSearchPrivate *priv = search->priv;
119 
120 	g_free (priv->sexp);
121 
122 	G_OBJECT_CLASS (cursor_search_parent_class)->finalize (object);
123 }
124 
125 static void
cursor_search_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)126 cursor_search_get_property (GObject *object,
127                             guint property_id,
128                             GValue *value,
129                             GParamSpec *pspec)
130 {
131 	CursorSearch        *search = CURSOR_SEARCH (object);
132 	CursorSearchPrivate *priv = search->priv;
133 
134 	switch (property_id) {
135 	case PROP_SEXP:
136 		g_value_set_string (value, priv->sexp);
137 		break;
138 
139 	default:
140 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
141 		break;
142 
143 	}
144 }
145 
146 /************************************************************************
147  *                              UI Callbacks                            *
148  ************************************************************************/
149 static void
cursor_search_option_toggled(CursorSearch * search,GtkWidget * item)150 cursor_search_option_toggled (CursorSearch *search,
151                               GtkWidget *item)
152 {
153 	CursorSearchPrivate *priv = search->priv;
154 
155 	if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item))) {
156 
157 		if (item == priv->name_radio)
158 			priv->type = SEARCH_NAME;
159 		else if (item == priv->phone_radio)
160 			priv->type = SEARCH_PHONE;
161 		else if (item == priv->email_radio)
162 			priv->type = SEARCH_EMAIL;
163 
164 		/* Refresh the search */
165 		cursor_search_entry_changed (search, NULL);
166 	}
167 }
168 
169 static void
cursor_search_entry_changed(CursorSearch * search,GtkEditable * entry)170 cursor_search_entry_changed (CursorSearch *search,
171                              GtkEditable *entry)
172 {
173 	CursorSearchPrivate *priv = search->priv;
174 	EBookQuery  *query = NULL;
175 	const gchar *text;
176 
177 	text = gtk_entry_get_text (GTK_ENTRY (search));
178 
179 	if (text && text[0]) {
180 		switch (priv->type) {
181 		case SEARCH_NAME:
182 			query = e_book_query_orv (
183 				e_book_query_field_test (E_CONTACT_FAMILY_NAME,
184 				E_BOOK_QUERY_CONTAINS,
185 				text),
186 				e_book_query_field_test (E_CONTACT_GIVEN_NAME,
187 				E_BOOK_QUERY_CONTAINS,
188 				text),
189 				NULL);
190 			break;
191 		case SEARCH_PHONE:
192 			query = e_book_query_field_test (
193 				E_CONTACT_TEL,
194 				E_BOOK_QUERY_CONTAINS,
195 				text);
196 			break;
197 		case SEARCH_EMAIL:
198 			query = e_book_query_field_test (
199 				E_CONTACT_EMAIL,
200 				E_BOOK_QUERY_CONTAINS,
201 				text);
202 			break;
203 		}
204 	}
205 
206 	g_free (priv->sexp);
207 
208 	if (query) {
209 		priv->sexp = e_book_query_to_string (query);
210 		e_book_query_unref (query);
211 	} else
212 		priv->sexp = g_strdup ("");
213 
214 	g_object_notify (G_OBJECT (search), "sexp");
215 }
216 
217 static void
cursor_search_icon_press(CursorSearch * search,GtkEntryIconPosition icon_pos,GdkEvent * event,GtkEntry * entry)218 cursor_search_icon_press (CursorSearch *search,
219                           GtkEntryIconPosition icon_pos,
220                           GdkEvent *event,
221                           GtkEntry *entry)
222 {
223 	CursorSearchPrivate *priv = search->priv;
224 	GdkEventButton *button_event = (GdkEventButton *) event;
225 
226 	if (icon_pos == GTK_ENTRY_ICON_PRIMARY)
227 		gtk_menu_popup (
228 			GTK_MENU (priv->popup),
229 				NULL, NULL, NULL, NULL,
230 				button_event->button,
231 				button_event->time);
232 }
233 
234 /************************************************************************
235  *                                API                                   *
236  ************************************************************************/
237 GtkWidget *
cursor_search_new(void)238 cursor_search_new (void)
239 {
240   return (GtkWidget *) g_object_new (CURSOR_TYPE_SEARCH, NULL);
241 }
242 
243 const gchar *
cursor_search_get_sexp(CursorSearch * search)244 cursor_search_get_sexp (CursorSearch *search)
245 {
246 	CursorSearchPrivate *priv;
247 
248 	g_return_val_if_fail (CURSOR_IS_SEARCH (search), NULL);
249 
250 	priv = search->priv;
251 
252 	return priv->sexp;
253 }
254