comparison src/view_file_list.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) 2004 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 #include "gqview.h"
13 #include "view_file_list.h"
14
15 #include "cache_maint.h"
16 #include "dnd.h"
17 #include "editors.h"
18 #include "img-view.h"
19 #include "info.h"
20 #include "layout.h"
21 #include "layout_image.h"
22 #include "menu.h"
23 #include "thumb.h"
24 #include "utilops.h"
25 #include "ui_bookmark.h"
26 #include "ui_fileops.h"
27 #include "ui_menu.h"
28 #include "ui_tree_edit.h"
29
30 #include <gdk/gdkkeysyms.h> /* for keyboard values */
31
32
33 enum {
34 FILE_COLUMN_POINTER = 0,
35 FILE_COLUMN_THUMB,
36 FILE_COLUMN_NAME,
37 FILE_COLUMN_SIZE,
38 FILE_COLUMN_DATE,
39 FILE_COLUMN_COLOR,
40 FILE_COLUMN_COUNT
41 };
42
43
44 static gint vflist_row_is_selected(ViewFileList *vfl, FileData *fd);
45 static gint vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data);
46 static void vflist_populate_view(ViewFileList *vfl);
47
48 /*
49 *-----------------------------------------------------------------------------
50 * signals
51 *-----------------------------------------------------------------------------
52 */
53
54 static void vflist_send_update(ViewFileList *vfl)
55 {
56 if (vfl->func_status) vfl->func_status(vfl, vfl->data_status);
57 }
58
59 /*
60 *-----------------------------------------------------------------------------
61 * misc
62 *-----------------------------------------------------------------------------
63 */
64
65 static gint vflist_find_row(ViewFileList *vfl, FileData *fd, GtkTreeIter *iter)
66 {
67 GtkTreeModel *store;
68 gint valid;
69 gint row = 0;
70
71 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfl->listview));
72 valid = gtk_tree_model_get_iter_first(store, iter);
73 while (valid)
74 {
75 FileData *fd_n;
76 gtk_tree_model_get(GTK_TREE_MODEL(store), iter, FILE_COLUMN_POINTER, &fd_n, -1);
77 if (fd_n == fd) return row;
78
79 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), iter);
80 row++;
81 }
82
83 return -1;
84 }
85
86 static void vflist_color_set(ViewFileList *vfl, FileData *fd, gint color_set)
87 {
88 GtkTreeModel *store;
89 GtkTreeIter iter;
90
91 if (vflist_find_row(vfl, fd, &iter) < 0) return;
92 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfl->listview));
93 gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_COLOR, color_set, -1);
94 }
95
96 static void vflist_move_cursor(ViewFileList *vfl, GtkTreeIter *iter)
97 {
98 GtkTreeModel *store;
99 GtkTreePath *tpath;
100
101 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfl->listview));
102
103 tpath = gtk_tree_model_get_path(store, iter);
104 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vfl->listview), tpath, NULL, FALSE);
105 gtk_tree_path_free(tpath);
106 }
107
108 /*
109 *-----------------------------------------------------------------------------
110 * dnd
111 *-----------------------------------------------------------------------------
112 */
113
114 static void vflist_dnd_get(GtkWidget *widget, GdkDragContext *context,
115 GtkSelectionData *selection_data, guint info,
116 guint time, gpointer data)
117 {
118 ViewFileList *vfl = data;
119 GList *list = NULL;
120 gchar *uri_text = NULL;
121 gint total;
122
123 if (!vfl->click_fd) return;
124
125 if (vflist_row_is_selected(vfl, vfl->click_fd))
126 {
127 list = vflist_selection_get_list(vfl);
128 }
129 else
130 {
131 list = g_list_append(NULL, g_strdup(vfl->click_fd->path));
132 }
133
134 if (!list) return;
135
136 uri_text = uri_text_from_list(list, &total, (info == TARGET_TEXT_PLAIN));
137 path_list_free(list);
138
139 if (debug) printf(uri_text);
140
141 gtk_selection_data_set(selection_data, selection_data->target, 8, uri_text, total);
142 g_free(uri_text);
143 }
144
145 static void vflist_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
146 {
147 ViewFileList *vfl = data;
148
149 vflist_color_set(vfl, vfl->click_fd, TRUE);
150
151 if (vfl->thumbs_enabled &&
152 vfl->click_fd && vfl->click_fd->pixbuf)
153 {
154 gint items;
155
156 if (vflist_row_is_selected(vfl, vfl->click_fd))
157 items = vflist_selection_count(vfl, NULL);
158 else
159 items = 1;
160
161 dnd_set_drag_icon(widget, context, vfl->click_fd->pixbuf, items);
162 }
163 }
164
165 static void vflist_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
166 {
167 ViewFileList *vfl = data;
168
169 vflist_color_set(vfl, vfl->click_fd, FALSE);
170
171 if (context->action == GDK_ACTION_MOVE)
172 {
173 vflist_refresh(vfl);
174 }
175 }
176
177 static void vflist_dnd_init(ViewFileList *vfl)
178 {
179 gtk_drag_source_set(vfl->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
180 dnd_file_drag_types, dnd_file_drag_types_count,
181 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
182 g_signal_connect(G_OBJECT(vfl->listview), "drag_data_get",
183 G_CALLBACK(vflist_dnd_get), vfl);
184 g_signal_connect(G_OBJECT(vfl->listview), "drag_begin",
185 G_CALLBACK(vflist_dnd_begin), vfl);
186 g_signal_connect(G_OBJECT(vfl->listview), "drag_end",
187 G_CALLBACK(vflist_dnd_end), vfl);
188 }
189
190 /*
191 *-----------------------------------------------------------------------------
192 * pop-up menu
193 *-----------------------------------------------------------------------------
194 */
195
196 static GList *vflist_pop_menu_file_list(ViewFileList *vfl)
197 {
198 if (!vfl->click_fd) return NULL;
199
200 if (vflist_row_is_selected(vfl, vfl->click_fd))
201 {
202 return vflist_selection_get_list(vfl);
203 }
204
205 return g_list_append(NULL, g_strdup(vfl->click_fd->path));
206 }
207
208 static void vflist_pop_menu_edit_cb(GtkWidget *widget, gpointer data)
209 {
210 ViewFileList *vfl;
211 gint n;
212 GList *list;
213
214 vfl = submenu_item_get_data(widget);
215 n = GPOINTER_TO_INT(data);
216
217 if (!vfl) return;
218
219 list = vflist_pop_menu_file_list(vfl);
220 start_editor_from_path_list(n, list);
221 path_list_free(list);
222 }
223
224 static void vflist_pop_menu_info_cb(GtkWidget *widget, gpointer data)
225 {
226 ViewFileList *vfl = data;
227
228 info_window_new(NULL, vflist_pop_menu_file_list(vfl));
229 }
230
231 static void vflist_pop_menu_view_cb(GtkWidget *widget, gpointer data)
232 {
233 ViewFileList *vfl = data;
234
235 if (vflist_row_is_selected(vfl, vfl->click_fd))
236 {
237 GList *list;
238
239 list = vflist_selection_get_list(vfl);
240 view_window_new_from_list(list);
241 path_list_free(list);
242 }
243 else
244 {
245 const gchar *path;
246
247 path = vfl->click_fd->path;
248 view_window_new(path);
249 }
250 }
251
252 static void vflist_pop_menu_copy_cb(GtkWidget *widget, gpointer data)
253 {
254 ViewFileList *vfl = data;
255
256 file_util_copy(NULL, vflist_pop_menu_file_list(vfl), NULL, vfl->listview);
257 }
258
259 static void vflist_pop_menu_move_cb(GtkWidget *widget, gpointer data)
260 {
261 ViewFileList *vfl = data;
262
263 file_util_move(NULL, vflist_pop_menu_file_list(vfl), NULL, vfl->listview);
264 }
265
266 static void vflist_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
267 {
268 ViewFileList *vfl = data;
269 GList *list;
270
271 list = vflist_pop_menu_file_list(vfl);
272 if (enable_in_place_rename &&
273 list && !list->next && vfl->click_fd)
274 {
275 GtkTreeModel *store;
276 GtkTreeIter iter;
277
278 path_list_free(list);
279
280 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfl->listview));
281 if (vflist_find_row(vfl, vfl->click_fd, &iter) >= 0)
282 {
283 GtkTreePath *tpath;
284
285 tpath = gtk_tree_model_get_path(store, &iter);
286 tree_edit_by_path(GTK_TREE_VIEW(vfl->listview), tpath,
287 FILE_COLUMN_NAME -1, vfl->click_fd->name,
288 vflist_row_rename_cb, vfl);
289 gtk_tree_path_free(tpath);
290 }
291 return;
292 }
293
294 file_util_rename(NULL, list, vfl->listview);
295 }
296
297 static void vflist_pop_menu_delete_cb(GtkWidget *widget, gpointer data)
298 {
299 ViewFileList *vfl = data;
300
301 file_util_delete(NULL, vflist_pop_menu_file_list(vfl), vfl->listview);
302 }
303
304 static void vflist_pop_menu_sort_cb(GtkWidget *widget, gpointer data)
305 {
306 ViewFileList *vfl;
307 SortType type;
308
309 vfl = submenu_item_get_data(widget);
310 if (!vfl) return;
311
312 type = (SortType)GPOINTER_TO_INT(data);
313
314 if (vfl->layout)
315 {
316 layout_sort_set(vfl->layout, type, vfl->sort_ascend);
317 }
318 else
319 {
320 vflist_sort_set(vfl, type, vfl->sort_ascend);
321 }
322 }
323
324 static void vflist_pop_menu_sort_ascend_cb(GtkWidget *widget, gpointer data)
325 {
326 ViewFileList *vfl = data;
327
328 if (vfl->layout)
329 {
330 layout_sort_set(vfl->layout, vfl->sort_method, !vfl->sort_ascend);
331 }
332 else
333 {
334 vflist_sort_set(vfl, vfl->sort_method, !vfl->sort_ascend);
335 }
336 }
337
338 static void vflist_pop_menu_icons_cb(GtkWidget *widget, gpointer data)
339 {
340 ViewFileList *vfl = data;
341
342 if (vfl->layout) layout_views_set(vfl->layout, vfl->layout->tree_view, TRUE);
343 }
344
345 static void vflist_pop_menu_thumbs_cb(GtkWidget *widget, gpointer data)
346 {
347 ViewFileList *vfl = data;
348
349 vflist_color_set(vfl, vfl->click_fd, FALSE);
350 if (vfl->layout)
351 {
352 layout_thumb_set(vfl->layout, !vfl->thumbs_enabled);
353 }
354 else
355 {
356 vflist_thumb_set(vfl, !vfl->thumbs_enabled);
357 }
358 }
359
360 static void vflist_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
361 {
362 ViewFileList *vfl = data;
363
364 vflist_color_set(vfl, vfl->click_fd, FALSE);
365 vflist_refresh(vfl);
366 }
367
368 static void vflist_popup_destroy_cb(GtkWidget *widget, gpointer data)
369 {
370 ViewFileList *vfl = data;
371 vflist_color_set(vfl, vfl->click_fd, FALSE);
372 vfl->click_fd = NULL;
373 vfl->popup = NULL;
374 }
375
376 static GtkWidget *vflist_pop_menu(ViewFileList *vfl, FileData *fd)
377 {
378 GtkWidget *menu;
379 GtkWidget *item;
380 GtkWidget *submenu;
381 gint active;
382
383 vflist_color_set(vfl, fd, TRUE);
384 active = (fd != NULL);
385
386 menu = popup_menu_short_lived();
387 g_signal_connect(G_OBJECT(menu), "destroy",
388 G_CALLBACK(vflist_popup_destroy_cb), vfl);
389
390 submenu_add_edit(menu, &item, G_CALLBACK(vflist_pop_menu_edit_cb), vfl);
391 gtk_widget_set_sensitive(item, active);
392
393 menu_item_add_stock_sensitive(menu, _("_Properties"), GTK_STOCK_PROPERTIES, active,
394 G_CALLBACK(vflist_pop_menu_info_cb), vfl);
395 menu_item_add_stock_sensitive(menu, _("View in _new window"), GTK_STOCK_NEW, active,
396 G_CALLBACK(vflist_pop_menu_view_cb), vfl);
397
398 menu_item_add_divider(menu);
399 menu_item_add_stock_sensitive(menu, _("_Copy..."), GTK_STOCK_COPY, active,
400 G_CALLBACK(vflist_pop_menu_copy_cb), vfl);
401 menu_item_add_sensitive(menu, _("_Move..."), active,
402 G_CALLBACK(vflist_pop_menu_move_cb), vfl);
403 menu_item_add_sensitive(menu, _("_Rename..."), active,
404 G_CALLBACK(vflist_pop_menu_rename_cb), vfl);
405 menu_item_add_stock_sensitive(menu, _("_Delete..."), GTK_STOCK_DELETE, active,
406 G_CALLBACK(vflist_pop_menu_delete_cb), vfl);
407
408 menu_item_add_divider(menu);
409
410 submenu = submenu_add_sort(NULL, G_CALLBACK(vflist_pop_menu_sort_cb), vfl,
411 FALSE, FALSE, TRUE, vfl->sort_method);
412 menu_item_add_divider(submenu);
413 menu_item_add_check(submenu, _("Ascending"), vfl->sort_ascend,
414 G_CALLBACK(vflist_pop_menu_sort_ascend_cb), vfl);
415
416 item = menu_item_add(menu, _("_Sort"), NULL, NULL);
417 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
418
419 menu_item_add_check(menu, _("View as _icons"), FALSE,
420 G_CALLBACK(vflist_pop_menu_icons_cb), vfl);
421 menu_item_add_check(menu, _("Show _thumbnails"), vfl->thumbs_enabled,
422 G_CALLBACK(vflist_pop_menu_thumbs_cb), vfl);
423 menu_item_add_stock(menu, _("Re_fresh"), GTK_STOCK_REFRESH, G_CALLBACK(vflist_pop_menu_refresh_cb), vfl);
424
425 return menu;
426 }
427
428 /*
429 *-----------------------------------------------------------------------------
430 * callbacks
431 *-----------------------------------------------------------------------------
432 */
433
434 static gint vflist_row_rename_cb(TreeEditData *td, const gchar *old, const gchar *new, gpointer data)
435 {
436 ViewFileList *vfl = data;
437 gchar *old_path;
438 gchar *new_path;
439
440 if (strlen(new) == 0) return FALSE;
441
442 old_path = concat_dir_and_file(vfl->path, old);
443 new_path = concat_dir_and_file(vfl->path, new);
444
445 if (strchr(new, '/') != NULL)
446 {
447 gchar *text = g_strdup_printf(_("Invalid file name:\n%s"), new);
448 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vfl->listview);
449 g_free(text);
450 }
451 else if (isfile(new_path))
452 {
453 gchar *text = g_strdup_printf(_("A file with name %s already exists."), new);
454 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vfl->listview);
455 g_free(text);
456 }
457 else if (!rename_file(old_path, new_path))
458 {
459 gchar *text = g_strdup_printf(_("Unable to rename file:\n%s\nto:\n%s"), old, new);
460 file_util_warning_dialog(_("Error renaming file"), text, GTK_STOCK_DIALOG_ERROR, vfl->listview);
461 g_free(text);
462 }
463 else
464 {
465 file_maint_renamed(old_path, new_path);
466 }
467
468 g_free(old_path);
469 g_free(new_path);
470
471 return FALSE;
472 }
473
474 static void vflist_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
475 {
476 ViewFileList *vfl = data;
477 GtkTreeModel *store;
478 GtkTreeIter iter;
479 GtkTreePath *tpath;
480 gint cw, ch;
481
482 if (vflist_find_row(vfl, vfl->click_fd, &iter) < 0) return;
483 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfl->listview));
484 tpath = gtk_tree_model_get_path(store, &iter);
485 tree_view_get_cell_clamped(GTK_TREE_VIEW(vfl->listview), tpath, FILE_COLUMN_NAME - 1, TRUE, x, y, &cw, &ch);
486 gtk_tree_path_free(tpath);
487 *y += ch;
488 popup_menu_position_clamp(menu, x, y, 0);
489 }
490
491 static gint vflist_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
492 {
493 ViewFileList *vfl = data;
494 GtkTreePath *tpath;
495
496 if (event->keyval != GDK_Menu) return FALSE;
497
498 gtk_tree_view_get_cursor(GTK_TREE_VIEW(vfl->listview), &tpath, NULL);
499 if (tpath)
500 {
501 GtkTreeModel *store;
502 GtkTreeIter iter;
503
504 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
505 gtk_tree_model_get_iter(store, &iter, tpath);
506 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &vfl->click_fd, -1);
507 gtk_tree_path_free(tpath);
508 }
509 else
510 {
511 vfl->click_fd = NULL;
512 }
513
514 vfl->popup = vflist_pop_menu(vfl, vfl->click_fd);
515 gtk_menu_popup(GTK_MENU(vfl->popup), NULL, NULL, vflist_menu_position_cb, vfl, 0, GDK_CURRENT_TIME);
516
517 return TRUE;
518 }
519
520 static gint vflist_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
521 {
522 ViewFileList *vfl = data;
523 GtkTreePath *tpath;
524 GtkTreeIter iter;
525 FileData *fd = NULL;
526
527 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
528 &tpath, NULL, NULL, NULL))
529 {
530 GtkTreeModel *store;
531
532 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
533 gtk_tree_model_get_iter(store, &iter, tpath);
534 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
535 #if 0
536 gtk_tree_view_set_cursor(GTK_TREE_VIEW(widget), tpath, NULL, FALSE);
537 #endif
538 gtk_tree_path_free(tpath);
539 }
540
541 vfl->click_fd = fd;
542
543 if (bevent->button == 3)
544 {
545 vfl->popup = vflist_pop_menu(vfl, vfl->click_fd);
546 gtk_menu_popup(GTK_MENU(vfl->popup), NULL, NULL, NULL, NULL,
547 bevent->button, bevent->time);
548 return TRUE;
549 }
550
551 if (!fd) return FALSE;
552
553 if (bevent->button == 2)
554 {
555 if (!vflist_row_is_selected(vfl, fd))
556 {
557 vflist_color_set(vfl, fd, TRUE);
558 }
559 return TRUE;
560 }
561
562
563 if (bevent->button == 1 && bevent->type == GDK_BUTTON_PRESS &&
564 !(bevent->state & GDK_SHIFT_MASK ) &&
565 !(bevent->state & GDK_CONTROL_MASK ) &&
566 vflist_row_is_selected(vfl, fd))
567 {
568 gtk_widget_grab_focus(widget);
569 return TRUE;
570 }
571
572 #if 0
573 if (bevent->button == 1 && bevent->type == GDK_2BUTTON_PRESS)
574 {
575 if (vfl->layout) layout_image_full_screen_start(vfl->layout);
576 }
577 #endif
578
579 return FALSE;
580 }
581
582 static gint vflist_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
583 {
584 ViewFileList *vfl = data;
585 GtkTreePath *tpath;
586 GtkTreeIter iter;
587 FileData *fd = NULL;
588
589 if (bevent->button == 2)
590 {
591 vflist_color_set(vfl, vfl->click_fd, FALSE);
592 }
593
594 if (bevent->button != 1 && bevent->button != 2)
595 {
596 return TRUE;
597 }
598
599 if ((bevent->x != 0 || bevent->y != 0) &&
600 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), bevent->x, bevent->y,
601 &tpath, NULL, NULL, NULL))
602 {
603 GtkTreeModel *store;
604
605 store = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
606 gtk_tree_model_get_iter(store, &iter, tpath);
607 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
608 gtk_tree_path_free(tpath);
609 }
610
611 if (bevent->button == 2)
612 {
613 if (fd && vfl->click_fd == fd)
614 {
615 GtkTreeSelection *selection;
616
617 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
618 if (vflist_row_is_selected(vfl, fd))
619 {
620 gtk_tree_selection_unselect_iter(selection, &iter);
621 }
622 else
623 {
624 gtk_tree_selection_select_iter(selection, &iter);
625 }
626 }
627 return TRUE;
628 }
629
630 if (fd && vfl->click_fd == fd &&
631 !(bevent->state & GDK_SHIFT_MASK ) &&
632 !(bevent->state & GDK_CONTROL_MASK ) &&
633 vflist_row_is_selected(vfl, fd))
634 {
635 GtkTreeSelection *selection;
636
637 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
638 gtk_tree_selection_unselect_all(selection);
639 gtk_tree_selection_select_iter(selection, &iter);
640 vflist_move_cursor(vfl, &iter);
641 return TRUE;
642 }
643
644 return FALSE;
645 }
646
647 static void vflist_select_image(ViewFileList *vfl, gint row)
648 {
649 const gchar *path;
650 const gchar *read_ahead_path = NULL;
651
652 path = vflist_index_get_path(vfl, row);
653 if (!path) return;
654
655 if (path && enable_read_ahead)
656 {
657 FileData *fd;
658 if (row > vflist_index_by_path(vfl, layout_image_get_path(vfl->layout)) &&
659 row + 1 < vflist_count(vfl, NULL))
660 {
661 fd = vflist_index_get_data(vfl, row + 1);
662 }
663 else if (row > 0)
664 {
665 fd = vflist_index_get_data(vfl, row - 1);
666 }
667 else
668 {
669 fd = NULL;
670 }
671 if (fd) read_ahead_path = fd->path;
672 }
673
674 layout_image_set_with_ahead(vfl->layout, path, read_ahead_path);
675 }
676
677 static gint vflist_select_idle_cb(gpointer data)
678 {
679 ViewFileList *vfl = data;
680
681 if (!vfl->layout)
682 {
683 vfl->select_idle_id = -1;
684 return FALSE;
685 }
686
687 vflist_send_update(vfl);
688
689 if (vfl->select_fd)
690 {
691 vflist_select_image(vfl, g_list_index(vfl->list, vfl->select_fd));
692 vfl->select_fd = NULL;
693 }
694
695 vfl->select_idle_id = -1;
696 return FALSE;
697 }
698
699 static void vflist_select_idle_cancel(ViewFileList *vfl)
700 {
701 if (vfl->select_idle_id != -1) g_source_remove(vfl->select_idle_id);
702 vfl->select_idle_id = -1;
703 }
704
705 static gboolean vflist_select_cb(GtkTreeSelection *selection, GtkTreeModel *store, GtkTreePath *tpath,
706 gboolean path_currently_selected, gpointer data)
707 {
708 ViewFileList *vfl = data;
709 GtkTreeIter iter;
710
711 if (!path_currently_selected &&
712 gtk_tree_model_get_iter(store, &iter, tpath))
713 {
714 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &vfl->select_fd, -1);
715 }
716 else
717 {
718 vfl->select_fd = NULL;
719 }
720
721 if (vfl->layout &&
722 vfl->select_idle_id == -1)
723 {
724 vfl->select_idle_id = g_idle_add(vflist_select_idle_cb, vfl);
725 }
726
727 return TRUE;
728 }
729
730 /*
731 *-----------------------------------------------------------------------------
732 * misc
733 *-----------------------------------------------------------------------------
734 */
735
736 void vflist_sort_set(ViewFileList *vfl, SortType type, gint ascend)
737 {
738 GtkListStore *store;
739 GList *work;
740
741 if (vfl->sort_method == type && vfl->sort_ascend == ascend) return;
742
743 vfl->sort_method = type;
744 vfl->sort_ascend = ascend;
745
746 if (!vfl->list) return;
747
748 vfl->list = filelist_sort(vfl->list, vfl->sort_method, vfl->sort_ascend);
749
750 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vfl->listview)));
751
752 /* reorder the treeview, maintaining current selection */
753 work = g_list_last(vfl->list);
754 while (work)
755 {
756 FileData *fd;
757 GtkTreeIter iter;
758
759 fd = work->data;
760 if (vflist_find_row(vfl, fd, &iter) >= 0)
761 {
762 gtk_list_store_move_after(store, &iter, NULL);
763 }
764
765 work = work->prev;
766 }
767 }
768
769 /*
770 *-----------------------------------------------------------------------------
771 * thumb updates
772 *-----------------------------------------------------------------------------
773 */
774
775 static gint vflist_thumb_next(ViewFileList *vfl);
776
777 static void vflist_thumb_status(ViewFileList *vfl, gdouble val, const gchar *text)
778 {
779 if (vfl->func_thumb_status)
780 {
781 vfl->func_thumb_status(vfl, val, text, vfl->data_thumb_status);
782 }
783 }
784
785 static void vflist_thumb_cleanup(ViewFileList *vfl)
786 {
787 vflist_thumb_status(vfl, 0.0, NULL);
788
789 vfl->thumbs_count = 0;
790 vfl->thumbs_running = FALSE;
791
792 thumb_loader_free(vfl->thumbs_loader);
793 vfl->thumbs_loader = NULL;
794
795 vfl->thumbs_filedata = NULL;
796 }
797
798 static void vflist_thumb_stop(ViewFileList *vfl)
799 {
800 if (vfl->thumbs_running) vflist_thumb_cleanup(vfl);
801 }
802
803 static void vflist_thumb_do(ViewFileList *vfl, ThumbLoader *tl, FileData *fd)
804 {
805 GtkListStore *store;
806 GtkTreeIter iter;
807
808 if (!fd || vflist_find_row(vfl, fd, &iter) < 0) return;
809
810 if (fd->pixbuf) g_object_unref(fd->pixbuf);
811 fd->pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
812
813 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vfl->listview)));
814 gtk_list_store_set(store, &iter, FILE_COLUMN_THUMB, fd->pixbuf, -1);
815
816 vflist_thumb_status(vfl, (gdouble)(vfl->thumbs_count) / g_list_length(vfl->list), _("Loading thumbs..."));
817 }
818
819 static void vflist_thumb_error_cb(ThumbLoader *tl, gpointer data)
820 {
821 ViewFileList *vfl = data;
822
823 if (vfl->thumbs_filedata && vfl->thumbs_loader == tl)
824 {
825 vflist_thumb_do(vfl, tl, vfl->thumbs_filedata);
826 }
827
828 while (vflist_thumb_next(vfl));
829 }
830
831 static void vflist_thumb_done_cb(ThumbLoader *tl, gpointer data)
832 {
833 ViewFileList *vfl = data;
834
835 if (vfl->thumbs_filedata && vfl->thumbs_loader == tl)
836 {
837 vflist_thumb_do(vfl, tl, vfl->thumbs_filedata);
838 }
839
840 while (vflist_thumb_next(vfl));
841 }
842
843 static gint vflist_thumb_next(ViewFileList *vfl)
844 {
845 GtkTreePath *tpath;
846 FileData *fd = NULL;
847
848 /* first check the visible files */
849
850 if (GTK_WIDGET_REALIZED(vfl->listview) &&
851 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vfl->listview), 0, 0, &tpath, NULL, NULL, NULL))
852 {
853 GtkTreeModel *store;
854 GtkTreeIter iter;
855 gint valid = TRUE;
856
857 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfl->listview));
858 gtk_tree_model_get_iter(store, &iter, tpath);
859 gtk_tree_path_free(tpath);
860
861 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vfl->listview), &iter, FALSE) == 0)
862 {
863 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
864 if (fd->pixbuf) fd = NULL;
865
866 valid = gtk_tree_model_iter_next(store, &iter);
867 }
868 }
869
870 /* then find first undone */
871
872 if (!fd)
873 {
874 GList *work = vfl->list;
875 while (work && !fd)
876 {
877 FileData *fd_p = work->data;
878 work = work->next;
879
880 if (!fd_p->pixbuf) fd = fd_p;
881 }
882 }
883
884 if (!fd)
885 {
886 /* done */
887 vflist_thumb_cleanup(vfl);
888 return FALSE;
889 }
890
891 vfl->thumbs_count++;
892
893 vfl->thumbs_filedata = fd;
894
895 thumb_loader_free(vfl->thumbs_loader);
896
897 vfl->thumbs_loader = thumb_loader_new(thumb_max_width, thumb_max_height);
898 thumb_loader_set_callbacks(vfl->thumbs_loader,
899 vflist_thumb_done_cb,
900 vflist_thumb_error_cb,
901 NULL,
902 vfl);
903
904 if (!thumb_loader_start(vfl->thumbs_loader, fd->path))
905 {
906 /* set icon to unknown, continue */
907 if (debug) printf("thumb loader start failed %s\n", vfl->thumbs_loader->path);
908 vflist_thumb_do(vfl, vfl->thumbs_loader, fd);
909
910 return TRUE;
911 }
912
913 return FALSE;
914 }
915
916 static void vflist_thumb_update(ViewFileList *vfl)
917 {
918 vflist_thumb_stop(vfl);
919 if (!vfl->thumbs_enabled) return;
920
921 vflist_thumb_status(vfl, 0.0, _("Loading thumbs..."));
922 vfl->thumbs_running = TRUE;
923
924 while (vflist_thumb_next(vfl));
925 }
926
927 /*
928 *-----------------------------------------------------------------------------
929 * row stuff
930 *-----------------------------------------------------------------------------
931 */
932
933 FileData *vflist_index_get_data(ViewFileList *vfl, gint row)
934 {
935 return g_list_nth_data(vfl->list, row);
936 }
937
938 gchar *vflist_index_get_path(ViewFileList *vfl, gint row)
939 {
940 FileData *fd;
941
942 fd = g_list_nth_data(vfl->list, row);
943
944 return (fd ? fd->path : NULL);
945 }
946
947 static gint vflist_row_by_path(ViewFileList *vfl, const gchar *path, FileData **fd)
948 {
949 gint p = 0;
950 GList *work;
951
952 if (!path) return -1;
953
954 work = vfl->list;
955 while (work)
956 {
957 FileData *fd_n = work->data;
958 if (strcmp(path, fd_n->path) == 0)
959 {
960 if (fd) *fd = fd_n;
961 return p;
962 }
963 work = work->next;
964 p++;
965 }
966
967 if (fd) *fd = NULL;
968 return -1;
969 }
970
971 gint vflist_index_by_path(ViewFileList *vfl, const gchar *path)
972 {
973 return vflist_row_by_path(vfl, path, NULL);
974 }
975
976 gint vflist_count(ViewFileList *vfl, gint64 *bytes)
977 {
978 if (bytes)
979 {
980 gint64 b = 0;
981 GList *work;
982
983 work = vfl->list;
984 while (work)
985 {
986 FileData *fd = work->data;
987 work = work->next;
988 b += fd->size;
989 }
990
991 *bytes = b;
992 }
993
994 return g_list_length(vfl->list);
995 }
996
997 GList *vflist_get_list(ViewFileList *vfl)
998 {
999 GList *list = NULL;
1000 GList *work;
1001
1002 work = vfl->list;
1003 while (work)
1004 {
1005 FileData *fd = work->data;
1006 work = work->next;
1007
1008 list = g_list_prepend(list, g_strdup(fd->path));
1009 }
1010
1011 return g_list_reverse(list);
1012 }
1013
1014 /*
1015 *-----------------------------------------------------------------------------
1016 * selections
1017 *-----------------------------------------------------------------------------
1018 */
1019
1020 static gint vflist_row_is_selected(ViewFileList *vfl, FileData *fd)
1021 {
1022 GtkTreeModel *store;
1023 GtkTreeSelection *selection;
1024 GList *slist;
1025 GList *work;
1026 gint found = FALSE;
1027
1028 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vfl->listview));
1029 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1030 work = slist;
1031 while (!found && work)
1032 {
1033 GtkTreePath *tpath = work->data;
1034 FileData *fd_n;
1035 GtkTreeIter iter;
1036
1037 gtk_tree_model_get_iter(store, &iter, tpath);
1038 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
1039 if (fd_n == fd) found = TRUE;
1040 work = work->next;
1041 }
1042 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1043 g_list_free(slist);
1044
1045 return found;
1046 }
1047
1048 gint vflist_index_is_selected(ViewFileList *vfl, gint row)
1049 {
1050 FileData *fd;
1051
1052 fd = vflist_index_get_data(vfl, row);
1053 return vflist_row_is_selected(vfl, fd);
1054 }
1055
1056 gint vflist_selection_count(ViewFileList *vfl, gint64 *bytes)
1057 {
1058 GtkTreeModel *store;
1059 GtkTreeSelection *selection;
1060 GList *slist;
1061 gint count;
1062
1063 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vfl->listview));
1064 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1065
1066 if (bytes)
1067 {
1068 gint64 b = 0;
1069 GList *work;
1070
1071 work = slist;
1072 while (work)
1073 {
1074 GtkTreePath *tpath = work->data;
1075 GtkTreeIter iter;
1076 FileData *fd;
1077
1078 gtk_tree_model_get_iter(store, &iter, tpath);
1079 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1080 b += fd->size;
1081
1082 work = work->next;
1083 }
1084
1085 *bytes = b;
1086 }
1087
1088 count = g_list_length(slist);
1089 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1090 g_list_free(slist);
1091
1092 return count;
1093 }
1094
1095 GList *vflist_selection_get_list(ViewFileList *vfl)
1096 {
1097 GtkTreeModel *store;
1098 GtkTreeSelection *selection;
1099 GList *slist;
1100 GList *list = NULL;
1101 GList *work;
1102
1103 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vfl->listview));
1104 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1105 work = slist;
1106 while (work)
1107 {
1108 GtkTreePath *tpath = work->data;
1109 FileData *fd;
1110 GtkTreeIter iter;
1111
1112 gtk_tree_model_get_iter(store, &iter, tpath);
1113 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1114
1115 list = g_list_prepend(list, g_strdup(fd->path));
1116
1117 work = work->next;
1118 }
1119 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1120 g_list_free(slist);
1121
1122 return g_list_reverse(list);
1123 }
1124
1125 GList *vflist_selection_get_list_by_index(ViewFileList *vfl)
1126 {
1127 GtkTreeModel *store;
1128 GtkTreeSelection *selection;
1129 GList *slist;
1130 GList *list = NULL;
1131 GList *work;
1132
1133 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vfl->listview));
1134 slist = gtk_tree_selection_get_selected_rows(selection, &store);
1135 work = slist;
1136 while (work)
1137 {
1138 GtkTreePath *tpath = work->data;
1139 FileData *fd;
1140 GtkTreeIter iter;
1141
1142 gtk_tree_model_get_iter(store, &iter, tpath);
1143 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd, -1);
1144
1145 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vfl->list, fd)));
1146
1147 work = work->next;
1148 }
1149 g_list_foreach(slist, (GFunc)gtk_tree_path_free, NULL);
1150 g_list_free(slist);
1151
1152 return g_list_reverse(list);
1153 }
1154
1155 void vflist_select_all(ViewFileList *vfl)
1156 {
1157 GtkTreeSelection *selection;
1158
1159 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vfl->listview));
1160 gtk_tree_selection_select_all(selection);
1161
1162 vfl->select_fd = NULL;
1163 }
1164
1165 void vflist_select_none(ViewFileList *vfl)
1166 {
1167 GtkTreeSelection *selection;
1168
1169 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vfl->listview));
1170 gtk_tree_selection_unselect_all(selection);
1171 }
1172
1173 void vflist_select_by_path(ViewFileList *vfl, const gchar *path)
1174 {
1175 FileData *fd;
1176 GtkTreeIter iter;
1177
1178 if (vflist_row_by_path(vfl, path, &fd) < 0) return;
1179 if (vflist_find_row(vfl, fd, &iter) < 0) return;
1180
1181 tree_view_row_make_visible(GTK_TREE_VIEW(vfl->listview), &iter, TRUE);
1182
1183 if (!vflist_row_is_selected(vfl, fd))
1184 {
1185 GtkTreeSelection *selection;
1186 GtkTreeModel *store;
1187 GtkTreePath *tpath;
1188
1189 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vfl->listview));
1190 gtk_tree_selection_unselect_all(selection);
1191 gtk_tree_selection_select_iter(selection, &iter);
1192 vflist_move_cursor(vfl, &iter);
1193
1194 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfl->listview));
1195 tpath = gtk_tree_model_get_path(store, &iter);
1196 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vfl->listview), tpath, NULL, FALSE);
1197 gtk_tree_path_free(tpath);
1198 }
1199 }
1200
1201 /*
1202 *-----------------------------------------------------------------------------
1203 * core (population)
1204 *-----------------------------------------------------------------------------
1205 */
1206
1207 static void vflist_listview_set_height(GtkWidget *listview, gint thumb)
1208 {
1209 GtkTreeViewColumn *column;
1210 GtkCellRenderer *cell;
1211 GList *list;
1212
1213 column = gtk_tree_view_get_column(GTK_TREE_VIEW(listview), FILE_COLUMN_THUMB - 1);
1214 if (!column) return;
1215
1216 gtk_tree_view_column_set_fixed_width(column, (thumb) ? thumb_max_width : 4);
1217
1218 list = gtk_tree_view_column_get_cell_renderers(column);
1219 if (!list) return;
1220 cell = list->data;
1221 g_list_free(list);
1222
1223 g_object_set(G_OBJECT(cell), "height", (thumb) ? thumb_max_height : -1, NULL);
1224 gtk_tree_view_columns_autosize(GTK_TREE_VIEW(listview));
1225 }
1226
1227 static void vflist_populate_view(ViewFileList *vfl)
1228 {
1229 GtkListStore *store;
1230 GtkTreeIter iter;
1231 gint thumbs;
1232 GList *work;
1233 GtkTreeRowReference *visible_row = NULL;
1234 GtkTreePath *tpath;
1235 gint valid;
1236
1237 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vfl->listview)));
1238 thumbs = vfl->thumbs_enabled;
1239
1240 vflist_thumb_stop(vfl);
1241
1242 if (!vfl->list)
1243 {
1244 gtk_list_store_clear(store);
1245 vflist_send_update(vfl);
1246 return;
1247 }
1248
1249 if (GTK_WIDGET_REALIZED(vfl->listview) &&
1250 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vfl->listview), 0, 0, &tpath, NULL, NULL, NULL))
1251 {
1252 visible_row = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), tpath);
1253 gtk_tree_path_free(tpath);
1254 }
1255
1256 vflist_listview_set_height(vfl->listview, thumbs);
1257
1258 valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
1259
1260 work = vfl->list;
1261 while (work)
1262 {
1263 gint match;
1264 FileData *fd = work->data;
1265 gint done = FALSE;
1266
1267 while (!done)
1268 {
1269 FileData *old_fd = NULL;
1270
1271 if (valid)
1272 {
1273 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, FILE_COLUMN_POINTER, &old_fd, -1);
1274 match = CASE_SORT(fd->name, old_fd->name);
1275 }
1276 else
1277 {
1278 match = -1;
1279 }
1280
1281 if (match < 0)
1282 {
1283 GtkTreeIter new;
1284 gchar *size;
1285
1286 size = text_from_size(fd->size);
1287 if (valid)
1288 {
1289 gtk_list_store_insert_before(store, &new, &iter);
1290 }
1291 else
1292 {
1293 gtk_list_store_append(store, &new);
1294 }
1295 gtk_list_store_set(store, &new, FILE_COLUMN_POINTER, fd,
1296 FILE_COLUMN_THUMB, (thumbs) ? fd->pixbuf : NULL,
1297 FILE_COLUMN_NAME, fd->name,
1298 FILE_COLUMN_SIZE, size,
1299 FILE_COLUMN_DATE, text_from_time(fd->date),
1300 FILE_COLUMN_COLOR, FALSE, -1);
1301 g_free(size);
1302
1303 done = TRUE;
1304 }
1305 else if (match > 0)
1306 {
1307 valid = gtk_list_store_remove(store, &iter);
1308 }
1309 else
1310 {
1311 gtk_list_store_set(store, &iter, FILE_COLUMN_POINTER, fd, -1);
1312 if (fd->date != old_fd->date)
1313 {
1314 gchar *size;
1315
1316 /* update, file changed */
1317 size = text_from_size(fd->size);
1318 gtk_list_store_set(store, &iter, FILE_COLUMN_SIZE, size,
1319 FILE_COLUMN_DATE, text_from_time(fd->date), -1);
1320 g_free(size);
1321 }
1322 else if (fd != old_fd)
1323 {
1324 /* preserve thumbnail */
1325 if (fd->pixbuf) g_object_unref(fd->pixbuf);
1326 fd->pixbuf = old_fd->pixbuf;
1327 if (fd->pixbuf) g_object_ref(fd->pixbuf);
1328 }
1329
1330 gtk_list_store_set(store, &iter, FILE_COLUMN_THUMB, (thumbs) ? fd->pixbuf : NULL, -1);
1331
1332 if (vfl->select_fd == old_fd) vfl->select_fd = fd;
1333
1334 if (valid) valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1335
1336 done = TRUE;
1337 }
1338 }
1339 work = work->next;
1340 }
1341
1342 while (valid)
1343 {
1344 valid = gtk_list_store_remove(store, &iter);
1345 }
1346
1347 if (visible_row)
1348 {
1349 if (gtk_tree_row_reference_valid(visible_row))
1350 {
1351 tpath = gtk_tree_row_reference_get_path(visible_row);
1352 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(vfl->listview), tpath, NULL, TRUE, 0.0, 0.0);
1353 gtk_tree_path_free(tpath);
1354 }
1355 gtk_tree_row_reference_free(visible_row);
1356 }
1357
1358 vflist_send_update(vfl);
1359 vflist_thumb_update(vfl);
1360 }
1361
1362 gint vflist_refresh(ViewFileList *vfl)
1363 {
1364 GList *old_list;
1365 gint ret = TRUE;
1366
1367 old_list = vfl->list;
1368 vfl->list = NULL;
1369
1370 if (vfl->path)
1371 {
1372 ret = filelist_read(vfl->path, &vfl->list, NULL);
1373 }
1374
1375 vfl->list = filelist_sort(vfl->list, vfl->sort_method, vfl->sort_ascend);
1376 vflist_populate_view(vfl);
1377
1378 filelist_free(old_list);
1379
1380 return ret;
1381 }
1382
1383 /* this overrides the low default of a GtkCellRenderer from 100 to CELL_HEIGHT_OVERRIDE, something sane for our purposes */
1384
1385 #define CELL_HEIGHT_OVERRIDE 512
1386
1387 static void cell_renderer_height_override(GtkCellRenderer *renderer)
1388 {
1389 GParamSpec *spec;
1390
1391 spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(renderer)), "height");
1392 if (spec && G_IS_PARAM_SPEC_INT(spec))
1393 {
1394 GParamSpecInt *spec_int;
1395
1396 spec_int = G_PARAM_SPEC_INT(spec);
1397 if (spec_int->maximum < CELL_HEIGHT_OVERRIDE) spec_int->maximum = CELL_HEIGHT_OVERRIDE;
1398 }
1399 }
1400
1401 static GdkColor *vflist_listview_color_shifted(GtkWidget *widget)
1402 {
1403 static GdkColor color;
1404 static GtkWidget *done = NULL;
1405
1406 if (done != widget)
1407 {
1408 GtkStyle *style;
1409
1410 style = gtk_widget_get_style(widget);
1411 memcpy(&color, &style->base[GTK_STATE_NORMAL], sizeof(color));
1412 shift_color(&color, -1, 0);
1413 done = widget;
1414 }
1415
1416 return &color;
1417 }
1418
1419 static void vflist_listview_color_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
1420 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
1421 {
1422 ViewFileList *vfl = data;
1423 gboolean set;
1424
1425 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_COLOR, &set, -1);
1426 g_object_set(G_OBJECT(cell),
1427 "cell-background-gdk", vflist_listview_color_shifted(vfl->listview),
1428 "cell-background-set", set, NULL);
1429 }
1430
1431 static void vflist_listview_add_column(ViewFileList *vfl, gint n, const gchar *title, gint image, gint right_justify)
1432 {
1433 GtkTreeViewColumn *column;
1434 GtkCellRenderer *renderer;
1435
1436 column = gtk_tree_view_column_new();
1437 gtk_tree_view_column_set_title(column, title);
1438 gtk_tree_view_column_set_min_width(column, 4);
1439
1440 if (!image)
1441 {
1442 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY);
1443 renderer = gtk_cell_renderer_text_new();
1444 if (right_justify)
1445 {
1446 g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1447 }
1448 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1449 gtk_tree_view_column_add_attribute(column, renderer, "text", n);
1450 }
1451 else
1452 {
1453 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
1454 renderer = gtk_cell_renderer_pixbuf_new();
1455 cell_renderer_height_override(renderer);
1456 gtk_tree_view_column_pack_start(column, renderer, TRUE);
1457 gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", n);
1458 }
1459
1460 gtk_tree_view_column_set_cell_data_func(column, renderer, vflist_listview_color_cb, vfl, NULL);
1461
1462 gtk_tree_view_append_column(GTK_TREE_VIEW(vfl->listview), column);
1463 }
1464
1465 /*
1466 *-----------------------------------------------------------------------------
1467 * base
1468 *-----------------------------------------------------------------------------
1469 */
1470
1471 gint vflist_set_path(ViewFileList *vfl, const gchar *path)
1472 {
1473 GtkListStore *store;
1474
1475 if (!path) return FALSE;
1476 if (vfl->path && strcmp(path, vfl->path) == 0) return TRUE;
1477
1478 g_free(vfl->path);
1479 vfl->path = g_strdup(path);
1480
1481 /* force complete reload */
1482 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vfl->listview)));
1483 gtk_list_store_clear(store);
1484
1485 filelist_free(vfl->list);
1486 vfl->list = NULL;
1487
1488 return vflist_refresh(vfl);
1489 }
1490
1491 static void vflist_destroy_cb(GtkWidget *widget, gpointer data)
1492 {
1493 ViewFileList *vfl = data;
1494
1495 if (vfl->popup)
1496 {
1497 g_signal_handlers_disconnect_matched(G_OBJECT(vfl->popup), G_SIGNAL_MATCH_DATA,
1498 0, 0, 0, NULL, vfl);
1499 gtk_widget_destroy(vfl->popup);
1500 }
1501
1502 vflist_select_idle_cancel(vfl);
1503 vflist_thumb_stop(vfl);
1504
1505 g_free(vfl->path);
1506 filelist_free(vfl->list);
1507 g_free(vfl);
1508 }
1509
1510 ViewFileList *vflist_new(const gchar *path, gint thumbs)
1511 {
1512 ViewFileList *vfl;
1513 GtkListStore *store;
1514 GtkTreeSelection *selection;
1515
1516 vfl = g_new0(ViewFileList, 1);
1517
1518 vfl->path = NULL;
1519 vfl->list = NULL;
1520 vfl->click_fd = NULL;
1521 vfl->select_fd = NULL;
1522 vfl->sort_method = SORT_NAME;
1523 vfl->sort_ascend = TRUE;
1524 vfl->thumbs_enabled = thumbs;
1525
1526 vfl->thumbs_running = FALSE;
1527 vfl->thumbs_count = 0;
1528 vfl->thumbs_loader = NULL;
1529 vfl->thumbs_filedata = NULL;
1530
1531 vfl->select_idle_id = -1;
1532
1533 vfl->popup = NULL;
1534
1535 vfl->widget = gtk_scrolled_window_new(NULL, NULL);
1536 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(vfl->widget), GTK_SHADOW_IN);
1537 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vfl->widget),
1538 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
1539 g_signal_connect(G_OBJECT(vfl->widget), "destroy",
1540 G_CALLBACK(vflist_destroy_cb), vfl);
1541
1542 store = gtk_list_store_new(6, G_TYPE_POINTER, GDK_TYPE_PIXBUF, G_TYPE_STRING,
1543 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
1544 vfl->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
1545 g_object_unref(store);
1546
1547 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vfl->listview));
1548 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_MULTIPLE);
1549 gtk_tree_selection_set_select_function(selection, vflist_select_cb, vfl, NULL);
1550
1551 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vfl->listview), FALSE);
1552 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vfl->listview), FALSE);
1553
1554 vflist_listview_add_column(vfl, FILE_COLUMN_THUMB, "", TRUE, FALSE);
1555 vflist_listview_add_column(vfl, FILE_COLUMN_NAME, _("Name"), FALSE, FALSE);
1556 vflist_listview_add_column(vfl, FILE_COLUMN_SIZE, _("Size"), FALSE, TRUE);
1557 vflist_listview_add_column(vfl, FILE_COLUMN_DATE, _("Date"), FALSE, TRUE);
1558
1559 g_signal_connect(G_OBJECT(vfl->listview), "key_press_event",
1560 G_CALLBACK(vflist_press_key_cb), vfl);
1561
1562 gtk_container_add (GTK_CONTAINER(vfl->widget), vfl->listview);
1563 gtk_widget_show(vfl->listview);
1564
1565 vflist_dnd_init(vfl);
1566
1567 g_signal_connect(G_OBJECT(vfl->listview), "button_press_event",
1568 G_CALLBACK(vflist_press_cb), vfl);
1569 g_signal_connect(G_OBJECT(vfl->listview), "button_release_event",
1570 G_CALLBACK(vflist_release_cb), vfl);
1571
1572 if (path) vflist_set_path(vfl, path);
1573
1574 return vfl;
1575 }
1576
1577 void vflist_set_status_func(ViewFileList *vfl,
1578 void (*func)(ViewFileList *vfl, gpointer data), gpointer data)
1579 {
1580 vfl->func_status = func;
1581 vfl->data_status = data;
1582 }
1583
1584 void vflist_set_thumb_status_func(ViewFileList *vfl,
1585 void (*func)(ViewFileList *vfl, gdouble val, const gchar *text, gpointer data),
1586 gpointer data)
1587 {
1588 vfl->func_thumb_status = func;
1589 vfl->data_thumb_status = data;
1590 }
1591
1592 void vflist_thumb_set(ViewFileList *vfl, gint enable)
1593 {
1594 if (vfl->thumbs_enabled == enable) return;
1595
1596 vfl->thumbs_enabled = enable;
1597 vflist_refresh(vfl);
1598 }
1599
1600 void vflist_set_layout(ViewFileList *vfl, LayoutWindow *layout)
1601 {
1602 vfl->layout = layout;
1603 }
1604
1605 /*
1606 *-----------------------------------------------------------------------------
1607 * maintenance (for rename, move, remove)
1608 *-----------------------------------------------------------------------------
1609 */
1610
1611 static gint vflist_maint_find_closest(ViewFileList *vfl, gint row, gint count, GList *ignore_list)
1612 {
1613 GList *list = NULL;
1614 GList *work;
1615 gint rev = row - 1;
1616 row ++;
1617
1618 work = ignore_list;
1619 while (work)
1620 {
1621 gint f = vflist_index_by_path(vfl, work->data);
1622 if (f >= 0) list = g_list_prepend(list, GINT_TO_POINTER(f));
1623 work = work->next;
1624 }
1625
1626 while (list)
1627 {
1628 gint c = TRUE;
1629 work = list;
1630 while (work && c)
1631 {
1632 gpointer p = work->data;
1633 work = work->next;
1634 if (row == GPOINTER_TO_INT(p))
1635 {
1636 row++;
1637 c = FALSE;
1638 }
1639 if (rev == GPOINTER_TO_INT(p))
1640 {
1641 rev--;
1642 c = FALSE;
1643 }
1644 if (!c) list = g_list_remove(list, p);
1645 }
1646 if (c && list)
1647 {
1648 g_list_free(list);
1649 list = NULL;
1650 }
1651 }
1652 if (row > count - 1)
1653 {
1654 if (rev < 0)
1655 return -1;
1656 else
1657 return rev;
1658 }
1659 else
1660 {
1661 return row;
1662 }
1663 }
1664
1665 gint vflist_maint_renamed(ViewFileList *vfl, const gchar *source, const gchar *dest)
1666 {
1667 gint ret = FALSE;
1668 gint row;
1669 gchar *source_base;
1670 gchar *dest_base;
1671 GList *work;
1672 FileData *fd;
1673
1674 row = vflist_index_by_path(vfl, source);
1675 if (row < 0) return FALSE;
1676
1677 source_base = remove_level_from_path(source);
1678 dest_base = remove_level_from_path(dest);
1679
1680 work = g_list_nth(vfl->list, row);
1681 fd = work->data;
1682
1683 if (strcmp(source_base, dest_base) == 0)
1684 {
1685 GtkListStore *store;
1686 GtkTreeIter iter;
1687 GtkTreeIter position;
1688 gint old_row;
1689 gint n;
1690
1691 old_row = g_list_index(vfl->list, fd);
1692
1693 vfl->list = g_list_remove(vfl->list, fd);
1694 g_free(fd->path);
1695
1696 fd->path = g_strdup(dest);
1697 fd->name = filename_from_path(fd->path);
1698
1699 vfl->list = filelist_insert_sort(vfl->list, fd, vfl->sort_method, vfl->sort_ascend);
1700 n = g_list_index(vfl->list, fd);
1701
1702 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vfl->listview)));
1703 row = vflist_find_row(vfl, fd, &iter);
1704 if (vflist_find_row(vfl, fd, &iter) >= 0 &&
1705 gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &position, NULL, n))
1706 {
1707 if (old_row >= n)
1708 {
1709 gtk_list_store_move_before(store, &iter, &position);
1710 }
1711 else
1712 {
1713 gtk_list_store_move_after(store, &iter, &position);
1714 }
1715 }
1716 gtk_list_store_set(store, &iter, FILE_COLUMN_NAME, fd->name, -1);
1717
1718 ret = TRUE;
1719 }
1720 else
1721 {
1722 ret = vflist_maint_removed(vfl, source, NULL);
1723 }
1724
1725 g_free(source_base);
1726 g_free(dest_base);
1727
1728 return ret;
1729 }
1730
1731 gint vflist_maint_removed(ViewFileList *vfl, const gchar *path, GList *ignore_list)
1732 {
1733 GtkTreeIter iter;
1734 GList *list;
1735 FileData *fd;
1736 gint row;
1737 gint new_row = -1;
1738
1739 row = vflist_index_by_path(vfl, path);
1740 if (row < 0) return FALSE;
1741
1742 if (vflist_index_is_selected(vfl, row) &&
1743 layout_image_get_collection(vfl->layout, NULL) == NULL)
1744 {
1745 gint n;
1746
1747 n = vflist_count(vfl, NULL);
1748 if (ignore_list)
1749 {
1750 new_row = vflist_maint_find_closest(vfl, row, n, ignore_list);
1751 if (debug) printf("row = %d, closest is %d\n", row, new_row);
1752 }
1753 else
1754 {
1755 if (row + 1 < n)
1756 {
1757 new_row = row + 1;
1758 }
1759 else if (row > 0)
1760 {
1761 new_row = row - 1;
1762 }
1763 }
1764 vflist_select_none(vfl);
1765 if (new_row >= 0)
1766 {
1767 fd = vflist_index_get_data(vfl, new_row);
1768 if (vflist_find_row(vfl, fd, &iter) >= 0)
1769 {
1770 GtkTreeSelection *selection;
1771
1772 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vfl->listview));
1773 gtk_tree_selection_select_iter(selection, &iter);
1774 vflist_move_cursor(vfl, &iter);
1775 }
1776 }
1777 }
1778
1779 fd = vflist_index_get_data(vfl, row);
1780 if (vflist_find_row(vfl, fd, &iter) >= 0)
1781 {
1782 GtkListStore *store;
1783 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vfl->listview)));
1784 gtk_list_store_remove(store, &iter);
1785 }
1786 list = g_list_nth(vfl->list, row);
1787 fd = list->data;
1788
1789 /* thumbnail loader check */
1790 if (fd == vfl->thumbs_filedata) vfl->thumbs_filedata = NULL;
1791
1792 vfl->list = g_list_remove(vfl->list, fd);
1793 file_data_free(fd);
1794
1795 vflist_send_update(vfl);
1796
1797 return TRUE;
1798 }
1799
1800 gint vflist_maint_moved(ViewFileList *vfl, const gchar *source, const gchar *dest, GList *ignore_list)
1801 {
1802 gint ret = FALSE;
1803 gchar *buf;
1804
1805 if (!source || !vfl->path) return FALSE;
1806
1807 buf = remove_level_from_path(source);
1808
1809 if (strcmp(buf, vfl->path) == 0)
1810 {
1811 ret = vflist_maint_removed(vfl, source, ignore_list);
1812 }
1813
1814 g_free(buf);
1815
1816 return ret;
1817 }
1818