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