Mercurial > geeqie.yaz
comparison src/search.c @ 9:d907d608745f
Sync to GQview 1.5.9 release.
########
DO NOT BASE ENHANCEMENTS OR TRANSLATION UPDATES ON CODE IN THIS CVS!
This CVS is never up to date with current development and is provided
solely for reference purposes, please use the latest official release
package when making any changes or translation updates.
########
author | gqview |
---|---|
date | Sat, 26 Feb 2005 00:13:35 +0000 |
parents | |
children | 04ff0df3ad2f |
comparison
equal
deleted
inserted
replaced
8:e0d0593d519e | 9:d907d608745f |
---|---|
1 /* | |
2 * GQview | |
3 * (C) 2005 John Ellis | |
4 * | |
5 * Author: John Ellis | |
6 * | |
7 * This software is released under the GNU General Public License (GNU GPL). | |
8 * Please read the included file COPYING for more information. | |
9 * This software comes with no warranty of any kind, use at your own risk! | |
10 */ | |
11 | |
12 | |
13 #include "gqview.h" | |
14 #include "search.h" | |
15 | |
16 #include "bar_info.h" | |
17 #include "cache.h" | |
18 #include "collect.h" | |
19 #include "collect-table.h" | |
20 #include "dnd.h" | |
21 #include "dupe.h" | |
22 #include "image-load.h" | |
23 #include "info.h" | |
24 #include "editors.h" | |
25 #include "img-view.h" | |
26 #include "filelist.h" | |
27 #include "layout_image.h" | |
28 #include "menu.h" | |
29 #include "print.h" | |
30 #include "thumb.h" | |
31 #include "utilops.h" | |
32 #include "ui_bookmark.h" | |
33 #include "ui_fileops.h" | |
34 #include "ui_menu.h" | |
35 #include "ui_misc.h" | |
36 #include "ui_spinner.h" | |
37 #include "ui_tabcomp.h" | |
38 #include "ui_tree_edit.h" | |
39 | |
40 #include <gdk/gdkkeysyms.h> /* for keyboard values */ | |
41 | |
42 | |
43 #define DEF_SEARCH_WIDTH 700 | |
44 #define DEF_SEARCH_HEIGHT 450 | |
45 | |
46 #define SEARCH_BUFFER_MATCH_LOAD 20 | |
47 #define SEARCH_BUFFER_MATCH_HIT 5 | |
48 #define SEARCH_BUFFER_MATCH_MISS 1 | |
49 #define SEARCH_BUFFER_FLUSH_SIZE 99 | |
50 | |
51 | |
52 typedef enum { | |
53 SEARCH_MATCH_NONE, | |
54 SEARCH_MATCH_EQUAL, | |
55 SEARCH_MATCH_CONTAINS, | |
56 SEARCH_MATCH_UNDER, | |
57 SEARCH_MATCH_OVER, | |
58 SEARCH_MATCH_BETWEEN, | |
59 SEARCH_MATCH_ALL, | |
60 SEARCH_MATCH_ANY | |
61 } MatchType; | |
62 | |
63 enum { | |
64 SEARCH_COLUMN_POINTER = 0, | |
65 SEARCH_COLUMN_RANK, | |
66 SEARCH_COLUMN_THUMB, | |
67 SEARCH_COLUMN_NAME, | |
68 SEARCH_COLUMN_SIZE, | |
69 SEARCH_COLUMN_DATE, | |
70 SEARCH_COLUMN_DIMENSIONS, | |
71 SEARCH_COLUMN_PATH, | |
72 SEARCH_COLUMN_COUNT /* total columns */ | |
73 }; | |
74 | |
75 typedef struct _SearchData SearchData; | |
76 struct _SearchData | |
77 { | |
78 GtkWidget *window; | |
79 | |
80 GtkWidget *button_thumbs; | |
81 GtkWidget *label_status; | |
82 GtkWidget *label_progress; | |
83 GtkWidget *button_start; | |
84 GtkWidget *button_stop; | |
85 GtkWidget *spinner; | |
86 | |
87 GtkWidget *box_search; | |
88 | |
89 GtkWidget *menu_path; | |
90 GtkWidget *path_entry; | |
91 GtkWidget *check_recurse; | |
92 | |
93 GtkWidget *result_view; | |
94 | |
95 GtkWidget *check_name; | |
96 GtkWidget *menu_name; | |
97 GtkWidget *entry_name; | |
98 GtkWidget *check_name_match_case; | |
99 | |
100 GtkWidget *check_size; | |
101 GtkWidget *menu_size; | |
102 GtkWidget *spin_size; | |
103 GtkWidget *spin_size_end; | |
104 | |
105 GtkWidget *check_date; | |
106 GtkWidget *menu_date; | |
107 GtkWidget *date_sel; | |
108 GtkWidget *date_sel_end; | |
109 | |
110 GtkWidget *check_dimensions; | |
111 GtkWidget *menu_dimensions; | |
112 GtkWidget *spin_width; | |
113 GtkWidget *spin_height; | |
114 GtkWidget *spin_width_end; | |
115 GtkWidget *spin_height_end; | |
116 | |
117 GtkWidget *check_similarity; | |
118 GtkWidget *spin_similarity; | |
119 GtkWidget *entry_similarity; | |
120 | |
121 GtkWidget *check_keywords; | |
122 GtkWidget *menu_keywords; | |
123 GtkWidget *entry_keywords; | |
124 | |
125 gchar *search_path; | |
126 gint search_path_recurse; | |
127 gchar *search_name; | |
128 gint search_name_match_case; | |
129 gint64 search_size; | |
130 gint64 search_size_end; | |
131 gint search_date_y; | |
132 gint search_date_m; | |
133 gint search_date_d; | |
134 gint search_date_end_y; | |
135 gint search_date_end_m; | |
136 gint search_date_end_d; | |
137 gint search_width; | |
138 gint search_height; | |
139 gint search_width_end; | |
140 gint search_height_end; | |
141 gint search_similarity; | |
142 gchar *search_similarity_path; | |
143 CacheData *search_similarity_cd; | |
144 GList *search_keyword_list; | |
145 | |
146 MatchType search_type; | |
147 | |
148 MatchType match_name; | |
149 MatchType match_size; | |
150 MatchType match_date; | |
151 MatchType match_dimensions; | |
152 MatchType match_keywords; | |
153 | |
154 gboolean match_name_enable; | |
155 gboolean match_size_enable; | |
156 gboolean match_date_enable; | |
157 gboolean match_dimensions_enable; | |
158 gboolean match_similarity_enable; | |
159 gboolean match_keywords_enable; | |
160 | |
161 GList *search_folder_list; | |
162 GList *search_done_list; | |
163 GList *search_file_list; | |
164 GList *search_buffer_list; | |
165 | |
166 gint search_count; | |
167 gint search_total; | |
168 gint search_buffer_count; | |
169 | |
170 gint search_idle_id; | |
171 gint update_idle_id; | |
172 | |
173 ImageLoader *img_loader; | |
174 CacheData *img_cd; | |
175 | |
176 FileData *click_fd; | |
177 | |
178 ThumbLoader *thumb_loader; | |
179 gint thumb_enable; | |
180 FileData *thumb_fd; | |
181 }; | |
182 | |
183 typedef struct _MatchFileData MatchFileData; | |
184 struct _MatchFileData | |
185 { | |
186 FileData fd; | |
187 gint width; | |
188 gint height; | |
189 gint rank; | |
190 }; | |
191 | |
192 typedef struct _MatchList MatchList; | |
193 struct _MatchList | |
194 { | |
195 const gchar *text; | |
196 const MatchType type; | |
197 }; | |
198 | |
199 static const MatchList text_search_menu_path[] = { | |
200 { N_("folder"), SEARCH_MATCH_NONE }, | |
201 { N_("comments"), SEARCH_MATCH_ALL }, | |
202 { N_("results"), SEARCH_MATCH_CONTAINS } | |
203 }; | |
204 | |
205 static const MatchList text_search_menu_name[] = { | |
206 { N_("contains"), SEARCH_MATCH_CONTAINS }, | |
207 { N_("is"), SEARCH_MATCH_EQUAL } | |
208 }; | |
209 | |
210 static const MatchList text_search_menu_size[] = { | |
211 { N_("equal to"), SEARCH_MATCH_EQUAL }, | |
212 { N_("less than"), SEARCH_MATCH_UNDER }, | |
213 { N_("greater than"), SEARCH_MATCH_OVER }, | |
214 { N_("between"), SEARCH_MATCH_BETWEEN } | |
215 }; | |
216 | |
217 static const MatchList text_search_menu_date[] = { | |
218 { N_("equal to"), SEARCH_MATCH_EQUAL }, | |
219 { N_("before"), SEARCH_MATCH_UNDER }, | |
220 { N_("after"), SEARCH_MATCH_OVER }, | |
221 { N_("between"), SEARCH_MATCH_BETWEEN } | |
222 }; | |
223 | |
224 static const MatchList text_search_menu_keyword[] = { | |
225 { N_("match all"), SEARCH_MATCH_ALL }, | |
226 { N_("match any"), SEARCH_MATCH_ANY }, | |
227 { N_("exclude"), SEARCH_MATCH_NONE } | |
228 }; | |
229 | |
230 static GList *search_window_list = NULL; | |
231 | |
232 | |
233 static gint search_result_selection_count(SearchData *sd, gint64 *bytes); | |
234 static gint search_result_count(SearchData *sd, gint64 *bytes); | |
235 | |
236 static void search_window_close(SearchData *sd); | |
237 | |
238 | |
239 /* | |
240 *------------------------------------------------------------------- | |
241 * utils | |
242 *------------------------------------------------------------------- | |
243 */ | |
244 | |
245 static time_t convert_dmy_to_time(gint day, gint month, gint year) | |
246 { | |
247 struct tm lt; | |
248 | |
249 lt.tm_sec = 0; | |
250 lt.tm_min = 0; | |
251 lt.tm_hour = 0; | |
252 lt.tm_mday = day; | |
253 lt.tm_mon = month - 1; | |
254 lt.tm_year = year - 1900; | |
255 lt.tm_isdst = 0; | |
256 | |
257 return mktime(<); | |
258 } | |
259 | |
260 static void search_status_update(SearchData *sd) | |
261 { | |
262 gchar *buf; | |
263 gint t; | |
264 gint s; | |
265 gint64 t_bytes; | |
266 gint64 s_bytes; | |
267 gchar *tt; | |
268 gchar *ts; | |
269 | |
270 t = search_result_count(sd, &t_bytes); | |
271 s = search_result_selection_count(sd, &s_bytes); | |
272 | |
273 if (s > 0) | |
274 { | |
275 tt = text_from_size_abrev(t_bytes); | |
276 ts = text_from_size_abrev(s_bytes); | |
277 buf = g_strdup_printf(_("%s, %d files (%s, %d)"), tt, t, ts, s); | |
278 g_free(tt); | |
279 g_free(ts); | |
280 } | |
281 else | |
282 { | |
283 tt = text_from_size_abrev(t_bytes); | |
284 buf = g_strdup_printf(_("%s, %d files"), tt, t); | |
285 g_free(tt); | |
286 } | |
287 | |
288 gtk_label_set_text(GTK_LABEL(sd->label_status), buf); | |
289 g_free(buf); | |
290 } | |
291 | |
292 static void search_progress_update(SearchData *sd, gint search, gdouble thumbs) | |
293 { | |
294 | |
295 if (search || thumbs >= 0.0) | |
296 { | |
297 gchar *buf; | |
298 const gchar *message; | |
299 | |
300 if (search && (sd->search_folder_list || sd->search_file_list)) | |
301 message = _("Searching..."); | |
302 else if (thumbs >= 0.0) | |
303 message = _("Loading thumbs..."); | |
304 else | |
305 message = ""; | |
306 | |
307 buf = g_strdup_printf("%s(%d / %d)", message, sd->search_count, sd->search_total); | |
308 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(sd->label_progress), buf); | |
309 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(sd->label_progress), | |
310 (thumbs >= 0.0) ? thumbs : 0.0); | |
311 g_free(buf); | |
312 } | |
313 else | |
314 { | |
315 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(sd->label_progress), ""); | |
316 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(sd->label_progress), 0.0); | |
317 } | |
318 } | |
319 | |
320 /* | |
321 *------------------------------------------------------------------- | |
322 * result list | |
323 *------------------------------------------------------------------- | |
324 */ | |
325 | |
326 static gint search_result_find_row(SearchData *sd, FileData *fd, GtkTreeIter *iter) | |
327 { | |
328 GtkTreeModel *store; | |
329 gint valid; | |
330 gint n = 0; | |
331 | |
332 store = gtk_tree_view_get_model(GTK_TREE_VIEW(sd->result_view)); | |
333 valid = gtk_tree_model_get_iter_first(store, iter); | |
334 while (valid) | |
335 { | |
336 FileData *fd_n; | |
337 n++; | |
338 | |
339 gtk_tree_model_get(store, iter, SEARCH_COLUMN_POINTER, &fd_n, -1); | |
340 if (fd_n == fd) return n; | |
341 valid = gtk_tree_model_iter_next(store, iter); | |
342 } | |
343 | |
344 return -1; | |
345 } | |
346 | |
347 static gint search_result_row_selected(SearchData *sd, FileData *fd) | |
348 { | |
349 GtkTreeModel *store; | |
350 GtkTreeSelection *selection; | |
351 GList *slist; | |
352 GList *work; | |
353 gint found = FALSE; | |
354 | |
355 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(sd->result_view)); | |
356 slist = gtk_tree_selection_get_selected_rows(selection, &store); | |
357 work = slist; | |
358 while (!found && work) | |
359 { | |
360 GtkTreePath *tpath = work->data; | |
361 FileData *fd_n; | |
362 GtkTreeIter iter; | |
363 | |
364 gtk_tree_model_get_iter(store, &iter, tpath); | |
365 gtk_tree_model_get(store, &iter, SEARCH_COLUMN_POINTER, &fd_n, -1); | |
366 if (fd_n == fd) found = TRUE; | |
367 work = work->next; | |
368 } | |
369 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL); | |
370 g_list_free(slist); | |
371 | |
372 return found; | |
373 } | |
374 | |
375 static gint search_result_selection_util(SearchData *sd, gint64 *bytes, GList **list) | |
376 { | |
377 GtkTreeModel *store; | |
378 GtkTreeSelection *selection; | |
379 GList *slist; | |
380 GList *work; | |
381 gint n = 0; | |
382 gint64 total = 0; | |
383 GList *plist = NULL; | |
384 | |
385 store = gtk_tree_view_get_model(GTK_TREE_VIEW(sd->result_view)); | |
386 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(sd->result_view)); | |
387 slist = gtk_tree_selection_get_selected_rows(selection, &store); | |
388 work = slist; | |
389 while (work) | |
390 { | |
391 n++; | |
392 | |
393 if (bytes || list) | |
394 { | |
395 GtkTreePath *tpath = work->data; | |
396 FileData *fd; | |
397 GtkTreeIter iter; | |
398 | |
399 gtk_tree_model_get_iter(store, &iter, tpath); | |
400 gtk_tree_model_get(store, &iter, SEARCH_COLUMN_POINTER, &fd, -1); | |
401 total += fd->size; | |
402 | |
403 if (list) plist = g_list_prepend(plist, g_strdup(fd->path)); | |
404 } | |
405 | |
406 work = work->next; | |
407 } | |
408 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL); | |
409 g_list_free(slist); | |
410 | |
411 if (bytes) *bytes = total; | |
412 if (list) *list = g_list_reverse(plist); | |
413 | |
414 return n; | |
415 } | |
416 | |
417 static GList *search_result_selection_list(SearchData *sd) | |
418 { | |
419 GList *list; | |
420 | |
421 search_result_selection_util(sd, NULL, &list); | |
422 return list; | |
423 } | |
424 | |
425 static gint search_result_selection_count(SearchData *sd, gint64 *bytes) | |
426 { | |
427 return search_result_selection_util(sd, bytes, NULL); | |
428 } | |
429 | |
430 static gint search_result_util(SearchData *sd, gint64 *bytes, GList **list) | |
431 { | |
432 GtkTreeModel *store; | |
433 GtkTreeIter iter; | |
434 gint valid; | |
435 gint n = 0; | |
436 gint64 total = 0; | |
437 GList *plist = NULL; | |
438 | |
439 store = gtk_tree_view_get_model(GTK_TREE_VIEW(sd->result_view)); | |
440 | |
441 valid = gtk_tree_model_get_iter_first(store, &iter); | |
442 while (valid) | |
443 { | |
444 n++; | |
445 if (bytes || list) | |
446 { | |
447 FileData *fd; | |
448 | |
449 gtk_tree_model_get(store, &iter, SEARCH_COLUMN_POINTER, &fd, -1); | |
450 total += fd->size; | |
451 | |
452 if (list) plist = g_list_prepend(plist, g_strdup(fd->path)); | |
453 } | |
454 valid = gtk_tree_model_iter_next(store, &iter); | |
455 } | |
456 | |
457 if (bytes) *bytes = total; | |
458 if (list) *list = g_list_reverse(plist); | |
459 | |
460 return n; | |
461 } | |
462 | |
463 static GList *search_result_get_path_list(SearchData *sd) | |
464 { | |
465 GList *list = NULL; | |
466 | |
467 search_result_util(sd, NULL, &list); | |
468 return list; | |
469 } | |
470 | |
471 static gint search_result_count(SearchData *sd, gint64 *bytes) | |
472 { | |
473 return search_result_util(sd, bytes, NULL); | |
474 } | |
475 | |
476 static void search_result_append(SearchData *sd, MatchFileData *mfd) | |
477 { | |
478 FileData *fd; | |
479 GtkListStore *store; | |
480 GtkTreeIter iter; | |
481 gchar *text_size; | |
482 gchar *text_dim = NULL; | |
483 | |
484 fd = (FileData *)mfd; | |
485 | |
486 if (!fd) return; | |
487 | |
488 text_size = text_from_size(fd->size); | |
489 if (mfd->width > 0 && mfd->height > 0) text_dim = g_strdup_printf("%d x %d", mfd->width, mfd->height); | |
490 | |
491 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(sd->result_view))); | |
492 gtk_list_store_append(store, &iter); | |
493 gtk_list_store_set(store, &iter, | |
494 SEARCH_COLUMN_POINTER, fd, | |
495 SEARCH_COLUMN_RANK, mfd->rank, | |
496 SEARCH_COLUMN_THUMB, fd->pixbuf, | |
497 SEARCH_COLUMN_NAME, fd->name, | |
498 SEARCH_COLUMN_SIZE, text_size, | |
499 SEARCH_COLUMN_DATE, text_from_time(fd->date), | |
500 SEARCH_COLUMN_DIMENSIONS, text_dim, | |
501 SEARCH_COLUMN_PATH, fd->path, | |
502 -1); | |
503 | |
504 g_free(text_size); | |
505 g_free(text_dim); | |
506 } | |
507 | |
508 static GList *search_result_refine_list(SearchData *sd) | |
509 { | |
510 GList *list = NULL; | |
511 GtkTreeModel *store; | |
512 GtkTreeIter iter; | |
513 gint valid; | |
514 | |
515 store = gtk_tree_view_get_model(GTK_TREE_VIEW(sd->result_view)); | |
516 | |
517 valid = gtk_tree_model_get_iter_first(store, &iter); | |
518 while (valid) | |
519 { | |
520 FileData *fd; | |
521 | |
522 gtk_tree_model_get(store, &iter, SEARCH_COLUMN_POINTER, &fd, -1); | |
523 list = g_list_prepend(list, fd); | |
524 | |
525 valid = gtk_tree_model_iter_next(store, &iter); | |
526 } | |
527 | |
528 /* clear it here, so that the FileData in list is not freed */ | |
529 gtk_list_store_clear(GTK_LIST_STORE(store)); | |
530 | |
531 return g_list_reverse(list); | |
532 } | |
533 | |
534 static gboolean search_result_free_node(GtkTreeModel *store, GtkTreePath *tpath, | |
535 GtkTreeIter *iter, gpointer data) | |
536 { | |
537 FileData *fd; | |
538 | |
539 gtk_tree_model_get(store, iter, SEARCH_COLUMN_POINTER, &fd, -1); | |
540 file_data_free(fd); | |
541 | |
542 return FALSE; | |
543 } | |
544 | |
545 static void search_result_clear(SearchData *sd) | |
546 { | |
547 GtkListStore *store; | |
548 | |
549 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(sd->result_view))); | |
550 | |
551 gtk_tree_model_foreach(GTK_TREE_MODEL(store), search_result_free_node, sd); | |
552 gtk_list_store_clear(store); | |
553 | |
554 sd->click_fd = NULL; | |
555 | |
556 thumb_loader_free(sd->thumb_loader); | |
557 sd->thumb_loader = NULL; | |
558 sd->thumb_fd = NULL; | |
559 | |
560 search_status_update(sd); | |
561 } | |
562 | |
563 static void search_result_remove_item(SearchData *sd, FileData *fd, GtkTreeIter *iter) | |
564 { | |
565 GtkTreeModel *store; | |
566 | |
567 if (!fd || !iter) return; | |
568 | |
569 store = gtk_tree_view_get_model(GTK_TREE_VIEW(sd->result_view)); | |
570 | |
571 tree_view_move_cursor_away(GTK_TREE_VIEW(sd->result_view), iter, TRUE); | |
572 | |
573 gtk_list_store_remove(GTK_LIST_STORE(store), iter); | |
574 if (sd->click_fd == fd) sd->click_fd = NULL; | |
575 if (sd->thumb_fd == fd) sd->thumb_fd = NULL; | |
576 file_data_free(fd); | |
577 } | |
578 | |
579 static void search_result_remove(SearchData *sd, FileData *fd) | |
580 { | |
581 GtkTreeModel *store; | |
582 GtkTreeIter iter; | |
583 gint valid; | |
584 | |
585 store = gtk_tree_view_get_model(GTK_TREE_VIEW(sd->result_view)); | |
586 valid = gtk_tree_model_get_iter_first(store, &iter); | |
587 while (valid) | |
588 { | |
589 FileData *fd_n; | |
590 | |
591 gtk_tree_model_get(store, &iter, SEARCH_COLUMN_POINTER, &fd_n, -1); | |
592 if (fd_n == fd) | |
593 { | |
594 search_result_remove_item(sd, fd_n, &iter); | |
595 return; | |
596 } | |
597 | |
598 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter); | |
599 } | |
600 } | |
601 | |
602 static void search_result_remove_selection(SearchData *sd) | |
603 { | |
604 GtkTreeSelection *selection; | |
605 GtkTreeModel *store; | |
606 GList *slist; | |
607 GList *flist = NULL; | |
608 GList *work; | |
609 | |
610 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(sd->result_view)); | |
611 slist = gtk_tree_selection_get_selected_rows(selection, &store); | |
612 work = slist; | |
613 while (work) | |
614 { | |
615 GtkTreePath *tpath = work->data; | |
616 GtkTreeIter iter; | |
617 FileData *fd; | |
618 | |
619 gtk_tree_model_get_iter(store, &iter, tpath); | |
620 gtk_tree_model_get(store, &iter, SEARCH_COLUMN_POINTER, &fd, -1); | |
621 flist = g_list_prepend(flist, fd); | |
622 work = work->next; | |
623 } | |
624 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL); | |
625 g_list_free(slist); | |
626 | |
627 work = flist; | |
628 while (work) | |
629 { | |
630 FileData *fd = work->data; | |
631 work = work->next; | |
632 | |
633 search_result_remove(sd, fd); | |
634 } | |
635 g_list_free(flist); | |
636 | |
637 search_status_update(sd); | |
638 } | |
639 | |
640 static void search_result_edit_selected(SearchData *sd, gint n) | |
641 { | |
642 GList *list; | |
643 | |
644 list = search_result_selection_list(sd); | |
645 start_editor_from_path_list(n, list); | |
646 path_list_free(list); | |
647 } | |
648 | |
649 static void search_result_collection_from_selection(SearchData *sd) | |
650 { | |
651 CollectWindow *w; | |
652 GList *list; | |
653 | |
654 list = search_result_selection_list(sd); | |
655 w = collection_window_new(NULL); | |
656 collection_table_add_path_list(w->table, list); | |
657 path_list_free(list); | |
658 } | |
659 | |
660 static gint search_result_update_idle_cb(gpointer data) | |
661 { | |
662 SearchData *sd = data; | |
663 | |
664 search_status_update(sd); | |
665 | |
666 sd->update_idle_id = -1; | |
667 return FALSE; | |
668 } | |
669 | |
670 static void search_result_update_idle_cancel(SearchData *sd) | |
671 { | |
672 if (sd->update_idle_id != -1) g_source_remove(sd->update_idle_id); | |
673 sd->update_idle_id = -1; | |
674 } | |
675 | |
676 static gboolean search_result_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, | |
677 GtkTreePath *tpath, gboolean selected, gpointer data) | |
678 { | |
679 SearchData *sd = data; | |
680 | |
681 if (sd->update_idle_id == -1) | |
682 { | |
683 sd->update_idle_id = g_idle_add(search_result_update_idle_cb, sd); | |
684 } | |
685 | |
686 return TRUE; | |
687 } | |
688 | |
689 /* | |
690 *------------------------------------------------------------------- | |
691 * result list thumbs | |
692 *------------------------------------------------------------------- | |
693 */ | |
694 | |
695 static void search_result_thumb_step(SearchData *sd); | |
696 | |
697 | |
698 static void search_result_thumb_set(SearchData *sd, FileData *fd, GtkTreeIter *iter) | |
699 { | |
700 GtkListStore *store; | |
701 GtkTreeIter iter_n; | |
702 | |
703 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(sd->result_view))); | |
704 if (!iter) | |
705 { | |
706 if (search_result_find_row(sd, fd, &iter_n) >= 0) iter = &iter_n; | |
707 } | |
708 | |
709 if (iter) gtk_list_store_set(store, iter, SEARCH_COLUMN_THUMB, fd->pixbuf, -1); | |
710 } | |
711 | |
712 static void search_result_thumb_do(SearchData *sd) | |
713 { | |
714 FileData *fd; | |
715 | |
716 if (!sd->thumb_loader || !sd->thumb_fd) return; | |
717 fd = sd->thumb_fd; | |
718 | |
719 if (fd->pixbuf) g_object_unref(fd->pixbuf); | |
720 fd->pixbuf = thumb_loader_get_pixbuf(sd->thumb_loader, TRUE); | |
721 | |
722 search_result_thumb_set(sd, fd, NULL); | |
723 } | |
724 | |
725 static void search_result_thumb_done_cb(ThumbLoader *tl, gpointer data) | |
726 { | |
727 SearchData *sd = data; | |
728 | |
729 search_result_thumb_do(sd); | |
730 search_result_thumb_step(sd); | |
731 } | |
732 | |
733 static void search_result_thumb_step(SearchData *sd) | |
734 { | |
735 GtkTreeModel *store; | |
736 GtkTreeIter iter; | |
737 FileData *fd = NULL; | |
738 gint valid; | |
739 gint row = 0; | |
740 gint length = 0; | |
741 | |
742 if (!sd->thumb_enable) return; | |
743 | |
744 store = gtk_tree_view_get_model(GTK_TREE_VIEW(sd->result_view)); | |
745 | |
746 valid = gtk_tree_model_get_iter_first(store, &iter); | |
747 while (!fd && valid) | |
748 { | |
749 GdkPixbuf *pixbuf; | |
750 | |
751 length++; | |
752 gtk_tree_model_get(store, &iter, SEARCH_COLUMN_POINTER, &fd, SEARCH_COLUMN_THUMB, &pixbuf, -1); | |
753 if (pixbuf || fd->pixbuf) | |
754 { | |
755 if (!pixbuf) gtk_list_store_set(GTK_LIST_STORE(store), &iter, SEARCH_COLUMN_THUMB, fd->pixbuf, -1); | |
756 row++; | |
757 fd = NULL; | |
758 } | |
759 valid = gtk_tree_model_iter_next(store, &iter); | |
760 } | |
761 if (valid) | |
762 { | |
763 while (gtk_tree_model_iter_next(store, &iter)) length++; | |
764 } | |
765 | |
766 if (!fd) | |
767 { | |
768 sd->thumb_fd = NULL; | |
769 thumb_loader_free(sd->thumb_loader); | |
770 sd->thumb_loader = NULL; | |
771 | |
772 search_progress_update(sd, TRUE, -1.0); | |
773 return; | |
774 } | |
775 | |
776 search_progress_update(sd, FALSE, (gdouble)row/length); | |
777 | |
778 sd->thumb_fd = fd; | |
779 thumb_loader_free(sd->thumb_loader); | |
780 sd->thumb_loader = thumb_loader_new(thumb_max_width, thumb_max_height); | |
781 | |
782 thumb_loader_set_callbacks(sd->thumb_loader, | |
783 search_result_thumb_done_cb, | |
784 search_result_thumb_done_cb, | |
785 NULL, | |
786 sd); | |
787 if (!thumb_loader_start(sd->thumb_loader, fd->path)) | |
788 { | |
789 search_result_thumb_do(sd); | |
790 search_result_thumb_step(sd); | |
791 } | |
792 } | |
793 | |
794 static void search_result_thumb_height(SearchData *sd) | |
795 { | |
796 GtkTreeViewColumn *column; | |
797 GtkCellRenderer *cell; | |
798 GList *list; | |
799 | |
800 column = gtk_tree_view_get_column(GTK_TREE_VIEW(sd->result_view), SEARCH_COLUMN_THUMB - 1); | |
801 if (!column) return; | |
802 | |
803 gtk_tree_view_column_set_fixed_width(column, (sd->thumb_enable) ? thumb_max_width : 4); | |
804 | |
805 list = gtk_tree_view_column_get_cell_renderers(column); | |
806 if (!list) return; | |
807 cell = list->data; | |
808 g_list_free(list); | |
809 | |
810 g_object_set(G_OBJECT(cell), "height", (sd->thumb_enable) ? thumb_max_height : -1, NULL); | |
811 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(sd->result_view)); | |
812 } | |
813 | |
814 static void search_result_thumb_enable(SearchData *sd, gint enable) | |
815 { | |
816 if (sd->thumb_enable == enable) return; | |
817 | |
818 if (sd->thumb_enable) | |
819 { | |
820 GtkTreeModel *store; | |
821 GtkTreeIter iter; | |
822 gint valid; | |
823 | |
824 thumb_loader_free(sd->thumb_loader); | |
825 sd->thumb_loader = NULL; | |
826 | |
827 store = gtk_tree_view_get_model(GTK_TREE_VIEW(sd->result_view)); | |
828 valid = gtk_tree_model_get_iter_first(store, &iter); | |
829 while (valid) | |
830 { | |
831 gtk_list_store_set(GTK_LIST_STORE(store), &iter, SEARCH_COLUMN_THUMB, NULL, -1); | |
832 valid = gtk_tree_model_iter_next(store, &iter); | |
833 } | |
834 search_progress_update(sd, TRUE, -1.0); | |
835 } | |
836 | |
837 sd->thumb_enable = enable; | |
838 | |
839 search_result_thumb_height(sd); | |
840 if (!sd->search_folder_list && !sd->search_file_list) search_result_thumb_step(sd); | |
841 } | |
842 | |
843 /* | |
844 *------------------------------------------------------------------- | |
845 * result list menu | |
846 *------------------------------------------------------------------- | |
847 */ | |
848 | |
849 static void sr_menu_view_cb(GtkWidget *widget, gpointer data) | |
850 { | |
851 SearchData *sd = data; | |
852 | |
853 if (sd->click_fd) layout_image_set_path(NULL, sd->click_fd->path); | |
854 } | |
855 | |
856 static void sr_menu_viewnew_cb(GtkWidget *widget, gpointer data) | |
857 { | |
858 SearchData *sd = data; | |
859 GList *list; | |
860 | |
861 list = search_result_selection_list(sd); | |
862 view_window_new_from_list(list); | |
863 path_list_free(list); | |
864 } | |
865 | |
866 static void sr_menu_select_all_cb(GtkWidget *widget, gpointer data) | |
867 { | |
868 SearchData *sd = data; | |
869 GtkTreeSelection *selection; | |
870 | |
871 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(sd->result_view)); | |
872 gtk_tree_selection_select_all(selection); | |
873 } | |
874 | |
875 static void sr_menu_select_none_cb(GtkWidget *widget, gpointer data) | |
876 { | |
877 SearchData *sd = data; | |
878 GtkTreeSelection *selection; | |
879 | |
880 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(sd->result_view)); | |
881 gtk_tree_selection_unselect_all(selection); | |
882 } | |
883 | |
884 static void sr_menu_edit_cb(GtkWidget *widget, gpointer data) | |
885 { | |
886 SearchData *sd; | |
887 gint n; | |
888 | |
889 sd = submenu_item_get_data(widget); | |
890 n = GPOINTER_TO_INT(data); | |
891 if (!sd) return; | |
892 | |
893 search_result_edit_selected(sd, n); | |
894 } | |
895 | |
896 static void sr_menu_info_cb(GtkWidget *widget, gpointer data) | |
897 { | |
898 SearchData *sd = data; | |
899 | |
900 info_window_new(NULL, search_result_selection_list(sd)); | |
901 } | |
902 | |
903 static void sr_menu_collection_cb(GtkWidget *widget, gpointer data) | |
904 { | |
905 SearchData *sd = data; | |
906 | |
907 search_result_collection_from_selection(sd); | |
908 } | |
909 | |
910 static void sr_menu_print_cb(GtkWidget *widget, gpointer data) | |
911 { | |
912 SearchData *sd = data; | |
913 const gchar *path; | |
914 | |
915 path = (sd->click_fd) ? sd->click_fd->path : NULL; | |
916 | |
917 print_window_new(path, search_result_selection_list(sd), | |
918 search_result_get_path_list(sd), sd->window); | |
919 } | |
920 | |
921 static void sr_menu_copy_cb(GtkWidget *widget, gpointer data) | |
922 { | |
923 SearchData *sd = data; | |
924 | |
925 file_util_copy(NULL, search_result_selection_list(sd), NULL, sd->window); | |
926 } | |
927 | |
928 static void sr_menu_move_cb(GtkWidget *widget, gpointer data) | |
929 { | |
930 SearchData *sd = data; | |
931 | |
932 file_util_move(NULL, search_result_selection_list(sd), NULL, sd->window); | |
933 } | |
934 | |
935 static void sr_menu_rename_cb(GtkWidget *widget, gpointer data) | |
936 { | |
937 SearchData *sd = data; | |
938 | |
939 file_util_rename(NULL, search_result_selection_list(sd), sd->window); | |
940 } | |
941 | |
942 static void sr_menu_delete_cb(GtkWidget *widget, gpointer data) | |
943 { | |
944 SearchData *sd = data; | |
945 | |
946 file_util_delete(NULL, search_result_selection_list(sd), sd->window); | |
947 } | |
948 | |
949 static void sr_menu_remove_cb(GtkWidget *widget, gpointer data) | |
950 { | |
951 SearchData *sd = data; | |
952 | |
953 search_result_remove_selection(sd); | |
954 } | |
955 | |
956 static void sr_menu_clear_cb(GtkWidget *widget, gpointer data) | |
957 { | |
958 SearchData *sd = data; | |
959 | |
960 search_result_clear(sd); | |
961 } | |
962 | |
963 static GtkWidget *search_result_menu(SearchData *sd, gint on_row, gint empty) | |
964 { | |
965 GtkWidget *menu; | |
966 GtkWidget *item; | |
967 | |
968 menu = popup_menu_short_lived(); | |
969 menu_item_add_sensitive(menu, _("_View"), on_row, | |
970 G_CALLBACK(sr_menu_view_cb), sd); | |
971 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, on_row, | |
972 G_CALLBACK(sr_menu_viewnew_cb), sd); | |
973 menu_item_add_divider(menu); | |
974 menu_item_add_sensitive(menu, _("Select all"), !empty, | |
975 G_CALLBACK(sr_menu_select_all_cb), sd); | |
976 menu_item_add_sensitive(menu, _("Select none"), !empty, | |
977 G_CALLBACK(sr_menu_select_none_cb), sd); | |
978 menu_item_add_divider(menu); | |
979 submenu_add_edit(menu, &item, G_CALLBACK(sr_menu_edit_cb), sd); | |
980 if (!on_row) gtk_widget_set_sensitive(item, FALSE); | |
981 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, on_row, | |
982 G_CALLBACK(sr_menu_info_cb), sd); | |
983 menu_item_add_stock_sensitive(menu, _("Add to new collection"), GTK_STOCK_INDEX, on_row, | |
984 G_CALLBACK(sr_menu_collection_cb), sd); | |
985 menu_item_add_stock_sensitive(menu, _("Print..."), GTK_STOCK_PRINT, on_row, | |
986 G_CALLBACK(sr_menu_print_cb), sd); | |
987 menu_item_add_divider(menu); | |
988 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, on_row, | |
989 G_CALLBACK(sr_menu_copy_cb), sd); | |
990 menu_item_add_sensitive(menu, _("_Move..."), on_row, | |
991 G_CALLBACK(sr_menu_move_cb), sd); | |
992 menu_item_add_sensitive(menu, _("_Rename..."), on_row, | |
993 G_CALLBACK(sr_menu_rename_cb), sd); | |
994 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, on_row, | |
995 G_CALLBACK(sr_menu_delete_cb), sd); | |
996 menu_item_add_divider(menu); | |
997 menu_item_add_stock_sensitive(menu, _("Rem_ove"), GTK_STOCK_REMOVE, on_row, | |
998 G_CALLBACK(sr_menu_remove_cb), sd); | |
999 menu_item_add_stock_sensitive(menu, _("C_lear"), GTK_STOCK_CLEAR, !empty, | |
1000 G_CALLBACK(sr_menu_clear_cb), sd); | |
1001 | |
1002 return menu; | |
1003 } | |
1004 | |
1005 static void search_result_menu_pos_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data) | |
1006 { | |
1007 SearchData *sd = data; | |
1008 GtkTreePath *tpath; | |
1009 gint cx, cy, cw, ch; | |
1010 | |
1011 gtk_tree_view_get_cursor(GTK_TREE_VIEW(sd->result_view), &tpath, NULL); | |
1012 if (!tpath) return; | |
1013 | |
1014 tree_view_get_cell_clamped(GTK_TREE_VIEW(sd->result_view), tpath, | |
1015 SEARCH_COLUMN_NAME - 1, TRUE, &cx, &cy, &cw, &ch); | |
1016 gtk_tree_path_free(tpath); | |
1017 cy += ch; | |
1018 popup_menu_position_clamp(menu, &cx, &cy, 0); | |
1019 *x = cx; | |
1020 *y = cy; | |
1021 } | |
1022 | |
1023 /* | |
1024 *------------------------------------------------------------------- | |
1025 * result list input | |
1026 *------------------------------------------------------------------- | |
1027 */ | |
1028 | |
1029 static gint search_result_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) | |
1030 { | |
1031 SearchData *sd = data; | |
1032 GtkTreeModel *store; | |
1033 GtkTreePath *tpath; | |
1034 GtkTreeIter iter; | |
1035 FileData *fd = NULL; | |
1036 | |
1037 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget)); | |
1038 | |
1039 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y, | |
1040 &tpath, NULL, NULL, NULL)) | |
1041 { | |
1042 gtk_tree_model_get_iter(store, &iter, tpath); | |
1043 gtk_tree_model_get(store, &iter, SEARCH_COLUMN_POINTER, &fd, -1); | |
1044 gtk_tree_path_free(tpath); | |
1045 } | |
1046 | |
1047 sd->click_fd = fd; | |
1048 | |
1049 if (bevent->button == 3) | |
1050 { | |
1051 GtkWidget *menu; | |
1052 | |
1053 menu = search_result_menu(sd, (fd != NULL), (search_result_count(sd, NULL) == 0)); | |
1054 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, bevent->button, bevent->time); | |
1055 } | |
1056 | |
1057 if (!fd) return FALSE; | |
1058 | |
1059 if (bevent->button == 1 && bevent->type == GDK_2BUTTON_PRESS) | |
1060 { | |
1061 layout_image_set_path(NULL, fd->path); | |
1062 } | |
1063 | |
1064 if (bevent->button == 2) return TRUE; | |
1065 | |
1066 if (bevent->button == 3) | |
1067 { | |
1068 if (!search_result_row_selected(sd, fd)) | |
1069 { | |
1070 GtkTreeSelection *selection; | |
1071 | |
1072 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); | |
1073 gtk_tree_selection_unselect_all(selection); | |
1074 gtk_tree_selection_select_iter(selection, &iter); | |
1075 | |
1076 tpath = gtk_tree_model_get_path(store, &iter); | |
1077 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE); | |
1078 gtk_tree_path_free(tpath); | |
1079 } | |
1080 return TRUE; | |
1081 } | |
1082 | |
1083 if (bevent->button == 1 && bevent->type == GDK_BUTTON_PRESS && | |
1084 !(bevent->state & GDK_SHIFT_MASK ) && | |
1085 !(bevent->state & GDK_CONTROL_MASK ) && | |
1086 search_result_row_selected(sd, fd)) | |
1087 { | |
1088 /* this selection handled on release_cb */ | |
1089 gtk_widget_grab_focus(widget); | |
1090 return TRUE; | |
1091 } | |
1092 | |
1093 return FALSE; | |
1094 } | |
1095 | |
1096 static gint search_result_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data) | |
1097 { | |
1098 SearchData *sd = data; | |
1099 GtkTreeModel *store; | |
1100 GtkTreePath *tpath; | |
1101 GtkTreeIter iter; | |
1102 | |
1103 FileData *fd = NULL; | |
1104 | |
1105 if (bevent->button != 1 && bevent->button != 2) return TRUE; | |
1106 | |
1107 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget)); | |
1108 | |
1109 if ((bevent->x != 0 || bevent->y != 0) && | |
1110 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y, | |
1111 &tpath, NULL, NULL, NULL)) | |
1112 { | |
1113 gtk_tree_model_get_iter(store, &iter, tpath); | |
1114 gtk_tree_model_get(store, &iter, SEARCH_COLUMN_POINTER, &fd, -1); | |
1115 gtk_tree_path_free(tpath); | |
1116 } | |
1117 | |
1118 if (bevent->button == 2) | |
1119 { | |
1120 if (fd && sd->click_fd == fd) | |
1121 { | |
1122 GtkTreeSelection *selection; | |
1123 | |
1124 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); | |
1125 if (search_result_row_selected(sd, fd)) | |
1126 { | |
1127 gtk_tree_selection_unselect_iter(selection, &iter); | |
1128 } | |
1129 else | |
1130 { | |
1131 gtk_tree_selection_select_iter(selection, &iter); | |
1132 } | |
1133 } | |
1134 return TRUE; | |
1135 } | |
1136 | |
1137 if (fd && sd->click_fd == fd && | |
1138 !(bevent->state & GDK_SHIFT_MASK ) && | |
1139 !(bevent->state & GDK_CONTROL_MASK ) && | |
1140 search_result_row_selected(sd, fd)) | |
1141 { | |
1142 GtkTreeSelection *selection; | |
1143 | |
1144 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); | |
1145 gtk_tree_selection_unselect_all(selection); | |
1146 gtk_tree_selection_select_iter(selection, &iter); | |
1147 | |
1148 tpath = gtk_tree_model_get_path(store, &iter); | |
1149 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE); | |
1150 gtk_tree_path_free(tpath); | |
1151 | |
1152 return TRUE; | |
1153 } | |
1154 | |
1155 return FALSE; | |
1156 } | |
1157 | |
1158 static gint search_result_keypress_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) | |
1159 { | |
1160 SearchData *sd = data; | |
1161 gint stop_signal = FALSE; | |
1162 GtkTreeModel *store; | |
1163 GtkTreeSelection *selection; | |
1164 GList *slist; | |
1165 FileData *fd = NULL; | |
1166 | |
1167 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(sd->result_view)); | |
1168 slist = gtk_tree_selection_get_selected_rows(selection, &store); | |
1169 if (slist) | |
1170 { | |
1171 GtkTreePath *tpath; | |
1172 GtkTreeIter iter; | |
1173 GList *last; | |
1174 | |
1175 last = g_list_last(slist); | |
1176 tpath = last->data; | |
1177 | |
1178 /* last is newest selected file */ | |
1179 gtk_tree_model_get_iter(store, &iter, tpath); | |
1180 gtk_tree_model_get(store, &iter, SEARCH_COLUMN_POINTER, &fd, -1); | |
1181 } | |
1182 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL); | |
1183 g_list_free(slist); | |
1184 | |
1185 if (event->state & GDK_CONTROL_MASK) | |
1186 { | |
1187 gint edit_val = -1; | |
1188 | |
1189 switch (event->keyval) | |
1190 { | |
1191 case '1': | |
1192 edit_val = 0; | |
1193 break; | |
1194 case '2': | |
1195 edit_val = 1; | |
1196 break; | |
1197 case '3': | |
1198 edit_val = 2; | |
1199 break; | |
1200 case '4': | |
1201 edit_val = 3; | |
1202 break; | |
1203 case '5': | |
1204 edit_val = 4; | |
1205 break; | |
1206 case '6': | |
1207 edit_val = 5; | |
1208 break; | |
1209 case '7': | |
1210 edit_val = 6; | |
1211 break; | |
1212 case '8': | |
1213 edit_val = 7; | |
1214 break; | |
1215 case '9': | |
1216 edit_val = 8; | |
1217 break; | |
1218 case '0': | |
1219 edit_val = 9; | |
1220 break; | |
1221 case 'C': case 'c': | |
1222 stop_signal = TRUE; | |
1223 file_util_copy(NULL, search_result_selection_list(sd), NULL, widget); | |
1224 break; | |
1225 case 'M': case 'm': | |
1226 stop_signal = TRUE; | |
1227 file_util_move(NULL, search_result_selection_list(sd), NULL, widget); | |
1228 break; | |
1229 case 'R': case 'r': | |
1230 stop_signal = TRUE; | |
1231 file_util_rename(NULL, search_result_selection_list(sd), widget); | |
1232 break; | |
1233 case 'D': case 'd': | |
1234 stop_signal = TRUE; | |
1235 file_util_delete(NULL, search_result_selection_list(sd), widget); | |
1236 break; | |
1237 case 'P': case 'p': | |
1238 stop_signal = TRUE; | |
1239 info_window_new(NULL, search_result_selection_list(sd)); | |
1240 break; | |
1241 case 'A': case 'a': | |
1242 if (event->state & GDK_SHIFT_MASK) | |
1243 { | |
1244 gtk_tree_selection_unselect_all(selection); | |
1245 } | |
1246 else | |
1247 { | |
1248 gtk_tree_selection_select_all(selection); | |
1249 } | |
1250 stop_signal = TRUE; | |
1251 break; | |
1252 case GDK_Delete: case GDK_KP_Delete: | |
1253 search_result_clear(sd); | |
1254 stop_signal = TRUE; | |
1255 break; | |
1256 default: | |
1257 break; | |
1258 } | |
1259 | |
1260 if (edit_val >= 0) | |
1261 { | |
1262 search_result_edit_selected(sd, edit_val); | |
1263 stop_signal = TRUE; | |
1264 } | |
1265 } | |
1266 else | |
1267 { | |
1268 switch (event->keyval) | |
1269 { | |
1270 case GDK_Return: case GDK_KP_Enter: | |
1271 if (fd) layout_image_set_path(NULL, fd->path); | |
1272 stop_signal = TRUE; | |
1273 break; | |
1274 case 'V': case 'v': | |
1275 { | |
1276 GList *list; | |
1277 | |
1278 list = search_result_selection_list(sd); | |
1279 view_window_new_from_list(list); | |
1280 path_list_free(list); | |
1281 stop_signal = TRUE; | |
1282 } | |
1283 break; | |
1284 case GDK_Delete: case GDK_KP_Delete: | |
1285 search_result_remove_selection(sd); | |
1286 stop_signal = TRUE; | |
1287 break; | |
1288 case 'C': case 'c': | |
1289 search_result_collection_from_selection(sd); | |
1290 stop_signal = TRUE; | |
1291 break; | |
1292 case GDK_Menu: | |
1293 case GDK_F10: | |
1294 { | |
1295 GtkWidget *menu; | |
1296 | |
1297 sd->click_fd = fd; | |
1298 menu = search_result_menu(sd, (fd != NULL), (search_result_count(sd, NULL) > 0)); | |
1299 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, | |
1300 search_result_menu_pos_cb, sd, 0, GDK_CURRENT_TIME); | |
1301 stop_signal = TRUE; | |
1302 } | |
1303 break; | |
1304 default: | |
1305 break; | |
1306 } | |
1307 } | |
1308 | |
1309 return stop_signal; | |
1310 } | |
1311 | |
1312 static gint search_window_keypress_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) | |
1313 { | |
1314 SearchData *sd = data; | |
1315 gint stop_signal = FALSE; | |
1316 | |
1317 if (event->state & GDK_CONTROL_MASK) | |
1318 { | |
1319 switch (event->keyval) | |
1320 { | |
1321 case 'T': case 't': | |
1322 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sd->button_thumbs), | |
1323 !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sd->button_thumbs))); | |
1324 stop_signal = TRUE; | |
1325 break; | |
1326 case 'W': case 'w': | |
1327 search_window_close(sd); | |
1328 stop_signal = TRUE; | |
1329 break; | |
1330 default: | |
1331 break; | |
1332 } | |
1333 } | |
1334 | |
1335 return stop_signal; | |
1336 } | |
1337 | |
1338 /* | |
1339 *------------------------------------------------------------------- | |
1340 * dnd | |
1341 *------------------------------------------------------------------- | |
1342 */ | |
1343 | |
1344 static GtkTargetEntry result_drag_types[] = { | |
1345 { "text/uri-list", 0, TARGET_URI_LIST }, | |
1346 { "text/plain", 0, TARGET_TEXT_PLAIN } | |
1347 }; | |
1348 static gint n_result_drag_types = 2; | |
1349 | |
1350 static void search_dnd_data_set(GtkWidget *widget, GdkDragContext *context, | |
1351 GtkSelectionData *selection_data, guint info, | |
1352 guint time, gpointer data) | |
1353 { | |
1354 SearchData *sd = data; | |
1355 gchar *uri_text; | |
1356 gint length; | |
1357 GList *list; | |
1358 | |
1359 switch (info) | |
1360 { | |
1361 case TARGET_URI_LIST: | |
1362 case TARGET_TEXT_PLAIN: | |
1363 list = search_result_selection_list(sd); | |
1364 if (!list) return; | |
1365 uri_text = uri_text_from_list(list, &length, (info == TARGET_TEXT_PLAIN)); | |
1366 path_list_free(list); | |
1367 break; | |
1368 default: | |
1369 uri_text = NULL; | |
1370 break; | |
1371 } | |
1372 | |
1373 if (uri_text) gtk_selection_data_set(selection_data, selection_data->target, | |
1374 8, uri_text, length); | |
1375 g_free(uri_text); | |
1376 } | |
1377 | |
1378 static void search_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data) | |
1379 { | |
1380 SearchData *sd = data; | |
1381 | |
1382 if (sd->click_fd && !search_result_row_selected(sd, sd->click_fd)) | |
1383 { | |
1384 GtkListStore *store; | |
1385 GtkTreeIter iter; | |
1386 | |
1387 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(widget))); | |
1388 if (search_result_find_row(sd, sd->click_fd, &iter) >= 0) | |
1389 { | |
1390 GtkTreeSelection *selection; | |
1391 GtkTreePath *tpath; | |
1392 | |
1393 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); | |
1394 gtk_tree_selection_unselect_all(selection); | |
1395 gtk_tree_selection_select_iter(selection, &iter); | |
1396 | |
1397 tpath = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter); | |
1398 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE); | |
1399 gtk_tree_path_free(tpath); | |
1400 } | |
1401 } | |
1402 | |
1403 if (sd->thumb_enable && | |
1404 sd->click_fd && sd->click_fd->pixbuf) | |
1405 { | |
1406 dnd_set_drag_icon(widget, context, sd->click_fd->pixbuf, search_result_selection_count(sd, NULL)); | |
1407 } | |
1408 } | |
1409 | |
1410 static void search_dnd_init(SearchData *sd) | |
1411 { | |
1412 gtk_drag_source_set(sd->result_view, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK, | |
1413 result_drag_types, n_result_drag_types, | |
1414 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK); | |
1415 g_signal_connect(G_OBJECT(sd->result_view), "drag_data_get", | |
1416 G_CALLBACK(search_dnd_data_set), sd); | |
1417 g_signal_connect(G_OBJECT(sd->result_view), "drag_begin", | |
1418 G_CALLBACK(search_dnd_begin), sd); | |
1419 #if 0 | |
1420 g_signal_connect(G_OBJECT(sd->result_view), "drag_end", | |
1421 G_CALLBACK(search_dnd_end), sd); | |
1422 #endif | |
1423 | |
1424 } | |
1425 | |
1426 /* | |
1427 *------------------------------------------------------------------- | |
1428 * search core | |
1429 *------------------------------------------------------------------- | |
1430 */ | |
1431 | |
1432 #define MATCH_IS_BETWEEN(val, a, b) (b > a ? (val >= a && val <= b) : (val >= b && val <= a)) | |
1433 | |
1434 static gint search_step_cb(gpointer data); | |
1435 | |
1436 | |
1437 static void search_buffer_flush(SearchData *sd) | |
1438 { | |
1439 GList *work; | |
1440 | |
1441 work = g_list_last(sd->search_buffer_list); | |
1442 while (work) | |
1443 { | |
1444 MatchFileData *mfd = work->data; | |
1445 work = work->prev; | |
1446 | |
1447 search_result_append(sd, mfd); | |
1448 } | |
1449 | |
1450 g_list_free(sd->search_buffer_list); | |
1451 sd->search_buffer_list = NULL; | |
1452 sd->search_buffer_count = 0; | |
1453 } | |
1454 | |
1455 static void search_stop(SearchData *sd) | |
1456 { | |
1457 if (sd->search_idle_id != -1) | |
1458 { | |
1459 g_source_remove(sd->search_idle_id); | |
1460 sd->search_idle_id = -1; | |
1461 } | |
1462 | |
1463 image_loader_free(sd->img_loader); | |
1464 sd->img_loader = NULL; | |
1465 cache_sim_data_free(sd->img_cd); | |
1466 sd->img_cd = NULL; | |
1467 | |
1468 cache_sim_data_free(sd->search_similarity_cd); | |
1469 sd->search_similarity_cd = NULL; | |
1470 | |
1471 search_buffer_flush(sd); | |
1472 | |
1473 filelist_free(sd->search_folder_list); | |
1474 sd->search_folder_list = NULL; | |
1475 | |
1476 g_list_free(sd->search_done_list); | |
1477 sd->search_done_list = NULL; | |
1478 | |
1479 filelist_free(sd->search_file_list); | |
1480 sd->search_file_list = NULL; | |
1481 | |
1482 gtk_widget_set_sensitive(sd->box_search, TRUE); | |
1483 spinner_set_interval(sd->spinner, -1); | |
1484 gtk_widget_set_sensitive(sd->button_start, TRUE); | |
1485 gtk_widget_set_sensitive(sd->button_stop, FALSE); | |
1486 search_progress_update(sd, TRUE, -1.0); | |
1487 search_status_update(sd); | |
1488 } | |
1489 | |
1490 static void search_file_load_process(SearchData *sd, CacheData *cd) | |
1491 { | |
1492 GdkPixbuf *pixbuf; | |
1493 | |
1494 pixbuf = image_loader_get_pixbuf(sd->img_loader); | |
1495 | |
1496 if (cd && pixbuf) | |
1497 { | |
1498 if (!cd->dimensions) | |
1499 { | |
1500 cache_sim_data_set_dimensions(cd, gdk_pixbuf_get_width(pixbuf), | |
1501 gdk_pixbuf_get_height(pixbuf)); | |
1502 } | |
1503 | |
1504 if (sd->match_similarity_enable && !cd->similarity) | |
1505 { | |
1506 ImageSimilarityData *sim; | |
1507 | |
1508 sim = image_sim_new_from_pixbuf(pixbuf); | |
1509 cache_sim_data_set_similarity(cd, sim); | |
1510 image_sim_free(sim); | |
1511 } | |
1512 | |
1513 if (enable_thumb_caching && | |
1514 sd->img_loader && sd->img_loader->path) | |
1515 { | |
1516 gchar *base; | |
1517 const gchar *path; | |
1518 mode_t mode = 0755; | |
1519 | |
1520 path = sd->img_loader->path; | |
1521 base = cache_get_location(CACHE_TYPE_SIM, path, FALSE, &mode); | |
1522 if (cache_ensure_dir_exists(base, mode)) | |
1523 { | |
1524 g_free(cd->path); | |
1525 cd->path = cache_get_location(CACHE_TYPE_SIM, path, TRUE, NULL); | |
1526 if (cache_sim_data_save(cd)) | |
1527 { | |
1528 filetime_set(cd->path, filetime(sd->img_loader->path)); | |
1529 } | |
1530 } | |
1531 g_free(base); | |
1532 } | |
1533 } | |
1534 | |
1535 image_loader_free(sd->img_loader); | |
1536 sd->img_loader = NULL; | |
1537 | |
1538 sd->search_idle_id = g_idle_add(search_step_cb, sd); | |
1539 } | |
1540 | |
1541 static void search_file_load_done_cb(ImageLoader *il, gpointer data) | |
1542 { | |
1543 SearchData *sd = data; | |
1544 search_file_load_process(sd, sd->img_cd); | |
1545 } | |
1546 | |
1547 static gint search_file_do_extra(SearchData *sd, FileData *fd, gint *match, | |
1548 gint *width, gint *height, gint *simval) | |
1549 { | |
1550 gint new_data = FALSE; | |
1551 gint tmatch = TRUE; | |
1552 gint tested = FALSE; | |
1553 | |
1554 if (!sd->img_cd) | |
1555 { | |
1556 gchar *cd_path; | |
1557 | |
1558 new_data = TRUE; | |
1559 | |
1560 cd_path = cache_find_location(CACHE_TYPE_SIM, fd->path); | |
1561 if (cd_path && filetime(fd->path) == filetime(cd_path)) | |
1562 { | |
1563 sd->img_cd = cache_sim_data_load(cd_path); | |
1564 } | |
1565 g_free(cd_path); | |
1566 } | |
1567 | |
1568 if (!sd->img_cd) | |
1569 { | |
1570 sd->img_cd = cache_sim_data_new(); | |
1571 } | |
1572 | |
1573 if (new_data) | |
1574 { | |
1575 if ((sd->match_dimensions_enable && !sd->img_cd->dimensions) || | |
1576 (sd->match_similarity_enable && !sd->img_cd->similarity)) | |
1577 { | |
1578 sd->img_loader = image_loader_new(fd->path); | |
1579 image_loader_set_error_func(sd->img_loader, search_file_load_done_cb, sd); | |
1580 if (image_loader_start(sd->img_loader, search_file_load_done_cb, sd)) | |
1581 { | |
1582 return TRUE; | |
1583 } | |
1584 else | |
1585 { | |
1586 image_loader_free(sd->img_loader); | |
1587 sd->img_loader = NULL; | |
1588 } | |
1589 } | |
1590 } | |
1591 | |
1592 if (tmatch && sd->match_dimensions_enable && sd->img_cd->dimensions) | |
1593 { | |
1594 CacheData *cd = sd->img_cd; | |
1595 | |
1596 tmatch = FALSE; | |
1597 tested = TRUE; | |
1598 | |
1599 if (sd->match_dimensions == SEARCH_MATCH_EQUAL) | |
1600 { | |
1601 tmatch = (cd->width == sd->search_width && cd->height == sd->search_height); | |
1602 } | |
1603 else if (sd->match_dimensions == SEARCH_MATCH_UNDER) | |
1604 { | |
1605 tmatch = (cd->width < sd->search_width && cd->height < sd->search_height); | |
1606 } | |
1607 else if (sd->match_dimensions == SEARCH_MATCH_OVER) | |
1608 { | |
1609 tmatch = (cd->width > sd->search_width && cd->height > sd->search_height); | |
1610 } | |
1611 else if (sd->match_dimensions == SEARCH_MATCH_BETWEEN) | |
1612 { | |
1613 tmatch = (MATCH_IS_BETWEEN(cd->width, sd->search_width, sd->search_width_end) && | |
1614 MATCH_IS_BETWEEN(cd->height, sd->search_height, sd->search_height_end)); | |
1615 } | |
1616 } | |
1617 | |
1618 if (tmatch && sd->match_similarity_enable && sd->img_cd->similarity) | |
1619 { | |
1620 gdouble value = 0.0; | |
1621 | |
1622 tmatch = FALSE; | |
1623 tested = TRUE; | |
1624 | |
1625 /* fixme: implement similarity checking */ | |
1626 if (sd->search_similarity_cd && sd->search_similarity_cd->similarity) | |
1627 { | |
1628 gdouble result; | |
1629 | |
1630 result = image_sim_compare_fast(sd->search_similarity_cd->sim, sd->img_cd->sim, | |
1631 (gdouble)sd->search_similarity / 100.0); | |
1632 result *= 100.0; | |
1633 if (result >= (gdouble)sd->search_similarity) | |
1634 { | |
1635 tmatch = TRUE; | |
1636 value = (gint)result; | |
1637 } | |
1638 } | |
1639 | |
1640 if (simval) *simval = value; | |
1641 } | |
1642 | |
1643 if (sd->img_cd->dimensions) | |
1644 { | |
1645 if (width) *width = sd->img_cd->width; | |
1646 if (height) *height = sd->img_cd->height; | |
1647 } | |
1648 | |
1649 cache_sim_data_free(sd->img_cd); | |
1650 sd->img_cd = NULL; | |
1651 | |
1652 *match = (tmatch && tested); | |
1653 | |
1654 return FALSE; | |
1655 } | |
1656 | |
1657 static gint search_file_next(SearchData *sd) | |
1658 { | |
1659 FileData *fd; | |
1660 gint match = TRUE; | |
1661 gint tested = FALSE; | |
1662 gint extra_only = FALSE; | |
1663 gint width = 0; | |
1664 gint height = 0; | |
1665 gint sim = 0; | |
1666 | |
1667 if (!sd->search_file_list) return FALSE; | |
1668 | |
1669 if (sd->img_cd) | |
1670 { | |
1671 /* on end of a CacheData load, skip recomparing non-extra match types */ | |
1672 extra_only = TRUE; | |
1673 match = FALSE; | |
1674 } | |
1675 else | |
1676 { | |
1677 sd->search_total++; | |
1678 } | |
1679 | |
1680 fd = sd->search_file_list->data; | |
1681 | |
1682 if (match && sd->match_name_enable && sd->search_name) | |
1683 { | |
1684 tested = TRUE; | |
1685 match = FALSE; | |
1686 | |
1687 if (sd->match_name == SEARCH_MATCH_EQUAL) | |
1688 { | |
1689 if (sd->search_name_match_case) | |
1690 { | |
1691 match = (strcmp(fd->name, sd->search_name) == 0); | |
1692 } | |
1693 else | |
1694 { | |
1695 match = (strcasecmp(fd->name, sd->search_name) == 0); | |
1696 } | |
1697 } | |
1698 else if (sd->match_name == SEARCH_MATCH_CONTAINS) | |
1699 { | |
1700 if (sd->search_name_match_case) | |
1701 { | |
1702 match = (strstr(fd->name, sd->search_name) != NULL); | |
1703 } | |
1704 else | |
1705 { | |
1706 /* sd->search_name is converted in search_start() */ | |
1707 gchar *haystack = g_utf8_strdown(fd->name, -1); | |
1708 match = (strstr(haystack, sd->search_name) != NULL); | |
1709 g_free(haystack); | |
1710 } | |
1711 } | |
1712 } | |
1713 | |
1714 if (match && sd->match_size_enable) | |
1715 { | |
1716 tested = TRUE; | |
1717 match = FALSE; | |
1718 | |
1719 if (sd->match_size == SEARCH_MATCH_EQUAL) | |
1720 { | |
1721 match = (fd->size == sd->search_size); | |
1722 } | |
1723 else if (sd->match_size == SEARCH_MATCH_UNDER) | |
1724 { | |
1725 match = (fd->size < sd->search_size); | |
1726 } | |
1727 else if (sd->match_size == SEARCH_MATCH_OVER) | |
1728 { | |
1729 match = (fd->size > sd->search_size); | |
1730 } | |
1731 else if (sd->match_size == SEARCH_MATCH_BETWEEN) | |
1732 { | |
1733 match = MATCH_IS_BETWEEN(fd->size, sd->search_size, sd->search_size_end); | |
1734 } | |
1735 } | |
1736 | |
1737 if (match && sd->match_date_enable) | |
1738 { | |
1739 tested = TRUE; | |
1740 match = FALSE; | |
1741 | |
1742 if (sd->match_date == SEARCH_MATCH_EQUAL) | |
1743 { | |
1744 struct tm *lt; | |
1745 | |
1746 lt = localtime(&fd->date); | |
1747 match = (lt && | |
1748 lt->tm_year == sd->search_date_y - 1900 && | |
1749 lt->tm_mon == sd->search_date_m - 1 && | |
1750 lt->tm_mday == sd->search_date_d); | |
1751 } | |
1752 else if (sd->match_date == SEARCH_MATCH_UNDER) | |
1753 { | |
1754 match = (fd->date < convert_dmy_to_time(sd->search_date_d, sd->search_date_m, sd->search_date_y)); | |
1755 } | |
1756 else if (sd->match_date == SEARCH_MATCH_OVER) | |
1757 { | |
1758 match = (fd->date > convert_dmy_to_time(sd->search_date_d, sd->search_date_m, sd->search_date_y) + 60 * 60 * 24 - 1); | |
1759 } | |
1760 else if (sd->match_date == SEARCH_MATCH_BETWEEN) | |
1761 { | |
1762 time_t a = convert_dmy_to_time(sd->search_date_d, sd->search_date_m, sd->search_date_y); | |
1763 time_t b = convert_dmy_to_time(sd->search_date_end_d, sd->search_date_end_m, sd->search_date_end_y); | |
1764 | |
1765 if (b >= a) | |
1766 { | |
1767 b += 60 * 60 * 24 - 1; | |
1768 } | |
1769 else | |
1770 { | |
1771 a += 60 * 60 * 24 - 1; | |
1772 } | |
1773 match = MATCH_IS_BETWEEN(fd->date, a, b); | |
1774 } | |
1775 } | |
1776 | |
1777 if (match && sd->match_keywords_enable && sd->search_keyword_list) | |
1778 { | |
1779 GList *list; | |
1780 | |
1781 tested = TRUE; | |
1782 match = FALSE; | |
1783 | |
1784 if (comment_cache_read(fd->path, &list, NULL)) | |
1785 { | |
1786 GList *needle; | |
1787 GList *haystack; | |
1788 | |
1789 if (sd->match_keywords == SEARCH_MATCH_ALL) | |
1790 { | |
1791 gint found = TRUE; | |
1792 | |
1793 needle = sd->search_keyword_list; | |
1794 while (needle && found) | |
1795 { | |
1796 found = FALSE; | |
1797 haystack = list; | |
1798 while (haystack && !found) | |
1799 { | |
1800 found = (strcasecmp((gchar *)needle->data, | |
1801 (gchar *)haystack->data) == 0); | |
1802 haystack = haystack->next; | |
1803 } | |
1804 needle = needle->next; | |
1805 } | |
1806 | |
1807 match = found; | |
1808 } | |
1809 else if (sd->match_keywords == SEARCH_MATCH_ANY) | |
1810 { | |
1811 gint found = FALSE; | |
1812 | |
1813 needle = sd->search_keyword_list; | |
1814 while (needle && !found) | |
1815 { | |
1816 haystack = list; | |
1817 while (haystack && !found) | |
1818 { | |
1819 found = (strcasecmp((gchar *)needle->data, | |
1820 (gchar *)haystack->data) == 0); | |
1821 haystack = haystack->next; | |
1822 } | |
1823 needle = needle->next; | |
1824 } | |
1825 | |
1826 match = found; | |
1827 } | |
1828 else if (sd->match_keywords == SEARCH_MATCH_NONE) | |
1829 { | |
1830 gint found = FALSE; | |
1831 | |
1832 needle = sd->search_keyword_list; | |
1833 while (needle && !found) | |
1834 { | |
1835 haystack = list; | |
1836 while (haystack && !found) | |
1837 { | |
1838 found = (strcasecmp((gchar *)needle->data, | |
1839 (gchar *)haystack->data) == 0); | |
1840 haystack = haystack->next; | |
1841 } | |
1842 needle = needle->next; | |
1843 } | |
1844 | |
1845 match = !found; | |
1846 } | |
1847 path_list_free(list); | |
1848 } | |
1849 else | |
1850 { | |
1851 match = (sd->match_keywords == SEARCH_MATCH_NONE); | |
1852 } | |
1853 } | |
1854 | |
1855 if ((match || extra_only) && | |
1856 (sd->match_dimensions_enable || sd->match_similarity_enable)) | |
1857 { | |
1858 tested = TRUE; | |
1859 | |
1860 if (search_file_do_extra(sd, fd, &match, &width, &height, &sim)) | |
1861 { | |
1862 sd->search_buffer_count += SEARCH_BUFFER_MATCH_LOAD; | |
1863 return TRUE; | |
1864 } | |
1865 } | |
1866 | |
1867 sd->search_file_list = g_list_remove(sd->search_file_list, fd); | |
1868 | |
1869 if (tested && match) | |
1870 { | |
1871 MatchFileData *mfd; | |
1872 | |
1873 mfd = g_new(MatchFileData, 1); | |
1874 memcpy(mfd, fd, sizeof(FileData)); | |
1875 g_free(fd); | |
1876 | |
1877 mfd->width = width; | |
1878 mfd->height = height; | |
1879 mfd->rank = sim; | |
1880 | |
1881 sd->search_buffer_list = g_list_prepend(sd->search_buffer_list, mfd); | |
1882 sd->search_buffer_count += SEARCH_BUFFER_MATCH_HIT; | |
1883 sd->search_count++; | |
1884 search_progress_update(sd, TRUE, -1.0); | |
1885 } | |
1886 else | |
1887 { | |
1888 file_data_free(fd); | |
1889 sd->search_buffer_count += SEARCH_BUFFER_MATCH_MISS; | |
1890 } | |
1891 | |
1892 return FALSE; | |
1893 } | |
1894 | |
1895 static gint search_step_cb(gpointer data) | |
1896 { | |
1897 SearchData *sd = data; | |
1898 FileData *fd; | |
1899 | |
1900 if (sd->search_buffer_count > SEARCH_BUFFER_FLUSH_SIZE) | |
1901 { | |
1902 search_buffer_flush(sd); | |
1903 search_progress_update(sd, TRUE, -1.0); | |
1904 } | |
1905 | |
1906 if (sd->search_file_list) | |
1907 { | |
1908 if (search_file_next(sd)) | |
1909 { | |
1910 sd->search_idle_id = -1; | |
1911 return FALSE; | |
1912 } | |
1913 return TRUE; | |
1914 } | |
1915 | |
1916 if (!sd->search_file_list && !sd->search_folder_list) | |
1917 { | |
1918 sd->search_idle_id = -1; | |
1919 | |
1920 search_stop(sd); | |
1921 search_result_thumb_step(sd); | |
1922 | |
1923 return FALSE; | |
1924 } | |
1925 | |
1926 fd = sd->search_folder_list->data; | |
1927 | |
1928 if (g_list_find(sd->search_done_list, fd) == NULL) | |
1929 { | |
1930 GList *list = NULL; | |
1931 GList *dlist = NULL; | |
1932 gint success = FALSE; | |
1933 | |
1934 sd->search_done_list = g_list_prepend(sd->search_done_list, fd); | |
1935 | |
1936 if (sd->search_type == SEARCH_MATCH_NONE) | |
1937 { | |
1938 success = filelist_read(fd->path, &list, &dlist); | |
1939 } | |
1940 else if (sd->search_type == SEARCH_MATCH_ALL && | |
1941 sd->search_path && | |
1942 strlen(fd->path) >= strlen(sd->search_path)) | |
1943 { | |
1944 const gchar *path; | |
1945 | |
1946 path = fd->path + strlen(sd->search_path); | |
1947 if (path != fd->path) success = filelist_read(path, &list, NULL); | |
1948 success |= filelist_read(fd->path, NULL, &dlist); | |
1949 if (success) | |
1950 { | |
1951 GList *work; | |
1952 | |
1953 work = list; | |
1954 while (work) | |
1955 { | |
1956 FileData *fdp; | |
1957 GList *link; | |
1958 gchar *meta_path; | |
1959 | |
1960 fdp = work->data; | |
1961 link = work; | |
1962 work = work->next; | |
1963 | |
1964 meta_path = cache_find_location(CACHE_TYPE_METADATA, fdp->path); | |
1965 if (!meta_path) | |
1966 { | |
1967 list = g_list_delete_link(list, link); | |
1968 file_data_free(fdp); | |
1969 } | |
1970 g_free(meta_path); | |
1971 } | |
1972 } | |
1973 } | |
1974 | |
1975 if (success) | |
1976 { | |
1977 list = filelist_sort(list, SORT_NAME, TRUE); | |
1978 sd->search_file_list = list; | |
1979 | |
1980 if (sd->search_path_recurse) | |
1981 { | |
1982 dlist = filelist_sort(dlist, SORT_NAME, TRUE); | |
1983 sd->search_folder_list = g_list_concat(dlist, sd->search_folder_list); | |
1984 } | |
1985 else | |
1986 { | |
1987 filelist_free(dlist); | |
1988 } | |
1989 } | |
1990 } | |
1991 else | |
1992 { | |
1993 sd->search_folder_list = g_list_remove(sd->search_folder_list, fd); | |
1994 sd->search_done_list = g_list_remove(sd->search_done_list, fd); | |
1995 file_data_free(fd); | |
1996 } | |
1997 | |
1998 return TRUE; | |
1999 } | |
2000 | |
2001 static void search_similarity_load_done_cb(ImageLoader *il, gpointer data) | |
2002 { | |
2003 SearchData *sd = data; | |
2004 search_file_load_process(sd, sd->search_similarity_cd); | |
2005 } | |
2006 | |
2007 static void search_start(SearchData *sd) | |
2008 { | |
2009 search_stop(sd); | |
2010 search_result_clear(sd); | |
2011 | |
2012 if (sd->search_path) | |
2013 { | |
2014 sd->search_folder_list = g_list_prepend(sd->search_folder_list, | |
2015 file_data_new_simple(sd->search_path)); | |
2016 } | |
2017 | |
2018 if (!sd->search_name_match_case) | |
2019 { | |
2020 /* convert to lowercase here, so that this is only done once per search */ | |
2021 gchar *tmp = g_utf8_strdown(sd->search_name, -1); | |
2022 g_free(sd->search_name); | |
2023 sd->search_name = tmp; | |
2024 } | |
2025 | |
2026 sd->search_count = 0; | |
2027 sd->search_total = 0; | |
2028 | |
2029 gtk_widget_set_sensitive(sd->box_search, FALSE); | |
2030 spinner_set_interval(sd->spinner, SPINNER_SPEED); | |
2031 gtk_widget_set_sensitive(sd->button_start, FALSE); | |
2032 gtk_widget_set_sensitive(sd->button_stop, TRUE); | |
2033 search_progress_update(sd, TRUE, -1.0); | |
2034 | |
2035 if (sd->match_similarity_enable && | |
2036 !sd->search_similarity_cd && | |
2037 isfile(sd->search_similarity_path)) | |
2038 { | |
2039 gchar *cd_path; | |
2040 | |
2041 cd_path = cache_find_location(CACHE_TYPE_SIM, sd->search_similarity_path); | |
2042 if (cd_path && filetime(sd->search_similarity_path) == filetime(cd_path)) | |
2043 { | |
2044 sd->search_similarity_cd = cache_sim_data_load(cd_path); | |
2045 } | |
2046 g_free(cd_path); | |
2047 | |
2048 if (!sd->search_similarity_cd || !sd->search_similarity_cd->similarity) | |
2049 { | |
2050 if (!sd->search_similarity_cd) | |
2051 { | |
2052 sd->search_similarity_cd = cache_sim_data_new(); | |
2053 } | |
2054 | |
2055 sd->img_loader = image_loader_new(sd->search_similarity_path); | |
2056 image_loader_set_error_func(sd->img_loader, search_similarity_load_done_cb, sd); | |
2057 if (image_loader_start(sd->img_loader, search_similarity_load_done_cb, sd)) | |
2058 { | |
2059 return; | |
2060 } | |
2061 image_loader_free(sd->img_loader); | |
2062 sd->img_loader = NULL; | |
2063 } | |
2064 | |
2065 } | |
2066 | |
2067 sd->search_idle_id = g_idle_add(search_step_cb, sd); | |
2068 } | |
2069 | |
2070 static void search_start_cb(GtkWidget *widget, gpointer data) | |
2071 { | |
2072 SearchData *sd = data; | |
2073 GtkTreeViewColumn *column; | |
2074 gchar *path; | |
2075 | |
2076 if (sd->search_folder_list) | |
2077 { | |
2078 search_stop(sd); | |
2079 search_result_thumb_step(sd); | |
2080 return; | |
2081 } | |
2082 | |
2083 if (sd->match_name_enable) history_combo_append_history(sd->entry_name, NULL); | |
2084 g_free(sd->search_name); | |
2085 sd->search_name = g_strdup(gtk_entry_get_text(GTK_ENTRY(sd->entry_name))); | |
2086 | |
2087 g_free(sd->search_similarity_path); | |
2088 sd->search_similarity_path = g_strdup(gtk_entry_get_text(GTK_ENTRY(sd->entry_similarity))); | |
2089 if (sd->match_similarity_enable) | |
2090 { | |
2091 if (!isfile(sd->search_similarity_path)) | |
2092 { | |
2093 file_util_warning_dialog(_("File not found"), | |
2094 _("Please enter an existing file for image content."), | |
2095 GTK_STOCK_DIALOG_WARNING, sd->window); | |
2096 return; | |
2097 } | |
2098 tab_completion_append_to_history(sd->entry_similarity, sd->search_similarity_path); | |
2099 } | |
2100 | |
2101 path_list_free(sd->search_keyword_list); | |
2102 sd->search_keyword_list = keyword_list_pull(sd->entry_keywords); | |
2103 | |
2104 date_selection_get(sd->date_sel, &sd->search_date_d, &sd->search_date_m, &sd->search_date_y); | |
2105 date_selection_get(sd->date_sel_end, &sd->search_date_end_d, &sd->search_date_end_m, &sd->search_date_end_y); | |
2106 | |
2107 column = gtk_tree_view_get_column(GTK_TREE_VIEW(sd->result_view), SEARCH_COLUMN_RANK - 1); | |
2108 gtk_tree_view_column_set_visible(column, sd->match_similarity_enable); | |
2109 if (!sd->match_similarity_enable) | |
2110 { | |
2111 GtkTreeSortable *sortable; | |
2112 gint id; | |
2113 GtkSortType order; | |
2114 | |
2115 sortable = GTK_TREE_SORTABLE(gtk_tree_view_get_model(GTK_TREE_VIEW(sd->result_view))); | |
2116 if (gtk_tree_sortable_get_sort_column_id(sortable, &id, &order) && | |
2117 id == SEARCH_COLUMN_RANK) | |
2118 { | |
2119 gtk_tree_sortable_set_sort_column_id(sortable, SEARCH_COLUMN_PATH, GTK_SORT_ASCENDING); | |
2120 } | |
2121 } | |
2122 | |
2123 if (sd->search_type == SEARCH_MATCH_NONE) | |
2124 { | |
2125 /* search path */ | |
2126 | |
2127 path = remove_trailing_slash(gtk_entry_get_text(GTK_ENTRY(sd->path_entry))); | |
2128 if (isdir(path)) | |
2129 { | |
2130 g_free(sd->search_path); | |
2131 sd->search_path = path; | |
2132 path = NULL; | |
2133 | |
2134 tab_completion_append_to_history(sd->path_entry, sd->search_path); | |
2135 | |
2136 search_start(sd); | |
2137 } | |
2138 else | |
2139 { | |
2140 file_util_warning_dialog(_("Folder not found"), | |
2141 _("Please enter an existing folder to search."), | |
2142 GTK_STOCK_DIALOG_WARNING, sd->window); | |
2143 } | |
2144 | |
2145 g_free(path); | |
2146 } | |
2147 else if (sd->search_type == SEARCH_MATCH_ALL) | |
2148 { | |
2149 /* search metadata */ | |
2150 | |
2151 g_free(sd->search_path); | |
2152 sd->search_path = g_strconcat(homedir(), "/", GQVIEW_CACHE_RC_METADATA, NULL); | |
2153 | |
2154 search_start(sd); | |
2155 } | |
2156 else if (sd->search_type == SEARCH_MATCH_CONTAINS) | |
2157 { | |
2158 /* search current result list */ | |
2159 GList *list; | |
2160 | |
2161 list = search_result_refine_list(sd); | |
2162 | |
2163 g_free(sd->search_path); | |
2164 sd->search_path = NULL; | |
2165 | |
2166 search_start(sd); | |
2167 | |
2168 sd->search_file_list = g_list_concat(sd->search_file_list, list); | |
2169 } | |
2170 } | |
2171 | |
2172 /* | |
2173 *------------------------------------------------------------------- | |
2174 * window construct | |
2175 *------------------------------------------------------------------- | |
2176 */ | |
2177 | |
2178 enum { | |
2179 MENU_CHOICE_COLUMN_NAME = 0, | |
2180 MENU_CHOICE_COLUMN_VALUE | |
2181 }; | |
2182 | |
2183 static void search_thumb_toggle_cb(GtkWidget *button, gpointer data) | |
2184 { | |
2185 SearchData *sd = data; | |
2186 | |
2187 search_result_thumb_enable(sd, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))); | |
2188 } | |
2189 | |
2190 static gint sort_matchdata_dimensions(MatchFileData *a, MatchFileData *b) | |
2191 { | |
2192 gint sa; | |
2193 gint sb; | |
2194 | |
2195 sa = a->width * a->height; | |
2196 sb = b->width * b->height; | |
2197 | |
2198 if (sa > sb) return 1; | |
2199 if (sa < sb) return -1; | |
2200 return 0; | |
2201 } | |
2202 | |
2203 static gint search_result_sort_cb(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data) | |
2204 { | |
2205 gint n = GPOINTER_TO_INT(data); | |
2206 FileData *fda; | |
2207 FileData *fdb; | |
2208 | |
2209 gtk_tree_model_get(model, a, SEARCH_COLUMN_POINTER, &fda, -1); | |
2210 gtk_tree_model_get(model, b, SEARCH_COLUMN_POINTER, &fdb, -1); | |
2211 | |
2212 if (!fda || !fdb) return 0; | |
2213 | |
2214 switch (n) | |
2215 { | |
2216 case SEARCH_COLUMN_RANK: | |
2217 if (((MatchFileData *)fda)->rank > ((MatchFileData *)fdb)->rank) return 1; | |
2218 if (((MatchFileData *)fda)->rank < ((MatchFileData *)fdb)->rank) return -1; | |
2219 return 0; | |
2220 break; | |
2221 case SEARCH_COLUMN_NAME: | |
2222 return CASE_SORT(fda->name, fdb->name); | |
2223 break; | |
2224 case SEARCH_COLUMN_SIZE: | |
2225 if (fda->size > fdb->size) return 1; | |
2226 if (fda->size < fdb->size) return -1; | |
2227 return 0; | |
2228 break; | |
2229 case SEARCH_COLUMN_DATE: | |
2230 if (fda->date > fdb->date) return 1; | |
2231 if (fda->date < fdb->date) return -1; | |
2232 return 0; | |
2233 break; | |
2234 case SEARCH_COLUMN_DIMENSIONS: | |
2235 return sort_matchdata_dimensions((MatchFileData *)fda, (MatchFileData *)fdb); | |
2236 break; | |
2237 case SEARCH_COLUMN_PATH: | |
2238 return CASE_SORT(fda->path, fdb->path); | |
2239 break; | |
2240 default: | |
2241 break; | |
2242 } | |
2243 | |
2244 return 0; | |
2245 } | |
2246 | |
2247 static void search_result_add_column(SearchData * sd, gint n, const gchar *title, gint image, gint right_justify) | |
2248 { | |
2249 GtkTreeViewColumn *column; | |
2250 GtkCellRenderer *renderer; | |
2251 | |
2252 column = gtk_tree_view_column_new(); | |
2253 gtk_tree_view_column_set_title(column, title); | |
2254 gtk_tree_view_column_set_min_width(column, 4); | |
2255 | |
2256 if (n != SEARCH_COLUMN_THUMB) gtk_tree_view_column_set_resizable(column, TRUE); | |
2257 | |
2258 if (!image) | |
2259 { | |
2260 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); | |
2261 renderer = gtk_cell_renderer_text_new(); | |
2262 if (right_justify) g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL); | |
2263 gtk_tree_view_column_pack_start(column, renderer, TRUE); | |
2264 gtk_tree_view_column_add_attribute(column, renderer, "text", n); | |
2265 | |
2266 gtk_tree_view_column_set_sort_column_id(column, n); | |
2267 } | |
2268 else | |
2269 { | |
2270 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); | |
2271 renderer = gtk_cell_renderer_pixbuf_new(); | |
2272 cell_renderer_height_override(renderer); | |
2273 gtk_tree_view_column_pack_start(column, renderer, TRUE); | |
2274 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n); | |
2275 } | |
2276 | |
2277 gtk_tree_view_append_column(GTK_TREE_VIEW(sd->result_view), column); | |
2278 } | |
2279 | |
2280 static void menu_choice_set_visible(GtkWidget *widget, gint visible) | |
2281 { | |
2282 if (visible) | |
2283 { | |
2284 if (!GTK_WIDGET_VISIBLE(widget)) gtk_widget_show(widget); | |
2285 } | |
2286 else | |
2287 { | |
2288 if (GTK_WIDGET_VISIBLE(widget)) gtk_widget_hide(widget); | |
2289 } | |
2290 } | |
2291 | |
2292 static void menu_choice_path_cb(GtkWidget *combo, gpointer data) | |
2293 { | |
2294 SearchData *sd = data; | |
2295 GtkTreeModel *store; | |
2296 GtkTreeIter iter; | |
2297 | |
2298 store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); | |
2299 if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter)) return; | |
2300 gtk_tree_model_get(store, &iter, MENU_CHOICE_COLUMN_VALUE, &sd->search_type, -1); | |
2301 | |
2302 menu_choice_set_visible(gtk_widget_get_parent(sd->check_recurse), | |
2303 (sd->search_type == SEARCH_MATCH_NONE)); | |
2304 } | |
2305 | |
2306 static void menu_choice_name_cb(GtkWidget *combo, gpointer data) | |
2307 { | |
2308 SearchData *sd = data; | |
2309 GtkTreeModel *store; | |
2310 GtkTreeIter iter; | |
2311 | |
2312 store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); | |
2313 if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter)) return; | |
2314 gtk_tree_model_get(store, &iter, MENU_CHOICE_COLUMN_VALUE, &sd->match_name, -1); | |
2315 } | |
2316 | |
2317 static void menu_choice_size_cb(GtkWidget *combo, gpointer data) | |
2318 { | |
2319 SearchData *sd = data; | |
2320 GtkTreeModel *store; | |
2321 GtkTreeIter iter; | |
2322 | |
2323 store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); | |
2324 if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter)) return; | |
2325 gtk_tree_model_get(store, &iter, MENU_CHOICE_COLUMN_VALUE, &sd->match_size, -1); | |
2326 | |
2327 menu_choice_set_visible(gtk_widget_get_parent(sd->spin_size_end), | |
2328 (sd->match_size == SEARCH_MATCH_BETWEEN)); | |
2329 } | |
2330 | |
2331 static void menu_choice_date_cb(GtkWidget *combo, gpointer data) | |
2332 { | |
2333 SearchData *sd = data; | |
2334 GtkTreeModel *store; | |
2335 GtkTreeIter iter; | |
2336 | |
2337 store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); | |
2338 if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter)) return; | |
2339 gtk_tree_model_get(store, &iter, MENU_CHOICE_COLUMN_VALUE, &sd->match_date, -1); | |
2340 | |
2341 menu_choice_set_visible(gtk_widget_get_parent(sd->date_sel_end), | |
2342 (sd->match_date == SEARCH_MATCH_BETWEEN)); | |
2343 } | |
2344 | |
2345 static void menu_choice_dimensions_cb(GtkWidget *combo, gpointer data) | |
2346 { | |
2347 SearchData *sd = data; | |
2348 GtkTreeModel *store; | |
2349 GtkTreeIter iter; | |
2350 | |
2351 store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); | |
2352 if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter)) return; | |
2353 gtk_tree_model_get(store, &iter, MENU_CHOICE_COLUMN_VALUE, &sd->match_dimensions, -1); | |
2354 | |
2355 menu_choice_set_visible(gtk_widget_get_parent(sd->spin_width_end), | |
2356 (sd->match_dimensions == SEARCH_MATCH_BETWEEN)); | |
2357 } | |
2358 | |
2359 static void menu_choice_keyword_cb(GtkWidget *combo, gpointer data) | |
2360 { | |
2361 SearchData *sd = data; | |
2362 GtkTreeModel *store; | |
2363 GtkTreeIter iter; | |
2364 | |
2365 store = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); | |
2366 if (!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter)) return; | |
2367 gtk_tree_model_get(store, &iter, MENU_CHOICE_COLUMN_VALUE, &sd->match_keywords, -1); | |
2368 } | |
2369 | |
2370 static void menu_choice_spin_cb(GtkAdjustment *adjustment, gpointer data) | |
2371 { | |
2372 gint *value = data; | |
2373 | |
2374 *value = (gint)gtk_adjustment_get_value(adjustment); | |
2375 } | |
2376 | |
2377 static GtkWidget *menu_spin(GtkWidget *box, gdouble min, gdouble max, gint value, | |
2378 GCallback func, gpointer data) | |
2379 { | |
2380 GtkWidget *spin; | |
2381 GtkAdjustment *adj; | |
2382 | |
2383 spin = gtk_spin_button_new_with_range(min, max, 1); | |
2384 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), (gdouble)value); | |
2385 adj = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(spin)); | |
2386 if (func) g_signal_connect(G_OBJECT(adj), "value_changed", | |
2387 G_CALLBACK(func), data); | |
2388 gtk_box_pack_start(GTK_BOX(box), spin, FALSE, FALSE, 0); | |
2389 gtk_widget_show(spin); | |
2390 | |
2391 return spin; | |
2392 } | |
2393 | |
2394 static void menu_choice_check_cb(GtkWidget *button, gpointer data) | |
2395 { | |
2396 GtkWidget *widget = data; | |
2397 gboolean active; | |
2398 gboolean *value; | |
2399 | |
2400 active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)); | |
2401 gtk_widget_set_sensitive(widget, active); | |
2402 | |
2403 value = g_object_get_data(G_OBJECT(button), "check_var"); | |
2404 if (value) *value = active; | |
2405 } | |
2406 | |
2407 static GtkWidget *menu_choice_menu(const MatchList *items, gint item_count, | |
2408 GCallback func, gpointer data) | |
2409 { | |
2410 GtkWidget *combo; | |
2411 GtkCellRenderer *renderer; | |
2412 GtkListStore *store; | |
2413 gint i; | |
2414 | |
2415 store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT); | |
2416 combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); | |
2417 g_object_unref(store); | |
2418 | |
2419 renderer = gtk_cell_renderer_text_new(); | |
2420 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), renderer, TRUE); | |
2421 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), renderer, | |
2422 "text", MENU_CHOICE_COLUMN_NAME, NULL); | |
2423 | |
2424 for (i = 0; i < item_count; i++) | |
2425 { | |
2426 GtkTreeIter iter; | |
2427 | |
2428 gtk_list_store_append(store, &iter); | |
2429 gtk_list_store_set(store, &iter, MENU_CHOICE_COLUMN_NAME, _(items[i].text), | |
2430 MENU_CHOICE_COLUMN_VALUE, items[i].type, -1); | |
2431 } | |
2432 | |
2433 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); | |
2434 | |
2435 if (func) g_signal_connect(G_OBJECT(combo), "changed", | |
2436 G_CALLBACK(func), data); | |
2437 | |
2438 return combo; | |
2439 } | |
2440 | |
2441 static GtkWidget *menu_choice(GtkWidget *box, GtkWidget **check, GtkWidget **menu, | |
2442 const gchar *text, gboolean *value, | |
2443 const MatchList *items, gint item_count, | |
2444 GCallback func, gpointer data) | |
2445 { | |
2446 GtkWidget *base_box; | |
2447 GtkWidget *hbox; | |
2448 GtkWidget *button; | |
2449 GtkWidget *option; | |
2450 | |
2451 base_box = gtk_hbox_new(FALSE, PREF_PAD_GAP); | |
2452 gtk_box_pack_start(GTK_BOX(box), base_box, FALSE, FALSE, 0); | |
2453 gtk_widget_show(base_box); | |
2454 | |
2455 button = gtk_check_button_new(); | |
2456 if (value) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), *value); | |
2457 gtk_box_pack_start(GTK_BOX(base_box), button, FALSE, FALSE, 0); | |
2458 gtk_widget_show(button); | |
2459 if (check) *check = button; | |
2460 if (value) g_object_set_data(G_OBJECT(button), "check_var", value); | |
2461 | |
2462 hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE); | |
2463 gtk_box_pack_start(GTK_BOX(base_box), hbox, TRUE, TRUE, 0); | |
2464 gtk_widget_show(hbox); | |
2465 | |
2466 g_signal_connect(G_OBJECT(button), "toggled", | |
2467 G_CALLBACK(menu_choice_check_cb), hbox); | |
2468 gtk_widget_set_sensitive(hbox, (value) ? *value : FALSE); | |
2469 | |
2470 pref_label_new(hbox, text); | |
2471 | |
2472 if (!items && !menu) return hbox; | |
2473 | |
2474 option = menu_choice_menu(items, item_count, func, data); | |
2475 gtk_box_pack_start(GTK_BOX(hbox), option, FALSE, FALSE, 0); | |
2476 gtk_widget_show(option); | |
2477 if (menu) *menu = option; | |
2478 | |
2479 return hbox; | |
2480 } | |
2481 | |
2482 static void search_window_close(SearchData *sd) | |
2483 { | |
2484 gtk_widget_destroy(sd->window); | |
2485 } | |
2486 | |
2487 static gint search_window_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer data) | |
2488 { | |
2489 SearchData *sd = data; | |
2490 | |
2491 search_window_close(sd); | |
2492 return TRUE; | |
2493 } | |
2494 | |
2495 static void search_window_destroy_cb(GtkWidget *widget, gpointer data) | |
2496 { | |
2497 SearchData *sd = data; | |
2498 | |
2499 search_window_list = g_list_remove(search_window_list, sd); | |
2500 | |
2501 search_result_update_idle_cancel(sd); | |
2502 | |
2503 filelist_free(sd->search_buffer_list); | |
2504 sd->search_buffer_list = NULL; | |
2505 | |
2506 search_stop(sd); | |
2507 search_result_clear(sd); | |
2508 | |
2509 g_free(sd->search_path); | |
2510 g_free(sd->search_name); | |
2511 g_free(sd->search_similarity_path); | |
2512 path_list_free(sd->search_keyword_list); | |
2513 | |
2514 g_free(sd); | |
2515 } | |
2516 | |
2517 void search_new(const gchar *path, const gchar *example_file) | |
2518 { | |
2519 SearchData *sd; | |
2520 GtkWidget *vbox; | |
2521 GtkWidget *hbox; | |
2522 GtkWidget *hbox2; | |
2523 GtkWidget *pad_box; | |
2524 GtkWidget *frame; | |
2525 GtkWidget *scrolled; | |
2526 GtkListStore *store; | |
2527 GtkTreeSortable *sortable; | |
2528 GtkTreeSelection *selection; | |
2529 GtkWidget *combo; | |
2530 GdkGeometry geometry; | |
2531 | |
2532 sd = g_new0(SearchData, 1); | |
2533 | |
2534 sd->search_path = g_strdup(path); | |
2535 sd->search_path_recurse = TRUE; | |
2536 sd->search_size = 0; | |
2537 sd->search_width = 640; | |
2538 sd->search_height = 480; | |
2539 sd->search_width_end = 1024; | |
2540 sd->search_height_end = 768; | |
2541 sd->search_name = NULL; | |
2542 sd->search_name_match_case = FALSE; | |
2543 | |
2544 sd->search_type = SEARCH_MATCH_NONE; | |
2545 | |
2546 sd->match_name = SEARCH_MATCH_CONTAINS; | |
2547 sd->match_size = SEARCH_MATCH_EQUAL; | |
2548 sd->match_date = SEARCH_MATCH_EQUAL; | |
2549 sd->match_dimensions = SEARCH_MATCH_EQUAL; | |
2550 sd->match_keywords = SEARCH_MATCH_ALL; | |
2551 | |
2552 sd->match_name_enable = TRUE; | |
2553 sd->match_size_enable = FALSE; | |
2554 sd->match_date_enable = FALSE; | |
2555 sd->match_dimensions_enable = FALSE; | |
2556 sd->match_similarity_enable = FALSE; | |
2557 sd->match_keywords_enable = FALSE; | |
2558 | |
2559 sd->search_similarity = 95; | |
2560 sd->search_similarity_path = g_strdup(example_file); | |
2561 sd->search_similarity_cd = NULL; | |
2562 | |
2563 sd->search_idle_id = -1; | |
2564 sd->update_idle_id = -1; | |
2565 | |
2566 sd->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
2567 window_set_icon(sd->window, NULL, NULL); | |
2568 | |
2569 gtk_window_set_resizable(GTK_WINDOW(sd->window), TRUE); | |
2570 gtk_window_set_title(GTK_WINDOW(sd->window), _("Image search - GQview")); | |
2571 gtk_window_set_wmclass(GTK_WINDOW(sd->window), "search", "GQview"); | |
2572 | |
2573 geometry.min_width = 32; | |
2574 geometry.min_height = 32; | |
2575 geometry.base_width = DEF_SEARCH_WIDTH; | |
2576 geometry.base_height = DEF_SEARCH_HEIGHT; | |
2577 gtk_window_set_geometry_hints(GTK_WINDOW(sd->window), NULL, &geometry, | |
2578 GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE); | |
2579 | |
2580 gtk_window_set_default_size(GTK_WINDOW(sd->window), DEF_SEARCH_WIDTH, DEF_SEARCH_HEIGHT); | |
2581 | |
2582 g_signal_connect(G_OBJECT(sd->window), "delete_event", | |
2583 G_CALLBACK(search_window_delete_cb), sd); | |
2584 g_signal_connect(G_OBJECT(sd->window), "destroy", | |
2585 G_CALLBACK(search_window_destroy_cb), sd); | |
2586 | |
2587 g_signal_connect(G_OBJECT(sd->window), "key_press_event", | |
2588 G_CALLBACK(search_window_keypress_cb), sd); | |
2589 | |
2590 vbox = gtk_vbox_new(FALSE, PREF_PAD_GAP); | |
2591 gtk_container_set_border_width(GTK_CONTAINER(vbox), PREF_PAD_GAP); | |
2592 gtk_container_add(GTK_CONTAINER(sd->window), vbox); | |
2593 gtk_widget_show(vbox); | |
2594 | |
2595 sd->box_search = pref_box_new(vbox, FALSE, GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP); | |
2596 | |
2597 hbox = pref_box_new(sd->box_search, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE); | |
2598 | |
2599 pref_label_new(hbox, _("Search:")); | |
2600 | |
2601 sd->menu_path = menu_choice_menu(text_search_menu_path, sizeof(text_search_menu_path) / sizeof(MatchList), | |
2602 G_CALLBACK(menu_choice_path_cb), sd); | |
2603 gtk_box_pack_start(GTK_BOX(hbox), sd->menu_path, FALSE, FALSE, 0); | |
2604 gtk_widget_show(sd->menu_path); | |
2605 | |
2606 hbox2 = pref_box_new(hbox, TRUE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE); | |
2607 combo = tab_completion_new_with_history(&sd->path_entry, sd->search_path, | |
2608 "search_path", -1, | |
2609 NULL, NULL); | |
2610 tab_completion_add_select_button(sd->path_entry, NULL, TRUE); | |
2611 gtk_box_pack_start(GTK_BOX(hbox2), combo, TRUE, TRUE, 0); | |
2612 gtk_widget_show(combo); | |
2613 sd->check_recurse = pref_checkbox_new_int(hbox2, _("Recurse"), | |
2614 sd->search_path_recurse, &sd->search_path_recurse); | |
2615 | |
2616 hbox = menu_choice(sd->box_search, &sd->check_name, &sd->menu_name, | |
2617 _("File name"), &sd->match_name_enable, | |
2618 text_search_menu_name, sizeof(text_search_menu_name) / sizeof(MatchList), | |
2619 G_CALLBACK(menu_choice_name_cb), sd); | |
2620 combo = history_combo_new(&sd->entry_name, "", "search_name", -1); | |
2621 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0); | |
2622 gtk_widget_show(combo); | |
2623 pref_checkbox_new_int(hbox, _("Match case"), | |
2624 sd->search_name_match_case, &sd->search_name_match_case); | |
2625 | |
2626 hbox = menu_choice(sd->box_search, &sd->check_size, &sd->menu_size, | |
2627 _("File size is"), &sd->match_size_enable, | |
2628 text_search_menu_size, sizeof(text_search_menu_size) / sizeof(MatchList), | |
2629 G_CALLBACK(menu_choice_size_cb), sd); | |
2630 sd->spin_size = menu_spin(hbox, 0, 1024*1024*1024, sd->search_size, | |
2631 G_CALLBACK(menu_choice_spin_cb), &sd->search_size); | |
2632 hbox2 = gtk_hbox_new(FALSE, PREF_PAD_SPACE); | |
2633 gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 0); | |
2634 pref_label_new(hbox2, _("and")); | |
2635 sd->spin_size_end = menu_spin(hbox2, 0, 1024*1024*1024, sd->search_size_end, | |
2636 G_CALLBACK(menu_choice_spin_cb), &sd->search_size_end); | |
2637 | |
2638 hbox = menu_choice(sd->box_search, &sd->check_date, &sd->menu_date, | |
2639 _("File date is"), &sd->match_date_enable, | |
2640 text_search_menu_date, sizeof(text_search_menu_date) / sizeof(MatchList), | |
2641 G_CALLBACK(menu_choice_date_cb), sd); | |
2642 sd->date_sel = date_selection_new(); | |
2643 date_selection_time_set(sd->date_sel, time(NULL)); | |
2644 gtk_box_pack_start(GTK_BOX(hbox), sd->date_sel, FALSE, FALSE, 0); | |
2645 gtk_widget_show(sd->date_sel); | |
2646 | |
2647 hbox2 = gtk_hbox_new(FALSE, PREF_PAD_SPACE); | |
2648 gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 0); | |
2649 pref_label_new(hbox2, _("and")); | |
2650 sd->date_sel_end = date_selection_new(); | |
2651 date_selection_time_set(sd->date_sel_end, time(NULL)); | |
2652 gtk_box_pack_start(GTK_BOX(hbox2), sd->date_sel_end, FALSE, FALSE, 0); | |
2653 gtk_widget_show(sd->date_sel_end); | |
2654 | |
2655 hbox = menu_choice(sd->box_search, &sd->check_dimensions, &sd->menu_dimensions, | |
2656 _("Image dimensions are"), &sd->match_dimensions_enable, | |
2657 text_search_menu_size, sizeof(text_search_menu_size) / sizeof(MatchList), | |
2658 G_CALLBACK(menu_choice_dimensions_cb), sd); | |
2659 pad_box = pref_box_new(hbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 2); | |
2660 sd->spin_width = menu_spin(pad_box, 0, 1000000, sd->search_width, | |
2661 G_CALLBACK(menu_choice_spin_cb), &sd->search_width); | |
2662 pref_label_new(pad_box, "x"); | |
2663 sd->spin_height = menu_spin(pad_box, 0, 1000000, sd->search_height, | |
2664 G_CALLBACK(menu_choice_spin_cb), &sd->search_height); | |
2665 hbox2 = gtk_hbox_new(FALSE, 2); | |
2666 gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 0); | |
2667 pref_label_new(hbox2, _("and")); | |
2668 pref_spacer(hbox2, PREF_PAD_SPACE - 2*2); | |
2669 sd->spin_width_end = menu_spin(hbox2, 0, 1000000, sd->search_width_end, | |
2670 G_CALLBACK(menu_choice_spin_cb), &sd->search_width_end); | |
2671 pref_label_new(hbox2, "x"); | |
2672 sd->spin_height_end = menu_spin(hbox2, 0, 1000000, sd->search_height_end, | |
2673 G_CALLBACK(menu_choice_spin_cb), &sd->search_height_end); | |
2674 | |
2675 hbox = menu_choice(sd->box_search, &sd->check_similarity, NULL, | |
2676 _("Image content is"), &sd->match_similarity_enable, | |
2677 NULL, 0, NULL, sd); | |
2678 sd->spin_similarity = menu_spin(hbox, 80, 100, sd->search_similarity, | |
2679 G_CALLBACK(menu_choice_spin_cb), &sd->search_similarity); | |
2680 | |
2681 /* xgettext:no-c-format */ | |
2682 pref_label_new(hbox, _("% similar to")); | |
2683 | |
2684 combo = tab_completion_new_with_history(&sd->entry_similarity, | |
2685 (sd->search_similarity_path) ? sd->search_similarity_path : "", | |
2686 "search_similarity_path", -1, NULL, NULL); | |
2687 tab_completion_add_select_button(sd->entry_similarity, NULL, FALSE); | |
2688 gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0); | |
2689 gtk_widget_show(combo); | |
2690 | |
2691 hbox = menu_choice(sd->box_search, &sd->check_keywords, &sd->menu_keywords, | |
2692 _("Keywords"), &sd->match_keywords_enable, | |
2693 text_search_menu_keyword, sizeof(text_search_menu_keyword) / sizeof(MatchList), | |
2694 G_CALLBACK(menu_choice_keyword_cb), sd); | |
2695 sd->entry_keywords = gtk_entry_new(); | |
2696 gtk_box_pack_start(GTK_BOX(hbox), sd->entry_keywords, TRUE, TRUE, 0); | |
2697 gtk_widget_set_sensitive(sd->entry_keywords, sd->match_keywords_enable); | |
2698 g_signal_connect(G_OBJECT(sd->check_keywords), "toggled", | |
2699 G_CALLBACK(menu_choice_check_cb), sd->entry_keywords); | |
2700 gtk_widget_show(sd->entry_keywords); | |
2701 | |
2702 scrolled = gtk_scrolled_window_new(NULL, NULL); | |
2703 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_IN); | |
2704 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), | |
2705 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
2706 gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 0); | |
2707 gtk_widget_show(scrolled); | |
2708 | |
2709 store = gtk_list_store_new(8, G_TYPE_POINTER, G_TYPE_INT, GDK_TYPE_PIXBUF, | |
2710 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, | |
2711 G_TYPE_STRING, G_TYPE_STRING); | |
2712 | |
2713 /* set up sorting */ | |
2714 sortable = GTK_TREE_SORTABLE(store); | |
2715 gtk_tree_sortable_set_sort_func(sortable, SEARCH_COLUMN_RANK, search_result_sort_cb, | |
2716 GINT_TO_POINTER(SEARCH_COLUMN_RANK), NULL); | |
2717 gtk_tree_sortable_set_sort_func(sortable, SEARCH_COLUMN_NAME, search_result_sort_cb, | |
2718 GINT_TO_POINTER(SEARCH_COLUMN_NAME), NULL); | |
2719 gtk_tree_sortable_set_sort_func(sortable, SEARCH_COLUMN_SIZE, search_result_sort_cb, | |
2720 GINT_TO_POINTER(SEARCH_COLUMN_SIZE), NULL); | |
2721 gtk_tree_sortable_set_sort_func(sortable, SEARCH_COLUMN_DATE, search_result_sort_cb, | |
2722 GINT_TO_POINTER(SEARCH_COLUMN_DATE), NULL); | |
2723 gtk_tree_sortable_set_sort_func(sortable, SEARCH_COLUMN_DIMENSIONS, search_result_sort_cb, | |
2724 GINT_TO_POINTER(SEARCH_COLUMN_DIMENSIONS), NULL); | |
2725 gtk_tree_sortable_set_sort_func(sortable, SEARCH_COLUMN_PATH, search_result_sort_cb, | |
2726 GINT_TO_POINTER(SEARCH_COLUMN_PATH), NULL); | |
2727 | |
2728 #if 0 | |
2729 /* by default, search results are unsorted until user selects a sort column - for speed, | |
2730 * using sort slows search speed by an order of magnitude with 1000's of results :-/ | |
2731 */ | |
2732 gtk_tree_sortable_set_sort_column_id(sortable, SEARCH_COLUMN_PATH, GTK_SORT_ASCENDING); | |
2733 #endif | |
2734 | |
2735 sd->result_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); | |
2736 g_object_unref(store); | |
2737 gtk_container_add(GTK_CONTAINER(scrolled), sd->result_view); | |
2738 gtk_widget_show(sd->result_view); | |
2739 | |
2740 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(sd->result_view)); | |
2741 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE); | |
2742 gtk_tree_selection_set_select_function(selection, search_result_select_cb, sd, NULL); | |
2743 | |
2744 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(sd->result_view), TRUE); | |
2745 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(sd->result_view), FALSE); | |
2746 | |
2747 #if 0 | |
2748 gtk_tree_view_set_search_column(GTK_TREE_VIEW(sd->result_view), SEARCH_COLUMN_NAME); | |
2749 #endif | |
2750 | |
2751 search_result_add_column(sd, SEARCH_COLUMN_RANK, _("Rank"), FALSE, FALSE); | |
2752 search_result_add_column(sd, SEARCH_COLUMN_THUMB, "", TRUE, FALSE); | |
2753 search_result_add_column(sd, SEARCH_COLUMN_NAME, _("Name"), FALSE, FALSE); | |
2754 search_result_add_column(sd, SEARCH_COLUMN_SIZE, _("Size"), FALSE, TRUE); | |
2755 search_result_add_column(sd, SEARCH_COLUMN_DATE, _("Date"), FALSE, TRUE); | |
2756 search_result_add_column(sd, SEARCH_COLUMN_DIMENSIONS, _("Dimensions"), FALSE, FALSE); | |
2757 search_result_add_column(sd, SEARCH_COLUMN_PATH, _("Path"), FALSE, FALSE); | |
2758 | |
2759 search_dnd_init(sd); | |
2760 | |
2761 g_signal_connect(G_OBJECT(sd->result_view), "button_press_event", | |
2762 G_CALLBACK(search_result_press_cb), sd); | |
2763 g_signal_connect(G_OBJECT(sd->result_view), "button_release_event", | |
2764 G_CALLBACK(search_result_release_cb), sd); | |
2765 g_signal_connect(G_OBJECT(sd->result_view), "key_press_event", | |
2766 G_CALLBACK(search_result_keypress_cb), sd); | |
2767 | |
2768 hbox = pref_box_new(vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, 0); | |
2769 | |
2770 sd->button_thumbs = pref_checkbox_new(hbox, _("Thumbnails"), FALSE, | |
2771 G_CALLBACK(search_thumb_toggle_cb), sd); | |
2772 | |
2773 frame = gtk_frame_new(NULL); | |
2774 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); | |
2775 gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, PREF_PAD_SPACE); | |
2776 gtk_widget_show(frame); | |
2777 | |
2778 sd->label_status = gtk_label_new(""); | |
2779 gtk_widget_set_size_request(sd->label_status, 50, -1); | |
2780 gtk_container_add(GTK_CONTAINER(frame), sd->label_status); | |
2781 gtk_widget_show(sd->label_status); | |
2782 | |
2783 sd->label_progress = gtk_progress_bar_new(); | |
2784 gtk_widget_set_size_request(sd->label_progress, 50, -1); | |
2785 gtk_box_pack_start(GTK_BOX(hbox), sd->label_progress, TRUE, TRUE, 0); | |
2786 gtk_widget_show(sd->label_progress); | |
2787 | |
2788 sd->spinner = spinner_new(NULL, -1); | |
2789 gtk_box_pack_start(GTK_BOX(hbox), sd->spinner, FALSE, FALSE, 0); | |
2790 gtk_widget_show(sd->spinner); | |
2791 | |
2792 sd->button_start = pref_button_new(hbox, GTK_STOCK_FIND, NULL, FALSE, | |
2793 G_CALLBACK(search_start_cb), sd); | |
2794 pref_spacer(hbox, PREF_PAD_BUTTON_GAP); | |
2795 sd->button_stop = pref_button_new(hbox, GTK_STOCK_STOP, NULL, FALSE, | |
2796 G_CALLBACK(search_start_cb), sd); | |
2797 gtk_widget_set_sensitive(sd->button_stop, FALSE); | |
2798 | |
2799 search_status_update(sd); | |
2800 search_progress_update(sd, FALSE, -1.0); | |
2801 | |
2802 search_window_list = g_list_append(search_window_list, sd); | |
2803 | |
2804 gtk_widget_show(sd->window); | |
2805 } | |
2806 | |
2807 /* | |
2808 *------------------------------------------------------------------- | |
2809 * maintenance (move, delete, etc.) | |
2810 *------------------------------------------------------------------- | |
2811 */ | |
2812 | |
2813 static void search_result_change_path(SearchData *sd, const gchar *path, const gchar *newpath) | |
2814 { | |
2815 GtkTreeModel *store; | |
2816 GtkTreeIter iter; | |
2817 gint valid; | |
2818 | |
2819 store = gtk_tree_view_get_model(GTK_TREE_VIEW(sd->result_view)); | |
2820 valid = gtk_tree_model_get_iter_first(store, &iter); | |
2821 while (valid) | |
2822 { | |
2823 GtkTreeIter current; | |
2824 FileData *fd; | |
2825 | |
2826 current = iter; | |
2827 valid = gtk_tree_model_iter_next(store, &iter); | |
2828 | |
2829 gtk_tree_model_get(store, ¤t, SEARCH_COLUMN_POINTER, &fd, -1); | |
2830 if (strcmp(fd->path, path) == 0) | |
2831 { | |
2832 if (newpath) | |
2833 { | |
2834 g_free(fd->path); | |
2835 fd->path = g_strdup(newpath); | |
2836 fd->name = filename_from_path(fd->path); | |
2837 | |
2838 gtk_list_store_set(GTK_LIST_STORE(store), ¤t, | |
2839 SEARCH_COLUMN_NAME, fd->name, | |
2840 SEARCH_COLUMN_PATH, fd->path, -1); | |
2841 } | |
2842 else | |
2843 { | |
2844 search_result_remove_item(sd, fd, ¤t); | |
2845 } | |
2846 } | |
2847 } | |
2848 } | |
2849 | |
2850 void search_maint_renamed(const gchar *source, const gchar *dest) | |
2851 { | |
2852 GList *work; | |
2853 | |
2854 work = search_window_list; | |
2855 while (work) | |
2856 { | |
2857 SearchData *sd = work->data; | |
2858 work = work->next; | |
2859 | |
2860 search_result_change_path(sd, source, dest); | |
2861 } | |
2862 } | |
2863 | |
2864 void search_maint_removed(const gchar *path) | |
2865 { | |
2866 search_maint_renamed(path, NULL); | |
2867 } | |
2868 |