GCC Code Coverage Report


Directory: ./
File: panels/search/cc-search-locations-dialog.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 373 0.0%
Functions: 0 33 0.0%
Branches: 0 160 0.0%

Line Branch Exec Source
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 *
3 * Copyright (C) 2012 Red Hat, Inc
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Author: Cosimo Cecchi <cosimoc@gnome.org>
19 */
20
21 #include "cc-search-locations-dialog.h"
22
23 #include <glib/gi18n.h>
24
25 #define TRACKER_SCHEMA "org.freedesktop.Tracker.Miner.Files"
26 #define TRACKER3_SCHEMA "org.freedesktop.Tracker3.Miner.Files"
27 #define TRACKER_KEY_RECURSIVE_DIRECTORIES "index-recursive-directories"
28 #define TRACKER_KEY_SINGLE_DIRECTORIES "index-single-directories"
29
30 typedef enum {
31 PLACE_XDG,
32 PLACE_BOOKMARKS,
33 PLACE_OTHER
34 } PlaceType;
35
36 typedef struct {
37 CcSearchLocationsDialog *dialog;
38 GFile *location;
39 gchar *display_name;
40 PlaceType place_type;
41 GCancellable *cancellable;
42 const gchar *settings_key;
43 } Place;
44
45 struct _CcSearchLocationsDialog {
46 AdwWindow parent;
47
48 GSettings *tracker_preferences;
49
50 GtkWidget *places_group;
51 GtkWidget *places_list;
52 GtkWidget *bookmarks_group;
53 GtkWidget *bookmarks_list;
54 GtkWidget *others_list;
55 GtkWidget *locations_add;
56 };
57
58 struct _CcSearchLocationsDialogClass {
59 AdwPreferencesWindowClass parent_class;
60 };
61
62 G_DEFINE_TYPE (CcSearchLocationsDialog, cc_search_locations_dialog, ADW_TYPE_WINDOW)
63
64 static const gchar *path_from_tracker_dir (const gchar *value);
65
66 static gboolean
67 keynav_failed_cb (CcSearchLocationsDialog *self,
68 GtkDirectionType direction)
69 {
70 GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (self)));
71
72 if (!toplevel)
73 return FALSE;
74
75 if (direction != GTK_DIR_UP && direction != GTK_DIR_DOWN)
76 return FALSE;
77
78 return gtk_widget_child_focus (toplevel, direction == GTK_DIR_UP ?
79 GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD);
80 }
81
82 static void
83 cc_search_locations_dialog_finalize (GObject *object)
84 {
85 CcSearchLocationsDialog *self = CC_SEARCH_LOCATIONS_DIALOG (object);
86
87 g_clear_object (&self->tracker_preferences);
88
89 G_OBJECT_CLASS (cc_search_locations_dialog_parent_class)->finalize (object);
90 }
91
92 static void
93 cc_search_locations_dialog_init (CcSearchLocationsDialog *self)
94 {
95 gtk_widget_init_template (GTK_WIDGET (self));
96 }
97
98 static gboolean
99 location_in_path_strv (GFile *location,
100 const char **paths)
101 {
102 gint i;
103 const gchar *path;
104
105 for (i = 0; paths[i] != NULL; i++)
106 {
107 g_autoptr(GFile) tracker_location = NULL;
108
109 path = path_from_tracker_dir (paths[i]);
110
111 if (path == NULL)
112 continue;
113
114 tracker_location = g_file_new_for_path (path);
115 if (g_file_equal (location, tracker_location))
116 return TRUE;
117 }
118
119 return FALSE;
120 }
121
122 static Place *
123 place_new (CcSearchLocationsDialog *dialog,
124 GFile *location,
125 gchar *display_name,
126 PlaceType place_type)
127 {
128 Place *new_place = g_new0 (Place, 1);
129 g_autoptr(GVariant) single_dir_default_var = NULL;
130 g_autofree const char **single_dir_default = NULL;
131 g_auto(GStrv) single_dir = NULL;
132
133 single_dir_default_var = g_settings_get_default_value (dialog->tracker_preferences,
134 TRACKER_KEY_SINGLE_DIRECTORIES);
135 single_dir_default = g_variant_get_strv (single_dir_default_var, NULL);
136 single_dir = g_settings_get_strv (dialog->tracker_preferences, TRACKER_KEY_SINGLE_DIRECTORIES);
137
138 new_place->dialog = dialog;
139 new_place->location = location;
140 if (display_name != NULL)
141 new_place->display_name = display_name;
142 else
143 new_place->display_name = g_file_get_basename (location);
144
145 if (location_in_path_strv (new_place->location, single_dir_default) ||
146 location_in_path_strv (new_place->location, (const char **) single_dir))
147 new_place->settings_key = TRACKER_KEY_SINGLE_DIRECTORIES;
148 else
149 new_place->settings_key = TRACKER_KEY_RECURSIVE_DIRECTORIES;
150 new_place->place_type = place_type;
151
152 return new_place;
153 }
154
155 static void
156 place_free (Place * p)
157 {
158 g_cancellable_cancel (p->cancellable);
159 g_clear_object (&p->cancellable);
160
161 g_object_unref (p->location);
162 g_free (p->display_name);
163
164 g_free (p);
165 }
166
167 G_DEFINE_AUTOPTR_CLEANUP_FUNC (Place, place_free)
168
169 static GList *
170 get_bookmarks (CcSearchLocationsDialog *self)
171 {
172 g_autoptr(GFile) file = NULL;
173 g_autofree gchar *contents = NULL;
174 g_autofree gchar *path = NULL;
175 GList *bookmarks = NULL;
176 g_autoptr(GError) error = NULL;
177
178 path = g_build_filename (g_get_user_config_dir (), "gtk-3.0",
179 "bookmarks", NULL);
180 file = g_file_new_for_path (path);
181 if (g_file_load_contents (file, NULL, &contents, NULL, NULL, &error))
182 {
183 gint idx;
184 g_auto(GStrv) lines = NULL;
185
186 lines = g_strsplit (contents, "\n", -1);
187 for (idx = 0; lines[idx]; idx++)
188 {
189 /* Ignore empty or invalid lines that cannot be parsed properly */
190 if (lines[idx][0] != '\0' && lines[idx][0] != ' ')
191 {
192 /* gtk 2.7/2.8 might have labels appended to bookmarks which are separated by a space */
193 /* we must seperate the bookmark uri and the potential label */
194 char *space, *label;
195 Place *bookmark;
196
197 label = NULL;
198 space = strchr (lines[idx], ' ');
199 if (space)
200 {
201 *space = '\0';
202 label = g_strdup (space + 1);
203 }
204
205 bookmark = place_new (self,
206 g_file_new_for_uri (lines[idx]),
207 label,
208 PLACE_BOOKMARKS);
209
210 bookmarks = g_list_prepend (bookmarks, bookmark);
211 }
212 }
213 }
214
215 return g_list_reverse (bookmarks);
216 }
217
218 static const gchar *
219 get_user_special_dir_if_not_home (GUserDirectory idx)
220 {
221 const gchar *path;
222 path = g_get_user_special_dir (idx);
223 if (g_strcmp0 (path, g_get_home_dir ()) == 0)
224 return NULL;
225
226 return path;
227 }
228
229 static GList *
230 get_xdg_dirs (CcSearchLocationsDialog *self)
231 {
232 GList *xdg_dirs = NULL;
233 gint idx;
234 const gchar *path;
235 Place *xdg_dir;
236
237 for (idx = 0; idx < G_USER_N_DIRECTORIES; idx++)
238 {
239 path = get_user_special_dir_if_not_home (idx);
240 if (path == NULL)
241 continue;
242
243 if (idx == G_USER_DIRECTORY_TEMPLATES ||
244 idx == G_USER_DIRECTORY_PUBLIC_SHARE ||
245 idx == G_USER_DIRECTORY_DESKTOP)
246 continue;
247
248 xdg_dir = place_new (self,
249 g_file_new_for_path (path),
250 NULL,
251 PLACE_XDG);
252
253 xdg_dirs = g_list_prepend (xdg_dirs, xdg_dir);
254 }
255
256 return g_list_reverse (xdg_dirs);
257 }
258
259 static const gchar *
260 path_to_tracker_dir (const gchar *path)
261 {
262 const gchar *value;
263
264 if (g_strcmp0 (path, get_user_special_dir_if_not_home (G_USER_DIRECTORY_DESKTOP)) == 0)
265 value = "&DESKTOP";
266 else if (g_strcmp0 (path, get_user_special_dir_if_not_home (G_USER_DIRECTORY_DOCUMENTS)) == 0)
267 value = "&DOCUMENTS";
268 else if (g_strcmp0 (path, get_user_special_dir_if_not_home (G_USER_DIRECTORY_DOWNLOAD)) == 0)
269 value = "&DOWNLOAD";
270 else if (g_strcmp0 (path, get_user_special_dir_if_not_home (G_USER_DIRECTORY_MUSIC)) == 0)
271 value = "&MUSIC";
272 else if (g_strcmp0 (path, get_user_special_dir_if_not_home (G_USER_DIRECTORY_PICTURES)) == 0)
273 value = "&PICTURES";
274 else if (g_strcmp0 (path, get_user_special_dir_if_not_home (G_USER_DIRECTORY_PUBLIC_SHARE)) == 0)
275 value = "&PUBLIC_SHARE";
276 else if (g_strcmp0 (path, get_user_special_dir_if_not_home (G_USER_DIRECTORY_TEMPLATES)) == 0)
277 value = "&TEMPLATES";
278 else if (g_strcmp0 (path, get_user_special_dir_if_not_home (G_USER_DIRECTORY_VIDEOS)) == 0)
279 value = "&VIDEOS";
280 else if (g_strcmp0 (path, g_get_home_dir ()) == 0)
281 value = "$HOME";
282 else
283 value = path;
284
285 return value;
286 }
287
288 static const gchar *
289 path_from_tracker_dir (const gchar *value)
290 {
291 const gchar *path;
292
293 if (g_strcmp0 (value, "&DESKTOP") == 0)
294 path = get_user_special_dir_if_not_home (G_USER_DIRECTORY_DESKTOP);
295 else if (g_strcmp0 (value, "&DOCUMENTS") == 0)
296 path = get_user_special_dir_if_not_home (G_USER_DIRECTORY_DOCUMENTS);
297 else if (g_strcmp0 (value, "&DOWNLOAD") == 0)
298 path = get_user_special_dir_if_not_home (G_USER_DIRECTORY_DOWNLOAD);
299 else if (g_strcmp0 (value, "&MUSIC") == 0)
300 path = get_user_special_dir_if_not_home (G_USER_DIRECTORY_MUSIC);
301 else if (g_strcmp0 (value, "&PICTURES") == 0)
302 path = get_user_special_dir_if_not_home (G_USER_DIRECTORY_PICTURES);
303 else if (g_strcmp0 (value, "&PUBLIC_SHARE") == 0)
304 path = get_user_special_dir_if_not_home (G_USER_DIRECTORY_PUBLIC_SHARE);
305 else if (g_strcmp0 (value, "&TEMPLATES") == 0)
306 path = get_user_special_dir_if_not_home (G_USER_DIRECTORY_TEMPLATES);
307 else if (g_strcmp0 (value, "&VIDEOS") == 0)
308 path = get_user_special_dir_if_not_home (G_USER_DIRECTORY_VIDEOS);
309 else if (g_strcmp0 (value, "$HOME") == 0)
310 path = g_get_home_dir ();
311 else
312 path = value;
313
314 return path;
315 }
316
317 static GPtrArray *
318 place_get_new_settings_values (CcSearchLocationsDialog *self,
319 Place *place,
320 gboolean remove)
321 {
322 g_auto(GStrv) values = NULL;
323 g_autofree gchar *path = NULL;
324 GPtrArray *new_values;
325 const gchar *tracker_dir;
326 gboolean found;
327 gint idx;
328
329 new_values = g_ptr_array_new_with_free_func (g_free);
330 values = g_settings_get_strv (self->tracker_preferences, place->settings_key);
331 path = g_file_get_path (place->location);
332 tracker_dir = path_to_tracker_dir (path);
333
334 found = FALSE;
335
336 for (idx = 0; values[idx] != NULL; idx++)
337 {
338 if (g_strcmp0 (values[idx], tracker_dir) == 0)
339 {
340 found = TRUE;
341
342 if (remove)
343 continue;
344 }
345
346 g_ptr_array_add (new_values, g_strdup (values[idx]));
347 }
348
349 if (!found && !remove)
350 g_ptr_array_add (new_values, g_strdup (tracker_dir));
351
352 g_ptr_array_add (new_values, NULL);
353
354 return new_values;
355 }
356
357
358 static GList *
359 get_tracker_locations (CcSearchLocationsDialog *self)
360 {
361 g_auto(GStrv) locations_single = NULL;
362 g_auto(GStrv) locations = NULL;
363 g_auto(GStrv) locations_all = NULL;
364 GFile *file;
365 GList *list;
366 Place *location;
367 const gchar *path;
368 g_autoptr (GStrvBuilder) builder = g_strv_builder_new ();
369
370 locations = g_settings_get_strv (self->tracker_preferences, TRACKER_KEY_RECURSIVE_DIRECTORIES);
371 locations_single = g_settings_get_strv (self->tracker_preferences, TRACKER_KEY_SINGLE_DIRECTORIES);
372 g_strv_builder_addv (builder, (const char **) locations);
373 g_strv_builder_addv (builder, (const char **) locations_single);
374 locations_all = g_strv_builder_end (builder);
375 list = NULL;
376
377 for (guint idx = 0; locations_all[idx] != NULL; idx++)
378 {
379 path = path_from_tracker_dir (locations_all[idx]);
380
381 if (path == NULL)
382 continue;
383
384 file = g_file_new_for_commandline_arg (path);
385 location = place_new (self,
386 file,
387 NULL,
388 PLACE_OTHER);
389
390 list = g_list_prepend (list, location);
391 }
392
393 return g_list_reverse (list);
394 }
395
396 static GList *
397 get_places_list (CcSearchLocationsDialog *self)
398 {
399 g_autoptr(GList) xdg_list = NULL;
400 g_autoptr(GList) tracker_list = NULL;
401 g_autoptr(GList) bookmark_list = NULL;
402 GList *l;
403 g_autoptr(GHashTable) places = NULL;
404 Place *place, *old_place;
405 GList *places_list;
406
407 places = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, NULL, (GDestroyNotify) place_free);
408
409 /* add home */
410 place = place_new (self,
411 g_file_new_for_path (g_get_home_dir ()),
412 g_strdup (_("Home")),
413 PLACE_XDG);
414 g_hash_table_insert (places, place->location, place);
415
416 /* first, load the XDG dirs */
417 xdg_list = get_xdg_dirs (self);
418 for (l = xdg_list; l != NULL; l = l->next)
419 {
420 place = l->data;
421 g_hash_table_insert (places, place->location, place);
422 }
423
424 /* then, insert all the tracker locations that are not XDG dirs */
425 tracker_list = get_tracker_locations (self);
426 for (l = tracker_list; l != NULL; l = l->next)
427 {
428 g_autoptr(Place) p = l->data;
429 old_place = g_hash_table_lookup (places, p->location);
430 if (old_place == NULL)
431 {
432 g_hash_table_insert (places, p->location, p);
433 g_steal_pointer (&p);
434 }
435 }
436
437 /* finally, load bookmarks, and possibly update attributes */
438 bookmark_list = get_bookmarks (self);
439 for (l = bookmark_list; l != NULL; l = l->next)
440 {
441 g_autoptr(Place) p = l->data;
442 old_place = g_hash_table_lookup (places, p->location);
443 if (old_place == NULL)
444 {
445 g_hash_table_insert (places, p->location, p);
446 g_steal_pointer (&p);
447 }
448 else
449 {
450 g_free (old_place->display_name);
451 old_place->display_name = g_strdup (p->display_name);
452
453 if (old_place->place_type == PLACE_OTHER)
454 old_place->place_type = PLACE_BOOKMARKS;
455 }
456 }
457
458 places_list = g_hash_table_get_values (places);
459 g_hash_table_steal_all (places);
460
461 return places_list;
462 }
463
464 static gboolean
465 switch_tracker_get_mapping (GValue *value,
466 GVariant *variant,
467 gpointer user_data)
468 {
469 Place *place = user_data;
470 g_autofree const gchar **locations = NULL;
471 gboolean found;
472
473 locations = g_variant_get_strv (variant, NULL);
474 found = location_in_path_strv (place->location, locations);
475
476 g_value_set_boolean (value, found);
477 return TRUE;
478 }
479
480 static GVariant *
481 switch_tracker_set_mapping (const GValue *value,
482 const GVariantType *expected_type,
483 gpointer user_data)
484 {
485 Place *place = user_data;
486 g_autoptr(GPtrArray) new_values = NULL;
487 gboolean remove;
488
489 remove = !g_value_get_boolean (value);
490 new_values = place_get_new_settings_values (place->dialog, place, remove);
491 return g_variant_new_strv ((const gchar **) new_values->pdata, -1);
492 }
493
494 static void
495 place_query_info_ready (GObject *source,
496 GAsyncResult *res,
497 gpointer user_data)
498 {
499 AdwActionRow *row = ADW_ACTION_ROW (user_data);
500 g_autoptr(GFileInfo) info = NULL;
501 g_autoptr(GError) error = NULL;
502 Place *place;
503
504 info = g_file_query_info_finish (G_FILE (source), res, &error);
505
506 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
507 adw_action_row_set_subtitle (row, _("Location not found"));
508
509 place = g_object_get_data (G_OBJECT (row), "place");
510 g_clear_object (&place->cancellable);
511 }
512
513 static void
514 remove_button_clicked (CcSearchLocationsDialog *self,
515 GtkWidget *button)
516 {
517 g_autoptr(GPtrArray) new_values = NULL;
518 Place *place;
519
520 place = g_object_get_data (G_OBJECT (button), "place");
521 new_values = place_get_new_settings_values (self, place, TRUE);
522 g_settings_set_strv (self->tracker_preferences, place->settings_key, (const gchar **) new_values->pdata);
523 }
524
525 static gint
526 place_compare_func (gconstpointer a,
527 gconstpointer b,
528 gpointer user_data)
529 {
530 GtkWidget *child_a, *child_b;
531 Place *place_a, *place_b;
532 g_autofree char *path_a = NULL;
533 g_autofree char *path_b = NULL;
534
535 child_a = GTK_WIDGET (a);
536 child_b = GTK_WIDGET (b);
537
538 place_a = g_object_get_data (G_OBJECT (child_a), "place");
539 place_b = g_object_get_data (G_OBJECT (child_b), "place");
540
541 path_a = g_file_get_path (place_a->location);
542 path_b = g_file_get_path (place_b->location);
543
544 if (g_strcmp0 (path_a, g_get_home_dir ()) == 0)
545 return -1;
546 else if (g_strcmp0 (path_b, g_get_home_dir ()) == 0)
547 return 1;
548
549 return g_utf8_collate (place_a->display_name, place_b->display_name);
550 }
551
552 static GtkWidget *
553 create_row_for_place (CcSearchLocationsDialog *self, Place *place)
554 {
555 AdwActionRow *row;
556 GtkWidget *index_switch, *remove_button;
557
558 row = ADW_ACTION_ROW (adw_action_row_new ());
559 adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), place->display_name);
560
561 g_object_set_data_full (G_OBJECT (row), "place", place, (GDestroyNotify) place_free);
562
563 if (g_str_equal (place->settings_key, TRACKER_KEY_SINGLE_DIRECTORIES))
564 adw_action_row_set_subtitle (row, _("Subfolders must be manually added for this location"));
565
566 if (place->place_type == PLACE_OTHER)
567 {
568 /* Other locations can only be removed */
569 remove_button = gtk_button_new_from_icon_name ("edit-delete-symbolic");
570
571 g_object_set_data (G_OBJECT (remove_button), "place", place);
572 gtk_widget_set_tooltip_text (GTK_WIDGET (remove_button), _("Remove Folder"));
573 gtk_widget_set_valign (remove_button, GTK_ALIGN_CENTER);
574 gtk_widget_add_css_class (remove_button, "flat");
575 adw_action_row_add_suffix (ADW_ACTION_ROW (row), remove_button);
576
577 g_signal_connect_swapped (remove_button, "clicked",
578 G_CALLBACK (remove_button_clicked), self);
579 }
580 else
581 {
582 /* Indexing for default/bookmark locations can be switched off, but not removed */
583 index_switch = gtk_switch_new ();
584
585 gtk_widget_set_valign (index_switch, GTK_ALIGN_CENTER);
586 adw_action_row_add_suffix (row, index_switch);
587 adw_action_row_set_activatable_widget (row, index_switch);
588
589 g_settings_bind_with_mapping (place->dialog->tracker_preferences, place->settings_key,
590 index_switch, "active",
591 G_SETTINGS_BIND_DEFAULT,
592 switch_tracker_get_mapping,
593 switch_tracker_set_mapping,
594 place, NULL);
595 }
596
597 place->cancellable = g_cancellable_new ();
598 g_file_query_info_async (place->location, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
599 G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT,
600 place->cancellable, place_query_info_ready, row);
601
602 return GTK_WIDGET (row);
603 }
604
605 static void
606 update_list_visibility (CcSearchLocationsDialog *self)
607 {
608 gtk_widget_set_visible (self->places_group,
609 gtk_list_box_get_row_at_index (GTK_LIST_BOX (self->places_list), 0)
610 != NULL);
611 gtk_widget_set_visible (self->bookmarks_group,
612 gtk_list_box_get_row_at_index (GTK_LIST_BOX (self->bookmarks_list), 0)
613 != NULL);
614 }
615
616 static void
617 populate_list_boxes (CcSearchLocationsDialog *self)
618 {
619 g_autoptr(GList) places = NULL;
620 GList *l;
621 Place *place;
622 GtkWidget *row;
623
624 places = get_places_list (self);
625 for (l = places; l != NULL; l = l->next)
626 {
627 place = l->data;
628 row = create_row_for_place (self, place);
629
630 switch (place->place_type)
631 {
632 case PLACE_XDG:
633 gtk_list_box_append (GTK_LIST_BOX (self->places_list), row);
634 break;
635 case PLACE_BOOKMARKS:
636 gtk_list_box_append (GTK_LIST_BOX (self->bookmarks_list), row);
637 break;
638 case PLACE_OTHER:
639 gtk_list_box_append (GTK_LIST_BOX (self->others_list), row);
640 break;
641 default:
642 g_assert_not_reached ();
643 }
644 }
645
646 update_list_visibility (self);
647 }
648
649 static void
650 add_file_chooser_response (GObject *source,
651 GAsyncResult *res,
652 gpointer user_data)
653 {
654 CcSearchLocationsDialog *self = CC_SEARCH_LOCATIONS_DIALOG (user_data);
655 GtkFileDialog *file_dialog = GTK_FILE_DIALOG (source);
656 g_autoptr(Place) place = NULL;
657 g_autoptr(GPtrArray) new_values = NULL;
658 g_autoptr(GError) error = NULL;
659 GFile *file;
660
661 file = gtk_file_dialog_select_folder_finish (file_dialog, res, &error);
662 if (!file)
663 {
664 if (error->code != GTK_DIALOG_ERROR_DISMISSED)
665 g_warning ("Failed to add search location: %s", error->message);
666 return;
667 }
668
669 place = place_new (self, file, NULL, 0);
670
671 place->settings_key = TRACKER_KEY_RECURSIVE_DIRECTORIES;
672
673 new_values = place_get_new_settings_values (self, place, FALSE);
674 g_settings_set_strv (self->tracker_preferences, place->settings_key, (const gchar **) new_values->pdata);
675 }
676
677 static void
678 add_button_clicked (CcSearchLocationsDialog *self)
679 {
680 GtkFileDialog *file_dialog;
681
682 file_dialog = gtk_file_dialog_new ();
683 gtk_file_dialog_set_title (file_dialog, _("Select Location"));
684 gtk_file_dialog_set_modal (file_dialog, TRUE);
685
686 gtk_file_dialog_select_folder (file_dialog, GTK_WINDOW (self),
687 NULL,
688 add_file_chooser_response,
689 self);
690 }
691
692 static void
693 other_places_refresh (CcSearchLocationsDialog *self)
694 {
695 g_autoptr(GList) places = NULL;
696 GList *l;
697 GtkListBoxRow *widget;
698
699 while ((widget = gtk_list_box_get_row_at_index (GTK_LIST_BOX (self->others_list), 0)))
700 gtk_list_box_remove (GTK_LIST_BOX (self->others_list), GTK_WIDGET (widget));
701
702 places = get_places_list (self);
703 for (l = places; l != NULL; l = l->next)
704 {
705 GtkWidget *row;
706 Place *place;
707
708 place = l->data;
709 if (place->place_type != PLACE_OTHER)
710 continue;
711
712 row = create_row_for_place (self, place);
713 gtk_list_box_append (GTK_LIST_BOX (self->others_list), row);
714 }
715
716 update_list_visibility (self);
717 }
718
719 CcSearchLocationsDialog *
720 cc_search_locations_dialog_new (void)
721 {
722 CcSearchLocationsDialog *self;
723 GSettingsSchemaSource *source;
724 g_autoptr(GSettingsSchema) schema = NULL;
725
726 self = g_object_new (CC_SEARCH_LOCATIONS_DIALOG_TYPE, NULL);
727
728 source = g_settings_schema_source_get_default ();
729 schema = g_settings_schema_source_lookup (source, TRACKER3_SCHEMA, TRUE);
730 if (schema)
731 self->tracker_preferences = g_settings_new (TRACKER3_SCHEMA);
732 else
733 self->tracker_preferences = g_settings_new (TRACKER_SCHEMA);
734
735 populate_list_boxes (self);
736
737 gtk_list_box_set_sort_func (GTK_LIST_BOX (self->places_list),
738 (GtkListBoxSortFunc) place_compare_func, NULL, NULL);
739 gtk_list_box_set_sort_func (GTK_LIST_BOX (self->bookmarks_list),
740 (GtkListBoxSortFunc) place_compare_func, NULL, NULL);
741 gtk_list_box_set_sort_func (GTK_LIST_BOX (self->others_list),
742 (GtkListBoxSortFunc) place_compare_func, NULL, NULL);
743
744 g_signal_connect_swapped (self->tracker_preferences, "changed::" TRACKER_KEY_RECURSIVE_DIRECTORIES,
745 G_CALLBACK (other_places_refresh), self);
746 g_signal_connect_swapped (self->tracker_preferences, "changed::" TRACKER_KEY_SINGLE_DIRECTORIES,
747 G_CALLBACK (other_places_refresh), self);
748
749 return self;
750 }
751
752 gboolean
753 cc_search_locations_dialog_is_available (void)
754 {
755 GSettingsSchemaSource *source;
756 g_autoptr(GSettingsSchema) schema = NULL;
757
758 source = g_settings_schema_source_get_default ();
759 if (!source)
760 return FALSE;
761
762 schema = g_settings_schema_source_lookup (source, TRACKER3_SCHEMA, TRUE);
763 if (schema)
764 return TRUE;
765
766 schema = g_settings_schema_source_lookup (source, TRACKER_SCHEMA, TRUE);
767 if (schema)
768 return TRUE;
769
770 return FALSE;
771 }
772
773 static void
774 cc_search_locations_dialog_class_init (CcSearchLocationsDialogClass *klass)
775 {
776 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
777 GObjectClass *object_class = G_OBJECT_CLASS (klass);
778
779 object_class->finalize = cc_search_locations_dialog_finalize;
780
781 gtk_widget_class_set_template_from_resource (widget_class,
782 "/org/gnome/control-center/search/cc-search-locations-dialog.ui");
783
784 gtk_widget_class_bind_template_child (widget_class, CcSearchLocationsDialog, places_group);
785 gtk_widget_class_bind_template_child (widget_class, CcSearchLocationsDialog, places_list);
786 gtk_widget_class_bind_template_child (widget_class, CcSearchLocationsDialog, bookmarks_group);
787 gtk_widget_class_bind_template_child (widget_class, CcSearchLocationsDialog, bookmarks_list);
788 gtk_widget_class_bind_template_child (widget_class, CcSearchLocationsDialog, others_list);
789 gtk_widget_class_bind_template_child (widget_class, CcSearchLocationsDialog, locations_add);
790
791 gtk_widget_class_bind_template_callback (widget_class, add_button_clicked);
792 gtk_widget_class_bind_template_callback (widget_class, keynav_failed_cb);
793
794 gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Escape, 0, "window.close", NULL);
795 }
796