No issues found
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3 * Copyright (C) 2005 Red Hat, Inc.
4 *
5 * Nautilus is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * Nautilus 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 GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public
16 * License along with this program; see the file COPYING. If not,
17 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 *
20 * Author: Alexander Larsson <alexl@redhat.com>
21 *
22 */
23
24 #include <config.h>
25 #include "nautilus-query-editor.h"
26
27 #include <string.h>
28 #include <glib/gi18n.h>
29 #include <gio/gio.h>
30 #include <gdk/gdkkeysyms.h>
31 #include <gtk/gtk.h>
32
33 #include <eel/eel-glib-extensions.h>
34 #include <libnautilus-private/nautilus-file-utilities.h>
35
36 typedef enum {
37 NAUTILUS_QUERY_EDITOR_ROW_TYPE,
38
39 NAUTILUS_QUERY_EDITOR_ROW_LAST
40 } NautilusQueryEditorRowType;
41
42 typedef struct {
43 NautilusQueryEditorRowType type;
44 NautilusQueryEditor *editor;
45 GtkWidget *toolbar;
46 GtkWidget *hbox;
47 GtkWidget *combo;
48
49 GtkWidget *type_widget;
50 } NautilusQueryEditorRow;
51
52
53 typedef struct {
54 const char *name;
55 GtkWidget * (*create_widgets) (NautilusQueryEditorRow *row);
56 void (*add_to_query) (NautilusQueryEditorRow *row,
57 NautilusQuery *query);
58 void (*add_rows_from_query) (NautilusQueryEditor *editor,
59 NautilusQuery *query);
60 } NautilusQueryEditorRowOps;
61
62 struct NautilusQueryEditorDetails {
63 GtkWidget *entry;
64 gboolean change_frozen;
65 guint typing_timeout_id;
66
67 GtkWidget *search_current_button;
68 GtkWidget *search_all_button;
69 char *current_uri;
70
71 GList *rows;
72 gboolean got_preedit;
73
74 NautilusQuery *query;
75 };
76
77 enum {
78 ACTIVATED,
79 CHANGED,
80 CANCEL,
81 LAST_SIGNAL
82 };
83
84 static guint signals[LAST_SIGNAL];
85
86 static void entry_activate_cb (GtkWidget *entry, NautilusQueryEditor *editor);
87 static void entry_changed_cb (GtkWidget *entry, NautilusQueryEditor *editor);
88 static void nautilus_query_editor_changed_force (NautilusQueryEditor *editor,
89 gboolean force);
90 static void nautilus_query_editor_changed (NautilusQueryEditor *editor);
91 static NautilusQueryEditorRow * nautilus_query_editor_add_row (NautilusQueryEditor *editor,
92 NautilusQueryEditorRowType type);
93
94 static GtkWidget *type_row_create_widgets (NautilusQueryEditorRow *row);
95 static void type_row_add_to_query (NautilusQueryEditorRow *row,
96 NautilusQuery *query);
97 static void type_add_rows_from_query (NautilusQueryEditor *editor,
98 NautilusQuery *query);
99
100
101
102 static NautilusQueryEditorRowOps row_type[] = {
103 { N_("File Type"),
104 type_row_create_widgets,
105 type_row_add_to_query,
106 type_add_rows_from_query
107 },
108 };
109
110 G_DEFINE_TYPE (NautilusQueryEditor, nautilus_query_editor, GTK_TYPE_BOX);
111
112 /* taken from gtk/gtktreeview.c */
113 static void
114 send_focus_change (GtkWidget *widget,
115 GdkDevice *device,
116 gboolean in)
117 {
118 GdkDeviceManager *device_manager;
119 GList *devices, *d;
120
121 device_manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
122 devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
123 devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_SLAVE));
124 devices = g_list_concat (devices, gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_FLOATING));
125
126 for (d = devices; d; d = d->next) {
127 GdkDevice *dev = d->data;
128 GdkEvent *fevent;
129 GdkWindow *window;
130
131 if (gdk_device_get_source (dev) != GDK_SOURCE_KEYBOARD)
132 continue;
133
134 window = gtk_widget_get_window (widget);
135
136 /* Skip non-master keyboards that haven't
137 * selected for events from this window
138 */
139 if (gdk_device_get_device_type (dev) != GDK_DEVICE_TYPE_MASTER &&
140 !gdk_window_get_device_events (window, dev))
141 continue;
142
143 fevent = gdk_event_new (GDK_FOCUS_CHANGE);
144
145 fevent->focus_change.type = GDK_FOCUS_CHANGE;
146 fevent->focus_change.window = g_object_ref (window);
147 fevent->focus_change.in = in;
148 gdk_event_set_device (fevent, device);
149
150 gtk_widget_send_focus_change (widget, fevent);
151
152 gdk_event_free (fevent);
153 }
154
155 g_list_free (devices);
156 }
157
158 static void
159 entry_focus_hack (GtkWidget *entry,
160 GdkDevice *device)
161 {
162 GtkEntryClass *entry_class;
163 GtkWidgetClass *entry_parent_class;
164
165 /* Grab focus will select all the text. We don't want that to happen, so we
166 * call the parent instance and bypass the selection change. This is probably
167 * really non-kosher. */
168 entry_class = g_type_class_peek (GTK_TYPE_ENTRY);
169 entry_parent_class = g_type_class_peek_parent (entry_class);
170 (entry_parent_class->grab_focus) (entry);
171
172 /* send focus-in event */
173 send_focus_change (entry, device, TRUE);
174 }
175
176 static void
177 entry_preedit_changed_cb (GtkEntry *entry,
178 gchar *preedit,
179 NautilusQueryEditor *editor)
180 {
181 editor->details->got_preedit = TRUE;
182 }
183
184 gboolean
185 nautilus_query_editor_handle_event (NautilusQueryEditor *editor,
186 GdkEventKey *event)
187 {
188 GdkEvent *new_event;
189 gboolean handled = FALSE;
190 gulong id;
191 gboolean retval;
192 gboolean text_changed;
193 char *old_text;
194 const char *new_text;
195
196 /* if we're focused already, no need to handle the event manually */
197 if (gtk_widget_has_focus (editor->details->entry)) {
198 return FALSE;
199 }
200
201 /* never handle these events */
202 if (event->keyval == GDK_KEY_slash || event->keyval == GDK_KEY_Delete) {
203 return FALSE;
204 }
205
206 /* don't activate search for these events */
207 if (!gtk_widget_get_visible (GTK_WIDGET (editor)) && event->keyval == GDK_KEY_space) {
208 return FALSE;
209 }
210
211 editor->details->got_preedit = FALSE;
212 if (!gtk_widget_get_realized (editor->details->entry)) {
213 gtk_widget_realize (editor->details->entry);
214 }
215
216 old_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (editor->details->entry)));
217
218 id = g_signal_connect (editor->details->entry, "preedit-changed",
219 G_CALLBACK (entry_preedit_changed_cb), editor);
220
221 new_event = gdk_event_copy ((GdkEvent *) event);
222 g_object_unref (((GdkEventKey *) new_event)->window);
223 ((GdkEventKey *) new_event)->window = g_object_ref
224 (gtk_widget_get_window (editor->details->entry));
225 retval = gtk_widget_event (editor->details->entry, new_event);
226 gdk_event_free (new_event);
227
228 g_signal_handler_disconnect (editor->details->entry, id);
229
230 new_text = gtk_entry_get_text (GTK_ENTRY (editor->details->entry));
231 text_changed = strcmp (old_text, new_text) != 0;
232 g_free (old_text);
233
234 handled = (editor->details->got_preedit) || (retval && text_changed);
235 editor->details->got_preedit = FALSE;
236
237 return handled;
238 }
239
240 static void
241 row_destroy (NautilusQueryEditorRow *row)
242 {
243 gtk_widget_destroy (row->toolbar);
244 g_free (row);
245 }
246
247 static void
248 nautilus_query_editor_dispose (GObject *object)
249 {
250 NautilusQueryEditor *editor;
251
252 editor = NAUTILUS_QUERY_EDITOR (object);
253
254 if (editor->details->typing_timeout_id > 0) {
255 g_source_remove (editor->details->typing_timeout_id);
256 editor->details->typing_timeout_id = 0;
257 }
258
259 g_clear_object (&editor->details->query);
260
261 g_list_free_full (editor->details->rows, (GDestroyNotify) row_destroy);
262 editor->details->rows = NULL;
263
264 G_OBJECT_CLASS (nautilus_query_editor_parent_class)->dispose (object);
265 }
266
267 static void
268 nautilus_query_editor_grab_focus (GtkWidget *widget)
269 {
270 NautilusQueryEditor *editor = NAUTILUS_QUERY_EDITOR (widget);
271
272 if (gtk_widget_get_visible (widget)) {
273 entry_focus_hack (editor->details->entry, gtk_get_current_event_device ());
274 }
275 }
276
277 static void
278 nautilus_query_editor_class_init (NautilusQueryEditorClass *class)
279 {
280 GObjectClass *gobject_class;
281 GtkWidgetClass *widget_class;
282 GtkBindingSet *binding_set;
283
284 gobject_class = G_OBJECT_CLASS (class);
285 gobject_class->dispose = nautilus_query_editor_dispose;
286
287 widget_class = GTK_WIDGET_CLASS (class);
288 widget_class->grab_focus = nautilus_query_editor_grab_focus;
289
290 signals[CHANGED] =
291 g_signal_new ("changed",
292 G_TYPE_FROM_CLASS (class),
293 G_SIGNAL_RUN_LAST,
294 G_STRUCT_OFFSET (NautilusQueryEditorClass, changed),
295 NULL, NULL,
296 g_cclosure_marshal_generic,
297 G_TYPE_NONE, 2, NAUTILUS_TYPE_QUERY, G_TYPE_BOOLEAN);
298
299 signals[CANCEL] =
300 g_signal_new ("cancel",
301 G_TYPE_FROM_CLASS (class),
302 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
303 G_STRUCT_OFFSET (NautilusQueryEditorClass, cancel),
304 NULL, NULL,
305 g_cclosure_marshal_VOID__VOID,
306 G_TYPE_NONE, 0);
307
308 signals[ACTIVATED] =
309 g_signal_new ("activated",
310 G_TYPE_FROM_CLASS (class),
311 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
312 G_STRUCT_OFFSET (NautilusQueryEditorClass, activated),
313 NULL, NULL,
314 g_cclosure_marshal_VOID__VOID,
315 G_TYPE_NONE, 0);
316
317 binding_set = gtk_binding_set_by_class (class);
318 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "cancel", 0);
319
320 g_type_class_add_private (class, sizeof (NautilusQueryEditorDetails));
321 }
322
323 GFile *
324 nautilus_query_editor_get_location (NautilusQueryEditor *editor)
325 {
326 GFile *file = NULL;
327 if (editor->details->current_uri != NULL)
328 file = g_file_new_for_uri (editor->details->current_uri);
329 return file;
330 }
331
332 static void
333 entry_activate_cb (GtkWidget *entry, NautilusQueryEditor *editor)
334 {
335 g_signal_emit (editor, signals[ACTIVATED], 0);
336 }
337
338 static gboolean
339 typing_timeout_cb (gpointer user_data)
340 {
341 NautilusQueryEditor *editor;
342
343 editor = NAUTILUS_QUERY_EDITOR (user_data);
344 editor->details->typing_timeout_id = 0;
345
346 nautilus_query_editor_changed (editor);
347
348 return FALSE;
349 }
350
351 #define TYPING_TIMEOUT 250
352
353 static void
354 entry_changed_cb (GtkWidget *entry, NautilusQueryEditor *editor)
355 {
356 if (editor->details->change_frozen) {
357 return;
358 }
359
360 if (editor->details->typing_timeout_id > 0) {
361 g_source_remove (editor->details->typing_timeout_id);
362 }
363
364 editor->details->typing_timeout_id =
365 g_timeout_add (TYPING_TIMEOUT,
366 typing_timeout_cb,
367 editor);
368 }
369
370 /* Type */
371
372 static gboolean
373 type_separator_func (GtkTreeModel *model,
374 GtkTreeIter *iter,
375 gpointer data)
376 {
377 char *text;
378 gboolean res;
379
380 gtk_tree_model_get (model, iter, 0, &text, -1);
381
382 res = text != NULL && strcmp (text, "---") == 0;
383
384 g_free (text);
385 return res;
386 }
387
388 struct {
389 char *name;
390 char *mimetypes[20];
391 } mime_type_groups[] = {
392 { N_("Documents"),
393 { "application/rtf",
394 "application/msword",
395 "application/vnd.sun.xml.writer",
396 "application/vnd.sun.xml.writer.global",
397 "application/vnd.sun.xml.writer.template",
398 "application/vnd.oasis.opendocument.text",
399 "application/vnd.oasis.opendocument.text-template",
400 "application/x-abiword",
401 "application/x-applix-word",
402 "application/x-mswrite",
403 "application/docbook+xml",
404 "application/x-kword",
405 "application/x-kword-crypt",
406 "application/x-lyx",
407 NULL
408 }
409 },
410 { N_("Music"),
411 { "application/ogg",
412 "audio/x-vorbis+ogg",
413 "audio/ac3",
414 "audio/basic",
415 "audio/midi",
416 "audio/x-flac",
417 "audio/mp4",
418 "audio/mpeg",
419 "audio/x-mpeg",
420 "audio/x-ms-asx",
421 "audio/x-pn-realaudio",
422 NULL
423 }
424 },
425 { N_("Video"),
426 { "video/mp4",
427 "video/3gpp",
428 "video/mpeg",
429 "video/quicktime",
430 "video/vivo",
431 "video/x-avi",
432 "video/x-mng",
433 "video/x-ms-asf",
434 "video/x-ms-wmv",
435 "video/x-msvideo",
436 "video/x-nsv",
437 "video/x-real-video",
438 NULL
439 }
440 },
441 { N_("Picture"),
442 { "application/vnd.oasis.opendocument.image",
443 "application/x-krita",
444 "image/bmp",
445 "image/cgm",
446 "image/gif",
447 "image/jpeg",
448 "image/jpeg2000",
449 "image/png",
450 "image/svg+xml",
451 "image/tiff",
452 "image/x-compressed-xcf",
453 "image/x-pcx",
454 "image/x-photo-cd",
455 "image/x-psd",
456 "image/x-tga",
457 "image/x-xcf",
458 NULL
459 }
460 },
461 { N_("Illustration"),
462 { "application/illustrator",
463 "application/vnd.corel-draw",
464 "application/vnd.stardivision.draw",
465 "application/vnd.oasis.opendocument.graphics",
466 "application/x-dia-diagram",
467 "application/x-karbon",
468 "application/x-killustrator",
469 "application/x-kivio",
470 "application/x-kontour",
471 "application/x-wpg",
472 NULL
473 }
474 },
475 { N_("Spreadsheet"),
476 { "application/vnd.lotus-1-2-3",
477 "application/vnd.ms-excel",
478 "application/vnd.stardivision.calc",
479 "application/vnd.sun.xml.calc",
480 "application/vnd.oasis.opendocument.spreadsheet",
481 "application/x-applix-spreadsheet",
482 "application/x-gnumeric",
483 "application/x-kspread",
484 "application/x-kspread-crypt",
485 "application/x-quattropro",
486 "application/x-sc",
487 "application/x-siag",
488 NULL
489 }
490 },
491 { N_("Presentation"),
492 { "application/vnd.ms-powerpoint",
493 "application/vnd.sun.xml.impress",
494 "application/vnd.oasis.opendocument.presentation",
495 "application/x-magicpoint",
496 "application/x-kpresenter",
497 NULL
498 }
499 },
500 { N_("Pdf / Postscript"),
501 { "application/pdf",
502 "application/postscript",
503 "application/x-dvi",
504 "image/x-eps",
505 NULL
506 }
507 },
508 { N_("Text File"),
509 { "text/plain",
510 NULL
511 }
512 }
513 };
514
515 static void
516 type_add_custom_type (NautilusQueryEditorRow *row,
517 const char *mime_type,
518 const char *description,
519 GtkTreeIter *iter)
520 {
521 GtkTreeModel *model;
522 GtkListStore *store;
523
524 model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget));
525 store = GTK_LIST_STORE (model);
526
527 gtk_list_store_append (store, iter);
528 gtk_list_store_set (store, iter,
529 0, description,
530 2, mime_type,
531 -1);
532 }
533
534
535 static void
536 type_combo_changed (GtkComboBox *combo_box, NautilusQueryEditorRow *row)
537 {
538 GtkTreeIter iter;
539 gboolean other;
540 GtkTreeModel *model;
541
542 if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (row->type_widget),
543 &iter)) {
544 return;
545 }
546
547 model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget));
548 gtk_tree_model_get (model, &iter, 3, &other, -1);
549
550 if (other) {
551 GList *mime_infos, *l;
552 GtkWidget *dialog;
553 GtkWidget *scrolled, *treeview;
554 GtkListStore *store;
555 GtkTreeViewColumn *column;
556 GtkCellRenderer *renderer;
557 GtkWidget *toplevel;
558 GtkTreeSelection *selection;
559
560 mime_infos = g_content_types_get_registered ();
561
562 store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
563 for (l = mime_infos; l != NULL; l = l->next) {
564 GtkTreeIter iter;
565 char *mime_type = l->data;
566 char *description;
567
568 description = g_content_type_get_description (mime_type);
569 if (description == NULL) {
570 description = g_strdup (mime_type);
571 }
572
573 gtk_list_store_append (store, &iter);
574 gtk_list_store_set (store, &iter,
575 0, description,
576 1, mime_type,
577 -1);
578
579 g_free (mime_type);
580 g_free (description);
581 }
582 g_list_free (mime_infos);
583
584
585
586 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
587 dialog = gtk_dialog_new_with_buttons (_("Select type"),
588 GTK_WINDOW (toplevel),
589 0,
590 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
591 _("Select"), GTK_RESPONSE_OK,
592 NULL);
593 gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 600);
594
595 scrolled = gtk_scrolled_window_new (NULL, NULL);
596 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
597 GTK_POLICY_AUTOMATIC,
598 GTK_POLICY_AUTOMATIC);
599 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
600 GTK_SHADOW_IN);
601
602 gtk_widget_show (scrolled);
603 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), scrolled, TRUE, TRUE, 6);
604
605 treeview = gtk_tree_view_new ();
606 gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
607 GTK_TREE_MODEL (store));
608 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), 0,
609 GTK_SORT_ASCENDING);
610
611 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
612 gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
613
614
615 renderer = gtk_cell_renderer_text_new ();
616 column = gtk_tree_view_column_new_with_attributes ("Name",
617 renderer,
618 "text",
619 0,
620 NULL);
621 gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
622 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
623
624 gtk_widget_show (treeview);
625 gtk_container_add (GTK_CONTAINER (scrolled), treeview);
626
627 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
628 char *mimetype, *description;
629
630 gtk_tree_selection_get_selected (selection, NULL, &iter);
631 gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
632 0, &description,
633 1, &mimetype,
634 -1);
635
636 type_add_custom_type (row, mimetype, description, &iter);
637 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (row->type_widget),
638 &iter);
639 } else {
640 gtk_combo_box_set_active (GTK_COMBO_BOX (row->type_widget), 0);
641 }
642
643 gtk_widget_destroy (dialog);
644 }
645
646 nautilus_query_editor_changed (row->editor);
647 }
648
649 static GtkWidget *
650 type_row_create_widgets (NautilusQueryEditorRow *row)
651 {
652 GtkWidget *combo;
653 GtkCellRenderer *cell;
654 GtkListStore *store;
655 GtkTreeIter iter;
656 int i;
657
658 store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN);
659 combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
660 g_object_unref (store);
661
662 cell = gtk_cell_renderer_text_new ();
663 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
664 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
665 "text", 0,
666 NULL);
667 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo),
668 type_separator_func,
669 NULL, NULL);
670
671 gtk_list_store_append (store, &iter);
672 gtk_list_store_set (store, &iter, 0, _("Any"), -1);
673 gtk_list_store_append (store, &iter);
674 gtk_list_store_set (store, &iter, 0, "---", -1);
675
676 for (i = 0; i < G_N_ELEMENTS (mime_type_groups); i++) {
677 gtk_list_store_append (store, &iter);
678 gtk_list_store_set (store, &iter,
679 0, gettext (mime_type_groups[i].name),
680 1, mime_type_groups[i].mimetypes,
681 -1);
682 }
683
684 gtk_list_store_append (store, &iter);
685 gtk_list_store_set (store, &iter, 0, "---", -1);
686 gtk_list_store_append (store, &iter);
687 gtk_list_store_set (store, &iter, 0, _("Other Type..."), 3, TRUE, -1);
688
689 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
690
691 g_signal_connect (combo, "changed",
692 G_CALLBACK (type_combo_changed),
693 row);
694
695 gtk_widget_show (combo);
696
697 gtk_box_pack_start (GTK_BOX (row->hbox), combo, FALSE, FALSE, 0);
698
699 return combo;
700 }
701
702 static void
703 type_row_add_to_query (NautilusQueryEditorRow *row,
704 NautilusQuery *query)
705 {
706 GtkTreeIter iter;
707 char **mimetypes;
708 char *mimetype;
709 GtkTreeModel *model;
710
711 if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (row->type_widget),
712 &iter)) {
713 return;
714 }
715
716 model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget));
717 gtk_tree_model_get (model, &iter, 1, &mimetypes, 2, &mimetype, -1);
718
719 if (mimetypes != NULL) {
720 while (*mimetypes != NULL) {
721 nautilus_query_add_mime_type (query, *mimetypes);
722 mimetypes++;
723 }
724 }
725 if (mimetype) {
726 nautilus_query_add_mime_type (query, mimetype);
727 g_free (mimetype);
728 }
729 }
730
731 static gboolean
732 all_group_types_in_list (char **group_types, GList *mime_types)
733 {
734 GList *l;
735 char **group_type;
736 char *mime_type;
737 gboolean found;
738
739 group_type = group_types;
740 while (*group_type != NULL) {
741 found = FALSE;
742
743 for (l = mime_types; l != NULL; l = l->next) {
744 mime_type = l->data;
745
746 if (strcmp (mime_type, *group_type) == 0) {
747 found = TRUE;
748 break;
749 }
750 }
751
752 if (!found) {
753 return FALSE;
754 }
755 group_type++;
756 }
757 return TRUE;
758 }
759
760 static GList *
761 remove_group_types_from_list (char **group_types, GList *mime_types)
762 {
763 GList *l, *next;
764 char **group_type;
765 char *mime_type;
766
767 group_type = group_types;
768 while (*group_type != NULL) {
769 for (l = mime_types; l != NULL; l = next) {
770 mime_type = l->data;
771 next = l->next;
772
773 if (strcmp (mime_type, *group_type) == 0) {
774 mime_types = g_list_remove_link (mime_types, l);
775 g_free (mime_type);
776 break;
777 }
778 }
779
780 group_type++;
781 }
782 return mime_types;
783 }
784
785
786 static void
787 type_add_rows_from_query (NautilusQueryEditor *editor,
788 NautilusQuery *query)
789 {
790 GList *mime_types;
791 char *mime_type;
792 const char *desc;
793 NautilusQueryEditorRow *row;
794 GtkTreeIter iter;
795 int i;
796 GtkTreeModel *model;
797 GList *l;
798
799 mime_types = nautilus_query_get_mime_types (query);
800
801 if (mime_types == NULL) {
802 return;
803 }
804
805 for (i = 0; i < G_N_ELEMENTS (mime_type_groups); i++) {
806 if (all_group_types_in_list (mime_type_groups[i].mimetypes,
807 mime_types)) {
808 mime_types = remove_group_types_from_list (mime_type_groups[i].mimetypes,
809 mime_types);
810
811 row = nautilus_query_editor_add_row (editor,
812 NAUTILUS_QUERY_EDITOR_ROW_TYPE);
813
814 model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget));
815
816 gtk_tree_model_iter_nth_child (model, &iter, NULL, i + 2);
817 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (row->type_widget),
818 &iter);
819 }
820 }
821
822 for (l = mime_types; l != NULL; l = l->next) {
823 mime_type = l->data;
824
825 desc = g_content_type_get_description (mime_type);
826 if (desc == NULL) {
827 desc = mime_type;
828 }
829
830 row = nautilus_query_editor_add_row (editor,
831 NAUTILUS_QUERY_EDITOR_ROW_TYPE);
832
833 type_add_custom_type (row, mime_type, desc, &iter);
834 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (row->type_widget),
835 &iter);
836 }
837
838 g_list_free_full (mime_types, g_free);
839 }
840
841 /* End of row types */
842
843 static NautilusQueryEditorRowType
844 get_next_free_type (NautilusQueryEditor *editor)
845 {
846 NautilusQueryEditorRow *row;
847 NautilusQueryEditorRowType type;
848 gboolean found;
849 GList *l;
850
851
852 for (type = 0; type < NAUTILUS_QUERY_EDITOR_ROW_LAST; type++) {
853 found = FALSE;
854 for (l = editor->details->rows; l != NULL; l = l->next) {
855 row = l->data;
856 if (row->type == type) {
857 found = TRUE;
858 break;
859 }
860 }
861 if (!found) {
862 return type;
863 }
864 }
865 return NAUTILUS_QUERY_EDITOR_ROW_TYPE;
866 }
867
868 static void
869 remove_row_cb (GtkButton *clicked_button, NautilusQueryEditorRow *row)
870 {
871 NautilusQueryEditor *editor;
872
873 editor = row->editor;
874 editor->details->rows = g_list_remove (editor->details->rows, row);
875 row_destroy (row);
876
877 nautilus_query_editor_changed (editor);
878 }
879
880 static void
881 create_type_widgets (NautilusQueryEditorRow *row)
882 {
883 row->type_widget = row_type[row->type].create_widgets (row);
884 }
885
886 static void
887 row_type_combo_changed_cb (GtkComboBox *combo_box, NautilusQueryEditorRow *row)
888 {
889 NautilusQueryEditorRowType type;
890
891 type = gtk_combo_box_get_active (combo_box);
892
893 if (type == row->type) {
894 return;
895 }
896
897 if (row->type_widget != NULL) {
898 gtk_widget_destroy (row->type_widget);
899 row->type_widget = NULL;
900 }
901
902 row->type = type;
903
904 create_type_widgets (row);
905
906 nautilus_query_editor_changed (row->editor);
907 }
908
909 static NautilusQueryEditorRow *
910 nautilus_query_editor_add_row (NautilusQueryEditor *editor,
911 NautilusQueryEditorRowType type)
912 {
913 GtkWidget *hbox, *button, *image, *combo;
914 GtkToolItem *item;
915 NautilusQueryEditorRow *row;
916 int i;
917
918 row = g_new0 (NautilusQueryEditorRow, 1);
919 row->editor = editor;
920 row->type = type;
921
922 /* create the toolbar and the box container for its contents */
923 row->toolbar = gtk_toolbar_new ();
924 gtk_box_pack_start (GTK_BOX (editor), row->toolbar, TRUE, TRUE, 0);
925
926 item = gtk_tool_item_new ();
927 gtk_tool_item_set_expand (item, TRUE);
928 gtk_toolbar_insert (GTK_TOOLBAR (row->toolbar), item, -1);
929
930 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
931 gtk_container_add (GTK_CONTAINER (item), hbox);
932 row->hbox = hbox;
933
934 /* create the criterion selector combobox */
935 combo = gtk_combo_box_text_new ();
936 row->combo = combo;
937 for (i = 0; i < NAUTILUS_QUERY_EDITOR_ROW_LAST; i++) {
938 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), gettext (row_type[i].name));
939 }
940 gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
941
942 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), row->type);
943
944 editor->details->rows = g_list_append (editor->details->rows, row);
945
946 g_signal_connect (combo, "changed",
947 G_CALLBACK (row_type_combo_changed_cb), row);
948
949 create_type_widgets (row);
950
951 /* create the remove row button */
952 button = gtk_button_new ();
953 gtk_style_context_add_class (gtk_widget_get_style_context (button),
954 GTK_STYLE_CLASS_RAISED);
955 gtk_widget_set_tooltip_text (button,
956 _("Remove this criterion from the search"));
957 gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
958
959 image = gtk_image_new_from_icon_name ("window-close-symbolic",
960 GTK_ICON_SIZE_MENU);
961 gtk_container_add (GTK_CONTAINER (button), image);
962
963 g_signal_connect (button, "clicked",
964 G_CALLBACK (remove_row_cb), row);
965
966 /* show everything */
967 gtk_widget_show_all (row->toolbar);
968
969 return row;
970 }
971
972 static void
973 add_new_row_cb (GtkButton *clicked_button, NautilusQueryEditor *editor)
974 {
975 nautilus_query_editor_add_row (editor, get_next_free_type (editor));
976 nautilus_query_editor_changed (editor);
977 }
978
979 static void
980 nautilus_query_editor_init (NautilusQueryEditor *editor)
981 {
982 editor->details = G_TYPE_INSTANCE_GET_PRIVATE (editor, NAUTILUS_TYPE_QUERY_EDITOR,
983 NautilusQueryEditorDetails);
984
985 gtk_orientable_set_orientation (GTK_ORIENTABLE (editor), GTK_ORIENTATION_VERTICAL);
986 }
987
988 static void
989 on_location_button_toggled (GtkToggleButton *button,
990 NautilusQueryEditor *editor)
991 {
992 nautilus_query_editor_changed (editor);
993 }
994
995 static gboolean
996 entry_key_press_event_cb (GtkWidget *widget,
997 GdkEventKey *event,
998 NautilusQueryEditor *editor)
999 {
1000 if (event->keyval == GDK_KEY_Down) {
1001 nautilus_window_grab_focus (NAUTILUS_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (widget))));
1002 }
1003 return FALSE;
1004 }
1005
1006 static void
1007 setup_widgets (NautilusQueryEditor *editor)
1008 {
1009 GtkToolItem *item;
1010 GtkWidget *toolbar, *button_box, *hbox;
1011 GtkWidget *button, *image;
1012
1013 /* create the toolbar and the box container for its contents */
1014 toolbar = gtk_toolbar_new ();
1015 gtk_style_context_add_class (gtk_widget_get_style_context (toolbar),
1016 GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
1017 gtk_box_pack_start (GTK_BOX (editor), toolbar, TRUE, TRUE, 0);
1018
1019 item = gtk_tool_item_new ();
1020 gtk_tool_item_set_expand (item, TRUE);
1021 gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1);
1022
1023 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1024 gtk_container_add (GTK_CONTAINER (item), hbox);
1025
1026 /* create the search entry */
1027 editor->details->entry = gtk_search_entry_new ();
1028 gtk_box_pack_start (GTK_BOX (hbox), editor->details->entry, TRUE, TRUE, 0);
1029
1030 g_signal_connect (editor->details->entry, "key-press-event",
1031 G_CALLBACK (entry_key_press_event_cb), editor);
1032 g_signal_connect (editor->details->entry, "activate",
1033 G_CALLBACK (entry_activate_cb), editor);
1034 g_signal_connect (editor->details->entry, "changed",
1035 G_CALLBACK (entry_changed_cb), editor);
1036
1037 /* create the Current/All Files selector */
1038 editor->details->search_current_button = gtk_radio_button_new_with_label (NULL, _("Current"));
1039 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (editor->details->search_current_button), FALSE);
1040 editor->details->search_all_button = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (editor->details->search_current_button),
1041 _("All Files"));
1042 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (editor->details->search_all_button), FALSE);
1043
1044 /* connect to the signal only on one of the two, since they're mutually exclusive */
1045 g_signal_connect (editor->details->search_current_button, "toggled",
1046 G_CALLBACK (on_location_button_toggled), editor);
1047
1048 button_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1049 gtk_box_pack_start (GTK_BOX (hbox), button_box, FALSE, FALSE, 0);
1050 gtk_style_context_add_class (gtk_widget_get_style_context (button_box),
1051 GTK_STYLE_CLASS_LINKED);
1052 gtk_style_context_add_class (gtk_widget_get_style_context (button_box),
1053 GTK_STYLE_CLASS_RAISED);
1054
1055 gtk_box_pack_start (GTK_BOX (button_box), editor->details->search_current_button, FALSE, FALSE, 0);
1056 gtk_box_pack_start (GTK_BOX (button_box), editor->details->search_all_button, FALSE, FALSE, 0);
1057
1058 /* finally, create the add new row button */
1059 button = gtk_button_new ();
1060 gtk_style_context_add_class (gtk_widget_get_style_context (button),
1061 GTK_STYLE_CLASS_RAISED);
1062 gtk_widget_set_tooltip_text (button,
1063 _("Add a new criterion to this search"));
1064 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1065
1066 image = gtk_image_new_from_icon_name ("list-add-symbolic",
1067 GTK_ICON_SIZE_MENU);
1068 gtk_container_add (GTK_CONTAINER (button), image);
1069
1070 g_signal_connect (button, "clicked",
1071 G_CALLBACK (add_new_row_cb), editor);
1072
1073 /* show everything */
1074 gtk_widget_show_all (toolbar);
1075 }
1076
1077 static void
1078 nautilus_query_editor_changed_force (NautilusQueryEditor *editor, gboolean force_reload)
1079 {
1080 NautilusQuery *query;
1081
1082 if (editor->details->change_frozen) {
1083 return;
1084 }
1085
1086 query = nautilus_query_editor_get_query (editor);
1087 g_signal_emit (editor, signals[CHANGED], 0,
1088 query, force_reload);
1089 g_object_unref (query);
1090 }
1091
1092 static void
1093 nautilus_query_editor_changed (NautilusQueryEditor *editor)
1094 {
1095 nautilus_query_editor_changed_force (editor, TRUE);
1096 }
1097
1098 static void
1099 add_location_to_query (NautilusQueryEditor *editor,
1100 NautilusQuery *query)
1101 {
1102 char *uri;
1103
1104 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (editor->details->search_all_button))) {
1105 uri = nautilus_get_home_directory_uri ();
1106 } else {
1107 uri = g_strdup (editor->details->current_uri);
1108 }
1109
1110 nautilus_query_set_location (query, uri);
1111 g_free (uri);
1112 }
1113
1114 NautilusQuery *
1115 nautilus_query_editor_get_query (NautilusQueryEditor *editor)
1116 {
1117 const char *query_text;
1118 NautilusQuery *query;
1119 GList *l;
1120 NautilusQueryEditorRow *row;
1121
1122 if (editor == NULL || editor->details == NULL || editor->details->entry == NULL) {
1123 return NULL;
1124 }
1125
1126 query_text = gtk_entry_get_text (GTK_ENTRY (editor->details->entry));
1127
1128 query = nautilus_query_new ();
1129 nautilus_query_set_text (query, query_text);
1130
1131 add_location_to_query (editor, query);
1132
1133 for (l = editor->details->rows; l != NULL; l = l->next) {
1134 row = l->data;
1135
1136 row_type[row->type].add_to_query (row, query);
1137 }
1138
1139 return query;
1140 }
1141
1142 GtkWidget *
1143 nautilus_query_editor_new (void)
1144 {
1145 GtkWidget *editor;
1146
1147 editor = g_object_new (NAUTILUS_TYPE_QUERY_EDITOR, NULL);
1148 setup_widgets (NAUTILUS_QUERY_EDITOR (editor));
1149
1150 return editor;
1151 }
1152
1153 static void
1154 update_location (NautilusQueryEditor *editor)
1155 {
1156 NautilusFile *file;
1157 GtkWidget *label;
1158
1159 file = nautilus_file_get_by_uri (editor->details->current_uri);
1160
1161 if (file != NULL) {
1162 char *name;
1163 if (nautilus_file_is_home (file)) {
1164 name = g_strdup (_("Home"));
1165 } else {
1166 char *filename;
1167 filename = nautilus_file_get_display_name (file);
1168 name = g_strdup_printf ("\342\200\234%s\342\200\235", filename);
1169 g_free (filename);
1170 }
1171
1172 gtk_button_set_label (GTK_BUTTON (editor->details->search_current_button), name);
1173 g_free (name);
1174
1175 label = gtk_bin_get_child (GTK_BIN (editor->details->search_current_button));
1176 gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_MIDDLE);
1177 g_object_set (label, "max-width-chars", 30, NULL);
1178
1179 nautilus_file_unref (file);
1180 }
1181 }
1182
1183 void
1184 nautilus_query_editor_set_location (NautilusQueryEditor *editor,
1185 GFile *location)
1186 {
1187 g_free (editor->details->current_uri);
1188 editor->details->current_uri = g_file_get_uri (location);
1189 update_location (editor);
1190 }
1191
1192 static void
1193 update_rows (NautilusQueryEditor *editor,
1194 NautilusQuery *query)
1195 {
1196 NautilusQueryEditorRowType type;
1197
1198 /* if we were just created, set the rows from query,
1199 * otherwise, re-use the query setting we have already.
1200 */
1201 if (query != NULL && editor->details->query == NULL) {
1202 for (type = 0; type < NAUTILUS_QUERY_EDITOR_ROW_LAST; type++) {
1203 row_type[type].add_rows_from_query (editor, query);
1204 }
1205 } else if (query == NULL && editor->details->query != NULL) {
1206 g_list_free_full (editor->details->rows, (GDestroyNotify) row_destroy);
1207 editor->details->rows = NULL;
1208 }
1209 }
1210
1211 void
1212 nautilus_query_editor_set_query (NautilusQueryEditor *editor,
1213 NautilusQuery *query)
1214 {
1215 char *text = NULL;
1216
1217 if (query != NULL) {
1218 text = nautilus_query_get_text (query);
1219 }
1220
1221 if (!text) {
1222 text = g_strdup ("");
1223 }
1224
1225 editor->details->change_frozen = TRUE;
1226 gtk_entry_set_text (GTK_ENTRY (editor->details->entry), text);
1227
1228 g_free (editor->details->current_uri);
1229 editor->details->current_uri = NULL;
1230
1231 update_rows (editor, query);
1232 g_clear_object (&editor->details->query);
1233
1234 if (query != NULL) {
1235 editor->details->query = g_object_ref (query);
1236 editor->details->current_uri = nautilus_query_get_location (query);
1237 update_location (editor);
1238 }
1239
1240 editor->details->change_frozen = FALSE;
1241 }