1 /*
2 * Photos - access, organize and share your photos on GNOME
3 * Copyright © 2014 Pranav Kant
4 * Copyright © 2014 – 2019 Red Hat, Inc.
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /* Based on code from:
21 * + Documents
22 */
23
24
25 #include "config.h"
26
27 #include <glib.h>
28
29 #include "photos-searchbar.h"
30
31
32 struct _PhotosSearchbarPrivate
33 {
34 GAction *search;
35 GtkWidget *search_container;
36 GtkWidget *search_entry;
37 gboolean search_change_blocked;
38 gulong search_state_id;
39 };
40
41 enum
42 {
43 ACTIVATE_RESULT,
44 LAST_SIGNAL
45 };
46
47 static guint signals[LAST_SIGNAL] = { 0 };
48
49
50 G_DEFINE_TYPE_WITH_PRIVATE (PhotosSearchbar, photos_searchbar, GTK_TYPE_SEARCH_BAR);
51
52
53 static void
photos_searchbar_default_hide(PhotosSearchbar * self)54 photos_searchbar_default_hide (PhotosSearchbar *self)
55 {
56 PhotosSearchbarPrivate *priv;
57
58 priv = photos_searchbar_get_instance_private (self);
59
60 gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (self), FALSE);
61
62 /* Clear all the search properties when hiding the entry */
63 gtk_entry_set_text (GTK_ENTRY (priv->search_entry), "");
64 }
65
66
67 static void
photos_searchbar_default_show(PhotosSearchbar * self)68 photos_searchbar_default_show (PhotosSearchbar *self)
69 {
70 gtk_search_bar_set_search_mode (GTK_SEARCH_BAR (self), TRUE);
71 }
72
73
74 static void
photos_searchbar_enable_search(PhotosSearchbar * self,gboolean enable)75 photos_searchbar_enable_search (PhotosSearchbar *self, gboolean enable)
76 {
77 PhotosSearchbarPrivate *priv;
78 GVariant *state;
79
80 priv = photos_searchbar_get_instance_private (self);
81
82 state = g_variant_new ("b", enable);
83 g_action_change_state (priv->search, state);
84 }
85
86
87 static void
photos_searchbar_notify_search_mode_enabled(PhotosSearchbar * self)88 photos_searchbar_notify_search_mode_enabled (PhotosSearchbar *self)
89 {
90 gboolean search_enabled;
91
92 search_enabled = gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (self));
93 photos_searchbar_enable_search (self, search_enabled);
94 }
95
96
97 static void
photos_searchbar_search_changed(PhotosSearchbar * self)98 photos_searchbar_search_changed (PhotosSearchbar *self)
99 {
100 PhotosSearchbarPrivate *priv;
101
102 priv = photos_searchbar_get_instance_private (self);
103
104 if (priv->search_change_blocked)
105 return;
106
107 PHOTOS_SEARCHBAR_GET_CLASS (self)->entry_changed (self);
108 }
109
110
111 static void
photos_searchbar_update_visibility(PhotosSearchbar * self)112 photos_searchbar_update_visibility (PhotosSearchbar *self)
113 {
114 PhotosSearchbarPrivate *priv;
115 g_autoptr (GVariant) state = NULL;
116
117 priv = photos_searchbar_get_instance_private (self);
118
119 state = g_action_get_state (priv->search);
120 g_return_if_fail (state != NULL);
121
122 if (g_variant_get_boolean (state))
123 photos_searchbar_show (self);
124 else
125 photos_searchbar_hide (self);
126 }
127
128
129 static void
photos_searchbar_search_notify_state(PhotosSearchbar * self)130 photos_searchbar_search_notify_state (PhotosSearchbar *self)
131 {
132 photos_searchbar_update_visibility (self);
133 }
134
135
136 static void
photos_searchbar_constructed(GObject * object)137 photos_searchbar_constructed (GObject *object)
138 {
139 PhotosSearchbar *self = PHOTOS_SEARCHBAR (object);
140 PhotosSearchbarPrivate *priv;
141 GApplication *app;
142
143 priv = photos_searchbar_get_instance_private (self);
144
145 G_OBJECT_CLASS (photos_searchbar_parent_class)->constructed (object);
146
147 PHOTOS_SEARCHBAR_GET_CLASS (self)->create_search_widgets (self);
148
149 gtk_container_add (GTK_CONTAINER (self), priv->search_container);
150 gtk_search_bar_connect_entry (GTK_SEARCH_BAR (self), GTK_ENTRY (priv->search_entry));
151
152 g_signal_connect_swapped (priv->search_entry,
153 "search-changed",
154 G_CALLBACK (photos_searchbar_search_changed),
155 self);
156
157 g_signal_connect (self,
158 "notify::search-mode-enabled",
159 G_CALLBACK (photos_searchbar_notify_search_mode_enabled),
160 NULL);
161
162 app = g_application_get_default ();
163 priv->search = g_action_map_lookup_action (G_ACTION_MAP (app), "search");
164
165 /* g_signal_connect_object will not be able to disconnect the
166 * handler in time because we change the state of the action during
167 * dispose.
168 */
169 priv->search_state_id = g_signal_connect_swapped (priv->search,
170 "notify::state",
171 G_CALLBACK (photos_searchbar_search_notify_state),
172 self);
173 photos_searchbar_update_visibility (self);
174
175 gtk_widget_show_all (GTK_WIDGET (self));
176 }
177
178
179 static void
photos_searchbar_dispose(GObject * object)180 photos_searchbar_dispose (GObject *object)
181 {
182 PhotosSearchbar *self = PHOTOS_SEARCHBAR (object);
183 PhotosSearchbarPrivate *priv;
184
185 priv = photos_searchbar_get_instance_private (self);
186
187 if (priv->search_state_id != 0)
188 {
189 g_signal_handler_disconnect (priv->search, priv->search_state_id);
190 priv->search_state_id = 0;
191 }
192
193 photos_searchbar_enable_search (self, FALSE);
194
195 G_OBJECT_CLASS (photos_searchbar_parent_class)->dispose (object);
196 }
197
198
199 static void
photos_searchbar_init(PhotosSearchbar * self)200 photos_searchbar_init (PhotosSearchbar *self)
201 {
202 }
203
204
205 static void
photos_searchbar_class_init(PhotosSearchbarClass * class)206 photos_searchbar_class_init (PhotosSearchbarClass *class)
207 {
208 GObjectClass *object_class = G_OBJECT_CLASS (class);
209
210 object_class->constructed = photos_searchbar_constructed;
211 object_class->dispose = photos_searchbar_dispose;
212 class->hide = photos_searchbar_default_hide;
213 class->show = photos_searchbar_default_show;
214
215 signals[ACTIVATE_RESULT] = g_signal_new ("activate-result",
216 G_TYPE_FROM_CLASS (class),
217 G_SIGNAL_RUN_LAST,
218 G_STRUCT_OFFSET (PhotosSearchbarClass, activate_result),
219 NULL, /* accumulator */
220 NULL, /* accu_data */
221 g_cclosure_marshal_VOID__VOID,
222 G_TYPE_NONE,
223 0);
224 }
225
226
227 GtkWidget *
photos_searchbar_new(void)228 photos_searchbar_new (void)
229 {
230 return g_object_new (PHOTOS_TYPE_SEARCHBAR, NULL);
231 }
232
233
234 gboolean
photos_searchbar_handle_event(PhotosSearchbar * self,GdkEventKey * event)235 photos_searchbar_handle_event (PhotosSearchbar *self, GdkEventKey *event)
236 {
237 PhotosSearchbarPrivate *priv;
238 gboolean ret_val = GDK_EVENT_PROPAGATE;
239 gboolean search_mode_enabled;
240
241 priv = photos_searchbar_get_instance_private (self);
242
243 search_mode_enabled = gtk_search_bar_get_search_mode (GTK_SEARCH_BAR (self));
244
245 /* Skip if the search bar is shown and the focus is elsewhere */
246 if (search_mode_enabled && !gtk_widget_is_focus (priv->search_entry))
247 goto out;
248
249 if (search_mode_enabled && event->keyval == GDK_KEY_Return)
250 {
251 g_signal_emit (self, signals[ACTIVATE_RESULT], 0);
252 ret_val = GDK_EVENT_STOP;
253 goto out;
254 }
255
256 ret_val = gtk_search_bar_handle_event (GTK_SEARCH_BAR (self), (GdkEvent *) event);
257 if (ret_val == GDK_EVENT_STOP)
258 gtk_entry_grab_focus_without_selecting (GTK_ENTRY (priv->search_entry));
259
260 out:
261 return ret_val;
262 }
263
264
265 void
photos_searchbar_hide(PhotosSearchbar * self)266 photos_searchbar_hide (PhotosSearchbar *self)
267 {
268 PHOTOS_SEARCHBAR_GET_CLASS (self)->hide (self);
269 }
270
271
272 gboolean
photos_searchbar_is_focus(PhotosSearchbar * self)273 photos_searchbar_is_focus (PhotosSearchbar *self)
274 {
275 PhotosSearchbarPrivate *priv;
276
277 priv = photos_searchbar_get_instance_private (self);
278 return gtk_widget_is_focus (priv->search_entry);
279 }
280
281
282 void
photos_searchbar_set_search_change_blocked(PhotosSearchbar * self,gboolean search_change_blocked)283 photos_searchbar_set_search_change_blocked (PhotosSearchbar *self, gboolean search_change_blocked)
284 {
285 PhotosSearchbarPrivate *priv;
286
287 priv = photos_searchbar_get_instance_private (self);
288 priv->search_change_blocked = search_change_blocked;
289 }
290
291
292 void
photos_searchbar_set_search_container(PhotosSearchbar * self,GtkWidget * search_container)293 photos_searchbar_set_search_container (PhotosSearchbar *self, GtkWidget *search_container)
294 {
295 PhotosSearchbarPrivate *priv;
296
297 priv = photos_searchbar_get_instance_private (self);
298 priv->search_container = search_container;
299 }
300
301
302 void
photos_searchbar_set_search_entry(PhotosSearchbar * self,GtkWidget * search_entry)303 photos_searchbar_set_search_entry (PhotosSearchbar *self, GtkWidget *search_entry)
304 {
305 PhotosSearchbarPrivate *priv;
306
307 priv = photos_searchbar_get_instance_private (self);
308 priv->search_entry = search_entry;
309 }
310
311
312 void
photos_searchbar_show(PhotosSearchbar * self)313 photos_searchbar_show (PhotosSearchbar *self)
314 {
315 PHOTOS_SEARCHBAR_GET_CLASS (self)->show (self);
316 }
317