comparison src/collect-io.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 ebbff299ad0d
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-io.h"
15
16 #include "collect.h"
17 #include "layout_util.h"
18 #include "rcfile.h"
19 #include "thumb.h"
20 #include "ui_fileops.h"
21
22
23 #define GQVIEW_COLLECTION_MARKER "#GQview"
24
25 #define GQVIEW_COLLECTION_FAIL_MIN 300
26 #define GQVIEW_COLLECTION_FAIL_PERCENT 98
27
28
29 static void collection_load_thumb_step(CollectionData *cd);
30
31
32 static gint scan_geometry(gchar *buffer, gint *x, gint *y, gint *w, gint *h)
33 {
34 gint nx, ny, nw, nh;
35
36 if(sscanf(buffer, "%d %d %d %d", &nx, &ny, &nw, &nh) != 4) return FALSE;
37
38 *x = nx;
39 *y = ny;
40 *w = nw;
41 *h = nh;
42
43 return TRUE;
44 }
45
46 static gint collection_load_private(CollectionData *cd, const gchar *path, gint append, gint flush)
47 {
48 gchar s_buf[2048];
49 FILE *f;
50 gchar *pathl;
51 gint official = FALSE;
52 gint success = TRUE;
53 guint total = 0;
54 guint fail = 0;
55
56 collection_load_stop(cd);
57
58 if (flush) collect_manager_flush();
59
60 if (!append)
61 {
62 collection_list_free(cd->list);
63 cd->list = NULL;
64 }
65
66 if (!path && !cd->path) return FALSE;
67
68 if (!path) path = cd->path;
69
70 /* load it */
71 pathl = path_from_utf8(path);
72 f = fopen(pathl, "r");
73 g_free(pathl);
74 if (!f)
75 {
76 printf("Failed to open collection file: \"%s\"\n", path);
77 return FALSE;
78 }
79
80 while (fgets(s_buf, sizeof(s_buf), f))
81 {
82 gchar *buf;
83 if (s_buf[0]=='#')
84 {
85 if (strncasecmp(s_buf, GQVIEW_COLLECTION_MARKER, strlen(GQVIEW_COLLECTION_MARKER)) == 0)
86 {
87 /* Looks like an official collection, allow unchecked input.
88 * All this does is allow adding files that may not exist,
89 * which is needed for the collection manager to work.
90 * Also unofficial files abort after too many invalid entries.
91 */
92 official = TRUE;
93 }
94 else if (strncmp(s_buf, "#geometry:", 10 ) == 0 &&
95 scan_geometry(s_buf + 10, &cd->window_x, &cd->window_y, &cd->window_w, &cd->window_h) )
96 {
97 cd->window_read = TRUE;
98 }
99 continue;
100 }
101 if (s_buf[0]=='\n') continue;
102
103 buf = quoted_value(s_buf);
104 if (buf)
105 {
106 gint valid;
107
108 valid = (buf[0] == '/' && collection_add_check(cd, buf, FALSE, flush));
109 g_free(buf);
110
111 total++;
112 if (!valid && !official)
113 {
114 fail++;
115 if (fail > GQVIEW_COLLECTION_FAIL_MIN &&
116 fail * 100 / total > GQVIEW_COLLECTION_FAIL_PERCENT)
117 {
118 printf("Too many invalid filenames in unoffical collection file, closing: %s\n", path);
119 success = FALSE;
120 break;
121 }
122 }
123 }
124 }
125
126 fclose(f);
127
128 cd->list = collection_list_sort(cd->list, cd->sort_method);
129 if (!append) cd->changed = FALSE;
130
131 return success;
132 }
133
134 gint collection_load(CollectionData *cd, const gchar *path, gint append)
135 {
136 if (collection_load_private(cd, path, append, TRUE))
137 {
138 layout_recent_add_path(cd->path);
139 return TRUE;
140 }
141
142 return FALSE;
143 }
144
145 static void collection_load_thumb_do(CollectionData *cd)
146 {
147 GdkPixbuf *pixbuf;
148
149 if (!cd->thumb_loader || !g_list_find(cd->list, cd->thumb_info)) return;
150
151 pixbuf = thumb_loader_get_pixbuf(cd->thumb_loader, TRUE);
152 collection_info_set_thumb(cd->thumb_info, pixbuf);
153 g_object_unref(pixbuf);
154
155 if (cd->info_updated_func) cd->info_updated_func(cd, cd->thumb_info, cd->info_updated_data);
156 }
157
158 static void collection_load_thumb_error_cb(ThumbLoader *tl, gpointer data)
159 {
160 CollectionData *cd = data;
161
162 collection_load_thumb_do(cd);
163 collection_load_thumb_step(cd);
164 }
165
166 static void collection_load_thumb_done_cb(ThumbLoader *tl, gpointer data)
167 {
168 CollectionData *cd = data;
169
170 collection_load_thumb_do(cd);
171 collection_load_thumb_step(cd);
172 }
173
174 static void collection_load_thumb_step(CollectionData *cd)
175 {
176 GList *work;
177 CollectInfo *ci;
178
179 if (!cd->list)
180 {
181 collection_load_stop(cd);
182 return;
183 }
184
185 work = cd->list;
186 ci = work->data;
187 work = work->next;
188 /* find first unloaded thumb */
189 while (work && ci->pixbuf)
190 {
191 ci = work->data;
192 work = work->next;
193 }
194
195 if (!ci || ci->pixbuf)
196 {
197 /* done */
198 collection_load_stop(cd);
199
200 /* send a NULL CollectInfo to notify end */
201 if (cd->info_updated_func) cd->info_updated_func(cd, NULL, cd->info_updated_data);
202
203 return;
204 }
205
206 /* setup loader and call it */
207 cd->thumb_info = ci;
208 thumb_loader_free(cd->thumb_loader);
209 cd->thumb_loader = thumb_loader_new(thumb_max_width, thumb_max_height);
210 thumb_loader_set_callbacks(cd->thumb_loader,
211 collection_load_thumb_done_cb,
212 collection_load_thumb_error_cb,
213 NULL,
214 cd);
215
216 /* start it */
217 if (!thumb_loader_start(cd->thumb_loader, ci->path))
218 {
219 /* error, handle it, do next */
220 if (debug) printf("error loading thumb for %s\n", ci->path);
221 collection_load_thumb_do(cd);
222 collection_load_thumb_step(cd);
223 }
224 }
225
226 void collection_load_thumb_idle(CollectionData *cd)
227 {
228 if (!cd->thumb_loader) collection_load_thumb_step(cd);
229 }
230
231 gint collection_load_begin(CollectionData *cd, const gchar *path, gint append)
232 {
233 if (!collection_load(cd, path, append)) return FALSE;
234
235 collection_load_thumb_idle(cd);
236
237 return TRUE;
238 }
239
240 void collection_load_stop(CollectionData *cd)
241 {
242 if (!cd->thumb_loader) return;
243
244 thumb_loader_free(cd->thumb_loader);
245 cd->thumb_loader = NULL;
246 }
247
248 static gint collection_save_private(CollectionData *cd, const gchar *path)
249 {
250 FILE *f;
251 GList *work;
252 gchar *tmp_path;
253 gchar *pathl;
254 mode_t save_mask;
255
256 if (!path && !cd->path) return FALSE;
257
258 if (!path)
259 {
260 path = cd->path;
261 }
262
263 tmp_path = unique_filename(path, ".tmp", "_", 3);
264 if (!tmp_path) return FALSE;
265
266 pathl = path_from_utf8(tmp_path);
267 save_mask = umask(0077);
268 f = fopen(pathl, "w");
269 umask(save_mask);
270 g_free(pathl);
271
272 if (!f)
273 {
274 /* file open failed */
275 printf("failed to open collection (write) \"%s\"\n", tmp_path);
276 g_free(tmp_path);
277 return FALSE;
278 }
279
280 fprintf(f, "%s collection\n", GQVIEW_COLLECTION_MARKER);
281 fprintf(f, "#created with GQview version %s\n", VERSION);
282
283 collection_update_geometry(cd);
284 if (cd->window_read)
285 {
286 fprintf(f, "#geometry: %d %d %d %d\n", cd->window_x, cd->window_y, cd->window_w, cd->window_h);
287 }
288
289 work = cd->list;
290 while (work)
291 {
292 CollectInfo *ci = work->data;
293 if (fprintf(f, "\"%s\"\n", ci->path) < 0)
294 {
295 fclose(f);
296 printf("Error writing to %s\n", tmp_path);
297 unlink_file(tmp_path);
298 g_free(tmp_path);
299 return FALSE;
300 }
301 work = work->next;
302 }
303
304 fprintf(f, "#end\n");
305
306 fclose(f);
307
308 copy_file_attributes(path, tmp_path, TRUE, FALSE);
309 if (!rename_file(tmp_path, path))
310 {
311 printf("collection save unable to rename %s to %s\n", tmp_path, path);
312 unlink_file(tmp_path);
313 g_free(tmp_path);
314 return FALSE;
315 }
316
317 g_free(tmp_path);
318
319 if (!cd->path || strcmp(path, cd->path) != 0)
320 {
321 gchar *buf = cd->path;
322 cd->path = g_strdup(path);
323 path = cd->path;
324 g_free(buf);
325
326 g_free(cd->name);
327 cd->name = g_strdup(filename_from_path(cd->path));
328
329 collection_path_changed(cd);
330 }
331
332 cd->changed = FALSE;
333
334 return TRUE;
335 }
336
337 gint collection_save(CollectionData *cd, const gchar *path)
338 {
339 if (collection_save_private(cd, path))
340 {
341 layout_recent_add_path(cd->path);
342 return TRUE;
343 }
344
345 return FALSE;
346 }
347
348 gint collection_load_only_geometry(CollectionData *cd, const gchar *path)
349 {
350 gchar s_buf[2048];
351 FILE *f;
352 gchar *pathl;
353
354 if (!path && !cd->path) return FALSE;
355
356 if (!path) path = cd->path;
357
358 /* load it */
359 pathl = path_from_utf8(path);
360 f = fopen(pathl, "r");
361 g_free(pathl);
362 if (!f) return FALSE;
363
364 while (fgets(s_buf, sizeof(s_buf), f))
365 {
366 if (s_buf[0]=='#' &&
367 strncmp(s_buf, "#geometry:", 10 ) == 0 &&
368 scan_geometry(s_buf + 10, &cd->window_x, &cd->window_y, &cd->window_w, &cd->window_h) )
369 {
370 cd->window_read = TRUE;
371 fclose(f);
372 return TRUE;
373 }
374 }
375 fclose(f);
376 return FALSE;
377 }
378
379
380 /*
381 *-------------------------------------------------------------------
382 * collection manager
383 *-------------------------------------------------------------------
384 */
385
386 #define COLLECT_MANAGER_ACTIONS_PER_IDLE 1000
387 #define COLLECT_MANAGER_FLUSH_DELAY 10000
388
389 typedef struct _CollectManagerEntry CollectManagerEntry;
390 struct _CollectManagerEntry
391 {
392 gchar *path;
393 GList *action_list;
394 };
395
396 typedef enum {
397 COLLECTION_MANAGER_UPDATE,
398 COLLECTION_MANAGER_ADD,
399 COLLECTION_MANAGER_REMOVE
400 } CollectManagerType;
401
402 typedef struct _CollectManagerAction CollectManagerAction;
403 struct _CollectManagerAction
404 {
405 gchar *oldpath;
406 gchar *newpath;
407
408 CollectManagerType type;
409
410 gint ref;
411 };
412
413
414 static GList *collection_manager_entry_list = NULL;
415 static GList *collection_manager_action_list = NULL;
416 static GList *collection_manager_action_tail = NULL;
417 static gint collection_manager_timer_id = -1;
418
419
420 static CollectManagerAction *collect_manager_action_new(const gchar *oldpath, const gchar *newpath,
421 CollectManagerType type)
422 {
423 CollectManagerAction *action;
424
425 action = g_new0(CollectManagerAction, 1);
426 action->ref = 1;
427
428 action->oldpath = g_strdup(oldpath);
429 action->newpath = g_strdup(newpath);
430
431 action->type = type;
432
433 return action;
434 }
435
436 static void collect_manager_action_ref(CollectManagerAction *action)
437 {
438 action->ref++;
439 }
440
441 static void collect_manager_action_unref(CollectManagerAction *action)
442 {
443 action->ref--;
444
445 if (action->ref > 0) return;
446
447 g_free(action->oldpath);
448 g_free(action->newpath);
449 g_free(action);
450 }
451
452 static CollectManagerEntry *collect_manager_entry_new(const gchar *path)
453 {
454 CollectManagerEntry *entry;
455
456 entry = g_new0(CollectManagerEntry, 1);
457 entry->path = g_strdup(path);
458 entry->action_list = NULL;
459
460 collection_manager_entry_list = g_list_append(collection_manager_entry_list, entry);
461
462 return entry;
463 }
464
465 static void collect_manager_entry_free(CollectManagerEntry *entry)
466 {
467 GList *work;
468
469 collection_manager_entry_list = g_list_remove(collection_manager_entry_list, entry);
470
471 work = entry->action_list;
472 while (work)
473 {
474 CollectManagerAction *action;
475
476 action = work->data;
477 work = work->next;
478
479 collect_manager_action_unref(action);
480 }
481 g_list_free(entry->action_list);
482
483 g_free(entry->path);
484 g_free(entry);
485 }
486
487 static void collect_manager_refresh(void)
488 {
489 GList *list = NULL;
490 GList *work;
491 gchar *base;
492
493 base = g_strconcat(homedir(), "/", GQVIEW_RC_DIR_COLLECTIONS, NULL);
494 path_list(base, &list, NULL);
495 g_free(base);
496
497 work = collection_manager_entry_list;
498 while (work && list)
499 {
500 CollectManagerEntry *entry;
501 GList *list_step;
502
503 entry = work->data;
504 work = work->next;
505
506 list_step = list;
507 while (list_step && entry)
508 {
509 gchar *path;
510
511 path = list_step->data;
512 list_step = list_step->next;
513
514 if (strcmp(path, entry->path) == 0)
515 {
516 list = g_list_remove(list, path);
517 g_free(path);
518
519 entry = NULL;
520 }
521 else
522 {
523 collect_manager_entry_free(entry);
524 }
525 }
526 }
527
528 work = list;
529 while (work)
530 {
531 gchar *path;
532
533 path = work->data;
534 work = work->next;
535
536 collect_manager_entry_new(path);
537 g_free(path);
538 }
539
540 g_list_free(list);
541 }
542
543 static void collect_manager_process_actions(gint max)
544 {
545 if (debug && collection_manager_action_list)
546 {
547 printf("collection manager processing actions\n");
548 }
549
550 while (collection_manager_action_list != NULL && max > 0)
551 {
552 CollectManagerAction *action;
553 GList *work;
554
555 action = collection_manager_action_list->data;
556 work = collection_manager_entry_list;
557 while (work)
558 {
559 CollectManagerEntry *entry;
560
561 entry = work->data;
562 work = work->next;
563
564 if (action->type == COLLECTION_MANAGER_UPDATE)
565 {
566 entry->action_list = g_list_prepend(entry->action_list, action);
567 collect_manager_action_ref(action);
568 }
569 else if (action->oldpath && action->newpath &&
570 strcmp(action->newpath, entry->path) == 0)
571 {
572 /* convert action to standard add format */
573 g_free(action->newpath);
574 if (action->type == COLLECTION_MANAGER_ADD)
575 {
576 action->newpath = action->oldpath;
577 action->oldpath = NULL;
578 }
579 else if (action->type == COLLECTION_MANAGER_REMOVE)
580 {
581 action->newpath = NULL;
582 }
583
584 entry->action_list = g_list_prepend(entry->action_list, action);
585 collect_manager_action_ref(action);
586 }
587
588 max--;
589 }
590
591 if (action->type != COLLECTION_MANAGER_UPDATE &&
592 action->oldpath && action->newpath)
593 {
594 printf("collection manager failed to %s %s for collection %s\n",
595 (action->type == COLLECTION_MANAGER_ADD) ? "add" : "remove",
596 action->oldpath, action->newpath);
597 }
598
599 if (collection_manager_action_tail == collection_manager_action_list)
600 {
601 collection_manager_action_tail = NULL;
602 }
603 collection_manager_action_list = g_list_remove(collection_manager_action_list, action);
604 collect_manager_action_unref(action);
605 }
606 }
607
608 static gint collect_manager_process_entry(CollectManagerEntry *entry)
609 {
610 CollectionData *cd;
611 gint success;
612 GList *work;
613
614 if (!entry->action_list) return FALSE;
615
616 cd = collection_new(entry->path);
617 success = collection_load_private(cd, entry->path, FALSE, FALSE);
618
619 work = g_list_last(entry->action_list);
620 while (work)
621 {
622 CollectManagerAction *action;
623
624 action = work->data;
625 work = work->prev;
626
627 if (!action->oldpath)
628 {
629 /* add image */
630 if (collection_list_find(cd->list, action->newpath) == NULL)
631 {
632 collection_add_check(cd, action->newpath, FALSE, FALSE);
633 }
634 }
635 else if (action->newpath)
636 {
637 /* rename image */
638 while (collection_rename(cd, action->oldpath, action->newpath));
639 }
640 else
641 {
642 /* remove image */
643 while (collection_remove(cd, action->oldpath));
644 }
645 collect_manager_action_unref(action);
646 }
647
648 if (success && cd->changed)
649 {
650 collection_save_private(cd, entry->path);
651 if (debug) printf("collection manager updated: %s\n", entry->path);
652 }
653 collection_unref(cd);
654
655 g_list_free(entry->action_list);
656 entry->action_list = NULL;
657
658 return TRUE;
659 }
660
661 static gint collect_manager_process_entry_list(void)
662 {
663 GList *work;
664
665 work = collection_manager_entry_list;
666 while (work)
667 {
668 CollectManagerEntry *entry;
669
670 entry = work->data;
671 work = work->next;
672 if (collect_manager_process_entry(entry)) return TRUE;
673 }
674
675 return FALSE;
676 }
677
678 static gint collect_manager_process_cb(gpointer data)
679 {
680 if (collection_manager_action_list) collect_manager_refresh();
681 collect_manager_process_actions(COLLECT_MANAGER_ACTIONS_PER_IDLE);
682 if (collection_manager_action_list) return TRUE;
683
684 if (collect_manager_process_entry_list()) return TRUE;
685
686 if (debug) printf("collection manager is up to date\n");
687 return FALSE;
688 }
689
690 static gint collect_manager_timer_cb(gpointer data)
691 {
692 if (debug) printf("collection manager timer expired\n");
693
694 g_idle_add_full(G_PRIORITY_LOW, collect_manager_process_cb, NULL, NULL);
695
696 collection_manager_timer_id = -1;
697 return FALSE;
698 }
699
700 static void collect_manager_timer_push(gint stop)
701 {
702 if (collection_manager_timer_id != -1)
703 {
704 if (!stop) return;
705
706 g_source_remove(collection_manager_timer_id);
707 collection_manager_timer_id = -1;
708 }
709
710 if (!stop)
711 {
712 collection_manager_timer_id = g_timeout_add(COLLECT_MANAGER_FLUSH_DELAY,
713 collect_manager_timer_cb, NULL);
714 if (debug) printf("collection manager timer started\n");
715 }
716 }
717
718 static void collect_manager_add_action(CollectManagerAction *action)
719 {
720 if (!action) return;
721
722 /* we keep track of the list's tail to keep this a n(1) operation */
723
724 if (collection_manager_action_tail)
725 {
726 g_list_append(collection_manager_action_tail, action);
727 collection_manager_action_tail = collection_manager_action_tail->next;
728 }
729 else
730 {
731 collection_manager_action_list = g_list_append(collection_manager_action_list, action);
732 collection_manager_action_tail = collection_manager_action_list;
733 }
734
735 collect_manager_timer_push(FALSE);
736 }
737
738 void collect_manager_moved(const gchar *oldpath, const gchar *newpath)
739 {
740 CollectManagerAction *action;
741
742 action = collect_manager_action_new(oldpath, newpath, COLLECTION_MANAGER_UPDATE);
743 collect_manager_add_action(action);
744 }
745
746 void collect_manager_add(const gchar *path, const gchar *collection)
747 {
748 CollectManagerAction *action;
749 CollectWindow *cw;
750
751 if (!path || !collection) return;
752
753 cw = collection_window_find_by_path(collection);
754 if (cw)
755 {
756 if (collection_list_find(cw->cd->list, path) == NULL)
757 {
758 collection_add(cw->cd, path, FALSE);
759 }
760 return;
761 }
762
763 action = collect_manager_action_new(path, collection, COLLECTION_MANAGER_ADD);
764 collect_manager_add_action(action);
765 }
766
767 void collect_manager_remove(const gchar *path, const gchar *collection)
768 {
769 CollectManagerAction *action;
770 CollectWindow *cw;
771
772 if (!path || !collection) return;
773
774 cw = collection_window_find_by_path(collection);
775 if (cw)
776 {
777 while (collection_remove(cw->cd, path));
778 return;
779 }
780
781 action = collect_manager_action_new(path, collection, COLLECTION_MANAGER_REMOVE);
782 collect_manager_add_action(action);
783 }
784
785 void collect_manager_flush(void)
786 {
787 collect_manager_timer_push(TRUE);
788
789 if (debug) printf("collection manager flushing\n");
790 while (collect_manager_process_cb(NULL));
791 }
792