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