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