comparison src/view_file_icon.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_icon.h"
14
15 #include "cellrenderericon.h"
16 #include "collect.h"
17 #include "collect-io.h"
18 #include "collect-table.h"
19 #include "dnd.h"
20 #include "editors.h"
21 #include "img-view.h"
22 #include "info.h"
23 #include "filelist.h"
24 #include "layout.h"
25 #include "layout_image.h"
26 #include "menu.h"
27 #include "thumb.h"
28 #include "utilops.h"
29 #include "ui_bookmark.h"
30 #include "ui_fileops.h"
31 #include "ui_menu.h"
32 #include "ui_tree_edit.h"
33
34
35 #include <gdk/gdkkeysyms.h> /* for keyboard values */
36
37 /* between these, the icon width is increased by thumb_max_width / 2 */
38 #define THUMB_MIN_ICON_WIDTH 128
39 #define THUMB_MAX_ICON_WIDTH 150
40
41 #define VFICON_MAX_COLUMNS 32
42 #define THUMB_BORDER_PADDING 2
43
44 #define VFICON_TIP_DELAY 500
45
46 #define ICON_DATA(x) ((IconData *)x)
47 #define FILE_DATA(x) ((FileData *)x)
48
49
50 enum {
51 FILE_COLUMN_POINTER = 0,
52 FILE_COLUMN_COUNT
53 };
54
55 typedef enum {
56 SELECTION_NONE = 0,
57 SELECTION_SELECTED = 1 << 0,
58 SELECTION_PRELIGHT = 1 << 1,
59 SELECTION_FOCUS = 1 << 2
60 } SelectionType;
61
62 typedef struct _IconData IconData;
63 struct _IconData
64 {
65 FileData fd;
66 SelectionType selected;
67 };
68
69
70 static gint iconlist_read(const gchar *path, GList **list)
71 {
72 GList *temp;
73 GList *work;
74
75 if (!filelist_read(path, &temp, NULL)) return FALSE;
76
77 work = temp;
78 while (work)
79 {
80 FileData *fd;
81 IconData *id;
82
83 fd = work->data;
84 id = g_new0(IconData, 1);
85
86 memcpy(id, fd, sizeof(FileData));
87 id->selected = SELECTION_NONE;
88
89 work->data = id;
90 g_free(fd);
91
92 work = work->next;
93 }
94
95 *list = temp;
96
97 return TRUE;
98 }
99
100 static void iconlist_free(GList *list)
101 {
102 filelist_free(list);
103 }
104
105
106 static void vficon_toggle_filenames(ViewFileIcon *vfi);
107 static void vficon_selection_remove(ViewFileIcon *vfi, FileData *fd, SelectionType mask, GtkTreeIter *iter);
108 static void vficon_move_focus(ViewFileIcon *vfi, gint row, gint col, gint relative);
109 static void vficon_set_focus(ViewFileIcon *vfi, FileData *fd);
110 static void vficon_thumb_update(ViewFileIcon *vfi);
111 static void vficon_populate_at_new_size(ViewFileIcon *vfi, gint w, gint h, gint force);
112
113
114 /*
115 *-----------------------------------------------------------------------------
116 * pop-up menu
117 *-----------------------------------------------------------------------------
118 */
119
120 static GList *vficon_pop_menu_file_list(ViewFileIcon *vfi)
121 {
122 if (!vfi->click_fd) return NULL;
123
124 if (ICON_DATA(vfi->click_fd)->selected & SELECTION_SELECTED)
125 {
126 return vficon_selection_get_list(vfi);
127 }
128
129 return g_list_append(NULL, g_strdup(vfi->click_fd->path));
130 }
131
132 static void vficon_pop_menu_edit_cb(GtkWidget *widget, gpointer data)
133 {
134 ViewFileIcon *vfi;
135 gint n;
136 GList *list;
137
138 vfi = submenu_item_get_data(widget);
139 n = GPOINTER_TO_INT(data);
140
141 if (!vfi) return;
142
143 list = vficon_pop_menu_file_list(vfi);
144 start_editor_from_path_list(n, list);
145 path_list_free(list);
146 }
147
148 static void vficon_pop_menu_info_cb(GtkWidget *widget, gpointer data)
149 {
150 ViewFileIcon *vfi = data;
151
152 info_window_new(NULL, vficon_pop_menu_file_list(vfi));
153 }
154
155 static void vficon_pop_menu_view_cb(GtkWidget *widget, gpointer data)
156 {
157 ViewFileIcon *vfi = data;
158
159 if (!vfi->click_fd) return;
160
161 if (ICON_DATA(vfi->click_fd)->selected & SELECTION_SELECTED)
162 {
163 GList *list;
164
165 list = vficon_selection_get_list(vfi);
166 view_window_new_from_list(list);
167 path_list_free(list);
168 }
169 else
170 {
171 view_window_new(vfi->click_fd->path);
172 }
173 }
174
175 static void vficon_pop_menu_copy_cb(GtkWidget *widget, gpointer data)
176 {
177 ViewFileIcon *vfi = data;
178
179 file_util_copy(NULL, vficon_pop_menu_file_list(vfi), NULL, vfi->listview);
180 }
181
182 static void vficon_pop_menu_move_cb(GtkWidget *widget, gpointer data)
183 {
184 ViewFileIcon *vfi = data;
185
186 file_util_move(NULL, vficon_pop_menu_file_list(vfi), NULL, vfi->listview);
187 }
188
189 static void vficon_pop_menu_rename_cb(GtkWidget *widget, gpointer data)
190 {
191 ViewFileIcon *vfi = data;
192
193 file_util_rename(NULL, vficon_pop_menu_file_list(vfi), vfi->listview);
194 }
195
196 static void vficon_pop_menu_delete_cb(GtkWidget *widget, gpointer data)
197 {
198 ViewFileIcon *vfi = data;
199
200 file_util_delete(NULL, vficon_pop_menu_file_list(vfi), vfi->listview);
201 }
202
203 static void vficon_pop_menu_sort_cb(GtkWidget *widget, gpointer data)
204 {
205 ViewFileIcon *vfi;
206 SortType type;
207
208 vfi = submenu_item_get_data(widget);
209 if (!vfi) return;
210
211 type = (SortType)GPOINTER_TO_INT(data);
212
213 if (vfi->layout)
214 {
215 layout_sort_set(vfi->layout, type, vfi->sort_ascend);
216 }
217 else
218 {
219 vficon_sort_set(vfi, type, vfi->sort_ascend);
220 }
221 }
222
223 static void vficon_pop_menu_sort_ascend_cb(GtkWidget *widget, gpointer data)
224 {
225 ViewFileIcon *vfi = data;
226
227 if (vfi->layout)
228 {
229 layout_sort_set(vfi->layout, vfi->sort_method, !vfi->sort_ascend);
230 }
231 else
232 {
233 vficon_sort_set(vfi, vfi->sort_method, !vfi->sort_ascend);
234 }
235 }
236
237 static void vficon_pop_menu_list_cb(GtkWidget *widget, gpointer data)
238 {
239 ViewFileIcon *vfi = data;
240
241 if (vfi->layout) layout_views_set(vfi->layout, vfi->layout->tree_view, FALSE);
242 }
243
244 static void vficon_pop_menu_show_names_cb(GtkWidget *widget, gpointer data)
245 {
246 ViewFileIcon *vfi = data;
247
248 vficon_toggle_filenames(vfi);
249 }
250
251 static void vficon_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
252 {
253 ViewFileIcon *vfi = data;
254
255 vficon_refresh(vfi);
256 }
257
258 static void vficon_popup_destroy_cb(GtkWidget *widget, gpointer data)
259 {
260 ViewFileIcon *vfi = data;
261 vficon_selection_remove(vfi, vfi->click_fd, SELECTION_PRELIGHT, NULL);
262 vfi->click_fd = NULL;
263 vfi->popup = NULL;
264 }
265
266 static GtkWidget *vficon_pop_menu(ViewFileIcon *vfi, gint active)
267 {
268 GtkWidget *menu;
269 GtkWidget *item;
270 GtkWidget *submenu;
271
272 menu = popup_menu_short_lived();
273
274 g_signal_connect(G_OBJECT(menu), "destroy",
275 G_CALLBACK(vficon_popup_destroy_cb), vfi);
276
277 submenu_add_edit(menu, &item, G_CALLBACK(vficon_pop_menu_edit_cb), vfi);
278 gtk_widget_set_sensitive(item, active);
279
280 item = menu_item_add_stock(menu, _("_Properties"), GTK_STOCK_PROPERTIES, G_CALLBACK(vficon_pop_menu_info_cb), vfi);
281 gtk_widget_set_sensitive(item, active);
282
283 item = menu_item_add_stock(menu, _("View in _new window"), GTK_STOCK_NEW, G_CALLBACK(vficon_pop_menu_view_cb), vfi);
284 gtk_widget_set_sensitive(item, active);
285
286 menu_item_add_divider(menu);
287 item = menu_item_add_stock(menu, _("_Copy..."), GTK_STOCK_COPY, G_CALLBACK(vficon_pop_menu_copy_cb), vfi);
288 gtk_widget_set_sensitive(item, active);
289 item = menu_item_add(menu, _("_Move..."), G_CALLBACK(vficon_pop_menu_move_cb), vfi);
290 gtk_widget_set_sensitive(item, active);
291 item = menu_item_add(menu, _("_Rename..."), G_CALLBACK(vficon_pop_menu_rename_cb), vfi);
292 gtk_widget_set_sensitive(item, active);
293 item = menu_item_add_stock(menu, _("_Delete..."), GTK_STOCK_DELETE, G_CALLBACK(vficon_pop_menu_delete_cb), vfi);
294 gtk_widget_set_sensitive(item, active);
295
296 menu_item_add_divider(menu);
297
298 submenu = submenu_add_sort(NULL, G_CALLBACK(vficon_pop_menu_sort_cb), vfi,
299 FALSE, FALSE, TRUE, vfi->sort_method);
300 menu_item_add_divider(submenu);
301 menu_item_add_check(submenu, _("Ascending"), vfi->sort_ascend,
302 G_CALLBACK(vficon_pop_menu_sort_ascend_cb), vfi);
303
304 item = menu_item_add(menu, _("_Sort"), NULL, NULL);
305 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
306
307 menu_item_add_check(menu, _("View as _icons"), TRUE,
308 G_CALLBACK(vficon_pop_menu_list_cb), vfi);
309 menu_item_add_check(menu, _("Show filename _text"), vfi->show_text,
310 G_CALLBACK(vficon_pop_menu_show_names_cb), vfi);
311 menu_item_add_stock(menu, _("Re_fresh"), GTK_STOCK_REFRESH, G_CALLBACK(vficon_pop_menu_refresh_cb), vfi);
312
313 return menu;
314 }
315
316 /*
317 *-------------------------------------------------------------------
318 * signals
319 *-------------------------------------------------------------------
320 */
321
322 static void vficon_send_update(ViewFileIcon *vfi)
323 {
324 if (vfi->func_status) vfi->func_status(vfi, vfi->data_status);
325 }
326
327 static void vficon_send_layout_select(ViewFileIcon *vfi, FileData *fd)
328 {
329 const gchar *read_ahead_path = NULL;
330
331 if (!vfi->layout || !fd) return;
332
333 if (enable_read_ahead)
334 {
335 FileData *fd_n;
336 gint row;
337
338 row = g_list_index(vfi->list, fd);
339 if (row > vficon_index_by_path(vfi, layout_image_get_path(vfi->layout)) &&
340 row + 1 < vficon_count(vfi, NULL))
341 {
342 fd_n = vficon_index_get_data(vfi, row + 1);
343 }
344 else if (row > 0)
345 {
346 fd_n = vficon_index_get_data(vfi, row - 1);
347 }
348 else
349 {
350 fd_n = NULL;
351 }
352 if (fd_n) read_ahead_path = fd_n->path;
353 }
354
355 layout_image_set_with_ahead(vfi->layout, fd->path, read_ahead_path);
356 }
357
358 static void vficon_toggle_filenames(ViewFileIcon *vfi)
359 {
360 vfi->show_text = !vfi->show_text;
361 show_icon_names = vfi->show_text;
362
363 vficon_populate_at_new_size(vfi, vfi->listview->allocation.width, vfi->listview->allocation.height, TRUE);
364 }
365
366 static gint vficon_get_icon_width(ViewFileIcon *vfi)
367 {
368 gint width;
369
370 if (!vfi->show_text) return thumb_max_width;
371
372 width = thumb_max_width + thumb_max_width / 2;
373 if (width < THUMB_MIN_ICON_WIDTH) width = THUMB_MIN_ICON_WIDTH;
374 if (width > THUMB_MAX_ICON_WIDTH) width = thumb_max_width;
375
376 return width;
377 }
378
379 /*
380 *-------------------------------------------------------------------
381 * misc utils
382 *-------------------------------------------------------------------
383 */
384
385 static gint vficon_find_position(ViewFileIcon *vfi, FileData *fd, gint *row, gint *col)
386 {
387 gint n;
388
389 n = g_list_index(vfi->list, fd);
390
391 if (n < 0) return FALSE;
392
393 *row = n / vfi->columns;
394 *col = n - (*row * vfi->columns);
395
396 return TRUE;
397 }
398
399 static gint vficon_find_iter(ViewFileIcon *vfi, FileData *fd, GtkTreeIter *iter, gint *column)
400 {
401 GtkTreeModel *store;
402 gint row, col;
403
404 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfi->listview));
405 if (!vficon_find_position(vfi, fd, &row, &col)) return FALSE;
406 if (!gtk_tree_model_iter_nth_child(store, iter, NULL, row)) return FALSE;
407 if (column) *column = col;
408
409 return TRUE;
410 }
411
412 static FileData *vficon_find_data(ViewFileIcon *vfi, gint row, gint col, GtkTreeIter *iter)
413 {
414 GtkTreeModel *store;
415 GtkTreeIter p;
416
417 if (row < 0 || col < 0) return NULL;
418
419 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfi->listview));
420 if (gtk_tree_model_iter_nth_child(store, &p, NULL, row))
421 {
422 GList *list;
423
424 gtk_tree_model_get(store, &p, FILE_COLUMN_POINTER, &list, -1);
425 if (!list) return NULL;
426
427 if (iter) *iter = p;
428
429 return g_list_nth_data(list, col);
430 }
431
432 return NULL;
433 }
434
435 static FileData *vficon_find_data_by_coord(ViewFileIcon *vfi, gint x, gint y, GtkTreeIter *iter)
436 {
437 GtkTreePath *tpath;
438 GtkTreeViewColumn *column;
439
440 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vfi->listview), x, y,
441 &tpath, &column, NULL, NULL))
442 {
443 GtkTreeModel *store;
444 GtkTreeIter row;
445 GList *list;
446 gint n;
447
448 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfi->listview));
449 gtk_tree_model_get_iter(store, &row, tpath);
450 gtk_tree_path_free(tpath);
451
452 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1);
453
454 n = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(column), "column_number"));
455 if (list)
456 {
457 if (iter) *iter = row;
458 return g_list_nth_data(list, n);
459 }
460 }
461
462 return NULL;
463 }
464
465 /*
466 *-------------------------------------------------------------------
467 * tooltip type window
468 *-------------------------------------------------------------------
469 */
470
471 static void tip_show(ViewFileIcon *vfi)
472 {
473 GtkWidget *label;
474 gint x, y;
475
476 if (vfi->tip_window) return;
477
478 gdk_window_get_pointer(gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vfi->listview)), &x, &y, NULL);
479
480 vfi->tip_fd = vficon_find_data_by_coord(vfi, x, y, NULL);
481 if (!vfi->tip_fd) return;
482
483 vfi->tip_window = gtk_window_new(GTK_WINDOW_POPUP);
484 gtk_window_set_resizable(GTK_WINDOW(vfi->tip_window), FALSE);
485 gtk_container_set_border_width(GTK_CONTAINER(vfi->tip_window), 2);
486
487 label = gtk_label_new(vfi->tip_fd->name);
488
489 g_object_set_data(G_OBJECT(vfi->tip_window), "tip_label", label);
490 gtk_container_add(GTK_CONTAINER(vfi->tip_window), label);
491 gtk_widget_show(label);
492
493 gdk_window_get_pointer(NULL, &x, &y, NULL);
494
495 if (!GTK_WIDGET_REALIZED(vfi->tip_window)) gtk_widget_realize(vfi->tip_window);
496 gtk_window_move(GTK_WINDOW(vfi->tip_window), x + 16, y + 16);
497 gtk_widget_show(vfi->tip_window);
498 }
499
500 static void tip_hide(ViewFileIcon *vfi)
501 {
502 if (vfi->tip_window) gtk_widget_destroy(vfi->tip_window);
503 vfi->tip_window = NULL;
504 }
505
506 static gint tip_schedule_cb(gpointer data)
507 {
508 ViewFileIcon *vfi = data;
509 GtkWidget *window;
510
511 if (vfi->tip_delay_id == -1) return FALSE;
512
513 window = gtk_widget_get_toplevel(vfi->listview);
514
515 if (GTK_WIDGET_SENSITIVE(window) &&
516 GTK_WINDOW(window)->has_focus)
517 {
518 tip_show(vfi);
519 }
520
521 vfi->tip_delay_id = -1;
522 return FALSE;
523 }
524
525 static void tip_schedule(ViewFileIcon *vfi)
526 {
527 tip_hide(vfi);
528
529 if (vfi->tip_delay_id != -1)
530 {
531 g_source_remove(vfi->tip_delay_id);
532 vfi->tip_delay_id = -1;
533 }
534
535 if (!vfi->show_text)
536 {
537 vfi->tip_delay_id = g_timeout_add(VFICON_TIP_DELAY, tip_schedule_cb, vfi);
538 }
539 }
540
541 static void tip_unschedule(ViewFileIcon *vfi)
542 {
543 tip_hide(vfi);
544
545 if (vfi->tip_delay_id != -1) g_source_remove(vfi->tip_delay_id);
546 vfi->tip_delay_id = -1;
547 }
548
549 static void tip_update(ViewFileIcon *vfi, FileData *fd)
550 {
551 if (vfi->tip_window)
552 {
553 gint x, y;
554
555 gdk_window_get_pointer(NULL, &x, &y, NULL);
556 gtk_window_move(GTK_WINDOW(vfi->tip_window), x + 16, y + 16);
557
558 if (fd != vfi->tip_fd)
559 {
560 GtkWidget *label;
561
562 vfi->tip_fd = fd;
563
564 if (!vfi->tip_fd)
565 {
566 tip_hide(vfi);
567 tip_schedule(vfi);
568 return;
569 }
570
571 label = g_object_get_data(G_OBJECT(vfi->tip_window), "tip_label");
572 gtk_label_set_text(GTK_LABEL(label), vfi->tip_fd->name);
573 }
574 }
575 else
576 {
577 tip_schedule(vfi);
578 }
579 }
580
581 /*
582 *-------------------------------------------------------------------
583 * dnd
584 *-------------------------------------------------------------------
585 */
586
587 static void vficon_dnd_get(GtkWidget *widget, GdkDragContext *context,
588 GtkSelectionData *selection_data, guint info,
589 guint time, gpointer data)
590 {
591 ViewFileIcon *vfi = data;
592 GList *list = NULL;
593 gchar *uri_text = NULL;
594 gint total;
595
596 if (!vfi->click_fd) return;
597
598 if (ICON_DATA(vfi->click_fd)->selected & SELECTION_SELECTED)
599 {
600 list = vficon_selection_get_list(vfi);
601 }
602 else
603 {
604 const gchar *path = vfi->click_fd->path;
605
606 list = g_list_append(NULL, g_strdup(path));
607 }
608
609 if (!list) return;
610 uri_text = uri_text_from_list(list, &total, (info == TARGET_TEXT_PLAIN));
611 path_list_free(list);
612
613 if (debug) printf(uri_text);
614
615 gtk_selection_data_set(selection_data, selection_data->target,
616 8, uri_text, total);
617 g_free(uri_text);
618 }
619
620 static void vficon_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointer data)
621 {
622 ViewFileIcon *vfi = data;
623
624 tip_unschedule(vfi);
625
626 if (vfi->click_fd && vfi->click_fd->pixbuf)
627 {
628 gint items;
629
630 if (ICON_DATA(vfi->click_fd)->selected & SELECTION_SELECTED)
631 items = g_list_length(vfi->selection);
632 else
633 items = 1;
634
635 dnd_set_drag_icon(widget, context, vfi->click_fd->pixbuf, items);
636 }
637 }
638
639 static void vficon_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer data)
640 {
641 ViewFileIcon *vfi = data;
642
643 vficon_selection_remove(vfi, vfi->click_fd, SELECTION_PRELIGHT, NULL);
644
645 if (context->action == GDK_ACTION_MOVE)
646 {
647 vficon_refresh(vfi);
648 }
649
650 tip_unschedule(vfi);
651 }
652
653 static void vficon_dnd_init(ViewFileIcon *vfi)
654 {
655 gtk_drag_source_set(vfi->listview, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
656 dnd_file_drag_types, dnd_file_drag_types_count,
657 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
658 g_signal_connect(G_OBJECT(vfi->listview), "drag_data_get",
659 G_CALLBACK(vficon_dnd_get), vfi);
660 g_signal_connect(G_OBJECT(vfi->listview), "drag_begin",
661 G_CALLBACK(vficon_dnd_begin), vfi);
662 g_signal_connect(G_OBJECT(vfi->listview), "drag_end",
663 G_CALLBACK(vficon_dnd_end), vfi);
664 }
665
666 /*
667 *-------------------------------------------------------------------
668 * cell updates
669 *-------------------------------------------------------------------
670 */
671
672 static void vficon_selection_set(ViewFileIcon *vfi, FileData *fd, SelectionType value, GtkTreeIter *iter)
673 {
674 IconData *id;
675 GtkTreeModel *store;
676 GList *list;
677
678 if (!fd) return;
679
680 id = ICON_DATA(fd);
681
682 if (id->selected == value) return;
683 id->selected = value;
684
685 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfi->listview));
686 if (iter)
687 {
688 gtk_tree_model_get(store, iter, FILE_COLUMN_POINTER, &list, -1);
689 if (list) gtk_list_store_set(GTK_LIST_STORE(store), iter, FILE_COLUMN_POINTER, list, -1);
690 }
691 else
692 {
693 GtkTreeIter row;
694
695 if (vficon_find_iter(vfi, fd, &row, NULL))
696 {
697 gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1);
698 if (list) gtk_list_store_set(GTK_LIST_STORE(store), &row, FILE_COLUMN_POINTER, list, -1);
699 }
700 }
701 }
702
703 static void vficon_selection_add(ViewFileIcon *vfi, FileData *fd, SelectionType mask, GtkTreeIter *iter)
704 {
705 if (!fd) return;
706
707 vficon_selection_set(vfi, fd, ICON_DATA(fd)->selected | mask, iter);
708 }
709
710 static void vficon_selection_remove(ViewFileIcon *vfi, FileData *fd, SelectionType mask, GtkTreeIter *iter)
711 {
712 if (!fd) return;
713
714 vficon_selection_set(vfi, fd, ICON_DATA(fd)->selected & ~mask, iter);
715 }
716
717 /*
718 *-------------------------------------------------------------------
719 * selections
720 *-------------------------------------------------------------------
721 */
722
723 static void vficon_verify_selections(ViewFileIcon *vfi)
724 {
725 GList *work;
726
727 work = vfi->selection;
728 while (work)
729 {
730 FileData *fd = work->data;
731 work = work->next;
732 if (!g_list_find(vfi->list, fd))
733 {
734 vfi->selection = g_list_remove(vfi->selection, fd);
735 }
736 }
737 }
738
739 void vficon_select_all(ViewFileIcon *vfi)
740 {
741 GList *work;
742
743 g_list_free(vfi->selection);
744 vfi->selection = NULL;
745
746 work = vfi->list;
747 while (work)
748 {
749 vfi->selection = g_list_append(vfi->selection, work->data);
750 vficon_selection_add(vfi, work->data, SELECTION_SELECTED, NULL);
751 work = work->next;
752 }
753
754 vficon_send_update(vfi);
755 }
756
757 void vficon_select_none(ViewFileIcon *vfi)
758 {
759 GList *work;
760
761 work = vfi->selection;
762 while (work)
763 {
764 vficon_selection_remove(vfi, work->data, SELECTION_SELECTED, NULL);
765 work = work->next;
766 }
767
768 g_list_free(vfi->selection);
769 vfi->selection = NULL;
770
771 vficon_send_update(vfi);
772 }
773
774 static void vficon_select(ViewFileIcon *vfi, FileData *fd)
775 {
776 vfi->prev_selection = fd;
777
778 if (!fd || ICON_DATA(fd)->selected & SELECTION_SELECTED) return;
779
780 vfi->selection = g_list_append(vfi->selection, fd);
781 vficon_selection_add(vfi, fd, SELECTION_SELECTED, NULL);
782
783 vficon_send_update(vfi);
784 }
785
786 static void vficon_unselect(ViewFileIcon *vfi, FileData *fd)
787 {
788 vfi->prev_selection = fd;
789
790 if (!fd || !(ICON_DATA(fd)->selected & SELECTION_SELECTED) ) return;
791
792 vfi->selection = g_list_remove(vfi->selection, fd);
793 vficon_selection_remove(vfi, fd, SELECTION_SELECTED, NULL);
794
795 vficon_send_update(vfi);
796 }
797
798 static void vficon_select_util(ViewFileIcon *vfi, FileData *fd, gint select)
799 {
800 if (select)
801 {
802 vficon_select(vfi, fd);
803 }
804 else
805 {
806 vficon_unselect(vfi, fd);
807 }
808 }
809
810 static void vficon_select_region_util(ViewFileIcon *vfi, FileData *start, FileData *end, gint select)
811 {
812 gint row1, col1;
813 gint row2, col2;
814 gint t;
815 gint i, j;
816
817 if (!vficon_find_position(vfi, start, &row1, &col1) ||
818 !vficon_find_position(vfi, end, &row2, &col2) ) return;
819
820 vfi->prev_selection = end;
821
822 if (!collection_rectangular_selection)
823 {
824 GList *work;
825 FileData *fd;
826
827 if (g_list_index(vfi->list, start) > g_list_index(vfi->list, end))
828 {
829 fd = start;
830 start = end;
831 end = fd;
832 }
833
834 work = g_list_find(vfi->list, start);
835 while (work)
836 {
837 fd = work->data;
838 vficon_select_util(vfi, fd, select);
839
840 if (work->data != end)
841 work = work->next;
842 else
843 work = NULL;
844 }
845 return;
846 }
847
848 if (row2 < row1)
849 {
850 t = row1;
851 row1 = row2;
852 row2 = t;
853 }
854 if (col2 < col1)
855 {
856 t = col1;
857 col1 = col2;
858 col2 = t;
859 }
860
861 if (debug) printf("table: %d x %d to %d x %d\n", row1, col1, row2, col2);
862
863 for (i = row1; i <= row2; i++)
864 {
865 for (j = col1; j <= col2; j++)
866 {
867 FileData *fd = vficon_find_data(vfi, i, j, NULL);
868 if (fd) vficon_select_util(vfi, fd, select);
869 }
870 }
871 }
872
873 gint vficon_index_is_selected(ViewFileIcon *vfi, gint row)
874 {
875 FileData *fd = g_list_nth_data(vfi->list, row);
876
877 if (!fd) return FALSE;
878
879 return (ICON_DATA(fd)->selected & SELECTION_SELECTED);
880 }
881
882 gint vficon_selection_count(ViewFileIcon *vfi, gint64 *bytes)
883 {
884 if (bytes)
885 {
886 gint64 b = 0;
887 GList *work;
888
889 work = vfi->selection;
890 while (work)
891 {
892 FileData *fd = work->data;
893 b += fd->size;
894
895 work = work->next;
896 }
897
898 *bytes = b;
899 }
900
901 return g_list_length(vfi->selection);
902 }
903
904 GList *vficon_selection_get_list(ViewFileIcon *vfi)
905 {
906 GList *list = NULL;
907 GList *work;
908
909 work = vfi->selection;
910 while (work)
911 {
912 FileData *fd = work->data;
913
914 list = g_list_prepend(list, g_strdup(fd->path));
915
916 work = work->next;
917 }
918
919 list = g_list_reverse(list);
920
921 return list;
922 }
923
924 GList *vficon_selection_get_list_by_index(ViewFileIcon *vfi)
925 {
926 GList *list = NULL;
927 GList *work;
928
929 work = vfi->selection;
930 while (work)
931 {
932 list = g_list_prepend(list, GINT_TO_POINTER(g_list_index(vfi->list, work->data)));
933 work = work->next;
934 }
935
936 return g_list_reverse(list);
937 }
938
939 void vficon_select_by_path(ViewFileIcon *vfi, const gchar *path)
940 {
941 FileData *fd;
942 GList *work;
943
944 if (!path) return;
945
946 fd = NULL;
947 work = vfi->list;
948 while (work && !fd)
949 {
950 FileData *chk = work->data;
951 work = work->next;
952 if (strcmp(chk->path, path) == 0) fd = chk;
953 }
954
955 if (!fd) return;
956
957 if (!(ICON_DATA(fd)->selected & SELECTION_SELECTED))
958 {
959 vficon_select_none(vfi);
960 vficon_select(vfi, fd);
961 }
962
963 vficon_set_focus(vfi, fd);
964 }
965
966 /*
967 *-------------------------------------------------------------------
968 * focus
969 *-------------------------------------------------------------------
970 */
971
972 static void vficon_move_focus(ViewFileIcon *vfi, gint row, gint col, gint relative)
973 {
974 gint new_row;
975 gint new_col;
976
977 if (relative)
978 {
979 new_row = vfi->focus_row;
980 new_col = vfi->focus_column;
981
982 new_row += row;
983 if (new_row < 0) new_row = 0;
984 if (new_row >= vfi->rows) new_row = vfi->rows - 1;
985
986 while(col != 0)
987 {
988 if (col < 0)
989 {
990 new_col--;
991 col++;
992 }
993 else
994 {
995 new_col++;
996 col--;
997 }
998
999 if (new_col < 0)
1000 {
1001 if (new_row > 0)
1002 {
1003 new_row--;
1004 new_col = vfi->columns - 1;
1005 }
1006 else
1007 {
1008 new_col = 0;
1009 }
1010 }
1011 if (new_col >= vfi->columns)
1012 {
1013 if (new_row < vfi->rows - 1)
1014 {
1015 new_row++;
1016 new_col = 0;
1017 }
1018 else
1019 {
1020 new_col = vfi->columns - 1;
1021 }
1022 }
1023 }
1024 }
1025 else
1026 {
1027 new_row = row;
1028 new_col = col;
1029
1030 if (new_row >= vfi->rows)
1031 {
1032 if (vfi->rows > 0)
1033 new_row = vfi->rows - 1;
1034 else
1035 new_row = 0;
1036 new_col = vfi->columns - 1;
1037 }
1038 if (new_col >= vfi->columns) new_col = vfi->columns - 1;
1039 }
1040
1041 if (new_row == vfi->rows - 1)
1042 {
1043 gint l;
1044
1045 /* if we moved beyond the last image, go to the last image */
1046
1047 l = g_list_length(vfi->list);
1048 if (vfi->rows > 1) l -= (vfi->rows - 1) * vfi->columns;
1049 if (new_col >= l) new_col = l - 1;
1050 }
1051
1052 vficon_set_focus(vfi, vficon_find_data(vfi, new_row, new_col, NULL));
1053 }
1054
1055 static void vficon_set_focus(ViewFileIcon *vfi, FileData *fd)
1056 {
1057 GtkTreeIter iter;
1058 gint row, col;
1059
1060 if (g_list_find(vfi->list, vfi->focus_fd))
1061 {
1062 if (fd == vfi->focus_fd)
1063 {
1064 /* ensure focus row col are correct */
1065 vficon_find_position(vfi, vfi->focus_fd, &vfi->focus_row, &vfi->focus_column);
1066 return;
1067 }
1068 vficon_selection_remove(vfi, vfi->focus_fd, SELECTION_FOCUS, NULL);
1069 }
1070
1071 if (!vficon_find_position(vfi, fd, &row, &col))
1072 {
1073 vfi->focus_fd = NULL;
1074 vfi->focus_row = -1;
1075 vfi->focus_column = -1;
1076 return;
1077 }
1078
1079 vfi->focus_fd = fd;
1080 vfi->focus_row = row;
1081 vfi->focus_column = col;
1082 vficon_selection_add(vfi, vfi->focus_fd, SELECTION_FOCUS, NULL);
1083
1084 if (vficon_find_iter(vfi, vfi->focus_fd, &iter, NULL))
1085 {
1086 GtkTreePath *tpath;
1087 GtkTreeViewColumn *column;
1088 GtkTreeModel *store;
1089
1090 tree_view_row_make_visible(GTK_TREE_VIEW(vfi->listview), &iter, FALSE);
1091
1092 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfi->listview));
1093 tpath = gtk_tree_model_get_path(store, &iter);
1094 /* focus is set to an extra column with 0 width to hide focus, we draw it ourself */
1095 column = gtk_tree_view_get_column(GTK_TREE_VIEW(vfi->listview), VFICON_MAX_COLUMNS);
1096 gtk_tree_view_set_cursor(GTK_TREE_VIEW(vfi->listview), tpath, column, FALSE);
1097 gtk_tree_path_free(tpath);
1098 }
1099 }
1100
1101 static void vficon_update_focus(ViewFileIcon *vfi)
1102 {
1103 gint new_row = 0;
1104 gint new_col = 0;
1105
1106 if (vfi->focus_fd && vficon_find_position(vfi, vfi->focus_fd, &new_row, &new_col))
1107 {
1108 /* first find the old focus, if it exists and is valid */
1109 }
1110 else
1111 {
1112 /* (try to) stay where we were */
1113 new_row = vfi->focus_row;
1114 new_col = vfi->focus_column;
1115 }
1116
1117 vficon_move_focus(vfi, new_row, new_col, FALSE);
1118 }
1119
1120 /* used to figure the page up/down distances */
1121 static gint page_height(ViewFileIcon *vfi)
1122 {
1123 GtkAdjustment *adj;
1124 gint page_size;
1125 gint row_height;
1126 gint ret;
1127
1128 adj = gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(vfi->listview));
1129 page_size = (gint)adj->page_increment;
1130
1131 row_height = thumb_max_height + THUMB_BORDER_PADDING * 2;
1132 if (vfi->show_text) row_height += thumb_max_height / 3;
1133
1134 ret = page_size / row_height;
1135 if (ret < 1) ret = 1;
1136
1137 return ret;
1138 }
1139
1140 /*
1141 *-------------------------------------------------------------------
1142 * keyboard
1143 *-------------------------------------------------------------------
1144 */
1145
1146 static void vfi_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer data)
1147 {
1148 ViewFileIcon *vfi = data;
1149 GtkTreeModel *store;
1150 GtkTreeIter iter;
1151 gint column;
1152 GtkTreePath *tpath;
1153 gint cw, ch;
1154
1155 if (!vficon_find_iter(vfi, vfi->click_fd, &iter, &column)) return;
1156 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfi->listview));
1157 tpath = gtk_tree_model_get_path(store, &iter);
1158 tree_view_get_cell_clamped(GTK_TREE_VIEW(vfi->listview), tpath, column, FALSE, x, y, &cw, &ch);
1159 gtk_tree_path_free(tpath);
1160 *y += ch;
1161 popup_menu_position_clamp(menu, x, y, 0);
1162 }
1163
1164 static gint vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data)
1165 {
1166 ViewFileIcon *vfi = data;
1167 gint stop_signal = FALSE;
1168 gint focus_row = 0;
1169 gint focus_col = 0;
1170 FileData *fd;
1171
1172 switch (event->keyval)
1173 {
1174 case GDK_Left: case GDK_KP_Left:
1175 focus_col = -1;
1176 stop_signal = TRUE;
1177 break;
1178 case GDK_Right: case GDK_KP_Right:
1179 focus_col = 1;
1180 stop_signal = TRUE;
1181 break;
1182 case GDK_Up: case GDK_KP_Up:
1183 focus_row = -1;
1184 stop_signal = TRUE;
1185 break;
1186 case GDK_Down: case GDK_KP_Down:
1187 focus_row = 1;
1188 stop_signal = TRUE;
1189 break;
1190 case GDK_Page_Up: case GDK_KP_Page_Up:
1191 focus_row = -page_height(vfi);
1192 stop_signal = TRUE;
1193 break;
1194 case GDK_Page_Down: case GDK_KP_Page_Down:
1195 focus_row = page_height(vfi);
1196 stop_signal = TRUE;
1197 break;
1198 case GDK_Home: case GDK_KP_Home:
1199 focus_row = -vfi->focus_row;
1200 focus_col = -vfi->focus_column;
1201 stop_signal = TRUE;
1202 break;
1203 case GDK_End: case GDK_KP_End:
1204 focus_row = vfi->rows - 1 - vfi->focus_row;
1205 focus_col = vfi->columns - 1 - vfi->focus_column;
1206 stop_signal = TRUE;
1207 break;
1208 case GDK_space:
1209 fd = vficon_find_data(vfi, vfi->focus_row, vfi->focus_column, NULL);
1210 if (fd)
1211 {
1212 vfi->click_fd = fd;
1213 if (event->state & GDK_CONTROL_MASK)
1214 {
1215 gint selected;
1216
1217 selected = ICON_DATA(fd)->selected & SELECTION_SELECTED;
1218 if (selected)
1219 {
1220 vficon_unselect(vfi, fd);
1221 }
1222 else
1223 {
1224 vficon_select(vfi, fd);
1225 vficon_send_layout_select(vfi, fd);
1226 }
1227 }
1228 else
1229 {
1230 vficon_select_none(vfi);
1231 vficon_select(vfi, fd);
1232 vficon_send_layout_select(vfi, fd);
1233 }
1234 }
1235 stop_signal = TRUE;
1236 break;
1237 case GDK_Menu:
1238 fd = vficon_find_data(vfi, vfi->focus_row, vfi->focus_column, NULL);
1239 vfi->click_fd = fd;
1240
1241 vficon_selection_add(vfi, vfi->click_fd, SELECTION_PRELIGHT, NULL);
1242 tip_unschedule(vfi);
1243
1244 vfi->popup = vficon_pop_menu(vfi, (fd != NULL));
1245 gtk_menu_popup(GTK_MENU(vfi->popup), NULL, NULL, vfi_menu_position_cb, vfi, 0, GDK_CURRENT_TIME);
1246 stop_signal = TRUE;
1247 break;
1248 default:
1249 break;
1250 }
1251
1252 if (focus_row != 0 || focus_col != 0)
1253 {
1254 FileData *new_fd;
1255 FileData *old_fd;
1256
1257 old_fd = vficon_find_data(vfi, vfi->focus_row, vfi->focus_column, NULL);
1258 vficon_move_focus(vfi, focus_row, focus_col, TRUE);
1259 new_fd = vficon_find_data(vfi, vfi->focus_row, vfi->focus_column, NULL);
1260
1261 if (new_fd != old_fd)
1262 {
1263 if (event->state & GDK_SHIFT_MASK)
1264 {
1265 if (!collection_rectangular_selection)
1266 {
1267 vficon_select_region_util(vfi, old_fd, new_fd, FALSE);
1268 }
1269 else
1270 {
1271 vficon_select_region_util(vfi, vfi->click_fd, old_fd, FALSE);
1272 }
1273 vficon_select_region_util(vfi, vfi->click_fd, new_fd, TRUE);
1274 vficon_send_layout_select(vfi, new_fd);
1275 }
1276 else if (event->state & GDK_CONTROL_MASK)
1277 {
1278 vfi->click_fd = new_fd;
1279 }
1280 else
1281 {
1282 vfi->click_fd = new_fd;
1283 vficon_select_none(vfi);
1284 vficon_select(vfi, new_fd);
1285 vficon_send_layout_select(vfi, new_fd);
1286 }
1287 }
1288 }
1289
1290 if (stop_signal)
1291 {
1292 g_signal_stop_emission_by_name(GTK_OBJECT(widget), "key_press_event");
1293 tip_unschedule(vfi);
1294 }
1295
1296 return stop_signal;
1297 }
1298
1299 /*
1300 *-------------------------------------------------------------------
1301 * mouse
1302 *-------------------------------------------------------------------
1303 */
1304
1305 static gint vficon_motion_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
1306 {
1307 ViewFileIcon *vfi = data;
1308 FileData *fd;
1309
1310 fd = vficon_find_data_by_coord(vfi, (gint)bevent->x, (gint)bevent->y, NULL);
1311 tip_update(vfi, fd);
1312
1313 return FALSE;
1314 }
1315
1316 static gint vficon_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
1317 {
1318 ViewFileIcon *vfi = data;
1319 GtkTreeIter iter;
1320 FileData *fd;
1321
1322 tip_unschedule(vfi);
1323
1324 fd = vficon_find_data_by_coord(vfi, (gint)bevent->x, (gint)bevent->y, &iter);
1325
1326 vfi->click_fd = fd;
1327 vficon_selection_add(vfi, vfi->click_fd, SELECTION_PRELIGHT, &iter);
1328
1329 switch (bevent->button)
1330 {
1331 case 1:
1332 if (!GTK_WIDGET_HAS_FOCUS(vfi->listview))
1333 {
1334 gtk_widget_grab_focus(vfi->listview);
1335 }
1336 #if 0
1337 if (bevent->type == GDK_2BUTTON_PRESS &&
1338 vfi->layout)
1339 {
1340 vficon_selection_remove(vfi, vfi->click_fd, SELECTION_PRELIGHT, &iter);
1341 layout_image_full_screen_start(vfi->layout);
1342 }
1343 #endif
1344 break;
1345 case 3:
1346 vfi->popup = vficon_pop_menu(vfi, (fd != NULL));
1347 gtk_menu_popup(GTK_MENU(vfi->popup), NULL, NULL, NULL, NULL, bevent->button, bevent->time);
1348 break;
1349 default:
1350 break;
1351 }
1352
1353 return TRUE;
1354 }
1355
1356 static gint vficon_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
1357 {
1358 ViewFileIcon *vfi = data;
1359 GtkTreeIter iter;
1360 FileData *fd = NULL;
1361 gint was_selected = FALSE;
1362
1363 tip_schedule(vfi);
1364
1365 if ((gint)bevent->x != 0 || (gint) bevent->y != 0)
1366 {
1367 fd = vficon_find_data_by_coord(vfi, (gint)bevent->x, (gint)bevent->y, &iter);
1368 }
1369
1370 if (vfi->click_fd)
1371 {
1372 vficon_selection_remove(vfi, vfi->click_fd, SELECTION_PRELIGHT, NULL);
1373 }
1374
1375 if (fd) was_selected = (ICON_DATA(fd)->selected & SELECTION_SELECTED);
1376
1377 if (bevent->button == 1 &&
1378 fd && vfi->click_fd == fd)
1379 {
1380 vficon_set_focus(vfi, fd);
1381
1382 if (bevent->state & GDK_CONTROL_MASK)
1383 {
1384 gint select;
1385
1386 select = !(ICON_DATA(fd)->selected & SELECTION_SELECTED);
1387 if ((bevent->state & GDK_SHIFT_MASK) && vfi->prev_selection)
1388 {
1389 vficon_select_region_util(vfi, vfi->prev_selection, fd, select);
1390 }
1391 else
1392 {
1393 vficon_select_util(vfi, fd, select);
1394 }
1395 }
1396 else
1397 {
1398 vficon_select_none(vfi);
1399
1400 if ((bevent->state & GDK_SHIFT_MASK) &&
1401 vfi->prev_selection)
1402 {
1403 vficon_select_region_util(vfi, vfi->prev_selection, fd, TRUE);
1404 }
1405 else
1406 {
1407 vficon_select_util(vfi, fd, TRUE);
1408 was_selected = FALSE;
1409 }
1410 }
1411 }
1412 else if (bevent->button == 2 &&
1413 fd && vfi->click_fd == fd)
1414 {
1415 vficon_select_util(vfi, fd, !(ICON_DATA(fd)->selected & SELECTION_SELECTED));
1416 }
1417
1418 if (fd && !was_selected &&
1419 (ICON_DATA(fd)->selected & SELECTION_SELECTED))
1420 {
1421 vficon_send_layout_select(vfi, fd);
1422 }
1423
1424 return TRUE;
1425 }
1426
1427 static gint vficon_leave_cb(GtkWidget *widget, GdkEventCrossing *event, gpointer data)
1428 {
1429 ViewFileIcon *vfi = data;
1430
1431 tip_unschedule(vfi);
1432 return FALSE;
1433 }
1434
1435 /*
1436 *-------------------------------------------------------------------
1437 * population
1438 *-------------------------------------------------------------------
1439 */
1440
1441 static gboolean vficon_destroy_node_cb(GtkTreeModel *store, GtkTreePath *tpath, GtkTreeIter *iter, gpointer data)
1442 {
1443 GList *list;
1444
1445 gtk_tree_model_get(store, iter, FILE_COLUMN_POINTER, &list, -1);
1446 g_list_free(list);
1447
1448 return FALSE;
1449 }
1450
1451 static void vficon_clear_store(ViewFileIcon *vfi)
1452 {
1453 GtkTreeModel *store;
1454
1455 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfi->listview));
1456 gtk_tree_model_foreach(store, vficon_destroy_node_cb, NULL);
1457
1458 gtk_list_store_clear(GTK_LIST_STORE(store));
1459 }
1460
1461 static void vficon_set_thumb(ViewFileIcon *vfi, FileData *fd, GdkPixbuf *pb)
1462 {
1463 GtkTreeModel *store;
1464 GtkTreeIter iter;
1465 GList *list;
1466
1467 if (!vficon_find_iter(vfi, fd, &iter, NULL)) return;
1468
1469 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfi->listview));
1470
1471 if (pb) g_object_ref(pb);
1472 if (fd->pixbuf) g_object_unref(fd->pixbuf);
1473 fd->pixbuf = pb;
1474
1475 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1476 gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
1477 }
1478
1479 static GList *vficon_add_row(ViewFileIcon *vfi, GtkTreeIter *iter)
1480 {
1481 GtkListStore *store;
1482 GList *list = NULL;
1483 gint i;
1484
1485 for (i = 0; i < vfi->columns; i++) list = g_list_prepend(list, NULL);
1486
1487 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(vfi->listview)));
1488 gtk_list_store_append(store, iter);
1489 gtk_list_store_set(store, iter, FILE_COLUMN_POINTER, list, -1);
1490
1491 return list;
1492 }
1493
1494 static void vficon_populate(ViewFileIcon *vfi, gint resize, gint keep_position)
1495 {
1496 GtkTreeModel *store;
1497 GtkTreePath *tpath;
1498 gint row;
1499 GList *work;
1500 FileData *visible_fd = NULL;
1501
1502 vficon_verify_selections(vfi);
1503
1504 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfi->listview));
1505
1506 if (keep_position && GTK_WIDGET_REALIZED(vfi->listview) &&
1507 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vfi->listview), 0, 0, &tpath, NULL, NULL, NULL))
1508 {
1509 GtkTreeIter iter;
1510 GList *list;
1511
1512 gtk_tree_model_get_iter(store, &iter, tpath);
1513 gtk_tree_path_free(tpath);
1514
1515 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1516 if (list) visible_fd = list->data;
1517 }
1518
1519 vficon_clear_store(vfi);
1520
1521 if (resize)
1522 {
1523 gint i;
1524 gint thumb_width;
1525
1526 thumb_width = vficon_get_icon_width(vfi);
1527
1528 for (i = 0; i < VFICON_MAX_COLUMNS; i++)
1529 {
1530 GtkTreeViewColumn *column;
1531 GtkCellRenderer *cell;
1532 GList *list;
1533
1534 column = gtk_tree_view_get_column(GTK_TREE_VIEW(vfi->listview), i);
1535 gtk_tree_view_column_set_visible(column, (i < vfi->columns));
1536 gtk_tree_view_column_set_fixed_width(column, thumb_width + (THUMB_BORDER_PADDING * 6));
1537
1538 list = gtk_tree_view_column_get_cell_renderers(column);
1539 cell = (list) ? list->data : NULL;
1540 g_list_free(list);
1541
1542 if (cell && GQV_IS_CELL_RENDERER_ICON(cell))
1543 {
1544 g_object_set(G_OBJECT(cell), "fixed_width", thumb_width,
1545 "fixed_height", thumb_max_height,
1546 "show_text", vfi->show_text, NULL);
1547 }
1548 }
1549 if (GTK_WIDGET_REALIZED(vfi->listview)) gtk_tree_view_columns_autosize(GTK_TREE_VIEW(vfi->listview));
1550 }
1551
1552 row = -1;
1553 work = vfi->list;
1554 while (work)
1555 {
1556 GList *list;
1557 GtkTreeIter iter;
1558
1559 row++;
1560
1561 list = vficon_add_row(vfi, &iter);
1562 while (work && list)
1563 {
1564 list->data = work->data;
1565 list = list->next;
1566 work = work->next;
1567 }
1568 }
1569
1570 if (visible_fd &&
1571 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vfi->listview), 0, 0, &tpath, NULL, NULL, NULL))
1572 {
1573 GtkTreeIter iter;
1574 GList *list;
1575
1576 gtk_tree_model_get_iter(store, &iter, tpath);
1577 gtk_tree_path_free(tpath);
1578
1579 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1580 if (g_list_find(list, visible_fd) == NULL &&
1581 vficon_find_iter(vfi, visible_fd, &iter, NULL))
1582 {
1583 tree_view_row_make_visible(GTK_TREE_VIEW(vfi->listview), &iter, FALSE);
1584 }
1585 }
1586
1587 vfi->rows = row + 1;
1588
1589 vficon_send_update(vfi);
1590 vficon_thumb_update(vfi);
1591 }
1592
1593 static void vficon_populate_at_new_size(ViewFileIcon *vfi, gint w, gint h, gint force)
1594 {
1595 gint new_cols;
1596 gint thumb_width;
1597
1598 thumb_width = vficon_get_icon_width(vfi);
1599
1600 new_cols = w / (thumb_width + (THUMB_BORDER_PADDING * 6));
1601 if (new_cols < 1) new_cols = 1;
1602
1603 if (!force && new_cols == vfi->columns) return;
1604
1605 vfi->columns = new_cols;
1606
1607 vficon_populate(vfi, TRUE, TRUE);
1608
1609 if (debug) printf("col tab pop cols=%d rows=%d\n", vfi->columns, vfi->rows);
1610 }
1611
1612 static void vficon_sync(ViewFileIcon *vfi)
1613 {
1614 GtkTreeModel *store;
1615 GtkTreeIter iter;
1616 GList *work;
1617 gint r, c;
1618
1619 if (vfi->rows == 0) return;
1620
1621 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfi->listview));
1622
1623 r = -1;
1624 c = 0;
1625
1626 work = vfi->list;
1627 while (work)
1628 {
1629 GList *list;
1630 r++;
1631 c = 0;
1632 if (gtk_tree_model_iter_nth_child(store, &iter, NULL, r))
1633 {
1634 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1635 gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
1636 }
1637 else
1638 {
1639 list = vficon_add_row(vfi, &iter);
1640 }
1641
1642 while (list)
1643 {
1644 FileData *fd;
1645 if (work)
1646 {
1647 fd = work->data;
1648 work = work->next;
1649 c++;
1650 }
1651 else
1652 {
1653 fd = NULL;
1654 }
1655 if (list)
1656 {
1657 list->data = fd;
1658 list = list->next;
1659 }
1660 }
1661 }
1662
1663 r++;
1664 while (gtk_tree_model_iter_nth_child(store, &iter, NULL, r))
1665 {
1666 GList *list;
1667
1668 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1669 gtk_list_store_remove(GTK_LIST_STORE(store), &iter);
1670 g_list_free(list);
1671 }
1672
1673 vfi->rows = r;
1674
1675 vficon_update_focus(vfi);
1676 }
1677
1678 static gint vficon_sync_idle_cb(gpointer data)
1679 {
1680 ViewFileIcon *vfi = data;
1681
1682 if (vfi->sync_idle_id == -1) return FALSE;
1683 vfi->sync_idle_id = -1;
1684
1685 vficon_sync(vfi);
1686 return FALSE;
1687 }
1688
1689 static void vficon_sync_idle(ViewFileIcon *vfi)
1690 {
1691 if (vfi->sync_idle_id == -1)
1692 {
1693 /* high priority, the view needs to be resynced before a redraw
1694 * may contain invalid pointers at this time
1695 */
1696 vfi->sync_idle_id = g_idle_add_full(G_PRIORITY_HIGH, vficon_sync_idle_cb, vfi, NULL);
1697 }
1698 }
1699
1700 static void vficon_sized_cb(GtkWidget *widget, GtkAllocation *allocation, gpointer data)
1701 {
1702 ViewFileIcon *vfi = data;
1703
1704 vficon_populate_at_new_size(vfi, allocation->width, allocation->height, FALSE);
1705 }
1706
1707 /*
1708 *-----------------------------------------------------------------------------
1709 * misc
1710 *-----------------------------------------------------------------------------
1711 */
1712
1713 void vficon_sort_set(ViewFileIcon *vfi, SortType type, gint ascend)
1714 {
1715 if (vfi->sort_method == type && vfi->sort_ascend == ascend) return;
1716
1717 vfi->sort_method = type;
1718 vfi->sort_ascend = ascend;
1719
1720 if (!vfi->list) return;
1721
1722 vfi->list = filelist_sort(vfi->list, vfi->sort_method, vfi->sort_ascend);
1723 vficon_sync(vfi);
1724 }
1725
1726 /*
1727 *-----------------------------------------------------------------------------
1728 * thumb updates
1729 *-----------------------------------------------------------------------------
1730 */
1731
1732 static gint vficon_thumb_next(ViewFileIcon *vfi);
1733
1734 static void vficon_thumb_status(ViewFileIcon *vfi, gdouble val, const gchar *text)
1735 {
1736 if (vfi->func_thumb_status)
1737 {
1738 vfi->func_thumb_status(vfi, val, text, vfi->data_thumb_status);
1739 }
1740 }
1741
1742 static void vficon_thumb_cleanup(ViewFileIcon *vfi)
1743 {
1744 vficon_thumb_status(vfi, 0.0, NULL);
1745
1746 g_list_free(vfi->thumbs_list);
1747 vfi->thumbs_list = NULL;
1748 vfi->thumbs_count = 0;
1749 vfi->thumbs_running = FALSE;
1750
1751 thumb_loader_free(vfi->thumbs_loader);
1752 vfi->thumbs_loader = NULL;
1753
1754 vfi->thumbs_fd = NULL;
1755 }
1756
1757 static void vficon_thumb_stop(ViewFileIcon *vfi)
1758 {
1759 if (vfi->thumbs_running) vficon_thumb_cleanup(vfi);
1760 }
1761
1762 static void vficon_thumb_do(ViewFileIcon *vfi, ThumbLoader *tl, FileData *fd)
1763 {
1764 GdkPixbuf *pixbuf;
1765
1766 if (!fd) return;
1767
1768 pixbuf = thumb_loader_get_pixbuf(tl, TRUE);
1769 vficon_set_thumb(vfi, fd, pixbuf);
1770 g_object_unref(pixbuf);
1771
1772 vficon_thumb_status(vfi, (gdouble)(vfi->thumbs_count) / g_list_length(vfi->list), _("Loading thumbs..."));
1773 }
1774
1775 static void vficon_thumb_error_cb(ThumbLoader *tl, gpointer data)
1776 {
1777 ViewFileIcon *vfi = data;
1778
1779 if (vfi->thumbs_fd && vfi->thumbs_loader == tl)
1780 {
1781 vficon_thumb_do(vfi, tl, vfi->thumbs_fd);
1782 }
1783
1784 while (vficon_thumb_next(vfi));
1785 }
1786
1787 static void vficon_thumb_done_cb(ThumbLoader *tl, gpointer data)
1788 {
1789 ViewFileIcon *vfi = data;
1790
1791 if (vfi->thumbs_fd && vfi->thumbs_loader == tl)
1792 {
1793 vficon_thumb_do(vfi, tl, vfi->thumbs_fd);
1794 }
1795
1796 while (vficon_thumb_next(vfi));
1797 }
1798
1799 static gint vficon_thumb_next(ViewFileIcon *vfi)
1800 {
1801 GtkTreePath *tpath;
1802 FileData *fd = NULL;
1803
1804 if (!GTK_WIDGET_REALIZED(vfi->listview))
1805 {
1806 vficon_thumb_status(vfi, 0.0, NULL);
1807 return FALSE;
1808 }
1809
1810 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vfi->listview), 0, 0, &tpath, NULL, NULL, NULL))
1811 {
1812 GtkTreeModel *store;
1813 GtkTreeIter iter;
1814 gint valid = TRUE;
1815
1816 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfi->listview));
1817 gtk_tree_model_get_iter(store, &iter, tpath);
1818 gtk_tree_path_free(tpath);
1819
1820 while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vfi->listview), &iter, FALSE) == 0)
1821 {
1822 GList *list;
1823
1824 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
1825
1826 while (!fd && list)
1827 {
1828 FileData *tfd = list->data;
1829 if (tfd && !tfd->pixbuf) fd = tfd;
1830 list = list->next;
1831 }
1832
1833 valid = gtk_tree_model_iter_next(store, &iter);
1834 }
1835 }
1836
1837 /* then find first undone */
1838
1839 if (!fd)
1840 {
1841 GList *work = vfi->list;
1842 while (work && !fd)
1843 {
1844 FileData *fd_p = work->data;
1845 work = work->next;
1846
1847 if (!fd_p->pixbuf) fd = fd_p;
1848 }
1849 }
1850
1851 if (!fd)
1852 {
1853 /* done */
1854 vficon_thumb_cleanup(vfi);
1855 return FALSE;
1856 }
1857
1858 vfi->thumbs_count++;
1859
1860 vfi->thumbs_fd = fd;
1861
1862 thumb_loader_free(vfi->thumbs_loader);
1863
1864 vfi->thumbs_loader = thumb_loader_new(thumb_max_width, thumb_max_height);
1865 thumb_loader_set_callbacks(vfi->thumbs_loader,
1866 vficon_thumb_done_cb,
1867 vficon_thumb_error_cb,
1868 NULL,
1869 vfi);
1870
1871 if (!thumb_loader_start(vfi->thumbs_loader, fd->path))
1872 {
1873 /* set icon to unknown, continue */
1874 if (debug) printf("thumb loader start failed %s\n", vfi->thumbs_loader->path);
1875 vficon_thumb_do(vfi, vfi->thumbs_loader, fd);
1876
1877 return TRUE;
1878 }
1879
1880 return FALSE;
1881 }
1882
1883 static void vficon_thumb_update(ViewFileIcon *vfi)
1884 {
1885 vficon_thumb_stop(vfi);
1886
1887 vficon_thumb_status(vfi, 0.0, _("Loading thumbs..."));
1888 vfi->thumbs_running = TRUE;
1889
1890 while (vficon_thumb_next(vfi));
1891 }
1892
1893 /*
1894 *-----------------------------------------------------------------------------
1895 * row stuff
1896 *-----------------------------------------------------------------------------
1897 */
1898
1899 FileData *vficon_index_get_data(ViewFileIcon *vfi, gint row)
1900 {
1901 return FILE_DATA(g_list_nth_data(vfi->list, row));
1902 }
1903
1904 gchar *vficon_index_get_path(ViewFileIcon *vfi, gint row)
1905 {
1906 FileData *fd;
1907
1908 fd = g_list_nth_data(vfi->list, row);
1909
1910 return (fd ? fd->path : NULL);
1911 }
1912
1913 gint vficon_index_by_path(ViewFileIcon *vfi, const gchar *path)
1914 {
1915 gint p = 0;
1916 GList *work;
1917
1918 if (!path) return -1;
1919
1920 work = vfi->list;
1921 while (work)
1922 {
1923 FileData *fd = work->data;
1924 if (strcmp(path, fd->path) == 0) return p;
1925 work = work->next;
1926 p++;
1927 }
1928
1929 return -1;
1930 }
1931
1932 gint vficon_count(ViewFileIcon *vfi, gint64 *bytes)
1933 {
1934 if (bytes)
1935 {
1936 gint64 b = 0;
1937 GList *work;
1938
1939 work = vfi->list;
1940 while (work)
1941 {
1942 FileData *fd = work->data;
1943 work = work->next;
1944 b += fd->size;
1945 }
1946
1947 *bytes = b;
1948 }
1949
1950 return g_list_length(vfi->list);
1951 }
1952
1953 GList *vficon_get_list(ViewFileIcon *vfi)
1954 {
1955 GList *list = NULL;
1956 GList *work;
1957
1958 work = vfi->list;
1959 while (work)
1960 {
1961 FileData *fd = work->data;
1962 work = work->next;
1963
1964 list = g_list_prepend(list, g_strdup(fd->path));
1965 }
1966
1967 return g_list_reverse(list);
1968 }
1969
1970 /*
1971 *-----------------------------------------------------------------------------
1972 *
1973 *-----------------------------------------------------------------------------
1974 */
1975
1976 static gint vficon_refresh_real(ViewFileIcon *vfi, gint keep_position)
1977 {
1978 gint ret = TRUE;
1979 GList *old_list;
1980 GList *work;
1981 FileData *focus_fd;
1982
1983 focus_fd = vfi->focus_fd;
1984
1985 old_list = vfi->list;
1986 vfi->list = NULL;
1987
1988 if (vfi->path)
1989 {
1990 ret = iconlist_read(vfi->path, &vfi->list);
1991 }
1992
1993 /* check for same files from old_list */
1994 work = old_list;
1995 while (work)
1996 {
1997 FileData *fd;
1998 GList *needle;
1999
2000 fd = work->data;
2001 needle = vfi->list;
2002 while (needle)
2003 {
2004 FileData *fdn = needle->data;
2005 if (strcmp(fd->name, fdn->name) == 0)
2006 {
2007 /* swap, to retain old thumb, selection */
2008 needle->data = fd;
2009 work->data = fdn;
2010 needle = NULL;
2011
2012 if (fd->date != fdn->date || fd->size != fdn->size)
2013 {
2014 /* file changed, update */
2015 fd->date = fdn->date;
2016 fd->size = fdn->size;
2017 vficon_set_thumb(vfi, fd, NULL);
2018 }
2019 }
2020 else
2021 {
2022 needle = needle->next;
2023 }
2024 }
2025
2026 work = work->next;
2027 }
2028
2029 vfi->list = filelist_sort(vfi->list, vfi->sort_method, vfi->sort_ascend);
2030
2031 work = old_list;
2032 while (work)
2033 {
2034 FileData *fd = work->data;
2035 work = work->next;
2036
2037 if (fd == vfi->prev_selection) vfi->prev_selection = NULL;
2038 if (fd == vfi->click_fd) vfi->click_fd = NULL;
2039 }
2040
2041 vficon_populate(vfi, TRUE, keep_position);
2042
2043 /* attempt to keep focus on same icon when refreshing */
2044 if (focus_fd && g_list_find(vfi->list, focus_fd))
2045 {
2046 vficon_set_focus(vfi, focus_fd);
2047 }
2048
2049 iconlist_free(old_list);
2050
2051 return ret;
2052 }
2053
2054 gint vficon_refresh(ViewFileIcon *vfi)
2055 {
2056 return vficon_refresh_real(vfi, TRUE);
2057 }
2058
2059 /*
2060 *-----------------------------------------------------------------------------
2061 * draw, etc.
2062 *-----------------------------------------------------------------------------
2063 */
2064
2065 typedef struct _ColumnData ColumnData;
2066 struct _ColumnData
2067 {
2068 ViewFileIcon *vfi;
2069 gint number;
2070 };
2071
2072 static void vficon_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
2073 GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
2074 {
2075 ColumnData *cd = data;
2076 ViewFileIcon *vfi;
2077 GtkStyle *style;
2078 GList *list;
2079 FileData *fd;
2080 GdkColor color_fg;
2081 GdkColor color_bg;
2082
2083 vfi = cd->vfi;
2084
2085 gtk_tree_model_get(tree_model, iter, FILE_COLUMN_POINTER, &list, -1);
2086 fd = g_list_nth_data(list, cd->number);
2087
2088 style = gtk_widget_get_style(vfi->listview);
2089 if (fd && ICON_DATA(fd)->selected & SELECTION_SELECTED)
2090 {
2091 memcpy(&color_fg, &style->text[GTK_STATE_SELECTED], sizeof(color_fg));
2092 memcpy(&color_bg, &style->base[GTK_STATE_SELECTED], sizeof(color_bg));
2093 }
2094 else
2095 {
2096 memcpy(&color_fg, &style->text[GTK_STATE_NORMAL], sizeof(color_fg));
2097 memcpy(&color_bg, &style->base[GTK_STATE_NORMAL], sizeof(color_bg));
2098 }
2099
2100 if (fd && ICON_DATA(fd)->selected & SELECTION_PRELIGHT)
2101 {
2102 #if 0
2103 shift_color(&color_fg, -1, 0);
2104 #endif
2105 shift_color(&color_bg, -1, 0);
2106 }
2107
2108 if (GQV_IS_CELL_RENDERER_ICON(cell))
2109 {
2110 if (fd)
2111 {
2112 g_object_set(cell, "pixbuf", fd->pixbuf,
2113 "text", fd->name,
2114 "cell-background-gdk", &color_bg,
2115 "cell-background-set", TRUE,
2116 "foreground-gdk", &color_fg,
2117 "foreground-set", TRUE,
2118 "has-focus", (vfi->focus_fd == fd), NULL);
2119 }
2120 else
2121 {
2122 g_object_set(cell, "pixbuf", NULL,
2123 "text", NULL,
2124 "cell-background-set", FALSE,
2125 "foreground-set", FALSE,
2126 "has-focus", FALSE, NULL);
2127 }
2128 }
2129 }
2130
2131 static void vficon_append_column(ViewFileIcon *vfi, gint n)
2132 {
2133 ColumnData *cd;
2134 GtkTreeViewColumn *column;
2135 GtkCellRenderer *renderer;
2136
2137 column = gtk_tree_view_column_new();
2138 gtk_tree_view_column_set_min_width(column, 0);
2139
2140 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2141 gtk_tree_view_column_set_alignment(column, 0.5);
2142
2143 renderer = gqv_cell_renderer_icon_new();
2144 gtk_tree_view_column_pack_start(column, renderer, FALSE);
2145 g_object_set(G_OBJECT(renderer), "xpad", THUMB_BORDER_PADDING * 2,
2146 "ypad", THUMB_BORDER_PADDING,
2147 "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
2148
2149 g_object_set_data(G_OBJECT(column), "column_number", GINT_TO_POINTER(n));
2150
2151 cd = g_new0(ColumnData, 1);
2152 cd->vfi = vfi;
2153 cd->number = n;
2154 gtk_tree_view_column_set_cell_data_func(column, renderer, vficon_cell_data_cb, cd, g_free);
2155
2156 gtk_tree_view_append_column(GTK_TREE_VIEW(vfi->listview), column);
2157 }
2158
2159 /*
2160 *-----------------------------------------------------------------------------
2161 * base
2162 *-----------------------------------------------------------------------------
2163 */
2164
2165 gint vficon_set_path(ViewFileIcon *vfi, const gchar *path)
2166 {
2167 gint ret;
2168
2169 if (!path) return FALSE;
2170 if (vfi->path && strcmp(path, vfi->path) == 0) return TRUE;
2171
2172 g_free(vfi->path);
2173 vfi->path = g_strdup(path);
2174
2175 g_list_free(vfi->selection);
2176 vfi->selection = NULL;
2177
2178 iconlist_free(vfi->list);
2179 vfi->list = NULL;
2180
2181 /* NOTE: populate will clear the store for us */
2182 ret = vficon_refresh_real(vfi, FALSE);
2183
2184 vfi->focus_fd = NULL;
2185 vficon_move_focus(vfi, 0, 0, FALSE);
2186
2187 return ret;
2188 }
2189
2190 static void vficon_destroy_cb(GtkWidget *widget, gpointer data)
2191 {
2192 ViewFileIcon *vfi = data;
2193
2194 if (vfi->popup)
2195 {
2196 g_signal_handlers_disconnect_matched(G_OBJECT(vfi->popup), G_SIGNAL_MATCH_DATA,
2197 0, 0, 0, NULL, vfi);
2198 gtk_widget_destroy(vfi->popup);
2199 }
2200
2201 if (vfi->sync_idle_id != -1) g_source_remove(vfi->sync_idle_id);
2202
2203 tip_unschedule(vfi);
2204
2205 vficon_thumb_cleanup(vfi);
2206
2207 g_free(vfi->path);
2208
2209 iconlist_free(vfi->list);
2210 g_list_free(vfi->selection);
2211 g_free(vfi);
2212 }
2213
2214 ViewFileIcon *vficon_new(const gchar *path)
2215 {
2216 ViewFileIcon *vfi;
2217 GtkListStore *store;
2218 GtkTreeSelection *selection;
2219 gint i;
2220
2221 vfi = g_new0(ViewFileIcon, 1);
2222
2223 vfi->path = NULL;
2224 vfi->sort_method = SORT_NAME;
2225 vfi->sort_ascend = TRUE;
2226
2227 vfi->selection = NULL;
2228 vfi->prev_selection = NULL;
2229
2230 vfi->tip_window = NULL;
2231 vfi->tip_delay_id = -1;
2232
2233 vfi->focus_row = 0;
2234 vfi->focus_column = 0;
2235 vfi->focus_fd = NULL;
2236
2237 vfi->show_text = show_icon_names;
2238
2239 vfi->sync_idle_id = -1;
2240
2241 vfi->popup = NULL;
2242
2243 vfi->widget = gtk_scrolled_window_new(NULL, NULL);
2244 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(vfi->widget), GTK_SHADOW_IN);
2245 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(vfi->widget),
2246 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
2247 g_signal_connect(G_OBJECT(vfi->widget), "destroy",
2248 G_CALLBACK(vficon_destroy_cb), vfi);
2249
2250 store = gtk_list_store_new(1, G_TYPE_POINTER);
2251 vfi->listview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
2252 g_object_unref(store);
2253
2254 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(vfi->listview));
2255 gtk_tree_selection_set_mode(GTK_TREE_SELECTION(selection), GTK_SELECTION_NONE);
2256
2257 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(vfi->listview), FALSE);
2258 gtk_tree_view_set_enable_search(GTK_TREE_VIEW(vfi->listview), FALSE);
2259
2260 for (i = 0; i < VFICON_MAX_COLUMNS; i++)
2261 {
2262 vficon_append_column(vfi, i);
2263 }
2264
2265 /* zero width column to hide tree view focus, we draw it ourselves */
2266 vficon_append_column(vfi, i);
2267 /* end column to fill white space */
2268 vficon_append_column(vfi, i);
2269
2270 g_signal_connect(G_OBJECT(vfi->listview), "size_allocate",
2271 G_CALLBACK(vficon_sized_cb), vfi);
2272 g_signal_connect(G_OBJECT(vfi->listview), "key_press_event",
2273 G_CALLBACK(vficon_press_key_cb), vfi);
2274
2275 gtk_container_add(GTK_CONTAINER(vfi->widget), vfi->listview);
2276 gtk_widget_show(vfi->listview);
2277
2278 vficon_dnd_init(vfi);
2279
2280 gtk_widget_set_events(vfi->listview, GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK |
2281 GDK_BUTTON_PRESS_MASK | GDK_LEAVE_NOTIFY_MASK);
2282 g_signal_connect(G_OBJECT(vfi->listview), "button_press_event",
2283 G_CALLBACK(vficon_press_cb), vfi);
2284 g_signal_connect(G_OBJECT(vfi->listview), "button_release_event",
2285 G_CALLBACK(vficon_release_cb), vfi);
2286 g_signal_connect(G_OBJECT(vfi->listview),"motion_notify_event",
2287 G_CALLBACK(vficon_motion_cb), vfi);
2288 g_signal_connect(G_OBJECT(vfi->listview), "leave_notify_event",
2289 G_CALLBACK(vficon_leave_cb), vfi);
2290
2291 /* force vfi->columns to be at least 1 (sane) - this will be corrected in the size_cb */
2292 vficon_populate_at_new_size(vfi, 1, 1, FALSE);
2293
2294 if (path) vficon_set_path(vfi, path);
2295
2296 return vfi;
2297 }
2298
2299 void vficon_set_status_func(ViewFileIcon *vfi,
2300 void (*func)(ViewFileIcon *vfi, gpointer data), gpointer data)
2301 {
2302 vfi->func_status = func;
2303 vfi->data_status = data;
2304 }
2305
2306 void vficon_set_thumb_status_func(ViewFileIcon *vfi,
2307 void (*func)(ViewFileIcon *vfi, gdouble val, const gchar *text, gpointer data),
2308 gpointer data)
2309 {
2310 vfi->func_thumb_status = func;
2311 vfi->data_thumb_status = data;
2312 }
2313
2314 void vficon_set_layout(ViewFileIcon *vfi, LayoutWindow *layout)
2315 {
2316 vfi->layout = layout;
2317 }
2318
2319 /*
2320 *-----------------------------------------------------------------------------
2321 * maintenance (for rename, move, remove)
2322 *-----------------------------------------------------------------------------
2323 */
2324
2325 static gint vficon_maint_find_closest(ViewFileIcon *vfi, gint row, gint count, GList *ignore_list)
2326 {
2327 GList *list = NULL;
2328 GList *work;
2329 gint rev = row - 1;
2330 row ++;
2331
2332 work = ignore_list;
2333 while (work)
2334 {
2335 gint f = vficon_index_by_path(vfi, work->data);
2336 if (f >= 0) list = g_list_prepend(list, GINT_TO_POINTER(f));
2337 work = work->next;
2338 }
2339
2340 while (list)
2341 {
2342 gint c = TRUE;
2343 work = list;
2344 while (work && c)
2345 {
2346 gpointer p = work->data;
2347 work = work->next;
2348 if (row == GPOINTER_TO_INT(p))
2349 {
2350 row++;
2351 c = FALSE;
2352 }
2353 if (rev == GPOINTER_TO_INT(p))
2354 {
2355 rev--;
2356 c = FALSE;
2357 }
2358 if (!c) list = g_list_remove(list, p);
2359 }
2360 if (c && list)
2361 {
2362 g_list_free(list);
2363 list = NULL;
2364 }
2365 }
2366 if (row > count - 1)
2367 {
2368 if (rev < 0)
2369 return -1;
2370 else
2371 return rev;
2372 }
2373 else
2374 {
2375 return row;
2376 }
2377 }
2378
2379 gint vficon_maint_renamed(ViewFileIcon *vfi, const gchar *source, const gchar *dest)
2380 {
2381 gint ret = FALSE;
2382 gint row;
2383 gchar *source_base;
2384 gchar *dest_base;
2385 GList *work;
2386 FileData *fd;
2387
2388 row = vficon_index_by_path(vfi, source);
2389 if (row < 0) return FALSE;
2390
2391 source_base = remove_level_from_path(source);
2392 dest_base = remove_level_from_path(dest);
2393
2394 work = g_list_nth(vfi->list, row);
2395 fd = work->data;
2396
2397 if (strcmp(source_base, dest_base) == 0)
2398 {
2399 vfi->list = g_list_remove(vfi->list, fd);
2400 g_free(fd->path);
2401
2402 fd->path = g_strdup(dest);
2403 fd->name = filename_from_path(fd->path);
2404
2405 vfi->list = filelist_insert_sort(vfi->list, fd, vfi->sort_method, vfi->sort_ascend);
2406
2407 vficon_sync(vfi);
2408 ret = TRUE;
2409 }
2410 else
2411 {
2412 ret = vficon_maint_removed(vfi, source, NULL);
2413 }
2414
2415 g_free(source_base);
2416 g_free(dest_base);
2417
2418 return ret;
2419 }
2420
2421 gint vficon_maint_removed(ViewFileIcon *vfi, const gchar *path, GList *ignore_list)
2422 {
2423 FileData *fd;
2424 gint row;
2425 gint new_row = -1;
2426 GtkTreeModel *store;
2427 GtkTreeIter iter;
2428
2429 row = vficon_index_by_path(vfi, path);
2430 if (row < 0) return FALSE;
2431
2432 fd = g_list_nth_data(vfi->list, row);
2433 if (!fd) return FALSE;
2434
2435 if ((ICON_DATA(fd)->selected & SELECTION_SELECTED) &&
2436 layout_image_get_collection(vfi->layout, NULL) == NULL)
2437 {
2438 vficon_unselect(vfi, fd);
2439
2440 if (!vfi->selection)
2441 {
2442 gint n;
2443
2444 n = vficon_count(vfi, NULL);
2445 if (ignore_list)
2446 {
2447 new_row = vficon_maint_find_closest(vfi, row, n, ignore_list);
2448 if (debug) printf("row = %d, closest is %d\n", row, new_row);
2449 }
2450 else
2451 {
2452 if (row + 1 < n)
2453 {
2454 new_row = row + 1;
2455 }
2456 else if (row > 0)
2457 {
2458 new_row = row - 1;
2459 }
2460 }
2461 }
2462 else if (ignore_list)
2463 {
2464 GList *work;
2465
2466 work = vfi->selection;
2467 while (work)
2468 {
2469 FileData *ignore_fd;
2470 GList *tmp;
2471 gint match = FALSE;
2472
2473 ignore_fd = work->data;
2474 work = work->next;
2475
2476 tmp = ignore_list;
2477 while (tmp && !match)
2478 {
2479 const gchar *ignore_path;
2480
2481 ignore_path = tmp->data;
2482 tmp = tmp->next;
2483
2484 if (strcmp(ignore_fd->path, ignore_path) == 0)
2485 {
2486 match = TRUE;
2487 }
2488 }
2489 if (!match)
2490 {
2491 new_row = g_list_index(vfi->list, ignore_fd);
2492 work = NULL;
2493 }
2494 }
2495 if (new_row == -1)
2496 {
2497 /* selection all ignored, use closest */
2498 new_row = vficon_maint_find_closest(vfi, row, vficon_count(vfi, NULL), ignore_list);
2499 }
2500 }
2501 else
2502 {
2503 new_row = g_list_index(vfi->list, vfi->selection->data);
2504 }
2505 if (new_row >= 0)
2506 {
2507 FileData *fdn;
2508
2509 fdn = g_list_nth_data(vfi->list, new_row);
2510 vficon_select(vfi, fdn);
2511 vficon_send_layout_select(vfi, fdn);
2512 }
2513 }
2514
2515 /* Thumb loader check */
2516 if (fd == vfi->thumbs_fd) vfi->thumbs_fd = NULL;
2517
2518 if (vfi->prev_selection == fd) vfi->prev_selection = NULL;
2519 if (vfi->click_fd == fd) vfi->click_fd = NULL;
2520
2521 /* remove pointer to this fd from grid */
2522 store = gtk_tree_view_get_model(GTK_TREE_VIEW(vfi->listview));
2523 if (gtk_tree_model_iter_nth_child(store, &iter, NULL, row / vfi->columns))
2524 {
2525 GList *list;
2526
2527 gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
2528 list = g_list_find(list, fd);
2529 if (list) list->data = NULL;
2530 }
2531
2532 vfi->list = g_list_remove(vfi->list, fd);
2533 file_data_free(fd);
2534
2535 vficon_sync_idle(vfi);
2536 vficon_send_update(vfi);
2537
2538 return TRUE;
2539 }
2540
2541 gint vficon_maint_moved(ViewFileIcon *vfi, const gchar *source, const gchar *dest, GList *ignore_list)
2542 {
2543 gint ret = FALSE;
2544 gchar *buf;
2545
2546 if (!source || !vfi->path) return FALSE;
2547
2548 buf = remove_level_from_path(source);
2549
2550 if (strcmp(buf, vfi->path) == 0)
2551 {
2552 ret = vficon_maint_removed(vfi, source, ignore_list);
2553 }
2554
2555 g_free(buf);
2556
2557 return ret;
2558 }
2559