improved debug messages
[geeqie.git] / src / collect.c
1 /*
2  * Geeqie
3  * (C) 2006 John Ellis
4  * Copyright (C) 2008 - 2009 The Geeqie Team
5  *
6  * Author: John Ellis
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13
14 #include "main.h"
15 #include "collect.h"
16
17 #include "collect-dlg.h"
18 #include "collect-io.h"
19 #include "collect-table.h"
20 #include "editors.h"
21 #include "filedata.h"
22 #include "img-view.h"
23 #include "layout.h"
24 #include "layout_image.h"
25 #include "misc.h"
26 #include "pixbuf_util.h"
27 #include "print.h"
28 #include "ui_fileops.h"
29 #include "ui_tree_edit.h"
30 #include "utilops.h"
31 #include "window.h"
32
33 #include <gdk/gdkkeysyms.h> /* for keyboard values */
34
35
36 #define COLLECT_DEF_WIDTH 440
37 #define COLLECT_DEF_HEIGHT 450
38
39 static GList *collection_list = NULL;
40 static GList *collection_window_list = NULL;
41
42 static void collection_window_get_geometry(CollectWindow *cw);
43 static void collection_window_refresh(CollectWindow *cw);
44 static void collection_window_update_title(CollectWindow *cw);
45 static void collection_window_add(CollectWindow *cw, CollectInfo *ci);
46 static void collection_window_insert(CollectWindow *cw, CollectInfo *ci);
47 static void collection_window_remove(CollectWindow *cw, CollectInfo *ci);
48 static void collection_window_update(CollectWindow *cw, CollectInfo *ci);
49
50 static void collection_window_close(CollectWindow *cw);
51
52 static void collection_notify_cb(FileData *fd, NotifyType type, gpointer data);
53
54 /*
55  *-------------------------------------------------------------------
56  * data, list handling
57  *-------------------------------------------------------------------
58  */
59
60 CollectInfo *collection_info_new(FileData *fd, struct stat *st, GdkPixbuf *pixbuf)
61 {
62         CollectInfo *ci;
63
64         if (!fd) return NULL;
65
66         ci = g_new0(CollectInfo, 1);
67         ci->fd = file_data_ref(fd);
68
69         ci->pixbuf = pixbuf;
70         if (ci->pixbuf) g_object_ref(ci->pixbuf);
71
72         return ci;
73 }
74
75 void collection_info_free_thumb(CollectInfo *ci)
76 {
77         if (ci->pixbuf) g_object_unref(ci->pixbuf);
78         ci->pixbuf = NULL;
79 }
80
81 void collection_info_free(CollectInfo *ci)
82 {
83         if (!ci) return;
84
85         file_data_unref(ci->fd);
86         collection_info_free_thumb(ci);
87         g_free(ci);
88 }
89
90 void collection_info_set_thumb(CollectInfo *ci, GdkPixbuf *pixbuf)
91 {
92         if (pixbuf) g_object_ref(pixbuf);
93         collection_info_free_thumb(ci);
94         ci->pixbuf = pixbuf;
95 }
96
97 gboolean collection_info_load_thumb(CollectInfo *ci)
98 {
99         if (!ci) return FALSE;
100
101         collection_info_free_thumb(ci);
102
103         log_printf("collection_info_load_thumb not implemented!\n(because an instant thumb loader not implemented)");
104         return FALSE;
105 #if 0
106         if (create_thumbnail(ci->fd->path, &ci->pixmap, &ci->mask) < 0) return FALSE;
107
108         if (ci->pixmap) gdk_pixmap_ref(ci->pixmap);
109         if (ci->mask) gdk_bitmap_ref(ci->mask);
110
111         return TRUE;
112 #endif
113 }
114
115 void collection_list_free(GList *list)
116 {
117         GList *work;
118         work = list;
119         while (work)
120                 {
121                 collection_info_free((CollectInfo *)work->data);
122                 work = work->next;
123                 }
124         g_list_free(list);
125 }
126
127 /* an ugly static var, well what ya gonna do ? */
128 static SortType collection_list_sort_method = SORT_NAME;
129
130 static gint collection_list_sort_cb(gconstpointer a, gconstpointer b)
131 {
132         const CollectInfo *cia = a;
133         const CollectInfo *cib = b;
134
135         switch (collection_list_sort_method)
136                 {
137                 case SORT_NAME:
138                         break;
139                 case SORT_NONE:
140                         return 0;
141                         break;
142                 case SORT_SIZE:
143                         if (cia->fd->size < cib->fd->size) return -1;
144                         if (cia->fd->size > cib->fd->size) return 1;
145                         return 0;
146                         break;
147                 case SORT_TIME:
148                         if (cia->fd->date < cib->fd->date) return -1;
149                         if (cia->fd->date > cib->fd->date) return 1;
150                         return 0;
151                         break;
152                 case SORT_PATH:
153                         return utf8_compare(cia->fd->path, cib->fd->path, options->file_sort.case_sensitive);
154                         break;
155 #ifdef HAVE_STRVERSCMP
156                 case SORT_NUMBER:
157                         return strverscmp(cia->fd->name, cib->fd->name);
158                         break;
159 #endif
160                 default:
161                         break;
162                 }
163
164         if (options->file_sort.case_sensitive)
165                 return strcmp(cia->fd->collate_key_name, cib->fd->collate_key_name);
166         else
167                 return strcmp(cia->fd->collate_key_name_nocase, cib->fd->collate_key_name_nocase);
168 }
169
170 GList *collection_list_sort(GList *list, SortType method)
171 {
172         if (method == SORT_NONE) return list;
173
174         collection_list_sort_method = method;
175
176         return g_list_sort(list, collection_list_sort_cb);
177 }
178
179 GList *collection_list_add(GList *list, CollectInfo *ci, SortType method)
180 {
181         if (method != SORT_NONE)
182                 {
183                 collection_list_sort_method = method;
184                 list = g_list_insert_sorted(list, ci, collection_list_sort_cb);
185                 }
186         else
187                 {
188                 list = g_list_append(list, ci);
189                 }
190
191         return list;
192 }
193
194 GList *collection_list_insert(GList *list, CollectInfo *ci, CollectInfo *insert_ci, SortType method)
195 {
196         if (method != SORT_NONE)
197                 {
198                 collection_list_sort_method = method;
199                 list = g_list_insert_sorted(list, ci, collection_list_sort_cb);
200                 }
201         else
202                 {
203                 GList *point;
204
205                 point = g_list_find(list, insert_ci);
206                 list = uig_list_insert_link(list, point, ci);
207                 }
208
209         return list;
210 }
211
212 GList *collection_list_remove(GList *list, CollectInfo *ci)
213 {
214         list = g_list_remove(list, ci);
215         collection_info_free(ci);
216         return list;
217 }
218
219 CollectInfo *collection_list_find_fd(GList *list, FileData *fd)
220 {
221         GList *work = list;
222
223         while (work)
224                 {
225                 CollectInfo *ci = work->data;
226                 if (ci->fd == fd) return ci;
227                 work = work->next;
228                 }
229
230         return NULL;
231 }
232
233 #if 0
234 static GList *collection_list_find_link(GList *list, gchar *path)
235 {
236         GList *work = list;
237
238         while (work)
239                 {
240                 CollectInfo *ci = work->data;
241                 if (strcmp(ci->fd->path, path) == 0) return work;
242                 work = work->next;
243                 }
244
245         return NULL;
246 }
247
248 static gint collection_list_find_index(GList *list, gchar *path)
249 {
250         gint c = 0;
251         GList *work = list;
252
253         while (work)
254                 {
255                 CollectInfo *ci = work->data;
256                 if (strcmp(ci->fd->path, path) == 0) return c;
257                 work = work->next;
258                 c++;
259                 }
260
261         return -1;
262 }
263 #endif
264
265 GList *collection_list_to_filelist(GList *list)
266 {
267         GList *filelist = NULL;
268         GList *work = list;
269
270         while (work)
271                 {
272                 CollectInfo *info = work->data;
273                 filelist = g_list_prepend(filelist, file_data_ref(info->fd));
274                 work = work->next;
275                 }
276
277         filelist = g_list_reverse(filelist);
278         return filelist;
279 }
280
281 CollectWindow *collection_window_find(CollectionData *cd)
282 {
283         GList *work;
284
285         work = collection_window_list;
286         while (work)
287                 {
288                 CollectWindow *cw = work->data;
289                 if (cw->cd == cd) return cw;
290                 work = work->next;
291                 }
292
293         return NULL;
294 }
295
296 CollectWindow *collection_window_find_by_path(const gchar *path)
297 {
298         GList *work;
299
300         if (!path) return NULL;
301
302         work = collection_window_list;
303         while (work)
304                 {
305                 CollectWindow *cw = work->data;
306                 if (cw->cd->path && strcmp(cw->cd->path, path) == 0) return cw;
307                 work = work->next;
308                 }
309
310         return NULL;
311 }
312
313 /*
314  *-------------------------------------------------------------------
315  * please use these to actually add/remove stuff
316  *-------------------------------------------------------------------
317  */
318
319 CollectionData *collection_new(const gchar *path)
320 {
321         CollectionData *cd;
322         static gint untitled_counter = 0;
323
324         cd = g_new0(CollectionData, 1);
325
326         cd->ref = 1;    /* starts with a ref of 1 */
327         cd->sort_method = SORT_NONE;
328         cd->window_w = COLLECT_DEF_WIDTH;
329         cd->window_h = COLLECT_DEF_HEIGHT;
330         cd->existence = g_hash_table_new(NULL, NULL);
331
332         if (path)
333                 {
334                 cd->path = g_strdup(path);
335                 cd->name = g_strdup(filename_from_path(cd->path));
336                 /* load it */
337                 }
338         else
339                 {
340                 if (untitled_counter == 0)
341                         {
342                         cd->name = g_strdup(_("Untitled"));
343                         }
344                 else
345                         {
346                         cd->name = g_strdup_printf(_("Untitled (%d)"), untitled_counter + 1);
347                         }
348
349                 untitled_counter++;
350                 }
351
352         file_data_register_notify_func(collection_notify_cb, cd, NOTIFY_PRIORITY_MEDIUM);
353
354
355         collection_list = g_list_append(collection_list, cd);
356         
357         return cd;
358 }
359
360 void collection_free(CollectionData *cd)
361 {
362         if (!cd) return;
363
364         DEBUG_1("collection \"%s\" freed", cd->name);
365
366         collection_load_stop(cd);
367         collection_list_free(cd->list);
368         
369         file_data_unregister_notify_func(collection_notify_cb, cd);
370
371         collection_list = g_list_remove(collection_list, cd);
372
373         g_hash_table_destroy(cd->existence);
374
375         g_free(cd->path);
376         g_free(cd->name);
377
378         g_free(cd);
379 }
380
381 void collection_ref(CollectionData *cd)
382 {
383         cd->ref++;
384
385         DEBUG_1("collection \"%s\" ref count = %d", cd->name, cd->ref);
386 }
387
388 void collection_unref(CollectionData *cd)
389 {
390         cd->ref--;
391
392         DEBUG_1("collection \"%s\" ref count = %d", cd->name, cd->ref);
393
394         if (cd->ref < 1)
395                 {
396                 collection_free(cd);
397                 }
398 }
399
400 void collection_path_changed(CollectionData *cd)
401 {
402         collection_window_update_title(collection_window_find(cd));
403 }
404
405 gint collection_to_number(CollectionData *cd)
406 {
407         return g_list_index(collection_list, cd);
408 }
409
410 CollectionData *collection_from_number(gint n)
411 {
412         return g_list_nth_data(collection_list, n);
413 }
414
415 CollectionData *collection_from_dnd_data(const gchar *data, GList **list, GList **info_list)
416 {
417         CollectionData *cd;
418         gint collection_number;
419         const gchar *ptr;
420
421         if (list) *list = NULL;
422         if (info_list) *info_list = NULL;
423
424         if (strncmp(data, "COLLECTION:", 11) != 0) return NULL;
425         
426         ptr = data + 11;
427                 
428         collection_number = atoi(ptr);
429         cd = collection_from_number(collection_number);
430         if (!cd) return NULL;
431
432         if (!list && !info_list) return cd;
433         
434         while (*ptr != '\0' && *ptr != '\n' ) ptr++;
435         if (*ptr == '\0') return cd;
436         ptr++;
437
438         while (*ptr != '\0')
439                 {
440                 guint item_number;
441                 CollectInfo *info;
442                 
443                 item_number = (guint) atoi(ptr);
444                 while (*ptr != '\n' && *ptr != '\0') ptr++;
445                 if (*ptr == '\0')
446                         break;
447                 else
448                         while (*ptr == '\n') ptr++;
449
450                 info = g_list_nth_data(cd->list, item_number);
451                 if (!info) continue;
452
453                 if (list) *list = g_list_append(*list, file_data_ref(info->fd));
454                 if (info_list) *info_list = g_list_append(*info_list, info);
455                 }
456         
457         return cd;
458 }
459
460 gchar *collection_info_list_to_dnd_data(CollectionData *cd, GList *list, gint *length)
461 {
462         GList *work;
463         GList *temp = NULL;
464         gchar *ptr;
465         gchar *text;
466         gchar *uri_text;
467         gint collection_number;
468
469         *length = 0;
470         if (!list) return NULL;
471
472         collection_number = collection_to_number(cd);
473         if (collection_number < 0) return NULL;
474
475         text = g_strdup_printf("COLLECTION:%d\n", collection_number);
476         *length += strlen(text);
477         temp = g_list_prepend(temp, text);
478
479         work = list;
480         while (work)
481                 {
482                 gint item_number = g_list_index(cd->list, work->data);
483
484                 work = work->next;
485
486                 if (item_number < 0) continue;
487                 
488                 text = g_strdup_printf("%d\n", item_number);
489                 temp = g_list_prepend(temp, text);
490                 *length += strlen(text);
491                 }
492
493         *length += 1; /* ending nul char */
494
495         uri_text = g_malloc(*length);
496         ptr = uri_text;
497
498         work = g_list_last(temp);
499         while (work)
500                 {
501                 gint len;
502                 gchar *text = work->data;
503
504                 work = work->prev;
505
506                 len = strlen(text);
507                 memcpy(ptr, text, len);
508                 ptr += len;
509                 }
510
511         ptr[0] = '\0';
512
513         string_list_free(temp);
514
515         return uri_text;
516 }
517
518 gint collection_info_valid(CollectionData *cd, CollectInfo *info)
519 {
520         if (collection_to_number(cd) < 0) return FALSE;
521
522         return (g_list_index(cd->list, info) != 0);
523 }
524
525 CollectInfo *collection_next_by_info(CollectionData *cd, CollectInfo *info)
526 {
527         GList *work;
528
529         work = g_list_find(cd->list, info);
530
531         if (!work) return NULL;
532         work = work->next;
533         if (work) return work->data;
534         return NULL;
535 }
536
537 CollectInfo *collection_prev_by_info(CollectionData *cd, CollectInfo *info)
538 {
539         GList *work;
540
541         work = g_list_find(cd->list, info);
542
543         if (!work) return NULL;
544         work = work->prev;
545         if (work) return work->data;
546         return NULL;
547 }
548
549 CollectInfo *collection_get_first(CollectionData *cd)
550 {
551         if (cd->list) return cd->list->data;
552
553         return NULL;
554 }
555
556 CollectInfo *collection_get_last(CollectionData *cd)
557 {
558         GList *list;
559
560         list = g_list_last(cd->list);
561
562         if (list) return list->data;
563
564         return NULL;
565 }
566
567 void collection_set_sort_method(CollectionData *cd, SortType method)
568 {
569         if (!cd) return;
570
571         if (cd->sort_method == method) return;
572
573         cd->sort_method = method;
574         cd->list = collection_list_sort(cd->list, cd->sort_method);
575         if (cd->list) cd->changed = TRUE;
576
577         collection_window_refresh(collection_window_find(cd));
578 }
579
580 void collection_set_update_info_func(CollectionData *cd,
581                                      void (*func)(CollectionData *, CollectInfo *, gpointer), gpointer data)
582 {
583         cd->info_updated_func = func;
584         cd->info_updated_data = data;
585 }
586
587 static CollectInfo *collection_info_new_if_not_exists(CollectionData *cd, struct stat *st, FileData *fd)
588 {
589         CollectInfo *ci;
590
591         if (g_hash_table_lookup(cd->existence, fd->path)) return NULL;
592
593         ci = collection_info_new(fd, st, NULL);
594         if (ci) g_hash_table_insert(cd->existence, fd->path, "");
595         return ci;
596 }
597
598 gboolean collection_add_check(CollectionData *cd, FileData *fd, gboolean sorted, gboolean must_exist)
599 {
600         struct stat st;
601         gboolean valid;
602
603         if (must_exist)
604                 {
605                 valid = (stat_utf8(fd->path, &st) && !S_ISDIR(st.st_mode));
606                 }
607         else
608                 {
609                 valid = TRUE;
610                 st.st_size = 0;
611                 st.st_mtime = 0;
612                 }
613
614         if (valid)
615                 {
616                 CollectInfo *ci;
617
618                 ci = collection_info_new_if_not_exists(cd, &st, fd);
619                 if (!ci) return FALSE;
620                 DEBUG_3("add to collection: %s", fd->path);
621
622                 cd->list = collection_list_add(cd->list, ci, sorted ? cd->sort_method : SORT_NONE);
623                 cd->changed = TRUE;
624
625                 if (!sorted || cd->sort_method == SORT_NONE)
626                         {
627                         collection_window_add(collection_window_find(cd), ci);
628                         }
629                 else
630                         {
631                         collection_window_insert(collection_window_find(cd), ci);
632                         }
633                 }
634
635         return valid;
636 }
637
638 gboolean collection_add(CollectionData *cd, FileData *fd, gboolean sorted)
639 {
640         return collection_add_check(cd, fd, sorted, TRUE);
641 }
642
643 gboolean collection_insert(CollectionData *cd, FileData *fd, CollectInfo *insert_ci, gboolean sorted)
644 {
645         struct stat st;
646
647         if (!insert_ci) return collection_add(cd, fd, sorted);
648
649         if (stat_utf8(fd->path, &st) >= 0 && !S_ISDIR(st.st_mode))
650                 {
651                 CollectInfo *ci;
652
653                 ci = collection_info_new_if_not_exists(cd, &st, fd);
654                 if (!ci) return FALSE;
655
656                 DEBUG_3("insert in collection: %s", fd->path);
657
658                 cd->list = collection_list_insert(cd->list, ci, insert_ci, sorted ? cd->sort_method : SORT_NONE);
659                 cd->changed = TRUE;
660
661                 collection_window_insert(collection_window_find(cd), ci);
662
663                 return TRUE;
664                 }
665
666         return FALSE;
667 }
668
669 gboolean collection_remove(CollectionData *cd, FileData *fd)
670 {
671         CollectInfo *ci;
672
673         ci = collection_list_find_fd(cd->list, fd);
674
675         if (!ci) return FALSE;
676
677         g_hash_table_remove(cd->existence, fd->path);
678
679         cd->list = g_list_remove(cd->list, ci);
680         cd->changed = TRUE;
681
682         collection_window_remove(collection_window_find(cd), ci);
683         collection_info_free(ci);
684
685         return TRUE;
686 }
687
688 static void collection_remove_by_info(CollectionData *cd, CollectInfo *info)
689 {
690         if (!info || !g_list_find(cd->list, info)) return;
691
692         cd->list = g_list_remove(cd->list, info);
693         cd->changed = (cd->list != NULL);
694
695         collection_window_remove(collection_window_find(cd), info);
696         collection_info_free(info);
697 }
698
699 void collection_remove_by_info_list(CollectionData *cd, GList *list)
700 {
701         GList *work;
702
703         if (!list) return;
704
705         if (!list->next)
706                 {
707                 /* more efficient (in collect-table) to remove a single item this way */
708                 collection_remove_by_info(cd, (CollectInfo *)list->data);
709                 return;
710                 }
711
712         work = list;
713         while (work)
714                 {
715                 cd->list = collection_list_remove(cd->list, work->data);
716                 work = work->next;
717                 }
718         cd->changed = (cd->list != NULL);
719
720         collection_window_refresh(collection_window_find(cd));
721 }
722
723 gboolean collection_rename(CollectionData *cd, FileData *fd)
724 {
725         CollectInfo *ci;
726         ci = collection_list_find_fd(cd->list, fd);
727
728         if (!ci) return FALSE;
729
730         cd->changed = TRUE;
731
732         collection_window_update(collection_window_find(cd), ci);
733
734         return TRUE;
735 }
736
737 void collection_update_geometry(CollectionData *cd)
738 {
739         collection_window_get_geometry(collection_window_find(cd));
740 }
741
742 /*
743  *-------------------------------------------------------------------
744  * simple maintenance for renaming, deleting
745  *-------------------------------------------------------------------
746  */
747
748 static void collection_notify_cb(FileData *fd, NotifyType type, gpointer data)
749 {
750         CollectionData *cd = data;
751
752         if (!(type & NOTIFY_CHANGE) || !fd->change) return;
753
754         DEBUG_1("Notify collection: %s %04x", fd->path, type);
755         
756         switch (fd->change->type)
757                 {
758                 case FILEDATA_CHANGE_MOVE:
759                 case FILEDATA_CHANGE_RENAME:
760                         collection_rename(cd, fd);
761                         break;
762                 case FILEDATA_CHANGE_COPY:
763                         break;
764                 case FILEDATA_CHANGE_DELETE:
765                         while (collection_remove(cd, fd));
766                         break;
767                 case FILEDATA_CHANGE_UNSPECIFIED:
768                 case FILEDATA_CHANGE_WRITE_METADATA:
769                         break;
770                 }
771
772 }
773
774
775 /*
776  *-------------------------------------------------------------------
777  * window key presses
778  *-------------------------------------------------------------------
779  */
780
781 static gboolean collection_window_keypress(GtkWidget *widget, GdkEventKey *event, gpointer data)
782 {
783         CollectWindow *cw = data;
784         gboolean stop_signal = FALSE;
785         gint edit_val = -1;
786         GList *list;
787
788         if (event->state & GDK_CONTROL_MASK)
789                 {
790                 stop_signal = TRUE;
791                 switch (event->keyval)
792                         {
793                         case '1':
794                                 edit_val = 0;
795                                 break;
796                         case '2':
797                                 edit_val = 1;
798                                 break;
799                         case '3':
800                                 edit_val = 2;
801                                 break;
802                         case '4':
803                                 edit_val = 3;
804                                 break;
805                         case '5':
806                                 edit_val = 4;
807                                 break;
808                         case '6':
809                                 edit_val = 5;
810                                 break;
811                         case '7':
812                                 edit_val = 6;
813                                 break;
814                         case '8':
815                                 edit_val = 7;
816                                 break;
817                         case '9':
818                                 edit_val = 8;
819                                 break;
820                         case '0':
821                                 edit_val = 9;
822                                 break;
823                         case 'A': case 'a':
824                                 if (event->state & GDK_SHIFT_MASK)
825                                         {
826                                         collection_table_unselect_all(cw->table);
827                                         }
828                                 else
829                                         {
830                                         collection_table_select_all(cw->table);
831                                         }
832                                 break;
833                         case 'L': case 'l':
834                                 list = layout_list(NULL);
835                                 if (list)
836                                         {
837                                         collection_table_add_filelist(cw->table, list);
838                                         filelist_free(list);
839                                         }
840                                 break;
841                         case 'C': case 'c':
842                                 file_util_copy(NULL, collection_table_selection_get_list(cw->table), NULL, cw->window);
843                                 break;
844                         case 'M': case 'm':
845                                 file_util_move(NULL, collection_table_selection_get_list(cw->table), NULL, cw->window);
846                                 break;
847                         case 'R': case 'r':
848                                 file_util_rename(NULL, collection_table_selection_get_list(cw->table), cw->window);
849                                 break;
850                         case 'D': case 'd':
851                                 file_util_delete(NULL, collection_table_selection_get_list(cw->table), cw->window);
852                                 break;
853                         case 'S': case 's':
854                                 collection_dialog_save_as(NULL, cw->cd);
855                                 break;
856                         case 'W': case 'w':
857                                 collection_window_close(cw);
858                                 break;
859                         default:
860                                 stop_signal = FALSE;
861                                 break;
862                         }
863                 }
864         else
865                 {
866                 stop_signal = TRUE;
867                 switch (event->keyval)
868                         {
869                         case GDK_Return: case GDK_KP_Enter:
870                                 layout_image_set_collection(NULL, cw->cd,
871                                         collection_table_get_focus_info(cw->table));
872                                 break;
873                         case 'V': case 'v':
874                                 view_window_new_from_collection(cw->cd,
875                                         collection_table_get_focus_info(cw->table));
876                                 break;
877                         case 'S': case 's':
878                                 if (!cw->cd->path)
879                                         {
880                                         collection_dialog_save_as(NULL, cw->cd);
881                                         }
882                                 else if (!collection_save(cw->cd, cw->cd->path))
883                                         {
884                                         log_printf("failed saving to collection path: %s\n", cw->cd->path);
885                                         }
886                                 break;
887                         case 'A': case 'a':
888                                 collection_dialog_append(NULL, cw->cd);
889                                 break;
890                         case 'N': case 'n':
891                                 collection_set_sort_method(cw->cd, SORT_NAME);
892                                 break;
893 #ifdef HAVE_STRVERSCMP
894                         case 'I': case 'i':
895                                 collection_set_sort_method(cw->cd, SORT_NUMBER);
896                                 break;
897 #endif
898                         case 'D': case 'd':
899                                 collection_set_sort_method(cw->cd, SORT_TIME);
900                                 break;
901                         case 'B': case 'b':
902                                 collection_set_sort_method(cw->cd, SORT_SIZE);
903                                 break;
904                         case 'P': case 'p':
905                                 if (event->state & GDK_SHIFT_MASK)
906                                         {
907                                         CollectInfo *info;
908
909                                         info = collection_table_get_focus_info(cw->table);
910
911                                         print_window_new(info->fd, collection_table_selection_get_list(cw->table),
912                                                          collection_list_to_filelist(cw->cd->list), cw->window);
913                                         }
914                                 else
915                                         {
916                                         collection_set_sort_method(cw->cd, SORT_PATH);
917                                         }
918                                 break;
919                         case GDK_Delete: case GDK_KP_Delete:
920                                 list = g_list_copy(cw->table->selection);
921                                 if (list)
922                                         {
923                                         collection_remove_by_info_list(cw->cd, list);
924                                         g_list_free(list);
925                                         }
926                                 else
927                                         {
928                                         collection_remove_by_info(cw->cd, collection_table_get_focus_info(cw->table));
929                                         }
930                                 break;
931                         default:
932                                 stop_signal = FALSE;
933                                 break;
934                         }
935                 }
936 #if 0
937         if (edit_val != -1)
938                 {
939                 list = collection_table_selection_get_list(cw->table);
940                 file_util_start_editor_from_filelist(edit_val, list, cw->window);
941                 filelist_free(list);
942                 }
943 #endif
944         return stop_signal;
945 }
946
947 /*
948  *-------------------------------------------------------------------
949  * window
950  *-------------------------------------------------------------------
951  */
952 static void collection_window_get_geometry(CollectWindow *cw)
953 {
954         CollectionData *cd;
955
956         if (!cw) return;
957
958         cd = cw->cd;
959         gdk_window_get_position(cw->window->window, &cd->window_x, &cd->window_y);
960         gdk_drawable_get_size(cw->window->window, &cd->window_w, &cd->window_h);
961         cd->window_read = TRUE;
962 }
963
964 static void collection_window_refresh(CollectWindow *cw)
965 {
966         if (!cw) return;
967
968         collection_table_refresh(cw->table);
969 }
970
971 static void collection_window_update_title(CollectWindow *cw)
972 {
973         gboolean free_name = FALSE;
974         gchar *name;
975         gchar *buf;
976
977         if (!cw) return;
978
979         if (file_extension_match(cw->cd->name, GQ_COLLECTION_EXT))
980                 {
981                 name = remove_extension_from_path(cw->cd->name);
982                 free_name = TRUE;
983                 }
984         else
985                 {
986                 name = cw->cd->name;
987                 }
988
989         buf = g_strdup_printf(_("%s - Collection - %s"), name, GQ_APPNAME);
990         if (free_name) g_free(name);
991         gtk_window_set_title(GTK_WINDOW(cw->window), buf);
992         g_free(buf);
993 }
994
995 static void collection_window_update_info(CollectionData *cd, CollectInfo *ci, gpointer data)
996 {
997         CollectWindow *cw = data;
998
999         collection_table_file_update(cw->table, ci);
1000 }
1001
1002 static void collection_window_add(CollectWindow *cw, CollectInfo *ci)
1003 {
1004         if (!cw) return;
1005
1006         if (!ci->pixbuf) collection_load_thumb_idle(cw->cd);
1007         collection_table_file_add(cw->table, ci);
1008 }
1009
1010 static void collection_window_insert(CollectWindow *cw, CollectInfo *ci)
1011 {
1012         if (!cw) return;
1013
1014         if (!ci->pixbuf) collection_load_thumb_idle(cw->cd);
1015         collection_table_file_insert(cw->table, ci);
1016         if (!cw) return;
1017 }
1018
1019 #if 0
1020 static void collection_window_move(CollectWindow *cw, CollectInfo *ci)
1021 {
1022         if (!cw) return;
1023 }
1024 #endif
1025
1026 static void collection_window_remove(CollectWindow *cw, CollectInfo *ci)
1027 {
1028         if (!cw) return;
1029
1030         collection_table_file_remove(cw->table, ci);
1031 }
1032
1033 static void collection_window_update(CollectWindow *cw, CollectInfo *ci)
1034 {
1035         if (!cw) return;
1036
1037         collection_table_file_update(cw->table, ci);
1038         collection_table_file_update(cw->table, NULL);
1039 }
1040
1041 static void collection_window_close_final(CollectWindow *cw)
1042 {
1043         if (cw->close_dialog) return;
1044
1045         collection_window_list = g_list_remove(collection_window_list, cw);
1046         collection_window_get_geometry(cw);
1047
1048         gtk_widget_destroy(cw->window);
1049
1050         collection_set_update_info_func(cw->cd, NULL, NULL);
1051         collection_unref(cw->cd);
1052
1053         g_free(cw);
1054 }
1055
1056 static void collection_close_save_cb(GenericDialog *gd, gpointer data)
1057 {
1058         CollectWindow *cw = data;
1059
1060         cw->close_dialog = NULL;
1061         generic_dialog_close(gd);
1062
1063         if (!cw->cd->path)
1064                 {
1065                 collection_dialog_save_close(NULL, cw->cd);
1066                 return;
1067                 }
1068         else if (!collection_save(cw->cd, cw->cd->path))
1069                 {
1070                 gchar *buf;
1071                 buf = g_strdup_printf(_("Failed to save the collection:\n%s"), cw->cd->path);
1072                 warning_dialog(_("Save Failed"), buf, GTK_STOCK_DIALOG_ERROR, cw->window);
1073                 g_free(buf);
1074                 return;
1075                 }
1076
1077         collection_window_close_final(cw);
1078 }
1079
1080 static void collection_close_close_cb(GenericDialog *gd, gpointer data)
1081 {
1082         CollectWindow *cw = data;
1083
1084         cw->close_dialog = NULL;
1085         generic_dialog_close(gd);
1086
1087         collection_window_close_final(cw);
1088 }
1089
1090 static void collection_close_cancel_cb(GenericDialog *gd, gpointer data)
1091 {
1092         CollectWindow *cw = data;
1093
1094         cw->close_dialog = NULL;
1095         generic_dialog_close(gd);
1096 }
1097
1098 static void collection_close_dlg_show(CollectWindow *cw)
1099 {
1100         GenericDialog *gd;
1101
1102         if (cw->close_dialog)
1103                 {
1104                 gtk_window_present(GTK_WINDOW(cw->close_dialog));
1105                 return;
1106                 }
1107
1108         gd = generic_dialog_new(_("Close collection"),
1109                                 "close_collection", cw->window, FALSE,
1110                                 collection_close_cancel_cb, cw);
1111         generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
1112                                    _("Close collection"),
1113                                    _("Collection has been modified.\nSave first?"));
1114
1115         generic_dialog_add_button(gd, GTK_STOCK_SAVE, NULL, collection_close_save_cb, TRUE);
1116         generic_dialog_add_button(gd, GTK_STOCK_DELETE, _("_Discard"), collection_close_close_cb, FALSE);
1117
1118         cw->close_dialog = gd->dialog;
1119
1120         gtk_widget_show(gd->dialog);
1121 }
1122
1123 static void collection_window_close(CollectWindow *cw)
1124 {
1125         if (!cw->cd->changed && !cw->close_dialog)
1126                 {
1127                 collection_window_close_final(cw);
1128                 return;
1129                 }
1130
1131         collection_close_dlg_show(cw);
1132 }
1133
1134 void collection_window_close_by_collection(CollectionData *cd)
1135 {
1136         CollectWindow *cw;
1137
1138         cw = collection_window_find(cd);
1139         if (cw) collection_window_close_final(cw);
1140 }
1141
1142 gboolean collection_window_modified_exists(void)
1143 {
1144         GList *work;
1145
1146         work = collection_window_list;
1147         while (work)
1148                 {
1149                 CollectWindow *cw = work->data;
1150                 if (cw->cd->changed) return TRUE;
1151                 work = work->next;
1152                 }
1153
1154         return FALSE;
1155 }
1156
1157 static gboolean collection_window_delete(GtkWidget *widget, GdkEvent *event, gpointer data)
1158 {
1159         CollectWindow *cw = data;
1160         collection_window_close(cw);
1161
1162         return TRUE;
1163 }
1164
1165 CollectWindow *collection_window_new(const gchar *path)
1166 {
1167         CollectWindow *cw;
1168         GtkWidget *vbox;
1169         GtkWidget *frame;
1170         GtkWidget *status_label;
1171         GtkWidget *extra_label;
1172         GdkGeometry geometry;
1173
1174         cw = g_new0(CollectWindow, 1);
1175
1176         collection_window_list = g_list_append(collection_window_list, cw);
1177
1178         cw->cd = collection_new(path);
1179
1180         cw->window = window_new(GTK_WINDOW_TOPLEVEL, "collection", PIXBUF_INLINE_ICON_BOOK, NULL, NULL);
1181
1182         geometry.min_width = DEFAULT_MINIMAL_WINDOW_SIZE;
1183         geometry.min_height = DEFAULT_MINIMAL_WINDOW_SIZE;
1184         geometry.base_width = COLLECT_DEF_WIDTH;
1185         geometry.base_height = COLLECT_DEF_HEIGHT;
1186         gtk_window_set_geometry_hints(GTK_WINDOW(cw->window), NULL, &geometry,
1187                                       GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE);
1188
1189
1190         if (options->save_window_positions && path && collection_load_only_geometry(cw->cd, path))
1191                 {
1192                 /* FIXME: x, y is not implemented */
1193                 gtk_window_set_default_size(GTK_WINDOW(cw->window), cw->cd->window_w, cw->cd->window_h);
1194                 }
1195         else
1196                 {
1197                 gtk_window_set_default_size(GTK_WINDOW(cw->window), COLLECT_DEF_WIDTH, COLLECT_DEF_HEIGHT);
1198                 }
1199
1200         gtk_window_set_resizable(GTK_WINDOW(cw->window), TRUE);
1201         collection_window_update_title(cw);
1202         gtk_container_set_border_width(GTK_CONTAINER(cw->window), 0);
1203
1204         g_signal_connect(G_OBJECT(cw->window), "delete_event",
1205                          G_CALLBACK(collection_window_delete), cw);
1206
1207         g_signal_connect(G_OBJECT(cw->window), "key_press_event",
1208                          G_CALLBACK(collection_window_keypress), cw);
1209
1210         vbox = gtk_vbox_new(FALSE, 0);
1211         gtk_container_add(GTK_CONTAINER(cw->window), vbox);
1212         gtk_widget_show(vbox);
1213
1214         cw->table = collection_table_new(cw->cd);
1215         gtk_box_pack_start(GTK_BOX(vbox), cw->table->scrolled, TRUE, TRUE, 0);
1216         gtk_widget_show(cw->table->scrolled);
1217
1218         cw->status_box = gtk_hbox_new(TRUE, 0);
1219         gtk_box_pack_start(GTK_BOX(vbox), cw->status_box, FALSE, FALSE, 0);
1220         gtk_widget_show(cw->status_box);
1221
1222         frame = gtk_frame_new(NULL);
1223         gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
1224         gtk_box_pack_start(GTK_BOX(cw->status_box), frame, TRUE, TRUE, 0);
1225         gtk_widget_show(frame);
1226
1227         status_label = gtk_label_new("");
1228         gtk_container_add(GTK_CONTAINER(frame), status_label);
1229         gtk_widget_show(status_label);
1230
1231         extra_label = gtk_progress_bar_new();
1232         gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(extra_label), 0.0);
1233         gtk_box_pack_start(GTK_BOX(cw->status_box), extra_label, TRUE, TRUE, 0);
1234         gtk_widget_show(extra_label);
1235
1236         collection_table_set_labels(cw->table, status_label, extra_label);
1237
1238         gtk_widget_show(cw->window);
1239         gtk_widget_grab_focus(cw->table->listview);
1240
1241         collection_set_update_info_func(cw->cd, collection_window_update_info, cw);
1242
1243         if (path && *path == G_DIR_SEPARATOR) collection_load_begin(cw->cd, NULL, COLLECTION_LOAD_NONE);
1244
1245         return cw;
1246 }
1247 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */