Mercurial > geeqie.yaz
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 |