1 /* nautilus-search-popover.c
2 *
3 * Copyright (C) 2015 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
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 3 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
19 #include "nautilus-enum-types.h"
20 #include "nautilus-search-popover.h"
21 #include "nautilus-mime-actions.h"
22
23 #include <glib/gi18n.h>
24 #include "nautilus-file.h"
25 #include "nautilus-ui-utilities.h"
26 #include "nautilus-global-preferences.h"
27
28 #define SEARCH_FILTER_MAX_YEARS 5
29
30 struct _NautilusSearchPopover
31 {
32 GtkPopover parent;
33
34 GtkWidget *around_revealer;
35 GtkWidget *around_stack;
36 GtkWidget *calendar;
37 GtkWidget *clear_date_button;
38 GtkWidget *dates_listbox;
39 GtkWidget *date_entry;
40 GtkWidget *date_stack;
41 GtkWidget *select_date_button;
42 GtkWidget *select_date_button_label;
43 GtkWidget *type_label;
44 GtkWidget *type_listbox;
45 GtkWidget *type_stack;
46 GtkWidget *last_used_button;
47 GtkWidget *last_modified_button;
48 GtkWidget *full_text_search_button;
49 GtkWidget *filename_search_button;
50
51 NautilusQuery *query;
52
53 gboolean fts_enabled;
54 };
55
56 static void show_date_selection_widgets (NautilusSearchPopover *popover,
57 gboolean visible);
58
59 static void show_other_types_dialog (NautilusSearchPopover *popover);
60
61 static void update_date_label (NautilusSearchPopover *popover,
62 GPtrArray *date_range);
63
64 G_DEFINE_TYPE (NautilusSearchPopover, nautilus_search_popover, GTK_TYPE_POPOVER)
65
66 enum
67 {
68 PROP_0,
69 PROP_QUERY,
70 PROP_FTS_ENABLED,
71 LAST_PROP
72 };
73
74 enum
75 {
76 MIME_TYPE,
77 TIME_TYPE,
78 DATE_RANGE,
79 LAST_SIGNAL
80 };
81
82 static guint signals[LAST_SIGNAL];
83
84
85 /* Callbacks */
86
87 static void
calendar_day_selected(GtkCalendar * calendar,NautilusSearchPopover * popover)88 calendar_day_selected (GtkCalendar *calendar,
89 NautilusSearchPopover *popover)
90 {
91 GDateTime *date;
92 guint year, month, day;
93 GPtrArray *date_range;
94
95 gtk_calendar_get_date (calendar, &year, &month, &day);
96
97 date = g_date_time_new_local (year, month + 1, day, 0, 0, 0);
98
99 date_range = g_ptr_array_new_full (2, (GDestroyNotify) g_date_time_unref);
100 g_ptr_array_add (date_range, g_date_time_ref (date));
101 g_ptr_array_add (date_range, g_date_time_ref (date));
102 update_date_label (popover, date_range);
103 g_signal_emit_by_name (popover, "date-range", date_range);
104
105 g_ptr_array_unref (date_range);
106 g_date_time_unref (date);
107 }
108
109 /* Range on dates are partially implemented. For now just use it for differentation
110 * between a exact day or a range of a first day until now.
111 */
112 static void
setup_date(NautilusSearchPopover * popover,NautilusQuery * query)113 setup_date (NautilusSearchPopover *popover,
114 NautilusQuery *query)
115 {
116 GPtrArray *date_range;
117 GDateTime *date_initial;
118
119 date_range = nautilus_query_get_date_range (query);
120
121 if (date_range)
122 {
123 date_initial = g_ptr_array_index (date_range, 0);
124
125 g_signal_handlers_block_by_func (popover->calendar, calendar_day_selected, popover);
126
127 gtk_calendar_select_month (GTK_CALENDAR (popover->calendar),
128 g_date_time_get_month (date_initial) - 1,
129 g_date_time_get_year (date_initial));
130
131 gtk_calendar_select_day (GTK_CALENDAR (popover->calendar),
132 g_date_time_get_day_of_month (date_initial));
133
134 update_date_label (popover, date_range);
135
136 g_signal_handlers_unblock_by_func (popover->calendar, calendar_day_selected, popover);
137 }
138 }
139
140 static void
query_date_changed(GObject * object,GParamSpec * pspec,NautilusSearchPopover * popover)141 query_date_changed (GObject *object,
142 GParamSpec *pspec,
143 NautilusSearchPopover *popover)
144 {
145 setup_date (popover, NAUTILUS_QUERY (object));
146 }
147
148 static void
clear_date_button_clicked(GtkButton * button,NautilusSearchPopover * popover)149 clear_date_button_clicked (GtkButton *button,
150 NautilusSearchPopover *popover)
151 {
152 nautilus_search_popover_reset_date_range (popover);
153 }
154
155 static void
date_entry_activate(GtkEntry * entry,NautilusSearchPopover * popover)156 date_entry_activate (GtkEntry *entry,
157 NautilusSearchPopover *popover)
158 {
159 if (gtk_entry_get_text_length (entry) > 0)
160 {
161 GDateTime *now;
162 GDateTime *date_time;
163 GDate *date;
164
165 date = g_date_new ();
166 g_date_set_parse (date, gtk_entry_get_text (entry));
167
168 /* Invalid date silently does nothing */
169 if (!g_date_valid (date))
170 {
171 g_date_free (date);
172 return;
173 }
174
175 now = g_date_time_new_now_local ();
176 date_time = g_date_time_new_local (g_date_get_year (date),
177 g_date_get_month (date),
178 g_date_get_day (date),
179 0,
180 0,
181 0);
182
183 /* Future dates also silently fails */
184 if (g_date_time_compare (date_time, now) != 1)
185 {
186 GPtrArray *date_range;
187
188 date_range = g_ptr_array_new_full (2, (GDestroyNotify) g_date_time_unref);
189 g_ptr_array_add (date_range, g_date_time_ref (date_time));
190 g_ptr_array_add (date_range, g_date_time_ref (date_time));
191 update_date_label (popover, date_range);
192 show_date_selection_widgets (popover, FALSE);
193 g_signal_emit_by_name (popover, "date-range", date_range);
194
195 g_ptr_array_unref (date_range);
196 }
197
198 g_date_time_unref (now);
199 g_date_time_unref (date_time);
200 g_date_free (date);
201 }
202 }
203
204 static void
dates_listbox_row_activated(GtkListBox * listbox,GtkListBoxRow * row,NautilusSearchPopover * popover)205 dates_listbox_row_activated (GtkListBox *listbox,
206 GtkListBoxRow *row,
207 NautilusSearchPopover *popover)
208 {
209 GDateTime *date;
210 GDateTime *now;
211 GPtrArray *date_range = NULL;
212
213 now = g_date_time_new_now_local ();
214 date = g_object_get_data (G_OBJECT (row), "date");
215 if (date)
216 {
217 date_range = g_ptr_array_new_full (2, (GDestroyNotify) g_date_time_unref);
218 g_ptr_array_add (date_range, g_date_time_ref (date));
219 g_ptr_array_add (date_range, g_date_time_ref (now));
220 }
221 update_date_label (popover, date_range);
222 show_date_selection_widgets (popover, FALSE);
223 g_signal_emit_by_name (popover, "date-range", date_range);
224
225 if (date_range)
226 {
227 g_ptr_array_unref (date_range);
228 }
229 g_date_time_unref (now);
230 }
231
232 static void
listbox_header_func(GtkListBoxRow * row,GtkListBoxRow * before,NautilusSearchPopover * popover)233 listbox_header_func (GtkListBoxRow *row,
234 GtkListBoxRow *before,
235 NautilusSearchPopover *popover)
236 {
237 gboolean show_separator;
238
239 show_separator = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row), "show-separator"));
240
241 if (show_separator)
242 {
243 GtkWidget *separator;
244
245 separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
246 gtk_widget_show (separator);
247
248 gtk_list_box_row_set_header (row, separator);
249 }
250 }
251
252 static void
select_date_button_clicked(GtkButton * button,NautilusSearchPopover * popover)253 select_date_button_clicked (GtkButton *button,
254 NautilusSearchPopover *popover)
255 {
256 /* Hide the type selection widgets when date selection
257 * widgets are shown.
258 */
259 gtk_stack_set_visible_child_name (GTK_STACK (popover->type_stack), "type-button");
260
261 show_date_selection_widgets (popover, TRUE);
262 }
263
264 static void
select_type_button_clicked(GtkButton * button,NautilusSearchPopover * popover)265 select_type_button_clicked (GtkButton *button,
266 NautilusSearchPopover *popover)
267 {
268 GtkListBoxRow *selected_row;
269
270 selected_row = gtk_list_box_get_selected_row (GTK_LIST_BOX (popover->type_listbox));
271
272 gtk_stack_set_visible_child_name (GTK_STACK (popover->type_stack), "type-list");
273 if (selected_row != NULL)
274 {
275 gtk_widget_grab_focus (GTK_WIDGET (selected_row));
276 }
277
278 /* Hide the date selection widgets when the type selection
279 * listbox is shown.
280 */
281 show_date_selection_widgets (popover, FALSE);
282 }
283
284 static void
toggle_calendar_icon_clicked(GtkEntry * entry,GtkEntryIconPosition position,GdkEvent * event,NautilusSearchPopover * popover)285 toggle_calendar_icon_clicked (GtkEntry *entry,
286 GtkEntryIconPosition position,
287 GdkEvent *event,
288 NautilusSearchPopover *popover)
289 {
290 const gchar *current_visible_child;
291 const gchar *child;
292 const gchar *icon_name;
293 const gchar *tooltip;
294
295 current_visible_child = gtk_stack_get_visible_child_name (GTK_STACK (popover->around_stack));
296
297 if (g_strcmp0 (current_visible_child, "date-list") == 0)
298 {
299 child = "date-calendar";
300 icon_name = "view-list-symbolic";
301 tooltip = _("Show a list to select the date");
302 }
303 else
304 {
305 child = "date-list";
306 icon_name = "x-office-calendar-symbolic";
307 tooltip = _("Show a calendar to select the date");
308 }
309
310 gtk_stack_set_visible_child_name (GTK_STACK (popover->around_stack), child);
311 gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, icon_name);
312 gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_SECONDARY, tooltip);
313 }
314
315 static void
types_listbox_row_activated(GtkListBox * listbox,GtkListBoxRow * row,NautilusSearchPopover * popover)316 types_listbox_row_activated (GtkListBox *listbox,
317 GtkListBoxRow *row,
318 NautilusSearchPopover *popover)
319 {
320 gint group;
321
322 group = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (row), "mimetype-group"));
323
324 /* The -1 group stands for the "Other Types" group, for which
325 * we should show the mimetype dialog.
326 */
327 if (group == -1)
328 {
329 show_other_types_dialog (popover);
330 }
331 else
332 {
333 gtk_label_set_label (GTK_LABEL (popover->type_label),
334 nautilus_mime_types_group_get_name (group));
335
336 g_signal_emit_by_name (popover, "mime-type", group, NULL);
337 }
338
339 gtk_stack_set_visible_child_name (GTK_STACK (popover->type_stack), "type-button");
340 }
341
342 static void
search_time_type_changed(GtkToggleButton * button,NautilusSearchPopover * popover)343 search_time_type_changed (GtkToggleButton *button,
344 NautilusSearchPopover *popover)
345 {
346 NautilusQuerySearchType type = -1;
347
348 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (popover->last_modified_button)))
349 {
350 type = NAUTILUS_QUERY_SEARCH_TYPE_LAST_MODIFIED;
351 }
352 else
353 {
354 type = NAUTILUS_QUERY_SEARCH_TYPE_LAST_ACCESS;
355 }
356
357 g_settings_set_enum (nautilus_preferences, "search-filter-time-type", type);
358
359 g_signal_emit_by_name (popover, "time-type", type, NULL);
360 }
361
362 static void
search_fts_mode_changed(GtkToggleButton * button,NautilusSearchPopover * popover)363 search_fts_mode_changed (GtkToggleButton *button,
364 NautilusSearchPopover *popover)
365 {
366 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (popover->full_text_search_button)) &&
367 popover->fts_enabled == FALSE)
368 {
369 popover->fts_enabled = TRUE;
370 g_settings_set_boolean (nautilus_preferences, NAUTILUS_PREFERENCES_FTS_ENABLED, TRUE);
371 g_object_notify (G_OBJECT (popover), "fts-enabled");
372 }
373 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (popover->filename_search_button)) &&
374 popover->fts_enabled == TRUE)
375 {
376 popover->fts_enabled = FALSE;
377 g_settings_set_boolean (nautilus_preferences, NAUTILUS_PREFERENCES_FTS_ENABLED, FALSE);
378 g_object_notify (G_OBJECT (popover), "fts-enabled");
379 }
380 }
381
382 /* Auxiliary methods */
383
384 static GtkWidget *
create_row_for_label(const gchar * text,gboolean show_separator)385 create_row_for_label (const gchar *text,
386 gboolean show_separator)
387 {
388 GtkWidget *row;
389 GtkWidget *label;
390
391 row = gtk_list_box_row_new ();
392
393 g_object_set_data (G_OBJECT (row), "show-separator", GINT_TO_POINTER (show_separator));
394
395 label = g_object_new (GTK_TYPE_LABEL,
396 "label", text,
397 "hexpand", TRUE,
398 "xalign", 0.0,
399 "margin-start", 6,
400 NULL);
401
402 gtk_container_add (GTK_CONTAINER (row), label);
403 gtk_widget_show_all (row);
404
405 return row;
406 }
407
408 static void
fill_fuzzy_dates_listbox(NautilusSearchPopover * popover)409 fill_fuzzy_dates_listbox (NautilusSearchPopover *popover)
410 {
411 GDateTime *maximum_dt, *now;
412 GtkWidget *row;
413 GDateTime *current_date;
414 GPtrArray *date_range;
415 gint days, max_days;
416
417 days = 1;
418 maximum_dt = g_date_time_new_from_unix_local (0);
419 now = g_date_time_new_now_local ();
420 max_days = SEARCH_FILTER_MAX_YEARS * 365;
421
422 /* Add the no date filter element first */
423 row = create_row_for_label (_("Any time"), TRUE);
424 gtk_container_add (GTK_CONTAINER (popover->dates_listbox), row);
425
426 /* This is a tricky loop. The main intention here is that each
427 * timeslice (day, week, month) have 2 or 3 entries.
428 *
429 * For the first appearance of each timeslice, there is made a
430 * check in order to be sure that there is no offset added to days.
431 */
432 while (days <= max_days)
433 {
434 gchar *label;
435 gint normalized;
436 gint step;
437
438 if (days < 7)
439 {
440 /* days */
441 normalized = days;
442 step = 2;
443 }
444 else if (days < 30)
445 {
446 /* weeks */
447 normalized = days / 7;
448 if (normalized == 1)
449 {
450 days = 7;
451 }
452 step = 7;
453 }
454 else if (days < 365)
455 {
456 /* months */
457 normalized = days / 30;
458 if (normalized == 1)
459 {
460 days = 30;
461 }
462 step = 90;
463 }
464 else
465 {
466 /* years */
467 normalized = days / 365;
468 if (normalized == 1)
469 {
470 days = 365;
471 }
472 step = 365;
473 }
474
475 current_date = g_date_time_add_days (now, -days);
476 date_range = g_ptr_array_new_full (2, (GDestroyNotify) g_date_time_unref);
477 g_ptr_array_add (date_range, g_date_time_ref (current_date));
478 g_ptr_array_add (date_range, g_date_time_ref (now));
479 label = get_text_for_date_range (date_range, FALSE);
480 row = create_row_for_label (label, normalized == 1);
481 g_object_set_data_full (G_OBJECT (row),
482 "date",
483 g_date_time_ref (current_date),
484 (GDestroyNotify) g_date_time_unref);
485
486 gtk_container_add (GTK_CONTAINER (popover->dates_listbox), row);
487
488 g_free (label);
489 g_date_time_unref (current_date);
490 g_ptr_array_unref (date_range);
491
492 days += step;
493 }
494
495 g_date_time_unref (maximum_dt);
496 g_date_time_unref (now);
497 }
498
499 static void
fill_types_listbox(NautilusSearchPopover * popover)500 fill_types_listbox (NautilusSearchPopover *popover)
501 {
502 GtkWidget *row;
503 int i;
504 gint n_groups;
505
506 n_groups = nautilus_mime_types_get_number_of_groups ();
507 /* Mimetypes */
508 for (i = 0; i < n_groups; i++)
509 {
510 /* On the third row, which is right below "Folders", there should be an
511 * separator to logically group the types.
512 */
513 row = create_row_for_label (nautilus_mime_types_group_get_name (i), i == 3);
514 g_object_set_data (G_OBJECT (row), "mimetype-group", GINT_TO_POINTER (i));
515
516 gtk_container_add (GTK_CONTAINER (popover->type_listbox), row);
517 }
518
519 /* Other types */
520 row = create_row_for_label (_("Other Type…"), TRUE);
521 g_object_set_data (G_OBJECT (row), "mimetype-group", GINT_TO_POINTER (-1));
522 gtk_container_add (GTK_CONTAINER (popover->type_listbox), row);
523 }
524
525 static void
show_date_selection_widgets(NautilusSearchPopover * popover,gboolean visible)526 show_date_selection_widgets (NautilusSearchPopover *popover,
527 gboolean visible)
528 {
529 gtk_stack_set_visible_child_name (GTK_STACK (popover->date_stack),
530 visible ? "date-entry" : "date-button");
531 gtk_stack_set_visible_child_name (GTK_STACK (popover->around_stack), "date-list");
532 gtk_entry_set_icon_from_icon_name (GTK_ENTRY (popover->date_entry),
533 GTK_ENTRY_ICON_SECONDARY,
534 "x-office-calendar-symbolic");
535
536 gtk_widget_set_visible (popover->around_revealer, visible);
537
538 gtk_revealer_set_reveal_child (GTK_REVEALER (popover->around_revealer), visible);
539 }
540
541 static void
show_other_types_dialog(NautilusSearchPopover * popover)542 show_other_types_dialog (NautilusSearchPopover *popover)
543 {
544 GList *mime_infos, *l;
545 GtkWidget *dialog;
546 GtkWidget *scrolled, *treeview;
547 GtkListStore *store;
548 GtkTreeViewColumn *column;
549 GtkCellRenderer *renderer;
550 GtkWidget *toplevel;
551 GtkTreeSelection *selection;
552
553 mime_infos = g_content_types_get_registered ();
554
555 store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
556 for (l = mime_infos; l != NULL; l = l->next)
557 {
558 GtkTreeIter iter;
559 char *mime_type = l->data;
560 char *description;
561
562 description = g_content_type_get_description (mime_type);
563 if (description == NULL)
564 {
565 description = g_strdup (mime_type);
566 }
567
568 gtk_list_store_append (store, &iter);
569 gtk_list_store_set (store, &iter,
570 0, description,
571 1, mime_type,
572 -1);
573
574 g_free (mime_type);
575 g_free (description);
576 }
577 g_list_free (mime_infos);
578
579 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (popover));
580 dialog = gtk_dialog_new_with_buttons (_("Select type"),
581 GTK_WINDOW (toplevel),
582 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR,
583 _("_Cancel"), GTK_RESPONSE_CANCEL,
584 _("Select"), GTK_RESPONSE_OK,
585 NULL);
586 gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 600);
587
588 scrolled = gtk_scrolled_window_new (NULL, NULL);
589 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
590 GTK_POLICY_AUTOMATIC,
591 GTK_POLICY_AUTOMATIC);
592
593 gtk_widget_show (scrolled);
594 gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 0);
595 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), scrolled, TRUE, TRUE, 0);
596
597 treeview = gtk_tree_view_new ();
598 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store));
599 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), 0, GTK_SORT_ASCENDING);
600
601 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
602 gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
603
604
605 renderer = gtk_cell_renderer_text_new ();
606 column = gtk_tree_view_column_new_with_attributes ("Name",
607 renderer,
608 "text",
609 0,
610 NULL);
611 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
612 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
613
614 gtk_widget_show (treeview);
615 gtk_container_add (GTK_CONTAINER (scrolled), treeview);
616
617 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
618 {
619 GtkTreeIter iter;
620 char *mimetype;
621 char *description;
622
623 gtk_tree_selection_get_selected (selection, NULL, &iter);
624 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
625 0, &description,
626 1, &mimetype,
627 -1);
628
629 gtk_label_set_label (GTK_LABEL (popover->type_label), description);
630
631 g_signal_emit_by_name (popover, "mime-type", -1, mimetype);
632
633 gtk_stack_set_visible_child_name (GTK_STACK (popover->type_stack), "type-button");
634 }
635
636 gtk_widget_destroy (dialog);
637 }
638
639 static void
update_date_label(NautilusSearchPopover * popover,GPtrArray * date_range)640 update_date_label (NautilusSearchPopover *popover,
641 GPtrArray *date_range)
642 {
643 if (date_range)
644 {
645 gint days;
646 GDateTime *initial_date;
647 GDateTime *end_date;
648 GDateTime *now;
649 gchar *label;
650
651 now = g_date_time_new_now_local ();
652 initial_date = g_ptr_array_index (date_range, 0);
653 end_date = g_ptr_array_index (date_range, 0);
654 days = g_date_time_difference (end_date, initial_date) / G_TIME_SPAN_DAY;
655
656 label = get_text_for_date_range (date_range, TRUE);
657
658 gtk_entry_set_text (GTK_ENTRY (popover->date_entry), days < 1 ? label : "");
659
660 gtk_widget_show (popover->clear_date_button);
661 gtk_label_set_label (GTK_LABEL (popover->select_date_button_label), label);
662
663 g_date_time_unref (now);
664 g_free (label);
665 }
666 else
667 {
668 gtk_label_set_label (GTK_LABEL (popover->select_date_button_label),
669 _("Select Dates…"));
670 gtk_entry_set_text (GTK_ENTRY (popover->date_entry), "");
671 gtk_widget_hide (popover->clear_date_button);
672 }
673 }
674
675 void
nautilus_search_popover_set_fts_sensitive(NautilusSearchPopover * popover,gboolean sensitive)676 nautilus_search_popover_set_fts_sensitive (NautilusSearchPopover *popover,
677 gboolean sensitive)
678 {
679 gtk_widget_set_sensitive (popover->full_text_search_button, sensitive);
680 gtk_widget_set_sensitive (popover->filename_search_button, sensitive);
681 }
682
683 static void
nautilus_search_popover_closed(GtkPopover * popover)684 nautilus_search_popover_closed (GtkPopover *popover)
685 {
686 NautilusSearchPopover *self = NAUTILUS_SEARCH_POPOVER (popover);
687 GDateTime *now;
688
689 /* Always switch back to the initial states */
690 gtk_stack_set_visible_child_name (GTK_STACK (self->type_stack), "type-button");
691 show_date_selection_widgets (self, FALSE);
692
693 /* If we're closing an ongoing query, the popover must not
694 * clear the current settings.
695 */
696 if (self->query)
697 {
698 return;
699 }
700
701 now = g_date_time_new_now_local ();
702
703 /* Reselect today at the calendar */
704 g_signal_handlers_block_by_func (self->calendar, calendar_day_selected, self);
705
706 gtk_calendar_select_month (GTK_CALENDAR (self->calendar),
707 g_date_time_get_month (now) - 1,
708 g_date_time_get_year (now));
709
710 gtk_calendar_select_day (GTK_CALENDAR (self->calendar),
711 g_date_time_get_day_of_month (now));
712
713 g_signal_handlers_unblock_by_func (self->calendar, calendar_day_selected, self);
714 }
715
716 static void
nautilus_search_popover_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)717 nautilus_search_popover_get_property (GObject *object,
718 guint prop_id,
719 GValue *value,
720 GParamSpec *pspec)
721 {
722 NautilusSearchPopover *self;
723
724 self = NAUTILUS_SEARCH_POPOVER (object);
725
726 switch (prop_id)
727 {
728 case PROP_QUERY:
729 {
730 g_value_set_object (value, self->query);
731 }
732 break;
733
734 case PROP_FTS_ENABLED:
735 {
736 g_value_set_boolean (value, self->fts_enabled);
737 }
738 break;
739
740 default:
741 {
742 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
743 }
744 }
745 }
746
747 static void
nautilus_search_popover_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)748 nautilus_search_popover_set_property (GObject *object,
749 guint prop_id,
750 const GValue *value,
751 GParamSpec *pspec)
752 {
753 NautilusSearchPopover *self;
754
755 self = NAUTILUS_SEARCH_POPOVER (object);
756
757 switch (prop_id)
758 {
759 case PROP_QUERY:
760 {
761 nautilus_search_popover_set_query (self, g_value_get_object (value));
762 }
763 break;
764
765 case PROP_FTS_ENABLED:
766 {
767 self->fts_enabled = g_value_get_boolean (value);
768 }
769 break;
770
771 default:
772 {
773 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
774 }
775 }
776 }
777
778
779 static void
nautilus_search_popover_class_init(NautilusSearchPopoverClass * klass)780 nautilus_search_popover_class_init (NautilusSearchPopoverClass *klass)
781 {
782 GtkPopoverClass *popover_class = GTK_POPOVER_CLASS (klass);
783 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
784 GObjectClass *object_class = G_OBJECT_CLASS (klass);
785
786 object_class->get_property = nautilus_search_popover_get_property;
787 object_class->set_property = nautilus_search_popover_set_property;
788
789 popover_class->closed = nautilus_search_popover_closed;
790
791 signals[DATE_RANGE] = g_signal_new ("date-range",
792 NAUTILUS_TYPE_SEARCH_POPOVER,
793 G_SIGNAL_RUN_LAST,
794 0,
795 NULL,
796 NULL,
797 g_cclosure_marshal_generic,
798 G_TYPE_NONE,
799 1,
800 G_TYPE_PTR_ARRAY);
801
802 signals[MIME_TYPE] = g_signal_new ("mime-type",
803 NAUTILUS_TYPE_SEARCH_POPOVER,
804 G_SIGNAL_RUN_LAST,
805 0,
806 NULL,
807 NULL,
808 g_cclosure_marshal_generic,
809 G_TYPE_NONE,
810 2,
811 G_TYPE_INT,
812 G_TYPE_STRING);
813
814 signals[TIME_TYPE] = g_signal_new ("time-type",
815 NAUTILUS_TYPE_SEARCH_POPOVER,
816 G_SIGNAL_RUN_LAST,
817 0,
818 NULL,
819 NULL,
820 g_cclosure_marshal_generic,
821 G_TYPE_NONE,
822 1,
823 G_TYPE_INT);
824
825 /**
826 * NautilusSearchPopover::query:
827 *
828 * The current #NautilusQuery being edited.
829 */
830 g_object_class_install_property (object_class,
831 PROP_QUERY,
832 g_param_spec_object ("query",
833 "Query of the popover",
834 "The current query being edited",
835 NAUTILUS_TYPE_QUERY,
836 G_PARAM_READWRITE));
837
838 g_object_class_install_property (object_class,
839 PROP_FTS_ENABLED,
840 g_param_spec_boolean ("fts-enabled",
841 "fts enabled",
842 "fts enabled",
843 FALSE,
844 G_PARAM_READWRITE));
845
846 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/ui/nautilus-search-popover.ui");
847
848 gtk_widget_class_bind_template_child (widget_class, NautilusSearchPopover, around_revealer);
849 gtk_widget_class_bind_template_child (widget_class, NautilusSearchPopover, around_stack);
850 gtk_widget_class_bind_template_child (widget_class, NautilusSearchPopover, clear_date_button);
851 gtk_widget_class_bind_template_child (widget_class, NautilusSearchPopover, calendar);
852 gtk_widget_class_bind_template_child (widget_class, NautilusSearchPopover, dates_listbox);
853 gtk_widget_class_bind_template_child (widget_class, NautilusSearchPopover, date_entry);
854 gtk_widget_class_bind_template_child (widget_class, NautilusSearchPopover, date_stack);
855 gtk_widget_class_bind_template_child (widget_class, NautilusSearchPopover, select_date_button);
856 gtk_widget_class_bind_template_child (widget_class, NautilusSearchPopover, select_date_button_label);
857 gtk_widget_class_bind_template_child (widget_class, NautilusSearchPopover, type_label);
858 gtk_widget_class_bind_template_child (widget_class, NautilusSearchPopover, type_listbox);
859 gtk_widget_class_bind_template_child (widget_class, NautilusSearchPopover, type_stack);
860 gtk_widget_class_bind_template_child (widget_class, NautilusSearchPopover, last_used_button);
861 gtk_widget_class_bind_template_child (widget_class, NautilusSearchPopover, last_modified_button);
862 gtk_widget_class_bind_template_child (widget_class, NautilusSearchPopover, full_text_search_button);
863 gtk_widget_class_bind_template_child (widget_class, NautilusSearchPopover, filename_search_button);
864
865 gtk_widget_class_bind_template_callback (widget_class, calendar_day_selected);
866 gtk_widget_class_bind_template_callback (widget_class, clear_date_button_clicked);
867 gtk_widget_class_bind_template_callback (widget_class, date_entry_activate);
868 gtk_widget_class_bind_template_callback (widget_class, dates_listbox_row_activated);
869 gtk_widget_class_bind_template_callback (widget_class, select_date_button_clicked);
870 gtk_widget_class_bind_template_callback (widget_class, select_type_button_clicked);
871 gtk_widget_class_bind_template_callback (widget_class, toggle_calendar_icon_clicked);
872 gtk_widget_class_bind_template_callback (widget_class, types_listbox_row_activated);
873 gtk_widget_class_bind_template_callback (widget_class, search_time_type_changed);
874 gtk_widget_class_bind_template_callback (widget_class, search_fts_mode_changed);
875 }
876
877 static void
nautilus_search_popover_init(NautilusSearchPopover * self)878 nautilus_search_popover_init (NautilusSearchPopover *self)
879 {
880 NautilusQuerySearchType filter_time_type;
881
882 gtk_widget_init_template (GTK_WIDGET (self));
883
884 /* Fuzzy dates listbox */
885 gtk_list_box_set_header_func (GTK_LIST_BOX (self->dates_listbox),
886 (GtkListBoxUpdateHeaderFunc) listbox_header_func,
887 self,
888 NULL);
889
890 fill_fuzzy_dates_listbox (self);
891
892 /* Types listbox */
893 gtk_list_box_set_header_func (GTK_LIST_BOX (self->type_listbox),
894 (GtkListBoxUpdateHeaderFunc) listbox_header_func,
895 self,
896 NULL);
897
898 fill_types_listbox (self);
899
900 gtk_list_box_select_row (GTK_LIST_BOX (self->type_listbox),
901 gtk_list_box_get_row_at_index (GTK_LIST_BOX (self->type_listbox), 0));
902
903 filter_time_type = g_settings_get_enum (nautilus_preferences, "search-filter-time-type");
904 if (filter_time_type == NAUTILUS_QUERY_SEARCH_TYPE_LAST_MODIFIED)
905 {
906 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->last_modified_button), TRUE);
907 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->last_used_button), FALSE);
908 }
909 else
910 {
911 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->last_modified_button), FALSE);
912 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->last_used_button), TRUE);
913 }
914
915 self->fts_enabled = g_settings_get_boolean (nautilus_preferences,
916 NAUTILUS_PREFERENCES_FTS_ENABLED);
917 if (self->fts_enabled)
918 {
919 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->full_text_search_button), TRUE);
920 }
921 else
922 {
923 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->filename_search_button), TRUE);
924 }
925 }
926
927 GtkWidget *
nautilus_search_popover_new(void)928 nautilus_search_popover_new (void)
929 {
930 return g_object_new (NAUTILUS_TYPE_SEARCH_POPOVER, NULL);
931 }
932
933 /**
934 * nautilus_search_popover_get_query:
935 * @popover: a #NautilusSearchPopover
936 *
937 * Gets the current query for @popover.
938 *
939 * Returns: (transfer none): the current #NautilusQuery from @popover.
940 */
941 NautilusQuery *
nautilus_search_popover_get_query(NautilusSearchPopover * popover)942 nautilus_search_popover_get_query (NautilusSearchPopover *popover)
943 {
944 g_return_val_if_fail (NAUTILUS_IS_SEARCH_POPOVER (popover), NULL);
945
946 return popover->query;
947 }
948
949 /**
950 * nautilus_search_popover_set_query:
951 * @popover: a #NautilusSearchPopover
952 * @query (nullable): a #NautilusQuery
953 *
954 * Sets the current query for @popover.
955 *
956 * Returns:
957 */
958 void
nautilus_search_popover_set_query(NautilusSearchPopover * popover,NautilusQuery * query)959 nautilus_search_popover_set_query (NautilusSearchPopover *popover,
960 NautilusQuery *query)
961 {
962 NautilusQuery *previous_query;
963
964 g_return_if_fail (NAUTILUS_IS_SEARCH_POPOVER (popover));
965
966 previous_query = popover->query;
967
968 if (popover->query != query)
969 {
970 /* Disconnect signals and bindings from the old query */
971 if (previous_query)
972 {
973 g_signal_handlers_disconnect_by_func (previous_query, query_date_changed, popover);
974 }
975
976 g_set_object (&popover->query, query);
977
978 if (query)
979 {
980 /* Date */
981 setup_date (popover, query);
982
983 g_signal_connect (query,
984 "notify::date",
985 G_CALLBACK (query_date_changed),
986 popover);
987 }
988 else
989 {
990 nautilus_search_popover_reset_mime_types (popover);
991 nautilus_search_popover_reset_date_range (popover);
992 }
993 }
994 }
995
996 void
nautilus_search_popover_reset_mime_types(NautilusSearchPopover * popover)997 nautilus_search_popover_reset_mime_types (NautilusSearchPopover *popover)
998 {
999 g_return_if_fail (NAUTILUS_IS_SEARCH_POPOVER (popover));
1000
1001 gtk_list_box_select_row (GTK_LIST_BOX (popover->type_listbox),
1002 gtk_list_box_get_row_at_index (GTK_LIST_BOX (popover->type_listbox), 0));
1003
1004 gtk_label_set_label (GTK_LABEL (popover->type_label),
1005 nautilus_mime_types_group_get_name (0));
1006 g_signal_emit_by_name (popover, "mime-type", 0, NULL);
1007 gtk_stack_set_visible_child_name (GTK_STACK (popover->type_stack), "type-button");
1008 }
1009
1010 void
nautilus_search_popover_reset_date_range(NautilusSearchPopover * popover)1011 nautilus_search_popover_reset_date_range (NautilusSearchPopover *popover)
1012 {
1013 g_return_if_fail (NAUTILUS_IS_SEARCH_POPOVER (popover));
1014
1015 gtk_list_box_select_row (GTK_LIST_BOX (popover->dates_listbox),
1016 gtk_list_box_get_row_at_index (GTK_LIST_BOX (popover->dates_listbox), 0));
1017
1018 update_date_label (popover, NULL);
1019 show_date_selection_widgets (popover, FALSE);
1020 g_signal_emit_by_name (popover, "date-range", NULL);
1021 }
1022
1023 gboolean
nautilus_search_popover_get_fts_enabled(NautilusSearchPopover * popover)1024 nautilus_search_popover_get_fts_enabled (NautilusSearchPopover *popover)
1025 {
1026 return popover->fts_enabled;
1027 }
1028