1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3  * Copyright (C) 2005 Red Hat, Inc.
4  *
5  * Caja 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  * Caja 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., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  *
22  */
23 
24 #include <config.h>
25 #include <string.h>
26 
27 #include <glib/gi18n.h>
28 #include <gio/gio.h>
29 #include <gdk/gdkkeysyms.h>
30 #include <gtk/gtk.h>
31 
32 #include <eel/eel-gtk-macros.h>
33 #include <eel/eel-glib-extensions.h>
34 #include <eel/eel-stock-dialogs.h>
35 
36 #include <libcaja-private/caja-global-preferences.h>
37 
38 #include "caja-query-editor.h"
39 #include "caja-src-marshal.h"
40 #include "caja-window-slot.h"
41 
42 enum
43 {
44         DURATION_INVALID,
45         DURATION_ONE_HOUR,
46         DURATION_ONE_DAY,
47         DURATION_ONE_WEEK,
48         DURATION_ONE_MONTH,
49         DURATION_SIX_MONTHS,
50         DURATION_ONE_YEAR,
51 };
52 
53 typedef enum
54 {
55     CAJA_QUERY_EDITOR_ROW_LOCATION,
56     CAJA_QUERY_EDITOR_ROW_TYPE,
57     CAJA_QUERY_EDITOR_ROW_TAGS,
58     CAJA_QUERY_EDITOR_ROW_TIME_MODIFIED,
59     CAJA_QUERY_EDITOR_ROW_SIZE,
60     CAJA_QUERY_EDITOR_ROW_CONTAINED_TEXT,
61 
62     CAJA_QUERY_EDITOR_ROW_LAST
63 } CajaQueryEditorRowType;
64 
65 typedef struct
66 {
67     CajaQueryEditorRowType type;
68     CajaQueryEditor *editor;
69     GtkWidget *hbox;
70     GtkWidget *combo;
71 
72     GtkWidget *type_widget;
73 
74     void *data;
75 } CajaQueryEditorRow;
76 
77 
78 typedef struct
79 {
80     const char *name;
81     GtkWidget * (*create_widgets)      (CajaQueryEditorRow *row);
82     void        (*add_to_query)        (CajaQueryEditorRow *row,
83                                         CajaQuery          *query);
84     void        (*free_data)           (CajaQueryEditorRow *row);
85     void        (*add_rows_from_query) (CajaQueryEditor *editor,
86                                         CajaQuery *query);
87 } CajaQueryEditorRowOps;
88 
89 struct CajaQueryEditorDetails
90 {
91     gboolean is_indexed;
92     GtkWidget *entry;
93     gboolean change_frozen;
94     guint typing_timeout_id;
95     gboolean is_visible;
96     GtkWidget *invisible_vbox;
97     GtkWidget *visible_vbox;
98 
99     GList *rows;
100     char *last_set_query_text;
101 
102     CajaSearchBar *bar;
103     CajaWindowSlot *slot;
104 };
105 
106 enum
107 {
108     CHANGED,
109     CANCEL,
110     LAST_SIGNAL
111 };
112 
113 static guint signals[LAST_SIGNAL] = { 0 };
114 
115 static void  caja_query_editor_class_init       (CajaQueryEditorClass *class);
116 static void  caja_query_editor_init             (CajaQueryEditor      *editor);
117 
118 static void go_search_cb (GtkButton *clicked_button, CajaQueryEditor *editor);
119 
120 static void entry_activate_cb (GtkWidget *entry, CajaQueryEditor *editor);
121 static void entry_changed_cb  (GtkWidget *entry, CajaQueryEditor *editor);
122 static void caja_query_editor_changed_force (CajaQueryEditor *editor,
123         gboolean             force);
124 static void caja_query_editor_changed (CajaQueryEditor *editor);
125 static CajaQueryEditorRow * caja_query_editor_add_row (CajaQueryEditor *editor,
126         CajaQueryEditorRowType type);
127 
128 static GtkWidget *location_row_create_widgets  (CajaQueryEditorRow *row);
129 static void       location_row_add_to_query    (CajaQueryEditorRow *row,
130         CajaQuery          *query);
131 static void       location_row_free_data       (CajaQueryEditorRow *row);
132 static void       location_add_rows_from_query (CajaQueryEditor    *editor,
133         CajaQuery          *query);
134 
135 static GtkWidget *tags_row_create_widgets      (CajaQueryEditorRow *row);
136 static void       tags_row_add_to_query        (CajaQueryEditorRow *row,
137                                                 CajaQuery          *query);
138 static void       tags_row_free_data           (CajaQueryEditorRow *row);
139 static void       tags_add_rows_from_query     (CajaQueryEditor    *editor,
140                                                 CajaQuery          *query);
141 
142 static GtkWidget *type_row_create_widgets      (CajaQueryEditorRow *row);
143 static void       type_row_add_to_query        (CajaQueryEditorRow *row,
144         CajaQuery          *query);
145 static void       type_row_free_data           (CajaQueryEditorRow *row);
146 static void       type_add_rows_from_query     (CajaQueryEditor    *editor,
147         CajaQuery          *query);
148 static GtkWidget   *modtime_row_create_widgets(CajaQueryEditorRow *row);
149 static void         modtime_row_add_to_query(CajaQueryEditorRow *row,
150                                              CajaQuery *query);
151 static void         modtime_row_free_data(CajaQueryEditorRow *row);
152 static void         modtime_add_rows_from_query(CajaQueryEditor *editor,
153                                                 CajaQuery *query);
154 static GtkWidget   *size_row_create_widgets(CajaQueryEditorRow *row);
155 static void         size_row_add_to_query(CajaQueryEditorRow *row,
156                                           CajaQuery *query);
157 static void         size_row_free_data(CajaQueryEditorRow *row);
158 static void         size_add_rows_from_query(CajaQueryEditor *editor,
159                                              CajaQuery *query);
160 
161 static GtkWidget   *contained_text_row_create_widgets(CajaQueryEditorRow *row);
162 static void         contained_text_row_add_to_query(CajaQueryEditorRow *row,
163                                              CajaQuery *query);
164 static void         contained_text_row_free_data(CajaQueryEditorRow *row);
165 static void         contained_text_add_rows_from_query(CajaQueryEditor *editor,
166                                                 CajaQuery *query);
167 
168 static CajaQueryEditorRowOps row_type[] =
169 {
170     {
171         N_("Location"),
172         location_row_create_widgets,
173         location_row_add_to_query,
174         location_row_free_data,
175         location_add_rows_from_query
176     },
177     {
178         N_("File Type"),
179         type_row_create_widgets,
180         type_row_add_to_query,
181         type_row_free_data,
182         type_add_rows_from_query
183     },
184     {
185         N_("Tags"),
186         tags_row_create_widgets,
187         tags_row_add_to_query,
188         tags_row_free_data,
189         tags_add_rows_from_query
190     },
191     {
192         N_("Modification Time"),
193         modtime_row_create_widgets,
194         modtime_row_add_to_query,
195         modtime_row_free_data,
196         modtime_add_rows_from_query
197     },
198     {
199         N_("Size"),
200         size_row_create_widgets,
201         size_row_add_to_query,
202         size_row_free_data,
203         size_add_rows_from_query
204     },
205     {
206         N_("Contained text"),
207         contained_text_row_create_widgets,
208         contained_text_row_add_to_query,
209         contained_text_row_free_data,
210         contained_text_add_rows_from_query
211     }
212 };
213 
EEL_CLASS_BOILERPLATE(CajaQueryEditor,caja_query_editor,GTK_TYPE_BOX)214 EEL_CLASS_BOILERPLATE (CajaQueryEditor,
215                        caja_query_editor,
216                        GTK_TYPE_BOX)
217 
218 static void
219 caja_query_editor_finalize (GObject *object)
220 {
221     CajaQueryEditor *editor;
222 
223     editor = CAJA_QUERY_EDITOR (object);
224 
225     g_free (editor->details);
226 
227     EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
228 }
229 
230 static void
caja_query_editor_dispose(GObject * object)231 caja_query_editor_dispose (GObject *object)
232 {
233     CajaQueryEditor *editor;
234 
235     editor = CAJA_QUERY_EDITOR (object);
236 
237     if (editor->details->typing_timeout_id)
238     {
239         g_source_remove (editor->details->typing_timeout_id);
240         editor->details->typing_timeout_id = 0;
241     }
242 
243     if (editor->details->bar != NULL)
244     {
245         g_signal_handlers_disconnect_by_func (editor->details->entry,
246                                               entry_activate_cb,
247                                               editor);
248         g_signal_handlers_disconnect_by_func (editor->details->entry,
249                                               entry_changed_cb,
250                                               editor);
251 
252         caja_search_bar_return_entry (editor->details->bar);
253         eel_remove_weak_pointer (&editor->details->bar);
254     }
255 
256     EEL_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
257 }
258 
259 static void
caja_query_editor_class_init(CajaQueryEditorClass * class)260 caja_query_editor_class_init (CajaQueryEditorClass *class)
261 {
262     GObjectClass *gobject_class;
263     GtkBindingSet *binding_set;
264 
265     gobject_class = G_OBJECT_CLASS (class);
266     gobject_class->finalize = caja_query_editor_finalize;
267     gobject_class->dispose = caja_query_editor_dispose;
268 
269     signals[CHANGED] =
270         g_signal_new ("changed",
271                       G_TYPE_FROM_CLASS (class),
272                       G_SIGNAL_RUN_LAST,
273                       G_STRUCT_OFFSET (CajaQueryEditorClass, changed),
274                       NULL, NULL,
275                       caja_src_marshal_VOID__OBJECT_BOOLEAN,
276                       G_TYPE_NONE, 2, CAJA_TYPE_QUERY, G_TYPE_BOOLEAN);
277 
278     signals[CANCEL] =
279         g_signal_new ("cancel",
280                       G_TYPE_FROM_CLASS (class),
281                       G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
282                       G_STRUCT_OFFSET (CajaQueryEditorClass, cancel),
283                       NULL, NULL,
284                       g_cclosure_marshal_VOID__VOID,
285                       G_TYPE_NONE, 0);
286 
287     binding_set = gtk_binding_set_by_class (class);
288 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "cancel", 0);
289 }
290 
291 static void
entry_activate_cb(GtkWidget * entry,CajaQueryEditor * editor)292 entry_activate_cb (GtkWidget *entry, CajaQueryEditor *editor)
293 {
294     if (editor->details->typing_timeout_id)
295     {
296         g_source_remove (editor->details->typing_timeout_id);
297         editor->details->typing_timeout_id = 0;
298     }
299 
300     caja_query_editor_changed_force (editor, TRUE);
301 }
302 
303 static gboolean
typing_timeout_cb(gpointer user_data)304 typing_timeout_cb (gpointer user_data)
305 {
306     CajaQueryEditor *editor;
307 
308     editor = CAJA_QUERY_EDITOR (user_data);
309 
310     caja_query_editor_changed (editor);
311 
312     editor->details->typing_timeout_id = 0;
313 
314     return FALSE;
315 }
316 
317 #define TYPING_TIMEOUT 750
318 
319 static void
entry_changed_cb(GtkWidget * entry,CajaQueryEditor * editor)320 entry_changed_cb (GtkWidget *entry, CajaQueryEditor *editor)
321 {
322     if (editor->details->change_frozen)
323     {
324         return;
325     }
326 
327     if (editor->details->typing_timeout_id)
328     {
329         g_source_remove (editor->details->typing_timeout_id);
330     }
331 
332     editor->details->typing_timeout_id =
333         g_timeout_add (TYPING_TIMEOUT,
334                        typing_timeout_cb,
335                        editor);
336 }
337 
338 static void
edit_clicked(GtkButton * button,CajaQueryEditor * editor)339 edit_clicked (GtkButton *button, CajaQueryEditor *editor)
340 {
341     caja_query_editor_set_visible (editor, TRUE);
342     caja_query_editor_grab_focus (editor);
343 }
344 
345 /* Location */
346 
347 static GtkWidget *
location_row_create_widgets(CajaQueryEditorRow * row)348 location_row_create_widgets (CajaQueryEditorRow *row)
349 {
350     GtkWidget *chooser;
351 
352     chooser = gtk_file_chooser_button_new (_("Select folder to search in"),
353                                            GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
354     gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (chooser), TRUE);
355     gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser),
356                                          g_get_home_dir ());
357     gtk_widget_show (chooser);
358 
359     g_signal_connect_swapped (chooser, "current-folder-changed",
360                               G_CALLBACK (caja_query_editor_changed),
361                               row->editor);
362 
363     gtk_box_pack_start (GTK_BOX (row->hbox), chooser, FALSE, FALSE, 0);
364 
365     return chooser;
366 }
367 
368 static void
location_row_add_to_query(CajaQueryEditorRow * row,CajaQuery * query)369 location_row_add_to_query (CajaQueryEditorRow *row,
370                            CajaQuery          *query)
371 {
372     char *folder, *uri;
373 
374     folder = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (row->type_widget));
375     if (folder == NULL)
376     {
377         /* I don't know why, but i got NULL here on initial search in browser mode
378            even with the location set to the homedir in create_widgets... */
379         folder = g_strdup (g_get_home_dir ());
380     }
381 
382     uri = g_filename_to_uri (folder, NULL, NULL);
383     g_free (folder);
384 
385     caja_query_set_location (query, uri);
386     g_free (uri);
387 }
388 
389 static void
location_row_free_data(CajaQueryEditorRow * row)390 location_row_free_data (CajaQueryEditorRow *row)
391 {
392 }
393 
394 static void
location_add_rows_from_query(CajaQueryEditor * editor,CajaQuery * query)395 location_add_rows_from_query (CajaQueryEditor    *editor,
396                               CajaQuery          *query)
397 {
398     CajaQueryEditorRow *row;
399     char *uri, *folder;
400 
401     uri = caja_query_get_location (query);
402 
403     if (uri == NULL)
404     {
405         return;
406     }
407     folder = g_filename_from_uri (uri, NULL, NULL);
408     g_free (uri);
409     if (folder == NULL)
410     {
411         return;
412     }
413 
414     row = caja_query_editor_add_row (editor,
415                                      CAJA_QUERY_EDITOR_ROW_LOCATION);
416     gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (row->type_widget),
417                                          folder);
418 
419     g_free (folder);
420 }
421 
422 /* Tags */
423 static void
tags_entry_changed_cb(GtkWidget * entry,gpointer * data)424 tags_entry_changed_cb (GtkWidget *entry, gpointer *data)
425 {
426   /* remove commas from string */
427   const gchar *text = gtk_entry_get_text ( GTK_ENTRY (entry));
428   if (g_strrstr (text, ",") == NULL) {
429     return;
430   }
431 
432   gchar **words = g_strsplit (text, ",", -1);
433   gchar *sanitized = g_strjoinv ("", words);
434   g_strfreev (words);
435 
436   gtk_entry_set_text (GTK_ENTRY (entry), sanitized);
437   g_free(sanitized);
438 }
439 
440 #define MAX_TAGS_ENTRY_LEN 4096 // arbitrary value.
441 
442 static GtkWidget *
tags_row_create_widgets(CajaQueryEditorRow * row)443 tags_row_create_widgets (CajaQueryEditorRow *row)
444 {
445     GtkWidget *entry = gtk_entry_new();
446     gtk_entry_set_max_length (GTK_ENTRY (entry), MAX_TAGS_ENTRY_LEN);
447     gtk_widget_set_tooltip_text (entry,
448         _("Tags separated by spaces. "
449           "Matches files that contains ALL specified tags."));
450 
451     gtk_entry_set_placeholder_text (GTK_ENTRY (entry),
452         _("Tags separated by spaces. "
453           "Matches files that contains ALL specified tags."));
454 
455     gtk_widget_show (entry);
456     gtk_box_pack_start (GTK_BOX (row->hbox), entry, TRUE, TRUE, 0);
457     g_signal_connect (entry, "changed", G_CALLBACK (tags_entry_changed_cb), entry);
458     g_signal_connect (entry, "activate", G_CALLBACK (go_search_cb), row->editor);
459 
460     return entry;
461 }
462 
463 static void
tags_row_add_to_query(CajaQueryEditorRow * row,CajaQuery * query)464 tags_row_add_to_query (CajaQueryEditorRow *row,
465                            CajaQuery      *query)
466 {
467     GtkEntry *entry = GTK_ENTRY (row->type_widget);
468     const gchar *tags = gtk_entry_get_text (entry);
469 
470     char **strv = g_strsplit (tags, " ", -1);
471     guint len = g_strv_length (strv);
472     int i;
473 
474     for (i = 0; i < len; ++i) {
475         strv[i] = g_strstrip (strv[i]);
476         if (strlen (strv[i]) > 0) {
477             caja_query_add_tag (query, strv[i]);
478         }
479     }
480     g_strfreev (strv);
481 }
482 
483 static void
tags_row_free_data(CajaQueryEditorRow * row)484 tags_row_free_data (CajaQueryEditorRow *row)
485 {
486 }
487 
488 static gchar *
xattr_tags_list_to_str(const GList * tags)489 xattr_tags_list_to_str (const GList *tags)
490 {
491     gchar *result = NULL;
492 
493     const GList *tags_iter = NULL;
494     for (tags_iter = tags; tags_iter; tags_iter = tags_iter->next) {
495         gchar *tmp;
496 
497         if (result != NULL) {
498             tmp = g_strconcat (result, ",", tags_iter->data, NULL);
499             g_free (result);
500         } else {
501             tmp = g_strdup (tags_iter->data);
502         }
503 
504         result = tmp;
505     }
506 
507     return result;
508 }
509 
510 static void
tags_add_rows_from_query(CajaQueryEditor * editor,CajaQuery * query)511 tags_add_rows_from_query (CajaQueryEditor *editor,
512                               CajaQuery   *query)
513 {
514     GList *tags = caja_query_get_tags (query);
515     if (tags == NULL) {
516         return;
517     }
518 
519     CajaQueryEditorRow *row;
520     row = caja_query_editor_add_row (editor, CAJA_QUERY_EDITOR_ROW_TAGS);
521 
522     gchar *tags_str = xattr_tags_list_to_str (tags);
523     g_list_free_full (tags, g_free);
524 
525     gtk_entry_set_text (GTK_ENTRY (row->type_widget), tags_str);
526     g_free (tags_str);
527 }
528 
529 
530 /* Type */
531 
532 static gboolean
type_separator_func(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)533 type_separator_func (GtkTreeModel      *model,
534                      GtkTreeIter       *iter,
535                      gpointer           data)
536 {
537     char *text;
538     gboolean res;
539 
540     gtk_tree_model_get (model, iter, 0, &text, -1);
541 
542     res = text != NULL && strcmp (text, "---") == 0;
543 
544     g_free (text);
545     return res;
546 }
547 
548 struct
549 {
550     char *name;
551     char *mimetypes[20];
552 } mime_type_groups[] =
553 {
554     {
555         N_("Documents"),
556         {
557             "application/rtf",
558             "application/msword",
559             "application/vnd.sun.xml.writer",
560             "application/vnd.sun.xml.writer.global",
561             "application/vnd.sun.xml.writer.template",
562             "application/vnd.oasis.opendocument.text",
563             "application/vnd.oasis.opendocument.text-template",
564             "application/x-abiword",
565             "application/x-applix-word",
566             "application/x-mswrite",
567             "application/docbook+xml",
568             "application/x-kword",
569             "application/x-kword-crypt",
570             "application/x-lyx",
571             NULL
572         }
573     },
574     {
575         N_("Music"),
576         {
577             "application/ogg",
578             "audio/ac3",
579             "audio/basic",
580             "audio/midi",
581             "audio/x-flac",
582             "audio/mp4",
583             "audio/mpeg",
584             "audio/x-mpeg",
585             "audio/x-ms-asx",
586             "audio/x-pn-realaudio",
587             NULL
588         }
589     },
590     {
591         N_("Video"),
592         {
593             "video/mp4",
594             "video/3gpp",
595             "video/mpeg",
596             "video/quicktime",
597             "video/vivo",
598             "video/x-avi",
599             "video/x-mng",
600             "video/x-ms-asf",
601             "video/x-ms-wmv",
602             "video/x-msvideo",
603             "video/x-nsv",
604             "video/x-real-video",
605             NULL
606         }
607     },
608     {
609         N_("Picture"),
610         {
611             "application/vnd.oasis.opendocument.image",
612             "application/x-krita",
613             "image/bmp",
614             "image/cgm",
615             "image/gif",
616             "image/jpeg",
617             "image/jpeg2000",
618             "image/png",
619             "image/svg+xml",
620             "image/tiff",
621             "image/x-compressed-xcf",
622             "image/x-pcx",
623             "image/x-photo-cd",
624             "image/x-psd",
625             "image/x-tga",
626             "image/x-xcf",
627             NULL
628         }
629     },
630     {
631         N_("Illustration"),
632         {
633             "application/illustrator",
634             "application/vnd.corel-draw",
635             "application/vnd.stardivision.draw",
636             "application/vnd.oasis.opendocument.graphics",
637             "application/x-dia-diagram",
638             "application/x-karbon",
639             "application/x-killustrator",
640             "application/x-kivio",
641             "application/x-kontour",
642             "application/x-wpg",
643             NULL
644         }
645     },
646     {
647         N_("Spreadsheet"),
648         {
649             "application/vnd.lotus-1-2-3",
650             "application/vnd.ms-excel",
651             "application/vnd.stardivision.calc",
652             "application/vnd.sun.xml.calc",
653             "application/vnd.oasis.opendocument.spreadsheet",
654             "application/x-applix-spreadsheet",
655             "application/x-gnumeric",
656             "application/x-kspread",
657             "application/x-kspread-crypt",
658             "application/x-quattropro",
659             "application/x-sc",
660             "application/x-siag",
661             NULL
662         }
663     },
664     {
665         N_("Presentation"),
666         {
667             "application/vnd.ms-powerpoint",
668             "application/vnd.sun.xml.impress",
669             "application/vnd.oasis.opendocument.presentation",
670             "application/x-magicpoint",
671             "application/x-kpresenter",
672             NULL
673         }
674     },
675     {
676         N_("Pdf / Postscript"),
677         {
678             "application/pdf",
679             "application/postscript",
680             "application/x-dvi",
681             "image/x-eps",
682             NULL
683         }
684     },
685     {
686         N_("Text File"),
687         {
688             "text/plain",
689             NULL
690         }
691     }
692 };
693 
694 static void
type_add_custom_type(CajaQueryEditorRow * row,const char * mime_type,const char * description,GtkTreeIter * iter)695 type_add_custom_type (CajaQueryEditorRow *row,
696                       const char *mime_type,
697                       const char *description,
698                       GtkTreeIter *iter)
699 {
700     GtkTreeModel *model;
701     GtkListStore *store;
702 
703     model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget));
704     store = GTK_LIST_STORE (model);
705 
706     gtk_list_store_append (store, iter);
707     gtk_list_store_set (store, iter,
708                         0, description,
709                         2, mime_type,
710                         -1);
711 }
712 
713 
714 static void
type_combo_changed(GtkComboBox * combo_box,CajaQueryEditorRow * row)715 type_combo_changed (GtkComboBox *combo_box, CajaQueryEditorRow *row)
716 {
717     GtkTreeIter iter;
718     gboolean other;
719     GtkTreeModel *model;
720 
721     if (!gtk_combo_box_get_active_iter  (GTK_COMBO_BOX (row->type_widget),
722                                          &iter))
723     {
724         return;
725     }
726 
727     model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget));
728     gtk_tree_model_get (model, &iter, 3, &other, -1);
729 
730     if (other)
731     {
732         GList *mime_infos, *l;
733         GtkWidget *dialog;
734         GtkWidget *scrolled, *treeview;
735         GtkListStore *store;
736         GtkTreeViewColumn *column;
737         GtkCellRenderer *renderer;
738         GtkWidget *toplevel;
739         GtkTreeSelection *selection;
740 
741         mime_infos = g_content_types_get_registered ();
742 
743         store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
744         for (l = mime_infos; l != NULL; l = l->next)
745         {
746             GtkTreeIter iter;
747             char *mime_type = l->data;
748             char *description;
749 
750             description = g_content_type_get_description (mime_type);
751             if (description == NULL)
752             {
753                 description = g_strdup (mime_type);
754             }
755 
756             gtk_list_store_append (store, &iter);
757             gtk_list_store_set (store, &iter,
758                                 0, description,
759                                 1, mime_type,
760                                 -1);
761 
762             g_free (mime_type);
763             g_free (description);
764         }
765         g_list_free (mime_infos);
766 
767 
768 
769         toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
770 
771         dialog = gtk_dialog_new ();
772         gtk_window_set_title (GTK_WINDOW (dialog), _("Select type"));
773         gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (toplevel));
774 
775         eel_dialog_add_button (GTK_DIALOG (dialog),
776                                _("_OK"),
777                                "gtk-ok",
778                                GTK_RESPONSE_OK);
779 
780         gtk_window_set_default_size (GTK_WINDOW (dialog), 400, 600);
781 
782         scrolled = gtk_scrolled_window_new (NULL, NULL);
783         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
784                                         GTK_POLICY_AUTOMATIC,
785                                         GTK_POLICY_AUTOMATIC);
786         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
787                                              GTK_SHADOW_IN);
788         gtk_scrolled_window_set_overlay_scrolling (GTK_SCROLLED_WINDOW (scrolled),
789                                                    FALSE);
790 
791         gtk_widget_show (scrolled);
792         gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), scrolled, TRUE, TRUE, 6);
793 
794         treeview = gtk_tree_view_new ();
795         gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
796                                  GTK_TREE_MODEL (store));
797         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), 0,
798                                               GTK_SORT_ASCENDING);
799 
800         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
801         gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
802 
803 
804         renderer = gtk_cell_renderer_text_new ();
805         column = gtk_tree_view_column_new_with_attributes ("Name",
806                  renderer,
807                  "text",
808                  0,
809                  NULL);
810         gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
811         gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
812 
813         gtk_widget_show (treeview);
814         gtk_container_add (GTK_CONTAINER (scrolled), treeview);
815 
816         if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
817         {
818             char *mimetype, *description;
819 
820             gtk_tree_selection_get_selected (selection, NULL, &iter);
821             gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
822                                 0, &description,
823                                 1, &mimetype,
824                                 -1);
825 
826             type_add_custom_type (row, mimetype, description, &iter);
827             gtk_combo_box_set_active_iter  (GTK_COMBO_BOX (row->type_widget),
828                                             &iter);
829         }
830         else
831         {
832             gtk_combo_box_set_active (GTK_COMBO_BOX (row->type_widget), 0);
833         }
834 
835         gtk_widget_destroy (dialog);
836     }
837 
838     caja_query_editor_changed (row->editor);
839 }
840 
841 static GtkWidget *
type_row_create_widgets(CajaQueryEditorRow * row)842 type_row_create_widgets (CajaQueryEditorRow *row)
843 {
844     GtkWidget *combo;
845     GtkCellRenderer *cell;
846     GtkListStore *store;
847     GtkTreeIter iter;
848     int i;
849 
850     store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_BOOLEAN);
851     combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
852     g_object_unref (store);
853 
854     cell = gtk_cell_renderer_text_new ();
855     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
856     gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
857                                     "text", 0,
858                                     NULL);
859     gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo),
860                                           type_separator_func,
861                                           NULL, NULL);
862 
863     gtk_list_store_append (store, &iter);
864     gtk_list_store_set (store, &iter, 0, _("Any"), -1);
865     gtk_list_store_append (store, &iter);
866     gtk_list_store_set (store, &iter, 0, "---",  -1);
867 
868     for (i = 0; i < G_N_ELEMENTS (mime_type_groups); i++)
869     {
870         gtk_list_store_append (store, &iter);
871         gtk_list_store_set (store, &iter,
872                             0, gettext (mime_type_groups[i].name),
873                             1, mime_type_groups[i].mimetypes,
874                             -1);
875     }
876 
877     gtk_list_store_append (store, &iter);
878     gtk_list_store_set (store, &iter, 0, "---",  -1);
879     gtk_list_store_append (store, &iter);
880     gtk_list_store_set (store, &iter, 0, _("Other Type..."), 3, TRUE, -1);
881 
882     gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
883 
884     g_signal_connect (combo, "changed",
885                       G_CALLBACK (type_combo_changed),
886                       row);
887 
888     gtk_widget_show (combo);
889 
890     gtk_box_pack_start (GTK_BOX (row->hbox), combo, FALSE, FALSE, 0);
891 
892     return combo;
893 }
894 
895 static void
type_row_add_to_query(CajaQueryEditorRow * row,CajaQuery * query)896 type_row_add_to_query (CajaQueryEditorRow *row,
897                        CajaQuery          *query)
898 {
899     GtkTreeIter iter;
900     char **mimetypes;
901     char *mimetype;
902     GtkTreeModel *model;
903 
904     if (!gtk_combo_box_get_active_iter  (GTK_COMBO_BOX (row->type_widget),
905                                          &iter))
906     {
907         return;
908     }
909 
910     model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget));
911     gtk_tree_model_get (model, &iter, 1, &mimetypes, 2, &mimetype, -1);
912 
913     if (mimetypes != NULL)
914     {
915         while (*mimetypes != NULL)
916         {
917             caja_query_add_mime_type (query, *mimetypes);
918             mimetypes++;
919         }
920     }
921     if (mimetype)
922     {
923         caja_query_add_mime_type (query, mimetype);
924         g_free (mimetype);
925     }
926 }
927 
928 static void
type_row_free_data(CajaQueryEditorRow * row)929 type_row_free_data (CajaQueryEditorRow *row)
930 {
931 }
932 
933 static gboolean
all_group_types_in_list(char ** group_types,GList * mime_types)934 all_group_types_in_list (char **group_types, GList *mime_types)
935 {
936     GList *l;
937     char **group_type;
938     char *mime_type;
939     gboolean found;
940 
941     group_type = group_types;
942     while (*group_type != NULL)
943     {
944         found = FALSE;
945 
946         for (l = mime_types; l != NULL; l = l->next)
947         {
948             mime_type = l->data;
949 
950             if (strcmp (mime_type, *group_type) == 0)
951             {
952                 found = TRUE;
953                 break;
954             }
955         }
956 
957         if (!found)
958         {
959             return FALSE;
960         }
961         group_type++;
962     }
963     return TRUE;
964 }
965 
966 static GList *
remove_group_types_from_list(char ** group_types,GList * mime_types)967 remove_group_types_from_list (char **group_types, GList *mime_types)
968 {
969     GList *l, *next;
970     char **group_type;
971     char *mime_type;
972 
973     group_type = group_types;
974     while (*group_type != NULL)
975     {
976         for (l = mime_types; l != NULL; l = next)
977         {
978             mime_type = l->data;
979             next = l->next;
980 
981             if (strcmp (mime_type, *group_type) == 0)
982             {
983                 mime_types = g_list_remove_link (mime_types, l);
984                 g_free (mime_type);
985                 break;
986             }
987         }
988 
989         group_type++;
990     }
991     return mime_types;
992 }
993 
994 
995 static void
type_add_rows_from_query(CajaQueryEditor * editor,CajaQuery * query)996 type_add_rows_from_query (CajaQueryEditor    *editor,
997                           CajaQuery          *query)
998 {
999     GList *mime_types;
1000     char *mime_type;
1001     CajaQueryEditorRow *row;
1002     GtkTreeIter iter;
1003     int i;
1004     GtkTreeModel *model;
1005     GList *l;
1006 
1007     mime_types = caja_query_get_mime_types (query);
1008 
1009     if (mime_types == NULL)
1010     {
1011         return;
1012     }
1013 
1014     for (i = 0; i < G_N_ELEMENTS (mime_type_groups); i++)
1015     {
1016         if (all_group_types_in_list (mime_type_groups[i].mimetypes,
1017                                      mime_types))
1018         {
1019             mime_types = remove_group_types_from_list (mime_type_groups[i].mimetypes,
1020                          mime_types);
1021 
1022             row = caja_query_editor_add_row (editor,
1023                                              CAJA_QUERY_EDITOR_ROW_TYPE);
1024 
1025             model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget));
1026 
1027             gtk_tree_model_iter_nth_child (model, &iter, NULL, i + 2);
1028             gtk_combo_box_set_active_iter  (GTK_COMBO_BOX (row->type_widget),
1029                                             &iter);
1030         }
1031     }
1032 
1033     for (l = mime_types; l != NULL; l = l->next)
1034     {
1035         const char *desc;
1036 
1037         mime_type = l->data;
1038 
1039         desc = g_content_type_get_description (mime_type);
1040         if (desc == NULL)
1041         {
1042             desc = mime_type;
1043         }
1044 
1045         row = caja_query_editor_add_row (editor,
1046                                          CAJA_QUERY_EDITOR_ROW_TYPE);
1047         model = gtk_combo_box_get_model (GTK_COMBO_BOX (row->type_widget));
1048 
1049         type_add_custom_type (row, mime_type, desc, &iter);
1050         gtk_combo_box_set_active_iter  (GTK_COMBO_BOX (row->type_widget),
1051                                         &iter);
1052     }
1053 
1054     g_list_free_full (mime_types, g_free);
1055 }
1056 
1057 /* End of row types */
1058 
1059 
modtime_row_create_widgets(CajaQueryEditorRow * row)1060 static GtkWidget *modtime_row_create_widgets(CajaQueryEditorRow *row)
1061 {
1062     GtkWidget *hbox = NULL;
1063     GtkWidget *combo = NULL;
1064     GtkWidget *duration_combo = NULL;
1065     GtkCellRenderer *cell = NULL;
1066     GtkListStore *store = NULL;
1067     GtkListStore *duration_store = NULL;
1068     GtkTreeIter iter;
1069 
1070     hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 7);
1071 
1072     store = gtk_list_store_new(2, G_TYPE_BOOLEAN, G_TYPE_STRING);
1073     combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
1074     g_object_unref(store);
1075 
1076     cell = gtk_cell_renderer_text_new();
1077     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE);
1078     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), cell, "text", 1,
1079                                    NULL);
1080 
1081     gtk_list_store_append(store, &iter);
1082     gtk_list_store_set(store, &iter, 0, FALSE, 1, _("Less than or equal to"), -1);
1083     gtk_list_store_append(store, &iter);
1084     gtk_list_store_set(store, &iter, 0, TRUE, 1, _("Greater than or equal to"), -1);
1085 
1086     gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1087 
1088     duration_store = gtk_list_store_new(2, G_TYPE_INT, G_TYPE_STRING);
1089     duration_combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(duration_store));
1090     g_object_unref(duration_store);
1091 
1092     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(duration_combo), cell, TRUE);
1093     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(duration_combo), cell,
1094                                    "text", 1, NULL);
1095 
1096     gtk_list_store_append(duration_store, &iter);
1097     gtk_list_store_set(duration_store, &iter, 0, DURATION_ONE_HOUR, 1, _("1 Hour"), -1);
1098     gtk_list_store_append(duration_store, &iter);
1099     gtk_list_store_set(duration_store, &iter, 0, DURATION_ONE_DAY, 1, _("1 Day"), -1);
1100     gtk_list_store_append(duration_store, &iter);
1101     gtk_list_store_set(duration_store, &iter, 0, DURATION_ONE_WEEK, 1, _("1 Week"), -1);
1102     gtk_list_store_append(duration_store, &iter);
1103     gtk_list_store_set(duration_store, &iter, 0, DURATION_ONE_MONTH, 1, _("1 Month"), -1);
1104     gtk_list_store_append(duration_store, &iter);
1105     gtk_list_store_set(duration_store, &iter, 0, DURATION_SIX_MONTHS, 1, _("6 Months"), -1);
1106     gtk_list_store_append(duration_store, &iter);
1107     gtk_list_store_set(duration_store, &iter, 0, DURATION_ONE_YEAR, 1, _("1 Year"), -1);
1108 
1109     gtk_combo_box_set_active(GTK_COMBO_BOX(duration_combo), 0);
1110 
1111     gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0);
1112     gtk_box_pack_start(GTK_BOX(hbox), duration_combo, FALSE, FALSE, 0);
1113     gtk_widget_show_all(hbox);
1114 
1115     gtk_box_pack_start(GTK_BOX(row->hbox), hbox, FALSE, FALSE, 0);
1116 
1117     return hbox;
1118 }
1119 
modtime_row_add_to_query(CajaQueryEditorRow * row,CajaQuery * query)1120 static void modtime_row_add_to_query(CajaQueryEditorRow *row, CajaQuery *query)
1121 {
1122     GList *children = NULL;
1123     GtkWidget *combo = NULL;
1124     GtkWidget *duration_combo = NULL;
1125     GtkTreeModel *model = NULL;
1126     GtkTreeModel *duration_model = NULL;
1127     GtkTreeIter iter;
1128     GtkTreeIter duration_iter;
1129     gboolean is_greater = FALSE;
1130     GDateTime *now, *datetime;
1131     gint duration;
1132     gint64 timestamp;
1133 
1134     if (!GTK_IS_CONTAINER(row->type_widget))
1135         return;
1136 
1137     children = gtk_container_get_children(GTK_CONTAINER(row->type_widget));
1138     if (g_list_length(children) != 2)
1139         return;
1140 
1141     combo = GTK_WIDGET(g_list_nth(children, 0)->data);
1142     duration_combo = GTK_WIDGET(g_list_nth(children, 1)->data);
1143     if (!combo || !duration_combo)
1144         return;
1145 
1146     if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter) ||
1147         !gtk_combo_box_get_active_iter(GTK_COMBO_BOX(duration_combo), &duration_iter)) {
1148         return;
1149     }
1150 
1151     model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
1152     gtk_tree_model_get(model, &iter, 0, &is_greater, -1);
1153 
1154     duration_model = gtk_combo_box_get_model(GTK_COMBO_BOX(duration_combo));
1155     gtk_tree_model_get(duration_model, &duration_iter, 0, &duration, -1);
1156 
1157     now = g_date_time_new_now_local ();
1158     datetime = now;
1159     switch (duration)
1160     {
1161         case DURATION_ONE_HOUR:
1162             datetime = g_date_time_add_hours (now, -1);
1163             break;
1164         case DURATION_ONE_DAY:
1165             datetime = g_date_time_add_days (now, -1);
1166             break;
1167         case DURATION_ONE_WEEK:
1168             datetime = g_date_time_add_weeks (now, -1);
1169             break;
1170         case DURATION_ONE_MONTH:
1171             datetime = g_date_time_add_months (now, -1);
1172             break;
1173         case DURATION_SIX_MONTHS:
1174             datetime = g_date_time_add_months (now, -6);
1175             break;
1176         case DURATION_ONE_YEAR:
1177             datetime = g_date_time_add_years (now, -1);
1178             break;
1179         default:
1180             g_assert_not_reached ();
1181     }
1182 
1183     g_date_time_unref (now);
1184     timestamp = g_date_time_to_unix (datetime);
1185     g_date_time_unref (datetime);
1186 
1187     caja_query_set_timestamp(query, is_greater ? timestamp: -timestamp);
1188 }
1189 
modtime_row_free_data(CajaQueryEditorRow * row)1190 static void modtime_row_free_data(CajaQueryEditorRow *row)
1191 {
1192 }
1193 
modtime_add_rows_from_query(CajaQueryEditor * editor,CajaQuery * query)1194 static void modtime_add_rows_from_query(CajaQueryEditor *editor, CajaQuery *query)
1195 {
1196 }
1197 
1198 
size_row_create_widgets(CajaQueryEditorRow * row)1199 static GtkWidget *size_row_create_widgets(CajaQueryEditorRow *row)
1200 {
1201     GtkWidget *hbox = NULL;
1202     GtkWidget *combo = NULL;
1203     GtkWidget *size_combo = NULL;
1204     GtkCellRenderer *cell = NULL;
1205     GtkListStore *store = NULL;
1206     GtkListStore *size_store = NULL;
1207     GtkTreeIter iter;
1208 
1209     hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 7);
1210 
1211     store = gtk_list_store_new(2, G_TYPE_BOOLEAN, G_TYPE_STRING);
1212     combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
1213     g_object_unref(store);
1214 
1215     cell = gtk_cell_renderer_text_new();
1216     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), cell, TRUE);
1217     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), cell, "text", 1,
1218                                    NULL);
1219 
1220     gtk_list_store_append(store, &iter);
1221     gtk_list_store_set(store, &iter, 0, FALSE, 1, _("Less than or equal to"), -1);
1222     gtk_list_store_append(store, &iter);
1223     gtk_list_store_set(store, &iter, 0, TRUE, 1, _("Greater than or equal to"), -1);
1224 
1225     gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1226 
1227     size_store = gtk_list_store_new(2, G_TYPE_INT64, G_TYPE_STRING);
1228     size_combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(size_store));
1229     g_object_unref(size_store);
1230 
1231     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(size_combo), cell, TRUE);
1232     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(size_combo), cell, "text",
1233                                    1, NULL);
1234 
1235     if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_USE_IEC_UNITS))
1236     {
1237         gtk_list_store_append(size_store, &iter);
1238         gtk_list_store_set(size_store, &iter, 0, 10240, 1, _("10 KiB"), -1);
1239         gtk_list_store_append(size_store, &iter);
1240         gtk_list_store_set(size_store, &iter, 0, 102400, 1, _("100 KiB"), -1);
1241         gtk_list_store_append(size_store, &iter);
1242         gtk_list_store_set(size_store, &iter, 0, 512000, 1, _("500 KiB"), -1);
1243         gtk_list_store_append(size_store, &iter);
1244         gtk_list_store_set(size_store, &iter, 0, 1048576, 1, _("1 MiB"), -1);
1245         gtk_list_store_append(size_store, &iter);
1246         gtk_list_store_set(size_store, &iter, 0, 5242880, 1, _("5 MiB"), -1);
1247         gtk_list_store_append(size_store, &iter);
1248         gtk_list_store_set(size_store, &iter, 0, 10485760, 1, _("10 MiB"), -1);
1249         gtk_list_store_append(size_store, &iter);
1250         gtk_list_store_set(size_store, &iter, 0, 104857600, 1, _("100 MiB"), -1);
1251         gtk_list_store_append(size_store, &iter);
1252         gtk_list_store_set(size_store, &iter, 0, 524288000, 1, _("500 MiB"), -1);
1253         gtk_list_store_append(size_store, &iter);
1254         gtk_list_store_set(size_store, &iter, 0, 1073741824, 1, _("1 GiB"), -1);
1255         gtk_list_store_append(size_store, &iter);
1256         gtk_list_store_set(size_store, &iter, 0, 2147483648, 1, _("2 GiB"), -1);
1257         gtk_list_store_append(size_store, &iter);
1258         gtk_list_store_set(size_store, &iter, 0, 4294967296, 1, _("4 GiB"), -1);
1259     } else {
1260         gtk_list_store_append(size_store, &iter);
1261         gtk_list_store_set(size_store, &iter, 0, 10000, 1, _("10 KB"), -1);
1262         gtk_list_store_append(size_store, &iter);
1263         gtk_list_store_set(size_store, &iter, 0, 100000, 1, _("100 KB"), -1);
1264         gtk_list_store_append(size_store, &iter);
1265         gtk_list_store_set(size_store, &iter, 0, 500000, 1, _("500 KB"), -1);
1266         gtk_list_store_append(size_store, &iter);
1267         gtk_list_store_set(size_store, &iter, 0, 1000000, 1, _("1 MB"), -1);
1268         gtk_list_store_append(size_store, &iter);
1269         gtk_list_store_set(size_store, &iter, 0, 5000000, 1, _("5 MB"), -1);
1270         gtk_list_store_append(size_store, &iter);
1271         gtk_list_store_set(size_store, &iter, 0, 10000000, 1, _("10 MB"), -1);
1272         gtk_list_store_append(size_store, &iter);
1273         gtk_list_store_set(size_store, &iter, 0, 100000000, 1, _("100 MB"), -1);
1274         gtk_list_store_append(size_store, &iter);
1275         gtk_list_store_set(size_store, &iter, 0, 500000000, 1, _("500 MB"), -1);
1276         gtk_list_store_append(size_store, &iter);
1277         gtk_list_store_set(size_store, &iter, 0, 1000000000, 1, _("1 GB"), -1);
1278         gtk_list_store_append(size_store, &iter);
1279         gtk_list_store_set(size_store, &iter, 0, 2000000000, 1, _("2 GB"), -1);
1280         gtk_list_store_append(size_store, &iter);
1281         gtk_list_store_set(size_store, &iter, 0, 4000000000, 1, _("4 GB"), -1);
1282     }
1283 
1284     gtk_combo_box_set_active(GTK_COMBO_BOX(size_combo), 0);
1285 
1286     gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0);
1287     gtk_box_pack_start(GTK_BOX(hbox), size_combo, FALSE, FALSE, 0);
1288     gtk_widget_show_all(hbox);
1289 
1290     gtk_box_pack_start(GTK_BOX(row->hbox), hbox, FALSE, FALSE, 0);
1291 
1292     return hbox;
1293 }
1294 
size_row_add_to_query(CajaQueryEditorRow * row,CajaQuery * query)1295 static void size_row_add_to_query(CajaQueryEditorRow *row, CajaQuery *query)
1296 {
1297     GList *children = NULL;
1298     GtkWidget *combo = NULL;
1299     GtkWidget *size_combo = NULL;
1300     GtkTreeModel *model = NULL;
1301     GtkTreeModel *size_model = NULL;
1302     GtkTreeIter iter;
1303     GtkTreeIter size_iter;
1304     gboolean is_greater = FALSE;
1305     gint64 size;
1306 
1307     if (!GTK_IS_CONTAINER(row->type_widget))
1308         return;
1309 
1310     children = gtk_container_get_children(GTK_CONTAINER(row->type_widget));
1311     if (g_list_length(children) != 2)
1312         return;
1313 
1314     combo = GTK_WIDGET(g_list_nth(children, 0)->data);
1315     size_combo = GTK_WIDGET(g_list_nth(children, 1)->data);
1316     if (!combo || !size_combo)
1317         return;
1318 
1319     if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter) ||
1320         !gtk_combo_box_get_active_iter(GTK_COMBO_BOX(size_combo), &size_iter)) {
1321         return;
1322     }
1323 
1324     model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo));
1325     gtk_tree_model_get(model, &iter, 0, &is_greater, -1);
1326 
1327     size_model = gtk_combo_box_get_model(GTK_COMBO_BOX(size_combo));
1328     gtk_tree_model_get(size_model, &size_iter, 0, &size, -1);
1329 
1330     caja_query_set_size(query, is_greater ? size : -size);
1331 }
1332 
size_row_free_data(CajaQueryEditorRow * row)1333 static void size_row_free_data(CajaQueryEditorRow *row)
1334 {
1335 }
1336 
size_add_rows_from_query(CajaQueryEditor * editor,CajaQuery * query)1337 static void size_add_rows_from_query(CajaQueryEditor *editor, CajaQuery *query)
1338 {
1339 }
1340 
1341 static GtkWidget *
contained_text_row_create_widgets(CajaQueryEditorRow * row)1342 contained_text_row_create_widgets (CajaQueryEditorRow *row)
1343 {
1344     GtkWidget *entry = gtk_entry_new();
1345     gtk_widget_set_tooltip_text (entry,
1346         _("Matches files that contains specified text."));
1347 
1348     gtk_entry_set_placeholder_text (GTK_ENTRY (entry),
1349         _("Matches files that contains specified text."));
1350 
1351     gtk_widget_show (entry);
1352     gtk_box_pack_start (GTK_BOX (row->hbox), entry, TRUE, TRUE, 0);
1353     g_signal_connect (entry, "activate", G_CALLBACK (go_search_cb), row->editor);
1354 
1355     return entry;
1356 }
1357 
1358 static void
contained_text_row_add_to_query(CajaQueryEditorRow * row,CajaQuery * query)1359 contained_text_row_add_to_query (CajaQueryEditorRow *row, CajaQuery *query)
1360 {
1361     GtkEntry *entry = GTK_ENTRY (row->type_widget);
1362     const gchar *text = gtk_entry_get_text (entry);
1363 
1364     caja_query_set_contained_text (query, text);
1365 }
1366 
1367 static void
contained_text_row_free_data(CajaQueryEditorRow * row)1368 contained_text_row_free_data (CajaQueryEditorRow *row)
1369 {
1370 }
1371 
1372 static void
contained_text_add_rows_from_query(CajaQueryEditor * editor,CajaQuery * query)1373 contained_text_add_rows_from_query (CajaQueryEditor *editor, CajaQuery *query)
1374 {
1375 }
1376 
1377 static CajaQueryEditorRowType
get_next_free_type(CajaQueryEditor * editor)1378 get_next_free_type (CajaQueryEditor *editor)
1379 {
1380     CajaQueryEditorRow *row;
1381     CajaQueryEditorRowType type;
1382     gboolean found;
1383     GList *l;
1384 
1385 
1386     for (type = 0; type < CAJA_QUERY_EDITOR_ROW_LAST; type++)
1387     {
1388         found = FALSE;
1389         for (l = editor->details->rows; l != NULL; l = l->next)
1390         {
1391             row = l->data;
1392             if (row->type == type)
1393             {
1394                 found = TRUE;
1395                 break;
1396             }
1397         }
1398         if (!found)
1399         {
1400             return type;
1401         }
1402     }
1403     return CAJA_QUERY_EDITOR_ROW_TYPE;
1404 }
1405 
1406 static void
remove_row_cb(GtkButton * clicked_button,CajaQueryEditorRow * row)1407 remove_row_cb (GtkButton *clicked_button, CajaQueryEditorRow *row)
1408 {
1409     CajaQueryEditor *editor;
1410 
1411     editor = row->editor;
1412     gtk_container_remove (GTK_CONTAINER (editor->details->visible_vbox),
1413                           row->hbox);
1414 
1415     editor->details->rows = g_list_remove (editor->details->rows, row);
1416 
1417     row_type[row->type].free_data (row);
1418     g_free (row);
1419 
1420     caja_query_editor_changed (editor);
1421 }
1422 
1423 static void
create_type_widgets(CajaQueryEditorRow * row)1424 create_type_widgets (CajaQueryEditorRow *row)
1425 {
1426     row->type_widget = row_type[row->type].create_widgets (row);
1427 }
1428 
1429 static void
row_type_combo_changed_cb(GtkComboBox * combo_box,CajaQueryEditorRow * row)1430 row_type_combo_changed_cb (GtkComboBox *combo_box, CajaQueryEditorRow *row)
1431 {
1432     CajaQueryEditorRowType type;
1433 
1434     type = gtk_combo_box_get_active (combo_box);
1435 
1436     if (type == row->type)
1437     {
1438         return;
1439     }
1440 
1441     if (row->type_widget != NULL)
1442     {
1443         gtk_widget_destroy (row->type_widget);
1444         row->type_widget = NULL;
1445     }
1446 
1447     row_type[row->type].free_data (row);
1448     row->data = NULL;
1449 
1450     row->type = type;
1451 
1452     create_type_widgets (row);
1453 
1454     caja_query_editor_changed (row->editor);
1455 }
1456 
1457 static CajaQueryEditorRow *
caja_query_editor_add_row(CajaQueryEditor * editor,CajaQueryEditorRowType type)1458 caja_query_editor_add_row (CajaQueryEditor *editor,
1459                            CajaQueryEditorRowType type)
1460 {
1461     GtkWidget *hbox, *button, *image, *combo;
1462     CajaQueryEditorRow *row;
1463     int i;
1464 
1465     row = g_new0 (CajaQueryEditorRow, 1);
1466     row->editor = editor;
1467     row->type = type;
1468 
1469     hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1470     row->hbox = hbox;
1471     gtk_widget_show (hbox);
1472     gtk_box_pack_start (GTK_BOX (editor->details->visible_vbox), hbox, FALSE, FALSE, 0);
1473 
1474     combo = gtk_combo_box_text_new ();
1475     row->combo = combo;
1476     for (i = 0; i < CAJA_QUERY_EDITOR_ROW_LAST; i++)
1477     {
1478         gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo), gettext (row_type[i].name));
1479     }
1480     gtk_widget_show (combo);
1481     gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
1482 
1483     gtk_combo_box_set_active (GTK_COMBO_BOX (combo), row->type);
1484 
1485     editor->details->rows = g_list_append (editor->details->rows, row);
1486 
1487     g_signal_connect (combo, "changed",
1488                       G_CALLBACK (row_type_combo_changed_cb), row);
1489 
1490     create_type_widgets (row);
1491 
1492     button = gtk_button_new ();
1493     image = gtk_image_new_from_icon_name ("remove",
1494                                       GTK_ICON_SIZE_SMALL_TOOLBAR);
1495     gtk_container_add (GTK_CONTAINER (button), image);
1496     gtk_widget_show (image);
1497     gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
1498     gtk_widget_show (button);
1499 
1500     g_signal_connect (button, "clicked",
1501                       G_CALLBACK (remove_row_cb), row);
1502     gtk_widget_set_tooltip_text (button,
1503                                  _("Remove this criterion from the search"));
1504 
1505     gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1506 
1507     return row;
1508 }
1509 
1510 static void
go_search_cb(GtkButton * clicked_button,CajaQueryEditor * editor)1511 go_search_cb (GtkButton *clicked_button, CajaQueryEditor *editor)
1512 {
1513     caja_query_editor_changed_force (editor, TRUE);
1514 }
1515 
1516 static void
add_new_row_cb(GtkButton * clicked_button,CajaQueryEditor * editor)1517 add_new_row_cb (GtkButton *clicked_button, CajaQueryEditor *editor)
1518 {
1519     caja_query_editor_add_row (editor, get_next_free_type (editor));
1520     caja_query_editor_changed (editor);
1521 }
1522 
1523 static void
caja_query_editor_init(CajaQueryEditor * editor)1524 caja_query_editor_init (CajaQueryEditor *editor)
1525 {
1526     GtkWidget *hbox, *label, *button;
1527     char *label_markup;
1528 
1529     editor->details = g_new0 (CajaQueryEditorDetails, 1);
1530     editor->details->is_visible = TRUE;
1531 
1532     editor->details->invisible_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
1533     gtk_orientable_set_orientation (GTK_ORIENTABLE (editor), GTK_ORIENTATION_VERTICAL);
1534     gtk_box_pack_start (GTK_BOX (editor), editor->details->invisible_vbox,
1535                         FALSE, FALSE, 0);
1536     editor->details->visible_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
1537     gtk_orientable_set_orientation (GTK_ORIENTABLE (editor), GTK_ORIENTATION_VERTICAL);
1538     gtk_box_pack_start (GTK_BOX (editor), editor->details->visible_vbox,
1539                         FALSE, FALSE, 0);
1540     /* Only show visible vbox */
1541     gtk_widget_show (editor->details->visible_vbox);
1542 
1543     /* Create invisible part: */
1544     hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1545     gtk_box_pack_start (GTK_BOX (editor->details->invisible_vbox),
1546                         hbox, FALSE, FALSE, 0);
1547     gtk_widget_show (hbox);
1548 
1549     label = gtk_label_new ("");
1550     label_markup = g_strconcat ("<b>", _("Search Folder"), "</b>", NULL);
1551     gtk_label_set_markup (GTK_LABEL (label), label_markup);
1552     g_free (label_markup);
1553     gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1554     gtk_widget_show (label);
1555 
1556     button = gtk_button_new_with_label (_("Edit"));
1557     gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1558     gtk_widget_show (button);
1559 
1560     g_signal_connect (button, "clicked",
1561                       G_CALLBACK (edit_clicked), editor);
1562 
1563     gtk_widget_set_tooltip_text (button,
1564                                  _("Edit the saved search"));
1565 }
1566 
1567 void
caja_query_editor_set_default_query(CajaQueryEditor * editor)1568 caja_query_editor_set_default_query (CajaQueryEditor *editor)
1569 {
1570     if (!editor->details->is_indexed)
1571     {
1572         caja_query_editor_add_row (editor, CAJA_QUERY_EDITOR_ROW_LOCATION);
1573         caja_query_editor_changed (editor);
1574     }
1575 }
1576 
1577 static void
finish_first_line(CajaQueryEditor * editor,GtkWidget * hbox,gboolean use_go)1578 finish_first_line (CajaQueryEditor *editor, GtkWidget *hbox, gboolean use_go)
1579 {
1580     GtkWidget *button, *image;
1581 
1582     button = gtk_button_new ();
1583     image = gtk_image_new_from_icon_name ("add",
1584                                       GTK_ICON_SIZE_SMALL_TOOLBAR);
1585     gtk_container_add (GTK_CONTAINER (button), image);
1586     gtk_widget_show (image);
1587     gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
1588     gtk_widget_show (button);
1589 
1590     g_signal_connect (button, "clicked",
1591                       G_CALLBACK (add_new_row_cb), editor);
1592 
1593     gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1594 
1595     gtk_widget_set_tooltip_text (button,
1596                                  _("Add a new criterion to this search"));
1597 
1598     if (!editor->details->is_indexed)
1599     {
1600         if (use_go)
1601         {
1602             button = gtk_button_new_with_label (_("Go"));
1603         }
1604         else
1605         {
1606             button = gtk_button_new_with_label (_("Reload"));
1607         }
1608         gtk_widget_show (button);
1609 
1610         gtk_widget_set_tooltip_text (button,
1611                                      _("Perform or update the search"));
1612 
1613         g_signal_connect (button, "clicked",
1614                           G_CALLBACK (go_search_cb), editor);
1615 
1616         gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1617     }
1618 }
1619 
1620 static void
setup_internal_entry(CajaQueryEditor * editor)1621 setup_internal_entry (CajaQueryEditor *editor)
1622 {
1623     GtkWidget *hbox, *label;
1624     char *label_markup;
1625 
1626     /* Create visible part: */
1627     hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1628     gtk_widget_show (hbox);
1629     gtk_box_pack_start (GTK_BOX (editor->details->visible_vbox), hbox, FALSE, FALSE, 0);
1630 
1631     label = gtk_label_new ("");
1632     label_markup = g_strconcat ("<b>", _("_Search for:"), "</b>", NULL);
1633     gtk_label_set_markup_with_mnemonic (GTK_LABEL (label), label_markup);
1634     g_free (label_markup);
1635     gtk_widget_show (label);
1636     gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1637 
1638     editor->details->entry = gtk_entry_new ();
1639     gtk_label_set_mnemonic_widget (GTK_LABEL (label), editor->details->entry);
1640     gtk_box_pack_start (GTK_BOX (hbox), editor->details->entry, TRUE, TRUE, 0);
1641 
1642     g_signal_connect (editor->details->entry, "activate",
1643                       G_CALLBACK (entry_activate_cb), editor);
1644     g_signal_connect (editor->details->entry, "changed",
1645                       G_CALLBACK (entry_changed_cb), editor);
1646     gtk_widget_show (editor->details->entry);
1647 
1648     finish_first_line (editor, hbox, TRUE);
1649 }
1650 
1651 static void
setup_external_entry(CajaQueryEditor * editor,GtkWidget * entry)1652 setup_external_entry (CajaQueryEditor *editor, GtkWidget *entry)
1653 {
1654     GtkWidget *hbox, *label;
1655 
1656     /* Create visible part: */
1657     hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1658     gtk_widget_show (hbox);
1659     gtk_box_pack_start (GTK_BOX (editor->details->visible_vbox), hbox, FALSE, FALSE, 0);
1660 
1661     label = gtk_label_new (_("Search results"));
1662     gtk_widget_show (label);
1663     gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1664 
1665     editor->details->entry = entry;
1666     g_signal_connect (editor->details->entry, "activate",
1667                       G_CALLBACK (entry_activate_cb), editor);
1668     g_signal_connect (editor->details->entry, "changed",
1669                       G_CALLBACK (entry_changed_cb), editor);
1670 
1671     finish_first_line (editor, hbox, FALSE);
1672 
1673 }
1674 
1675 void
caja_query_editor_set_visible(CajaQueryEditor * editor,gboolean visible)1676 caja_query_editor_set_visible (CajaQueryEditor *editor,
1677                                gboolean visible)
1678 {
1679     editor->details->is_visible = visible;
1680     if (visible)
1681     {
1682         gtk_widget_show (editor->details->visible_vbox);
1683         gtk_widget_hide (editor->details->invisible_vbox);
1684     }
1685     else
1686     {
1687         gtk_widget_hide (editor->details->visible_vbox);
1688         gtk_widget_show (editor->details->invisible_vbox);
1689     }
1690 }
1691 
1692 static gboolean
query_is_valid(CajaQueryEditor * editor)1693 query_is_valid (CajaQueryEditor *editor)
1694 {
1695     const char *text;
1696 
1697     text = gtk_entry_get_text (GTK_ENTRY (editor->details->entry));
1698 
1699     return text != NULL && text[0] != '\0';
1700 }
1701 
1702 static void
caja_query_editor_changed_force(CajaQueryEditor * editor,gboolean force_reload)1703 caja_query_editor_changed_force (CajaQueryEditor *editor, gboolean force_reload)
1704 {
1705     if (editor->details->change_frozen)
1706     {
1707         return;
1708     }
1709 
1710     if (query_is_valid (editor))
1711     {
1712         CajaQuery *query;
1713 
1714         query = caja_query_editor_get_query (editor);
1715         g_signal_emit (editor, signals[CHANGED], 0,
1716                        query, editor->details->is_indexed || force_reload);
1717         g_object_unref (query);
1718     }
1719 }
1720 
1721 static void
caja_query_editor_changed(CajaQueryEditor * editor)1722 caja_query_editor_changed (CajaQueryEditor *editor)
1723 {
1724     caja_query_editor_changed_force (editor, FALSE);
1725 }
1726 
1727 void
caja_query_editor_grab_focus(CajaQueryEditor * editor)1728 caja_query_editor_grab_focus (CajaQueryEditor *editor)
1729 {
1730     if (editor->details->is_visible)
1731     {
1732         gtk_widget_grab_focus (editor->details->entry);
1733     }
1734 }
1735 
1736 CajaQuery *
caja_query_editor_get_query(CajaQueryEditor * editor)1737 caja_query_editor_get_query (CajaQueryEditor *editor)
1738 {
1739     const char *query_text;
1740     CajaQuery *query;
1741     GList *l;
1742     CajaQueryEditorRow *row = NULL;
1743 
1744     if (editor == NULL || editor->details == NULL || editor->details->entry == NULL)
1745     {
1746         return NULL;
1747     }
1748 
1749     query_text = gtk_entry_get_text (GTK_ENTRY (editor->details->entry));
1750 
1751     /* Empty string is a NULL query */
1752     if (query_text && query_text[0] == '\0')
1753     {
1754         return NULL;
1755     }
1756 
1757     query = caja_query_new ();
1758     caja_query_set_text (query, query_text);
1759 
1760     for (l = editor->details->rows; l != NULL; l = l->next)
1761     {
1762         row = l->data;
1763 
1764         row_type[row->type].add_to_query (row, query);
1765     }
1766 
1767     return query;
1768 }
1769 
1770 void
caja_query_editor_clear_query(CajaQueryEditor * editor)1771 caja_query_editor_clear_query (CajaQueryEditor *editor)
1772 {
1773     editor->details->change_frozen = TRUE;
1774     gtk_entry_set_text (GTK_ENTRY (editor->details->entry), "");
1775 
1776     g_free (editor->details->last_set_query_text);
1777     editor->details->last_set_query_text = g_strdup ("");
1778 
1779     editor->details->change_frozen = FALSE;
1780 }
1781 
1782 GtkWidget *
caja_query_editor_new(gboolean start_hidden,gboolean is_indexed)1783 caja_query_editor_new (gboolean start_hidden,
1784                        gboolean is_indexed)
1785 {
1786     GtkWidget *editor;
1787 
1788     editor = g_object_new (CAJA_TYPE_QUERY_EDITOR, NULL);
1789 
1790     CAJA_QUERY_EDITOR (editor)->details->is_indexed = is_indexed;
1791 
1792     caja_query_editor_set_visible (CAJA_QUERY_EDITOR (editor),
1793                                    !start_hidden);
1794 
1795     setup_internal_entry (CAJA_QUERY_EDITOR (editor));
1796 
1797     return editor;
1798 }
1799 
1800 static void
detach_from_external_entry(CajaQueryEditor * editor)1801 detach_from_external_entry (CajaQueryEditor *editor)
1802 {
1803     if (editor->details->bar != NULL)
1804     {
1805         caja_search_bar_return_entry (editor->details->bar);
1806         g_signal_handlers_block_by_func (editor->details->entry,
1807                                          entry_activate_cb,
1808                                          editor);
1809         g_signal_handlers_block_by_func (editor->details->entry,
1810                                          entry_changed_cb,
1811                                          editor);
1812     }
1813 }
1814 
1815 static void
attach_to_external_entry(CajaQueryEditor * editor)1816 attach_to_external_entry (CajaQueryEditor *editor)
1817 {
1818     if (editor->details->bar != NULL)
1819     {
1820         caja_search_bar_borrow_entry (editor->details->bar);
1821         g_signal_handlers_unblock_by_func (editor->details->entry,
1822                                            entry_activate_cb,
1823                                            editor);
1824         g_signal_handlers_unblock_by_func (editor->details->entry,
1825                                            entry_changed_cb,
1826                                            editor);
1827 
1828         editor->details->change_frozen = TRUE;
1829         gtk_entry_set_text (GTK_ENTRY (editor->details->entry),
1830                             editor->details->last_set_query_text);
1831         editor->details->change_frozen = FALSE;
1832     }
1833 }
1834 
1835 GtkWidget*
caja_query_editor_new_with_bar(gboolean start_hidden,gboolean is_indexed,gboolean start_attached,CajaSearchBar * bar,CajaWindowSlot * slot)1836 caja_query_editor_new_with_bar (gboolean start_hidden,
1837                                 gboolean is_indexed,
1838                                 gboolean start_attached,
1839                                 CajaSearchBar *bar,
1840                                 CajaWindowSlot *slot)
1841 {
1842     GtkWidget *entry;
1843     CajaQueryEditor *editor;
1844 
1845     editor = CAJA_QUERY_EDITOR (g_object_new (CAJA_TYPE_QUERY_EDITOR, NULL));
1846     editor->details->is_indexed = is_indexed;
1847 
1848     caja_query_editor_set_visible (editor, !start_hidden);
1849 
1850     editor->details->bar = bar;
1851     eel_add_weak_pointer (&editor->details->bar);
1852 
1853     editor->details->slot = slot;
1854 
1855     entry = caja_search_bar_borrow_entry (bar);
1856     setup_external_entry (editor, entry);
1857     if (!start_attached)
1858     {
1859         detach_from_external_entry (editor);
1860     }
1861 
1862     g_signal_connect_object (slot, "active",
1863                              G_CALLBACK (attach_to_external_entry),
1864                              editor, G_CONNECT_SWAPPED);
1865     g_signal_connect_object (slot, "inactive",
1866                              G_CALLBACK (detach_from_external_entry),
1867                              editor, G_CONNECT_SWAPPED);
1868 
1869     return GTK_WIDGET (editor);
1870 }
1871 
1872 void
caja_query_editor_set_query(CajaQueryEditor * editor,CajaQuery * query)1873 caja_query_editor_set_query (CajaQueryEditor *editor, CajaQuery *query)
1874 {
1875     CajaQueryEditorRowType type;
1876     char *text;
1877 
1878     if (!query)
1879     {
1880         caja_query_editor_clear_query (editor);
1881         return;
1882     }
1883 
1884     text = caja_query_get_text (query);
1885 
1886     if (!text)
1887     {
1888         text = g_strdup ("");
1889     }
1890 
1891     editor->details->change_frozen = TRUE;
1892     gtk_entry_set_text (GTK_ENTRY (editor->details->entry), text);
1893 
1894     for (type = 0; type < CAJA_QUERY_EDITOR_ROW_LAST; type++)
1895     {
1896         row_type[type].add_rows_from_query (editor, query);
1897     }
1898 
1899     editor->details->change_frozen = FALSE;
1900 
1901     g_free (editor->details->last_set_query_text);
1902     editor->details->last_set_query_text = text;
1903 }
1904