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