Mercurial > geeqie.yaz
annotate src/collect-io.c @ 933:8c5ba3e94e54
warn about changed file extensions
author | nadvornik |
---|---|
date | Sat, 26 Jul 2008 19:01:20 +0000 |
parents | 912726c2f5e3 |
children | 650c02c0c8ff |
rev | line source |
---|---|
9 | 1 /* |
196 | 2 * Geeqie |
9 | 3 * (C) 2004 John Ellis |
475 | 4 * Copyright (C) 2008 The Geeqie Team |
9 | 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 | |
281 | 14 #include "main.h" |
9 | 15 #include "collect-io.h" |
16 | |
17 #include "collect.h" | |
586 | 18 #include "filedata.h" |
9 | 19 #include "layout_util.h" |
20 #include "rcfile.h" | |
312 | 21 #include "secure_save.h" |
9 | 22 #include "thumb.h" |
23 #include "ui_fileops.h" | |
24 | |
25 | |
288
d1f74154463e
Replace occurences of Geeqie / geeqie by constants defined in main.h.
zas_
parents:
283
diff
changeset
|
26 #define GQ_COLLECTION_MARKER "#" GQ_APPNAME |
9 | 27 |
283 | 28 #define GQ_COLLECTION_FAIL_MIN 300 |
29 #define GQ_COLLECTION_FAIL_PERCENT 98 | |
360
822040a51249
Increase collection_load() buffer size and do not activate
zas_
parents:
359
diff
changeset
|
30 #define GQ_COLLECTION_READ_BUFSIZE 4096 |
9 | 31 |
138 | 32 typedef struct _CollectManagerEntry CollectManagerEntry; |
9 | 33 |
34 static void collection_load_thumb_step(CollectionData *cd); | |
138 | 35 static gint collection_save_private(CollectionData *cd, const gchar *path); |
36 | |
37 static CollectManagerEntry *collect_manager_get_entry(const gchar *path); | |
38 static void collect_manager_entry_reset(CollectManagerEntry *entry); | |
235 | 39 static gint collect_manager_process_action(CollectManagerEntry *entry, gchar **path_ptr); |
9 | 40 |
41 | |
42 static gint scan_geometry(gchar *buffer, gint *x, gint *y, gint *w, gint *h) | |
43 { | |
44 gint nx, ny, nw, nh; | |
45 | |
855 | 46 if (sscanf(buffer, "%d %d %d %d", &nx, &ny, &nw, &nh) != 4) return FALSE; |
9 | 47 |
48 *x = nx; | |
49 *y = ny; | |
50 *w = nw; | |
51 *h = nh; | |
52 | |
53 return TRUE; | |
54 } | |
55 | |
358 | 56 static gint collection_load_private(CollectionData *cd, const gchar *path, CollectionLoadFlags flags) |
9 | 57 { |
360
822040a51249
Increase collection_load() buffer size and do not activate
zas_
parents:
359
diff
changeset
|
58 gchar s_buf[GQ_COLLECTION_READ_BUFSIZE]; |
9 | 59 FILE *f; |
60 gchar *pathl; | |
361
e0295af4f716
collection_load(): replace official by limit_failures (and
zas_
parents:
360
diff
changeset
|
61 gint limit_failures = TRUE; |
9 | 62 gint success = TRUE; |
365 | 63 gint has_official_header = FALSE; |
64 gint has_geometry_header = FALSE; | |
65 gint has_gqview_header = FALSE; | |
66 gint need_header = TRUE; | |
9 | 67 guint total = 0; |
68 guint fail = 0; | |
138 | 69 gboolean changed = FALSE; |
70 CollectManagerEntry *entry = NULL; | |
366
ce00494827e2
collection_load(): use booleans and always count failures (for debug).
zas_
parents:
365
diff
changeset
|
71 guint flush = !!(flags & COLLECTION_LOAD_FLUSH); |
ce00494827e2
collection_load(): use booleans and always count failures (for debug).
zas_
parents:
365
diff
changeset
|
72 guint append = !!(flags & COLLECTION_LOAD_APPEND); |
ce00494827e2
collection_load(): use booleans and always count failures (for debug).
zas_
parents:
365
diff
changeset
|
73 guint only_geometry = !!(flags & COLLECTION_LOAD_GEOMETRY); |
9 | 74 |
359
96fb24f948b7
Merge collection_load_only_geometry() into collection_load().
zas_
parents:
358
diff
changeset
|
75 if (!only_geometry) |
96fb24f948b7
Merge collection_load_only_geometry() into collection_load().
zas_
parents:
358
diff
changeset
|
76 { |
96fb24f948b7
Merge collection_load_only_geometry() into collection_load().
zas_
parents:
358
diff
changeset
|
77 collection_load_stop(cd); |
9 | 78 |
359
96fb24f948b7
Merge collection_load_only_geometry() into collection_load().
zas_
parents:
358
diff
changeset
|
79 if (flush) |
96fb24f948b7
Merge collection_load_only_geometry() into collection_load().
zas_
parents:
358
diff
changeset
|
80 collect_manager_flush(); |
96fb24f948b7
Merge collection_load_only_geometry() into collection_load().
zas_
parents:
358
diff
changeset
|
81 else |
96fb24f948b7
Merge collection_load_only_geometry() into collection_load().
zas_
parents:
358
diff
changeset
|
82 entry = collect_manager_get_entry(path); |
9 | 83 |
359
96fb24f948b7
Merge collection_load_only_geometry() into collection_load().
zas_
parents:
358
diff
changeset
|
84 if (!append) |
96fb24f948b7
Merge collection_load_only_geometry() into collection_load().
zas_
parents:
358
diff
changeset
|
85 { |
96fb24f948b7
Merge collection_load_only_geometry() into collection_load().
zas_
parents:
358
diff
changeset
|
86 collection_list_free(cd->list); |
96fb24f948b7
Merge collection_load_only_geometry() into collection_load().
zas_
parents:
358
diff
changeset
|
87 cd->list = NULL; |
96fb24f948b7
Merge collection_load_only_geometry() into collection_load().
zas_
parents:
358
diff
changeset
|
88 } |
9 | 89 } |
90 | |
91 if (!path && !cd->path) return FALSE; | |
92 | |
93 if (!path) path = cd->path; | |
442 | 94 |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
95 DEBUG_1("collection load: append=%d flush=%d only_geometry=%d path=%s", |
360
822040a51249
Increase collection_load() buffer size and do not activate
zas_
parents:
359
diff
changeset
|
96 append, flush, only_geometry, path); |
9 | 97 |
98 /* load it */ | |
99 pathl = path_from_utf8(path); | |
100 f = fopen(pathl, "r"); | |
101 g_free(pathl); | |
102 if (!f) | |
103 { | |
673
fbebf5cf4a55
Do not use printf() directly but use new wrapper function log_printf() instead.
zas_
parents:
671
diff
changeset
|
104 log_printf("Failed to open collection file: \"%s\"\n", path); |
9 | 105 return FALSE; |
106 } | |
107 | |
108 while (fgets(s_buf, sizeof(s_buf), f)) | |
109 { | |
110 gchar *buf; | |
363
5b8617c3821c
collection_load(): accept whitespaces at start of lines.
zas_
parents:
362
diff
changeset
|
111 gchar *p = s_buf; |
5b8617c3821c
collection_load(): accept whitespaces at start of lines.
zas_
parents:
362
diff
changeset
|
112 |
5b8617c3821c
collection_load(): accept whitespaces at start of lines.
zas_
parents:
362
diff
changeset
|
113 /* Skip whitespaces and empty lines */ |
5b8617c3821c
collection_load(): accept whitespaces at start of lines.
zas_
parents:
362
diff
changeset
|
114 while (*p && g_ascii_isspace(*p)) p++; |
5b8617c3821c
collection_load(): accept whitespaces at start of lines.
zas_
parents:
362
diff
changeset
|
115 if (*p == '\n' || *p == '\r') continue; |
5b8617c3821c
collection_load(): accept whitespaces at start of lines.
zas_
parents:
362
diff
changeset
|
116 |
5b8617c3821c
collection_load(): accept whitespaces at start of lines.
zas_
parents:
362
diff
changeset
|
117 /* Parse comments */ |
5b8617c3821c
collection_load(): accept whitespaces at start of lines.
zas_
parents:
362
diff
changeset
|
118 if (*p == '#') |
9 | 119 { |
365 | 120 if (!need_header) continue; |
605
651ae2be1031
Use g_ascii_strncasecmp() instead of strncasecmp() where applicable.
zas_
parents:
586
diff
changeset
|
121 if (g_ascii_strncasecmp(p, GQ_COLLECTION_MARKER, strlen(GQ_COLLECTION_MARKER)) == 0) |
9 | 122 { |
123 /* Looks like an official collection, allow unchecked input. | |
124 * All this does is allow adding files that may not exist, | |
125 * which is needed for the collection manager to work. | |
126 * Also unofficial files abort after too many invalid entries. | |
127 */ | |
365 | 128 has_official_header = TRUE; |
361
e0295af4f716
collection_load(): replace official by limit_failures (and
zas_
parents:
360
diff
changeset
|
129 limit_failures = FALSE; |
9 | 130 } |
363
5b8617c3821c
collection_load(): accept whitespaces at start of lines.
zas_
parents:
362
diff
changeset
|
131 else if (strncmp(p, "#geometry:", 10 ) == 0 && |
442 | 132 scan_geometry(p + 10, &cd->window_x, &cd->window_y, &cd->window_w, &cd->window_h)) |
9 | 133 { |
365 | 134 has_geometry_header = TRUE; |
9 | 135 cd->window_read = TRUE; |
365 | 136 if (only_geometry) break; |
9 | 137 } |
605
651ae2be1031
Use g_ascii_strncasecmp() instead of strncasecmp() where applicable.
zas_
parents:
586
diff
changeset
|
138 else if (g_ascii_strncasecmp(p, "#GQview collection", strlen("#GQview collection")) == 0) |
360
822040a51249
Increase collection_load() buffer size and do not activate
zas_
parents:
359
diff
changeset
|
139 { |
822040a51249
Increase collection_load() buffer size and do not activate
zas_
parents:
359
diff
changeset
|
140 /* As 2008/04/15 there is no difference between our collection file format |
822040a51249
Increase collection_load() buffer size and do not activate
zas_
parents:
359
diff
changeset
|
141 * and GQview 2.1.5 collection file format so ignore failures as well. */ |
365 | 142 has_gqview_header = TRUE; |
361
e0295af4f716
collection_load(): replace official by limit_failures (and
zas_
parents:
360
diff
changeset
|
143 limit_failures = FALSE; |
360
822040a51249
Increase collection_load() buffer size and do not activate
zas_
parents:
359
diff
changeset
|
144 } |
365 | 145 need_header = (!has_official_header && !has_gqview_header) || !has_geometry_header; |
9 | 146 continue; |
147 } | |
148 | |
882
912726c2f5e3
Fix a segfault occuring when opening some collection files.
zas_
parents:
864
diff
changeset
|
149 if (only_geometry) continue; |
912726c2f5e3
Fix a segfault occuring when opening some collection files.
zas_
parents:
864
diff
changeset
|
150 |
363
5b8617c3821c
collection_load(): accept whitespaces at start of lines.
zas_
parents:
362
diff
changeset
|
151 /* Read filenames */ |
5b8617c3821c
collection_load(): accept whitespaces at start of lines.
zas_
parents:
362
diff
changeset
|
152 buf = quoted_value(p, NULL); |
9 | 153 if (buf) |
154 { | |
155 gint valid; | |
442 | 156 |
138 | 157 if (!flush) |
158 changed |= collect_manager_process_action(entry, &buf); | |
442 | 159 |
726 | 160 valid = (buf[0] == G_DIR_SEPARATOR && collection_add_check(cd, file_data_new_simple(buf), FALSE, TRUE)); |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
161 if (!valid) DEBUG_1("collection invalid file: %s", buf); |
9 | 162 g_free(buf); |
163 | |
164 total++; | |
366
ce00494827e2
collection_load(): use booleans and always count failures (for debug).
zas_
parents:
365
diff
changeset
|
165 if (!valid) |
9 | 166 { |
167 fail++; | |
366
ce00494827e2
collection_load(): use booleans and always count failures (for debug).
zas_
parents:
365
diff
changeset
|
168 if (limit_failures && |
ce00494827e2
collection_load(): use booleans and always count failures (for debug).
zas_
parents:
365
diff
changeset
|
169 fail > GQ_COLLECTION_FAIL_MIN && |
283 | 170 fail * 100 / total > GQ_COLLECTION_FAIL_PERCENT) |
9 | 171 { |
673
fbebf5cf4a55
Do not use printf() directly but use new wrapper function log_printf() instead.
zas_
parents:
671
diff
changeset
|
172 log_printf("%d invalid filenames in unofficial collection file, closing: %s\n", fail, path); |
9 | 173 success = FALSE; |
174 break; | |
175 } | |
176 } | |
177 } | |
178 } | |
179 | |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
180 DEBUG_1("collection files: total = %d fail = %d official=%d gqview=%d geometry=%d", |
442 | 181 total, fail, has_official_header, has_gqview_header, has_geometry_header); |
362 | 182 |
9 | 183 fclose(f); |
365 | 184 if (only_geometry) return has_geometry_header; |
359
96fb24f948b7
Merge collection_load_only_geometry() into collection_load().
zas_
parents:
358
diff
changeset
|
185 |
138 | 186 if (!flush) |
187 { | |
188 gchar *buf = NULL; | |
189 while (collect_manager_process_action(entry, &buf)) | |
190 { | |
191 collection_add_check(cd, file_data_new_simple(buf), FALSE, TRUE); | |
192 changed = TRUE; | |
193 g_free(buf); | |
492
dead6fb690c5
Fix adding a selection to a collection with no window open
zas_
parents:
475
diff
changeset
|
194 buf = NULL; |
138 | 195 } |
196 } | |
9 | 197 |
198 cd->list = collection_list_sort(cd->list, cd->sort_method); | |
442 | 199 |
138 | 200 if (!flush && changed && success) |
201 collection_save_private(cd, path); | |
442 | 202 |
138 | 203 if (!flush) |
204 collect_manager_entry_reset(entry); | |
442 | 205 |
9 | 206 if (!append) cd->changed = FALSE; |
207 | |
208 return success; | |
209 } | |
210 | |
358 | 211 gint collection_load(CollectionData *cd, const gchar *path, CollectionLoadFlags flags) |
9 | 212 { |
358 | 213 if (collection_load_private(cd, path, flags | COLLECTION_LOAD_FLUSH)) |
9 | 214 { |
215 layout_recent_add_path(cd->path); | |
216 return TRUE; | |
217 } | |
218 | |
219 return FALSE; | |
220 } | |
221 | |
222 static void collection_load_thumb_do(CollectionData *cd) | |
223 { | |
224 GdkPixbuf *pixbuf; | |
225 | |
226 if (!cd->thumb_loader || !g_list_find(cd->list, cd->thumb_info)) return; | |
227 | |
864 | 228 pixbuf = thumb_loader_get_pixbuf(cd->thumb_loader); |
9 | 229 collection_info_set_thumb(cd->thumb_info, pixbuf); |
230 g_object_unref(pixbuf); | |
231 | |
232 if (cd->info_updated_func) cd->info_updated_func(cd, cd->thumb_info, cd->info_updated_data); | |
233 } | |
234 | |
235 static void collection_load_thumb_error_cb(ThumbLoader *tl, gpointer data) | |
236 { | |
237 CollectionData *cd = data; | |
238 | |
239 collection_load_thumb_do(cd); | |
240 collection_load_thumb_step(cd); | |
241 } | |
242 | |
243 static void collection_load_thumb_done_cb(ThumbLoader *tl, gpointer data) | |
244 { | |
245 CollectionData *cd = data; | |
246 | |
247 collection_load_thumb_do(cd); | |
248 collection_load_thumb_step(cd); | |
249 } | |
250 | |
251 static void collection_load_thumb_step(CollectionData *cd) | |
252 { | |
253 GList *work; | |
254 CollectInfo *ci; | |
255 | |
256 if (!cd->list) | |
257 { | |
258 collection_load_stop(cd); | |
259 return; | |
260 } | |
261 | |
262 work = cd->list; | |
263 ci = work->data; | |
264 work = work->next; | |
265 /* find first unloaded thumb */ | |
266 while (work && ci->pixbuf) | |
267 { | |
268 ci = work->data; | |
269 work = work->next; | |
270 } | |
271 | |
272 if (!ci || ci->pixbuf) | |
273 { | |
274 /* done */ | |
275 collection_load_stop(cd); | |
276 | |
277 /* send a NULL CollectInfo to notify end */ | |
278 if (cd->info_updated_func) cd->info_updated_func(cd, NULL, cd->info_updated_data); | |
279 | |
280 return; | |
281 } | |
282 | |
283 /* setup loader and call it */ | |
284 cd->thumb_info = ci; | |
285 thumb_loader_free(cd->thumb_loader); | |
333 | 286 cd->thumb_loader = thumb_loader_new(options->thumbnails.max_width, options->thumbnails.max_height); |
9 | 287 thumb_loader_set_callbacks(cd->thumb_loader, |
288 collection_load_thumb_done_cb, | |
289 collection_load_thumb_error_cb, | |
290 NULL, | |
291 cd); | |
292 | |
293 /* start it */ | |
838 | 294 if (!thumb_loader_start(cd->thumb_loader, ci->fd)) |
9 | 295 { |
296 /* error, handle it, do next */ | |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
297 DEBUG_1("error loading thumb for %s", ci->fd->path); |
9 | 298 collection_load_thumb_do(cd); |
299 collection_load_thumb_step(cd); | |
300 } | |
301 } | |
302 | |
303 void collection_load_thumb_idle(CollectionData *cd) | |
304 { | |
305 if (!cd->thumb_loader) collection_load_thumb_step(cd); | |
306 } | |
307 | |
358 | 308 gint collection_load_begin(CollectionData *cd, const gchar *path, CollectionLoadFlags flags) |
9 | 309 { |
358 | 310 if (!collection_load(cd, path, flags)) return FALSE; |
9 | 311 |
312 collection_load_thumb_idle(cd); | |
313 | |
314 return TRUE; | |
315 } | |
316 | |
317 void collection_load_stop(CollectionData *cd) | |
318 { | |
319 if (!cd->thumb_loader) return; | |
320 | |
321 thumb_loader_free(cd->thumb_loader); | |
322 cd->thumb_loader = NULL; | |
323 } | |
324 | |
325 static gint collection_save_private(CollectionData *cd, const gchar *path) | |
326 { | |
312 | 327 SecureSaveInfo *ssi; |
9 | 328 GList *work; |
329 gchar *pathl; | |
330 | |
331 if (!path && !cd->path) return FALSE; | |
332 | |
333 if (!path) | |
334 { | |
335 path = cd->path; | |
336 } | |
337 | |
338 | |
312 | 339 pathl = path_from_utf8(path); |
340 ssi = secure_open(pathl); | |
9 | 341 g_free(pathl); |
312 | 342 if (!ssi) |
343 { | |
694 | 344 log_printf(_("failed to open collection (write) \"%s\"\n"), path); |
9 | 345 return FALSE; |
346 } | |
347 | |
312 | 348 secure_fprintf(ssi, "%s collection\n", GQ_COLLECTION_MARKER); |
349 secure_fprintf(ssi, "#created with %s version %s\n", GQ_APPNAME, VERSION); | |
9 | 350 |
351 collection_update_geometry(cd); | |
352 if (cd->window_read) | |
353 { | |
312 | 354 secure_fprintf(ssi, "#geometry: %d %d %d %d\n", cd->window_x, cd->window_y, cd->window_w, cd->window_h); |
9 | 355 } |
356 | |
357 work = cd->list; | |
312 | 358 while (work && secsave_errno == SS_ERR_NONE) |
9 | 359 { |
360 CollectInfo *ci = work->data; | |
312 | 361 secure_fprintf(ssi, "\"%s\"\n", ci->fd->path); |
9 | 362 work = work->next; |
363 } | |
364 | |
312 | 365 secure_fprintf(ssi, "#end\n"); |
9 | 366 |
312 | 367 if (secure_close(ssi)) |
9 | 368 { |
694 | 369 log_printf(_("error saving collection file: %s\nerror: %s\n"), path, |
403 | 370 secsave_strerror(secsave_errno)); |
9 | 371 return FALSE; |
372 } | |
373 | |
374 if (!cd->path || strcmp(path, cd->path) != 0) | |
375 { | |
376 gchar *buf = cd->path; | |
377 cd->path = g_strdup(path); | |
378 path = cd->path; | |
379 g_free(buf); | |
380 | |
381 g_free(cd->name); | |
382 cd->name = g_strdup(filename_from_path(cd->path)); | |
383 | |
384 collection_path_changed(cd); | |
385 } | |
386 | |
387 cd->changed = FALSE; | |
388 | |
389 return TRUE; | |
390 } | |
391 | |
392 gint collection_save(CollectionData *cd, const gchar *path) | |
393 { | |
394 if (collection_save_private(cd, path)) | |
395 { | |
396 layout_recent_add_path(cd->path); | |
397 return TRUE; | |
398 } | |
399 | |
400 return FALSE; | |
401 } | |
402 | |
403 gint collection_load_only_geometry(CollectionData *cd, const gchar *path) | |
404 { | |
359
96fb24f948b7
Merge collection_load_only_geometry() into collection_load().
zas_
parents:
358
diff
changeset
|
405 return collection_load(cd, path, COLLECTION_LOAD_GEOMETRY); |
9 | 406 } |
407 | |
408 | |
409 /* | |
410 *------------------------------------------------------------------- | |
411 * collection manager | |
412 *------------------------------------------------------------------- | |
413 */ | |
414 | |
415 #define COLLECT_MANAGER_ACTIONS_PER_IDLE 1000 | |
416 #define COLLECT_MANAGER_FLUSH_DELAY 10000 | |
417 | |
418 struct _CollectManagerEntry | |
419 { | |
420 gchar *path; | |
138 | 421 GList *add_list; |
422 GHashTable *oldpath_hash; | |
423 GHashTable *newpath_hash; | |
424 gboolean empty; | |
9 | 425 }; |
426 | |
427 typedef enum { | |
428 COLLECTION_MANAGER_UPDATE, | |
429 COLLECTION_MANAGER_ADD, | |
430 COLLECTION_MANAGER_REMOVE | |
431 } CollectManagerType; | |
432 | |
433 typedef struct _CollectManagerAction CollectManagerAction; | |
434 struct _CollectManagerAction | |
435 { | |
436 gchar *oldpath; | |
437 gchar *newpath; | |
438 | |
439 CollectManagerType type; | |
440 | |
441 gint ref; | |
442 }; | |
443 | |
444 | |
445 static GList *collection_manager_entry_list = NULL; | |
446 static GList *collection_manager_action_list = NULL; | |
447 static GList *collection_manager_action_tail = NULL; | |
448 static gint collection_manager_timer_id = -1; | |
449 | |
450 | |
451 static CollectManagerAction *collect_manager_action_new(const gchar *oldpath, const gchar *newpath, | |
452 CollectManagerType type) | |
453 { | |
454 CollectManagerAction *action; | |
455 | |
456 action = g_new0(CollectManagerAction, 1); | |
457 action->ref = 1; | |
458 | |
459 action->oldpath = g_strdup(oldpath); | |
460 action->newpath = g_strdup(newpath); | |
461 | |
462 action->type = type; | |
463 | |
464 return action; | |
465 } | |
466 | |
467 static void collect_manager_action_ref(CollectManagerAction *action) | |
468 { | |
469 action->ref++; | |
470 } | |
471 | |
472 static void collect_manager_action_unref(CollectManagerAction *action) | |
473 { | |
474 action->ref--; | |
475 | |
476 if (action->ref > 0) return; | |
477 | |
478 g_free(action->oldpath); | |
479 g_free(action->newpath); | |
480 g_free(action); | |
481 } | |
482 | |
138 | 483 static void collect_manager_entry_free_data(CollectManagerEntry *entry) |
9 | 484 { |
485 GList *work; | |
486 | |
138 | 487 work = entry->add_list; |
9 | 488 while (work) |
489 { | |
490 CollectManagerAction *action; | |
491 | |
492 action = work->data; | |
493 work = work->next; | |
494 | |
495 collect_manager_action_unref(action); | |
496 } | |
138 | 497 g_list_free(entry->add_list); |
498 g_hash_table_destroy(entry->oldpath_hash); | |
499 g_hash_table_destroy(entry->newpath_hash); | |
500 } | |
501 | |
502 static void collect_manager_entry_init_data(CollectManagerEntry *entry) | |
503 { | |
504 entry->add_list = NULL; | |
505 entry->oldpath_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify) collect_manager_action_unref); | |
506 entry->newpath_hash = g_hash_table_new(g_str_hash, g_str_equal); | |
507 entry->empty = TRUE; | |
508 | |
509 } | |
510 | |
511 static CollectManagerEntry *collect_manager_entry_new(const gchar *path) | |
512 { | |
513 CollectManagerEntry *entry; | |
514 | |
515 entry = g_new0(CollectManagerEntry, 1); | |
516 entry->path = g_strdup(path); | |
517 collect_manager_entry_init_data(entry); | |
518 | |
519 collection_manager_entry_list = g_list_append(collection_manager_entry_list, entry); | |
520 | |
521 return entry; | |
522 } | |
523 | |
524 | |
525 static void collect_manager_entry_free(CollectManagerEntry *entry) | |
526 { | |
527 collection_manager_entry_list = g_list_remove(collection_manager_entry_list, entry); | |
528 | |
529 collect_manager_entry_free_data(entry); | |
9 | 530 |
531 g_free(entry->path); | |
532 g_free(entry); | |
533 } | |
534 | |
138 | 535 static void collect_manager_entry_reset(CollectManagerEntry *entry) |
536 { | |
537 collect_manager_entry_free_data(entry); | |
538 collect_manager_entry_init_data(entry); | |
539 } | |
540 | |
541 static CollectManagerEntry *collect_manager_get_entry(const gchar *path) | |
542 { | |
543 GList *work; | |
544 | |
545 work = collection_manager_entry_list; | |
546 while (work) | |
547 { | |
548 CollectManagerEntry *entry; | |
549 | |
550 entry = work->data; | |
551 work = work->next; | |
442 | 552 if (strcmp(entry->path, path) == 0) |
138 | 553 { |
554 return entry; | |
555 } | |
556 } | |
557 return NULL; | |
558 | |
559 } | |
560 | |
561 static void collect_manager_entry_add_action(CollectManagerEntry *entry, CollectManagerAction *action) | |
562 { | |
563 | |
564 CollectManagerAction *orig_action; | |
442 | 565 |
566 entry->empty = FALSE; | |
567 | |
138 | 568 if (action->oldpath == NULL) |
569 { | |
570 /* add file */ | |
571 if (action->newpath == NULL) | |
572 { | |
573 return; | |
574 } | |
442 | 575 |
138 | 576 orig_action = g_hash_table_lookup(entry->newpath_hash, action->newpath); |
577 if (orig_action) | |
578 { | |
579 /* target already exists */ | |
673
fbebf5cf4a55
Do not use printf() directly but use new wrapper function log_printf() instead.
zas_
parents:
671
diff
changeset
|
580 log_printf("collection manager failed to add another action for target %s in collection %s\n", |
138 | 581 action->newpath, entry->path); |
582 return; | |
583 } | |
584 entry->add_list = g_list_append(entry->add_list, action); | |
585 g_hash_table_insert(entry->newpath_hash, action->newpath, action); | |
586 collect_manager_action_ref(action); | |
587 return; | |
588 } | |
589 | |
590 orig_action = g_hash_table_lookup(entry->newpath_hash, action->oldpath); | |
591 if (orig_action) | |
592 { | |
593 /* new action with the same file */ | |
594 CollectManagerAction *new_action = collect_manager_action_new(orig_action->oldpath, action->newpath, action->type); | |
442 | 595 |
138 | 596 if (new_action->oldpath) |
597 { | |
598 g_hash_table_steal(entry->oldpath_hash, orig_action->oldpath); | |
599 g_hash_table_insert(entry->oldpath_hash, new_action->oldpath, new_action); | |
600 } | |
601 else | |
602 { | |
603 GList *work = g_list_find(entry->add_list, orig_action); | |
604 work->data = new_action; | |
605 } | |
442 | 606 |
138 | 607 g_hash_table_steal(entry->newpath_hash, orig_action->newpath); |
442 | 608 if (new_action->newpath) |
138 | 609 { |
442 | 610 g_hash_table_insert(entry->newpath_hash, new_action->newpath, new_action); |
138 | 611 } |
612 collect_manager_action_unref(orig_action); | |
613 return; | |
614 } | |
615 | |
616 | |
617 orig_action = g_hash_table_lookup(entry->oldpath_hash, action->oldpath); | |
618 if (orig_action) | |
619 { | |
620 /* another action for the same source, ignore */ | |
673
fbebf5cf4a55
Do not use printf() directly but use new wrapper function log_printf() instead.
zas_
parents:
671
diff
changeset
|
621 log_printf("collection manager failed to add another action for source %s in collection %s\n", |
138 | 622 action->oldpath, entry->path); |
623 return; | |
624 } | |
442 | 625 |
138 | 626 g_hash_table_insert(entry->oldpath_hash, action->oldpath, action); |
627 if (action->newpath) | |
628 { | |
442 | 629 g_hash_table_insert(entry->newpath_hash, action->newpath, action); |
138 | 630 } |
631 collect_manager_action_ref(action); | |
632 } | |
633 | |
235 | 634 static gint collect_manager_process_action(CollectManagerEntry *entry, gchar **path_ptr) |
138 | 635 { |
636 gchar *path = *path_ptr; | |
637 CollectManagerAction *action; | |
442 | 638 |
138 | 639 if (path == NULL) |
640 { | |
641 /* get new files */ | |
642 if (entry->add_list) | |
643 { | |
644 action = entry->add_list->data; | |
645 g_assert(action->oldpath == NULL); | |
646 entry->add_list = g_list_remove(entry->add_list, action); | |
647 path = g_strdup(action->newpath); | |
648 g_hash_table_remove(entry->newpath_hash, path); | |
649 collect_manager_action_unref(action); | |
650 } | |
651 *path_ptr = path; | |
652 return (path != NULL); | |
653 } | |
442 | 654 |
138 | 655 action = g_hash_table_lookup(entry->oldpath_hash, path); |
442 | 656 |
138 | 657 if (action) |
658 { | |
659 g_free(path); | |
660 path = g_strdup(action->newpath); | |
661 *path_ptr = path; | |
662 return TRUE; | |
663 } | |
664 | |
665 return FALSE; /* no change */ | |
666 } | |
667 | |
9 | 668 static void collect_manager_refresh(void) |
669 { | |
780
44128da39e13
Drop initialization to NULL since filelist_read() will take care of it.
zas_
parents:
726
diff
changeset
|
670 GList *list; |
9 | 671 GList *work; |
672 gchar *base; | |
783 | 673 FileData *dir_fd; |
9 | 674 |
711 | 675 base = g_build_filename(homedir(), GQ_RC_DIR_COLLECTIONS, NULL); |
783 | 676 dir_fd = file_data_new_simple(base); |
677 filelist_read(dir_fd, &list, NULL); | |
678 file_data_unref(dir_fd); | |
9 | 679 g_free(base); |
680 | |
681 work = collection_manager_entry_list; | |
682 while (work && list) | |
683 { | |
684 CollectManagerEntry *entry; | |
685 GList *list_step; | |
686 | |
687 entry = work->data; | |
688 work = work->next; | |
689 | |
690 list_step = list; | |
691 while (list_step && entry) | |
692 { | |
576
9dc0513837b5
dropped path_list functions, use filelist functions everywhere
nadvornik
parents:
507
diff
changeset
|
693 FileData *fd; |
9 | 694 |
576
9dc0513837b5
dropped path_list functions, use filelist functions everywhere
nadvornik
parents:
507
diff
changeset
|
695 fd = list_step->data; |
9 | 696 list_step = list_step->next; |
697 | |
576
9dc0513837b5
dropped path_list functions, use filelist functions everywhere
nadvornik
parents:
507
diff
changeset
|
698 if (strcmp(fd->path, entry->path) == 0) |
9 | 699 { |
576
9dc0513837b5
dropped path_list functions, use filelist functions everywhere
nadvornik
parents:
507
diff
changeset
|
700 list = g_list_remove(list, fd); |
9dc0513837b5
dropped path_list functions, use filelist functions everywhere
nadvornik
parents:
507
diff
changeset
|
701 file_data_unref(fd); |
9 | 702 |
703 entry = NULL; | |
704 } | |
705 else | |
706 { | |
707 collect_manager_entry_free(entry); | |
708 } | |
709 } | |
710 } | |
711 | |
712 work = list; | |
713 while (work) | |
714 { | |
576
9dc0513837b5
dropped path_list functions, use filelist functions everywhere
nadvornik
parents:
507
diff
changeset
|
715 FileData *fd; |
9 | 716 |
576
9dc0513837b5
dropped path_list functions, use filelist functions everywhere
nadvornik
parents:
507
diff
changeset
|
717 fd = work->data; |
9 | 718 work = work->next; |
719 | |
576
9dc0513837b5
dropped path_list functions, use filelist functions everywhere
nadvornik
parents:
507
diff
changeset
|
720 collect_manager_entry_new(fd->path); |
9 | 721 } |
722 | |
576
9dc0513837b5
dropped path_list functions, use filelist functions everywhere
nadvornik
parents:
507
diff
changeset
|
723 filelist_free(list); |
9 | 724 } |
725 | |
726 static void collect_manager_process_actions(gint max) | |
727 { | |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
728 if (collection_manager_action_list) DEBUG_1("collection manager processing actions"); |
495 | 729 |
9 | 730 while (collection_manager_action_list != NULL && max > 0) |
731 { | |
732 CollectManagerAction *action; | |
733 GList *work; | |
734 | |
735 action = collection_manager_action_list->data; | |
736 work = collection_manager_entry_list; | |
737 while (work) | |
738 { | |
739 CollectManagerEntry *entry; | |
740 | |
741 entry = work->data; | |
742 work = work->next; | |
743 | |
744 if (action->type == COLLECTION_MANAGER_UPDATE) | |
745 { | |
138 | 746 collect_manager_entry_add_action(entry, action); |
9 | 747 } |
748 else if (action->oldpath && action->newpath && | |
749 strcmp(action->newpath, entry->path) == 0) | |
750 { | |
751 /* convert action to standard add format */ | |
752 g_free(action->newpath); | |
753 if (action->type == COLLECTION_MANAGER_ADD) | |
754 { | |
755 action->newpath = action->oldpath; | |
756 action->oldpath = NULL; | |
757 } | |
758 else if (action->type == COLLECTION_MANAGER_REMOVE) | |
759 { | |
760 action->newpath = NULL; | |
761 } | |
138 | 762 collect_manager_entry_add_action(entry, action); |
9 | 763 } |
764 | |
765 max--; | |
766 } | |
767 | |
768 if (action->type != COLLECTION_MANAGER_UPDATE && | |
769 action->oldpath && action->newpath) | |
770 { | |
673
fbebf5cf4a55
Do not use printf() directly but use new wrapper function log_printf() instead.
zas_
parents:
671
diff
changeset
|
771 log_printf("collection manager failed to %s %s for collection %s\n", |
9 | 772 (action->type == COLLECTION_MANAGER_ADD) ? "add" : "remove", |
773 action->oldpath, action->newpath); | |
774 } | |
775 | |
776 if (collection_manager_action_tail == collection_manager_action_list) | |
777 { | |
778 collection_manager_action_tail = NULL; | |
779 } | |
780 collection_manager_action_list = g_list_remove(collection_manager_action_list, action); | |
781 collect_manager_action_unref(action); | |
782 } | |
783 } | |
784 | |
785 static gint collect_manager_process_entry(CollectManagerEntry *entry) | |
786 { | |
787 CollectionData *cd; | |
788 gint success; | |
789 | |
138 | 790 if (entry->empty) return FALSE; |
9 | 791 |
792 cd = collection_new(entry->path); | |
358 | 793 success = collection_load_private(cd, entry->path, COLLECTION_LOAD_NONE); |
9 | 794 |
795 collection_unref(cd); | |
796 | |
797 return TRUE; | |
798 } | |
799 | |
800 static gint collect_manager_process_entry_list(void) | |
801 { | |
802 GList *work; | |
803 | |
804 work = collection_manager_entry_list; | |
805 while (work) | |
806 { | |
807 CollectManagerEntry *entry; | |
808 | |
809 entry = work->data; | |
810 work = work->next; | |
811 if (collect_manager_process_entry(entry)) return TRUE; | |
812 } | |
813 | |
814 return FALSE; | |
815 } | |
816 | |
138 | 817 |
818 | |
9 | 819 static gint collect_manager_process_cb(gpointer data) |
820 { | |
821 if (collection_manager_action_list) collect_manager_refresh(); | |
822 collect_manager_process_actions(COLLECT_MANAGER_ACTIONS_PER_IDLE); | |
823 if (collection_manager_action_list) return TRUE; | |
824 | |
825 if (collect_manager_process_entry_list()) return TRUE; | |
826 | |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
827 DEBUG_1("collection manager is up to date"); |
9 | 828 return FALSE; |
829 } | |
830 | |
831 static gint collect_manager_timer_cb(gpointer data) | |
832 { | |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
833 DEBUG_1("collection manager timer expired"); |
9 | 834 |
835 g_idle_add_full(G_PRIORITY_LOW, collect_manager_process_cb, NULL, NULL); | |
836 | |
837 collection_manager_timer_id = -1; | |
838 return FALSE; | |
839 } | |
840 | |
841 static void collect_manager_timer_push(gint stop) | |
842 { | |
843 if (collection_manager_timer_id != -1) | |
844 { | |
845 if (!stop) return; | |
846 | |
847 g_source_remove(collection_manager_timer_id); | |
848 collection_manager_timer_id = -1; | |
849 } | |
850 | |
851 if (!stop) | |
852 { | |
853 collection_manager_timer_id = g_timeout_add(COLLECT_MANAGER_FLUSH_DELAY, | |
854 collect_manager_timer_cb, NULL); | |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
855 DEBUG_1("collection manager timer started"); |
9 | 856 } |
857 } | |
858 | |
859 static void collect_manager_add_action(CollectManagerAction *action) | |
860 { | |
861 if (!action) return; | |
862 | |
863 /* we keep track of the list's tail to keep this a n(1) operation */ | |
864 | |
827 | 865 if (collection_manager_action_tail) |
866 { | |
867 collection_manager_action_tail = g_list_append(collection_manager_action_tail, action); | |
868 collection_manager_action_tail = collection_manager_action_tail->next; | |
869 } | |
870 else | |
871 { | |
872 collection_manager_action_list = g_list_append(collection_manager_action_list, action); | |
873 collection_manager_action_tail = collection_manager_action_list; | |
874 } | |
9 | 875 |
876 collect_manager_timer_push(FALSE); | |
877 } | |
878 | |
138 | 879 void collect_manager_moved(FileData *fd) |
9 | 880 { |
881 CollectManagerAction *action; | |
138 | 882 const gchar *oldpath = fd->change->source; |
883 const gchar *newpath = fd->change->dest; | |
9 | 884 |
885 action = collect_manager_action_new(oldpath, newpath, COLLECTION_MANAGER_UPDATE); | |
886 collect_manager_add_action(action); | |
887 } | |
888 | |
138 | 889 void collect_manager_add(FileData *fd, const gchar *collection) |
9 | 890 { |
891 CollectManagerAction *action; | |
892 CollectWindow *cw; | |
893 | |
138 | 894 if (!fd || !collection) return; |
9 | 895 |
896 cw = collection_window_find_by_path(collection); | |
897 if (cw) | |
898 { | |
799 | 899 if (collection_list_find_fd(cw->cd->list, fd) == NULL) |
9 | 900 { |
138 | 901 collection_add(cw->cd, fd, FALSE); |
9 | 902 } |
903 return; | |
904 } | |
905 | |
138 | 906 action = collect_manager_action_new(fd->path, collection, COLLECTION_MANAGER_ADD); |
9 | 907 collect_manager_add_action(action); |
908 } | |
909 | |
138 | 910 void collect_manager_remove(FileData *fd, const gchar *collection) |
9 | 911 { |
912 CollectManagerAction *action; | |
913 CollectWindow *cw; | |
914 | |
138 | 915 if (!fd || !collection) return; |
9 | 916 |
917 cw = collection_window_find_by_path(collection); | |
918 if (cw) | |
919 { | |
138 | 920 while (collection_remove(cw->cd, fd)); |
9 | 921 return; |
922 } | |
923 | |
138 | 924 action = collect_manager_action_new(fd->path, collection, COLLECTION_MANAGER_REMOVE); |
9 | 925 collect_manager_add_action(action); |
926 } | |
927 | |
928 void collect_manager_flush(void) | |
929 { | |
930 collect_manager_timer_push(TRUE); | |
931 | |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
932 DEBUG_1("collection manager flushing"); |
9 | 933 while (collect_manager_process_cb(NULL)); |
934 } | |
799 | 935 |
936 void collect_manager_notify_cb(FileData *fd, NotifyType type, gpointer data) | |
937 { | |
938 | |
803 | 939 if (type != NOTIFY_TYPE_CHANGE || !fd->change) return; |
799 | 940 |
941 switch(fd->change->type) | |
942 { | |
943 case FILEDATA_CHANGE_MOVE: | |
944 collect_manager_moved(fd); | |
945 break; | |
946 case FILEDATA_CHANGE_COPY: | |
947 break; | |
948 case FILEDATA_CHANGE_RENAME: | |
949 collect_manager_moved(fd); | |
950 break; | |
951 case FILEDATA_CHANGE_DELETE: | |
952 break; | |
953 case FILEDATA_CHANGE_UNSPECIFIED: | |
954 break; | |
955 } | |
956 | |
957 } |