Mercurial > geeqie.yaz
annotate src/filedata.c @ 801:9b676bb76a2e
various refresh and notification fixes
author | nadvornik |
---|---|
date | Sat, 07 Jun 2008 22:44:17 +0000 |
parents | a25b228978a0 |
children | 8620e6934cfb |
rev | line source |
---|---|
586 | 1 /* |
2 * Geeqie | |
3 * (C) 2006 John Ellis | |
4 * Copyright (C) 2008 The Geeqie Team | |
5 * | |
6 * Author: John Ellis | |
7 * | |
8 * This software is released under the GNU General Public License (GNU GPL). | |
9 * Please read the included file COPYING for more information. | |
10 * This software comes with no warranty of any kind, use at your own risk! | |
11 */ | |
12 | |
13 | |
14 #include "main.h" | |
15 #include "filedata.h" | |
16 | |
17 #include "filefilter.h" | |
18 #include "cache.h" | |
19 #include "rcfile.h" | |
20 #include "secure_save.h" | |
21 #include "thumb_standard.h" | |
22 #include "ui_fileops.h" | |
23 | |
24 | |
785 | 25 static GHashTable *file_data_pool = NULL; |
26 | |
586 | 27 static gint sidecar_file_priority(const gchar *path); |
28 | |
29 | |
30 /* | |
31 *----------------------------------------------------------------------------- | |
32 * text conversion utils | |
33 *----------------------------------------------------------------------------- | |
34 */ | |
35 | |
36 gchar *text_from_size(gint64 size) | |
37 { | |
38 gchar *a, *b; | |
39 gchar *s, *d; | |
40 gint l, n, i; | |
41 | |
42 /* what I would like to use is printf("%'d", size) | |
43 * BUT: not supported on every libc :( | |
44 */ | |
45 if (size > G_MAXUINT) | |
46 { | |
47 /* the %lld conversion is not valid in all libcs, so use a simple work-around */ | |
48 a = g_strdup_printf("%d%09d", (guint)(size / 1000000000), (guint)(size % 1000000000)); | |
49 } | |
50 else | |
51 { | |
52 a = g_strdup_printf("%d", (guint)size); | |
53 } | |
54 l = strlen(a); | |
55 n = (l - 1)/ 3; | |
56 if (n < 1) return a; | |
57 | |
58 b = g_new(gchar, l + n + 1); | |
59 | |
60 s = a; | |
61 d = b; | |
62 i = l - n * 3; | |
63 while (*s != '\0') | |
64 { | |
65 if (i < 1) | |
66 { | |
67 i = 3; | |
68 *d = ','; | |
69 d++; | |
70 } | |
71 | |
72 *d = *s; | |
73 s++; | |
74 d++; | |
75 i--; | |
76 } | |
77 *d = '\0'; | |
78 | |
79 g_free(a); | |
80 return b; | |
81 } | |
82 | |
83 gchar *text_from_size_abrev(gint64 size) | |
84 { | |
85 if (size < (gint64)1024) | |
86 { | |
87 return g_strdup_printf(_("%d bytes"), (gint)size); | |
88 } | |
89 if (size < (gint64)1048576) | |
90 { | |
91 return g_strdup_printf(_("%.1f K"), (double)size / 1024.0); | |
92 } | |
93 if (size < (gint64)1073741824) | |
94 { | |
95 return g_strdup_printf(_("%.1f MB"), (double)size / 1048576.0); | |
96 } | |
97 | |
98 /* to avoid overflowing the double, do division in two steps */ | |
99 size /= 1048576; | |
100 return g_strdup_printf(_("%.1f GB"), (double)size / 1024.0); | |
101 } | |
102 | |
103 /* note: returned string is valid until next call to text_from_time() */ | |
104 const gchar *text_from_time(time_t t) | |
105 { | |
106 static gchar *ret = NULL; | |
107 gchar buf[128]; | |
108 gint buflen; | |
109 struct tm *btime; | |
110 GError *error = NULL; | |
111 | |
112 btime = localtime(&t); | |
113 | |
114 /* the %x warning about 2 digit years is not an error */ | |
115 buflen = strftime(buf, sizeof(buf), "%x %H:%M", btime); | |
116 if (buflen < 1) return ""; | |
117 | |
118 g_free(ret); | |
119 ret = g_locale_to_utf8(buf, buflen, NULL, NULL, &error); | |
120 if (error) | |
121 { | |
673
fbebf5cf4a55
Do not use printf() directly but use new wrapper function log_printf() instead.
zas_
parents:
671
diff
changeset
|
122 log_printf("Error converting locale strftime to UTF-8: %s\n", error->message); |
586 | 123 g_error_free(error); |
124 return ""; | |
125 } | |
126 | |
127 return ret; | |
128 } | |
129 | |
130 /* | |
131 *----------------------------------------------------------------------------- | |
132 * file info struct | |
133 *----------------------------------------------------------------------------- | |
134 */ | |
135 | |
136 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source); | |
137 static void file_data_check_sidecars(FileData *fd); | |
138 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd); | |
139 | |
140 | |
763
81f9e8dbb4bf
improved infrastructure for tracing changes, optimized vflist_populate_view
nadvornik
parents:
753
diff
changeset
|
141 void file_data_increment_version(FileData *fd) |
81f9e8dbb4bf
improved infrastructure for tracing changes, optimized vflist_populate_view
nadvornik
parents:
753
diff
changeset
|
142 { |
81f9e8dbb4bf
improved infrastructure for tracing changes, optimized vflist_populate_view
nadvornik
parents:
753
diff
changeset
|
143 fd->version++; |
81f9e8dbb4bf
improved infrastructure for tracing changes, optimized vflist_populate_view
nadvornik
parents:
753
diff
changeset
|
144 if (fd->parent) fd->parent->version++; |
81f9e8dbb4bf
improved infrastructure for tracing changes, optimized vflist_populate_view
nadvornik
parents:
753
diff
changeset
|
145 } |
81f9e8dbb4bf
improved infrastructure for tracing changes, optimized vflist_populate_view
nadvornik
parents:
753
diff
changeset
|
146 |
785 | 147 static void file_data_set_collate_keys(FileData *fd) |
148 { | |
149 gchar *caseless_name; | |
150 | |
151 g_assert(g_utf8_validate(fd->name, -1, NULL)); | |
152 | |
153 caseless_name = g_utf8_casefold(fd->name, -1); | |
154 | |
155 g_free(fd->collate_key_name); | |
156 g_free(fd->collate_key_name_nocase); | |
157 | |
158 #if GLIB_CHECK_VERSION(2, 8, 0) | |
159 fd->collate_key_name = g_utf8_collate_key_for_filename(fd->name, -1); | |
160 fd->collate_key_name_nocase = g_utf8_collate_key_for_filename(caseless_name, -1); | |
161 #else | |
162 fd->collate_key_name = g_utf8_collate_key(fd->name, -1); | |
163 fd->collate_key_name_nocase = g_utf8_collate_key(caseless_name, -1); | |
164 #endif | |
165 g_free(caseless_name); | |
166 } | |
763
81f9e8dbb4bf
improved infrastructure for tracing changes, optimized vflist_populate_view
nadvornik
parents:
753
diff
changeset
|
167 |
586 | 168 static void file_data_set_path(FileData *fd, const gchar *path) |
169 { | |
790 | 170 g_assert(path /* && *path*/); /* view_dir_tree uses FileData with zero length path */ |
785 | 171 g_assert(file_data_pool); |
172 | |
173 g_free(fd->path); | |
174 | |
175 if (fd->original_path) | |
176 { | |
177 g_hash_table_remove(file_data_pool, fd->original_path); | |
178 g_free(fd->original_path); | |
179 } | |
180 fd->original_path = g_strdup(path); | |
181 g_hash_table_insert(file_data_pool, fd->original_path, fd); | |
586 | 182 |
725 | 183 if (strcmp(path, G_DIR_SEPARATOR_S) == 0) |
586 | 184 { |
185 fd->path = g_strdup(path); | |
186 fd->name = fd->path; | |
187 fd->extension = fd->name + 1; | |
785 | 188 file_data_set_collate_keys(fd); |
586 | 189 return; |
190 } | |
191 | |
192 fd->path = g_strdup(path); | |
193 fd->name = filename_from_path(fd->path); | |
194 | |
195 if (strcmp(fd->name, "..") == 0) | |
196 { | |
197 gchar *dir = remove_level_from_path(path); | |
198 g_free(fd->path); | |
199 fd->path = remove_level_from_path(dir); | |
200 g_free(dir); | |
201 fd->name = ".."; | |
202 fd->extension = fd->name + 2; | |
785 | 203 file_data_set_collate_keys(fd); |
586 | 204 return; |
205 } | |
206 else if (strcmp(fd->name, ".") == 0) | |
207 { | |
208 g_free(fd->path); | |
209 fd->path = remove_level_from_path(path); | |
210 fd->name = "."; | |
211 fd->extension = fd->name + 1; | |
785 | 212 file_data_set_collate_keys(fd); |
586 | 213 return; |
214 } | |
215 | |
216 fd->extension = extension_from_path(fd->path); | |
217 if (fd->extension == NULL) | |
218 fd->extension = fd->name + strlen(fd->name); | |
785 | 219 |
220 file_data_set_collate_keys(fd); | |
586 | 221 } |
222 | |
801 | 223 static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st) |
586 | 224 { |
801 | 225 gboolean ret = FALSE; |
586 | 226 GList *work; |
227 if (fd->size != st->st_size || | |
228 fd->date != st->st_mtime) | |
229 { | |
230 fd->size = st->st_size; | |
231 fd->date = st->st_mtime; | |
232 if (fd->pixbuf) g_object_unref(fd->pixbuf); | |
233 fd->pixbuf = NULL; | |
763
81f9e8dbb4bf
improved infrastructure for tracing changes, optimized vflist_populate_view
nadvornik
parents:
753
diff
changeset
|
234 file_data_increment_version(fd); |
800 | 235 file_data_send_notification(fd, NOTIFY_TYPE_REREAD); |
801 | 236 ret = TRUE; |
586 | 237 } |
238 | |
239 work = fd->sidecar_files; | |
240 while (work) | |
241 { | |
242 FileData *sfd = work->data; | |
243 struct stat st; | |
801 | 244 work = work->next; |
586 | 245 |
246 if (!stat_utf8(sfd->path, &st)) | |
247 { | |
801 | 248 fd->size = 0; |
249 fd->date = 0; | |
586 | 250 file_data_disconnect_sidecar_file(fd, sfd); |
801 | 251 ret = TRUE; |
252 continue; | |
586 | 253 } |
254 | |
801 | 255 ret |= file_data_check_changed_files_recursive(sfd, &st); |
586 | 256 } |
801 | 257 return ret; |
258 } | |
259 | |
260 | |
261 static gboolean file_data_check_changed_files(FileData *fd) | |
262 { | |
263 gboolean ret = FALSE; | |
264 struct stat st; | |
265 if (fd->parent) fd = fd->parent; | |
266 | |
267 if (!stat_utf8(fd->path, &st)) | |
268 { | |
269 /* parent is missing, we have to rebuild whole group */ | |
270 ret = TRUE; | |
271 fd->size = 0; | |
272 fd->date = 0; | |
273 GList *work = fd->sidecar_files; | |
274 FileData *sfd = NULL; | |
275 while (work) | |
276 { | |
277 sfd = work->data; | |
278 work = work->next; | |
279 | |
280 file_data_disconnect_sidecar_file(fd, sfd); | |
281 } | |
282 if (sfd) file_data_check_sidecars(sfd); /* this will group the sidecars back together */ | |
283 file_data_send_notification(fd, NOTIFY_TYPE_REREAD); | |
284 } | |
285 else | |
286 ret |= file_data_check_changed_files_recursive(fd, &st); | |
287 return ret; | |
586 | 288 } |
289 | |
290 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars) | |
291 { | |
292 FileData *fd; | |
293 | |
294 DEBUG_2("file_data_new: '%s' %d", path_utf8, check_sidecars); | |
295 | |
296 if (!file_data_pool) | |
297 file_data_pool = g_hash_table_new(g_str_hash, g_str_equal); | |
298 | |
299 fd = g_hash_table_lookup(file_data_pool, path_utf8); | |
300 if (fd) | |
301 { | |
801 | 302 gboolean changed; |
303 if (fd->parent) | |
304 changed = file_data_check_changed_files(fd); | |
305 else | |
306 changed = file_data_check_changed_files_recursive(fd, st); | |
307 if (changed && check_sidecars && sidecar_file_priority(fd->extension)) | |
308 file_data_check_sidecars(fd); | |
309 DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : ""); | |
586 | 310 return file_data_ref(fd); |
311 } | |
312 | |
313 fd = g_new0(FileData, 1); | |
785 | 314 |
315 fd->path = NULL; | |
316 fd->name = NULL; | |
317 fd->collate_key_name = NULL; | |
318 fd->collate_key_name_nocase = NULL; | |
319 fd->original_path = NULL; | |
586 | 320 |
321 fd->size = st->st_size; | |
322 fd->date = st->st_mtime; | |
323 fd->pixbuf = NULL; | |
324 fd->sidecar_files = NULL; | |
325 fd->ref = 1; | |
326 fd->magick = 0x12345678; | |
327 | |
785 | 328 file_data_set_path(fd, path_utf8); /* set path, name, collate_key_*, original_path */ |
586 | 329 |
330 if (check_sidecars && sidecar_file_priority(fd->extension)) | |
331 file_data_check_sidecars(fd); | |
785 | 332 |
586 | 333 return fd; |
334 } | |
335 | |
336 static void file_data_check_sidecars(FileData *fd) | |
337 { | |
338 int base_len = fd->extension - fd->path; | |
339 GString *fname = g_string_new_len(fd->path, base_len); | |
340 FileData *parent_fd = NULL; | |
341 GList *work = sidecar_ext_get_list(); | |
342 while (work) | |
343 { | |
344 /* check for possible sidecar files; | |
345 the sidecar files created here are referenced only via fd->sidecar_files or fd->parent, | |
346 they have fd->ref set to 0 and file_data unref must chack and free them all together | |
347 (using fd->ref would cause loops and leaks) | |
348 */ | |
349 | |
350 FileData *new_fd; | |
351 | |
352 gchar *ext = work->data; | |
353 work = work->next; | |
354 | |
355 if (strcmp(ext, fd->extension) == 0) | |
356 { | |
357 new_fd = fd; /* processing the original file */ | |
358 } | |
359 else | |
360 { | |
361 struct stat nst; | |
362 g_string_truncate(fname, base_len); | |
363 g_string_append(fname, ext); | |
364 | |
365 if (!stat_utf8(fname->str, &nst)) | |
366 continue; | |
367 | |
368 new_fd = file_data_new(fname->str, &nst, FALSE); | |
369 new_fd->ref--; /* do not use ref here */ | |
370 } | |
371 | |
372 if (!parent_fd) | |
373 parent_fd = new_fd; /* parent is the one with the highest prio, found first */ | |
374 else | |
375 file_data_merge_sidecar_files(parent_fd, new_fd); | |
376 } | |
377 g_string_free(fname, TRUE); | |
378 } | |
379 | |
380 | |
381 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars) | |
382 { | |
383 gchar *path_utf8 = path_to_utf8(path); | |
384 FileData *ret = file_data_new(path_utf8, st, check_sidecars); | |
385 g_free(path_utf8); | |
386 return ret; | |
387 } | |
388 | |
389 FileData *file_data_new_simple(const gchar *path_utf8) | |
390 { | |
391 struct stat st; | |
392 | |
393 if (!stat_utf8(path_utf8, &st)) | |
394 { | |
395 st.st_size = 0; | |
396 st.st_mtime = 0; | |
397 } | |
398 | |
399 return file_data_new(path_utf8, &st, TRUE); | |
400 } | |
401 | |
402 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd) | |
403 { | |
404 sfd->parent = target; | |
405 if(!g_list_find(target->sidecar_files, sfd)) | |
406 target->sidecar_files = g_list_prepend(target->sidecar_files, sfd); | |
801 | 407 file_data_increment_version(sfd); /* increments both sfd and target */ |
586 | 408 return target; |
409 } | |
410 | |
411 | |
412 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source) | |
413 { | |
414 GList *work; | |
415 file_data_add_sidecar_file(target, source); | |
416 | |
417 work = source->sidecar_files; | |
418 while (work) | |
419 { | |
420 FileData *sfd = work->data; | |
421 file_data_add_sidecar_file(target, sfd); | |
422 work = work->next; | |
423 } | |
424 | |
425 g_list_free(source->sidecar_files); | |
426 source->sidecar_files = NULL; | |
427 | |
428 target->sidecar_files = filelist_sort(target->sidecar_files, SORT_NAME, TRUE); | |
429 return target; | |
430 } | |
431 | |
432 | |
433 | |
434 FileData *file_data_ref(FileData *fd) | |
435 { | |
436 if (fd == NULL) return NULL; | |
437 | |
438 // return g_memdup(fd, sizeof(FileData)); | |
439 g_assert(fd->magick == 0x12345678); | |
440 fd->ref++; | |
441 return fd; | |
442 } | |
443 | |
444 static void file_data_free(FileData *fd) | |
445 { | |
446 g_assert(fd->magick == 0x12345678); | |
447 g_assert(fd->ref == 0); | |
448 | |
449 g_hash_table_remove(file_data_pool, fd->original_path); | |
450 | |
451 g_free(fd->path); | |
452 g_free(fd->original_path); | |
785 | 453 g_free(fd->collate_key_name); |
454 g_free(fd->collate_key_name_nocase); | |
586 | 455 if (fd->pixbuf) g_object_unref(fd->pixbuf); |
456 | |
457 | |
458 g_assert(fd->sidecar_files == NULL); /* sidecar files must be freed before calling this */ | |
459 | |
460 file_data_change_info_free(NULL, fd); | |
461 g_free(fd); | |
462 } | |
463 | |
464 void file_data_unref(FileData *fd) | |
465 { | |
466 if (fd == NULL) return; | |
467 g_assert(fd->magick == 0x12345678); | |
468 | |
469 fd->ref--; | |
470 DEBUG_2("file_data_unref (%d): '%s'", fd->ref, fd->path); | |
471 | |
472 if (fd->ref == 0) | |
473 { | |
474 FileData *parent = fd->parent ? fd->parent : fd; | |
475 | |
476 GList *work; | |
477 | |
478 if (parent->ref > 0) | |
479 return; | |
480 | |
481 work = parent->sidecar_files; | |
482 while (work) | |
483 { | |
484 FileData *sfd = work->data; | |
485 if (sfd->ref > 0) | |
486 return; | |
487 work = work->next; | |
488 } | |
489 | |
490 /* none of parent/children is referenced, we can free everything */ | |
491 | |
492 DEBUG_2("file_data_unref: deleting '%s', parent '%s'", fd->path, parent->path); | |
493 | |
494 work = parent->sidecar_files; | |
495 while (work) | |
496 { | |
497 FileData *sfd = work->data; | |
498 file_data_free(sfd); | |
499 work = work->next; | |
500 } | |
501 | |
502 g_list_free(parent->sidecar_files); | |
503 parent->sidecar_files = NULL; | |
504 | |
505 file_data_free(parent); | |
506 | |
507 } | |
508 } | |
509 | |
510 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd) | |
511 { | |
512 sfd->parent = target; | |
513 g_assert(g_list_find(target->sidecar_files, sfd)); | |
801 | 514 |
515 file_data_increment_version(sfd); /* increments both sfd and target */ | |
586 | 516 |
517 target->sidecar_files = g_list_remove(target->sidecar_files, sfd); | |
518 sfd->parent = NULL; | |
519 | |
520 if (sfd->ref == 0) { | |
521 file_data_free(sfd); | |
522 return NULL; | |
523 } | |
524 | |
525 return sfd; | |
526 } | |
527 | |
528 /* compare name without extension */ | |
529 gint file_data_compare_name_without_ext(FileData *fd1, FileData *fd2) | |
530 { | |
531 size_t len1 = fd1->extension - fd1->name; | |
532 size_t len2 = fd2->extension - fd2->name; | |
533 | |
534 if (len1 < len2) return -1; | |
535 if (len1 > len2) return 1; | |
536 | |
785 | 537 return strncmp(fd1->name, fd2->name, len1); /* FIXME: utf8 */ |
586 | 538 } |
539 | |
540 gboolean file_data_add_change_info(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest) | |
541 { | |
542 | |
543 FileDataChangeInfo *fdci; | |
544 | |
545 if (fd->change) return FALSE; | |
546 | |
547 fdci = g_new0(FileDataChangeInfo, 1); | |
548 | |
549 fdci->type = type; | |
550 | |
551 if (src) | |
552 fdci->source = g_strdup(src); | |
553 else | |
554 fdci->source = g_strdup(fd->path); | |
555 | |
556 if (dest) | |
557 fdci->dest = g_strdup(dest); | |
558 | |
559 fd->change = fdci; | |
560 return TRUE; | |
561 } | |
562 | |
563 void file_data_change_info_free(FileDataChangeInfo *fdci, FileData *fd) | |
564 { | |
565 if (!fdci && fd) | |
566 fdci = fd->change; | |
567 | |
568 if (!fdci) | |
569 return; | |
570 | |
571 g_free(fdci->source); | |
572 g_free(fdci->dest); | |
573 | |
574 g_free(fdci); | |
575 | |
576 if (fd) | |
577 fd->change = NULL; | |
578 } | |
579 | |
580 | |
581 | |
582 | |
583 /* | |
584 *----------------------------------------------------------------------------- | |
585 * sidecar file info struct | |
586 *----------------------------------------------------------------------------- | |
587 */ | |
588 | |
589 | |
590 | |
591 static gint sidecar_file_priority(const gchar *path) | |
592 { | |
593 const char *extension = extension_from_path(path); | |
594 int i = 1; | |
595 GList *work; | |
596 if (extension == NULL) | |
597 return 0; | |
598 | |
599 work = sidecar_ext_get_list(); | |
600 | |
601 while (work) { | |
602 gchar *ext = work->data; | |
603 work = work->next; | |
604 if (strcmp(extension, ext) == 0) return i; | |
605 i++; | |
606 } | |
607 return 0; | |
608 } | |
609 | |
610 | |
611 /* | |
612 *----------------------------------------------------------------------------- | |
613 * load file list | |
614 *----------------------------------------------------------------------------- | |
615 */ | |
616 | |
617 static SortType filelist_sort_method = SORT_NONE; | |
618 static gint filelist_sort_ascend = TRUE; | |
619 | |
620 | |
621 gint filelist_sort_compare_filedata(FileData *fa, FileData *fb) | |
622 { | |
623 if (!filelist_sort_ascend) | |
624 { | |
625 FileData *tmp = fa; | |
626 fa = fb; | |
627 fb = tmp; | |
628 } | |
629 | |
630 switch (filelist_sort_method) | |
631 { | |
785 | 632 case SORT_NAME: |
633 break; | |
586 | 634 case SORT_SIZE: |
635 if (fa->size < fb->size) return -1; | |
636 if (fa->size > fb->size) return 1; | |
785 | 637 /* fall back to name */ |
586 | 638 break; |
639 case SORT_TIME: | |
640 if (fa->date < fb->date) return -1; | |
641 if (fa->date > fb->date) return 1; | |
785 | 642 /* fall back to name */ |
586 | 643 break; |
644 #ifdef HAVE_STRVERSCMP | |
645 case SORT_NUMBER: | |
646 return strverscmp(fa->name, fb->name); | |
647 break; | |
648 #endif | |
649 default: | |
650 break; | |
651 } | |
785 | 652 |
653 if (options->file_sort.case_sensitive) | |
654 return strcmp(fa->collate_key_name, fb->collate_key_name); | |
655 else | |
656 return strcmp(fa->collate_key_name_nocase, fb->collate_key_name_nocase); | |
586 | 657 } |
658 | |
659 gint filelist_sort_compare_filedata_full(FileData *fa, FileData *fb, SortType method, gint ascend) | |
660 { | |
661 filelist_sort_method = method; | |
662 filelist_sort_ascend = ascend; | |
663 return filelist_sort_compare_filedata(fa, fb); | |
664 } | |
665 | |
666 static gint filelist_sort_file_cb(void *a, void *b) | |
667 { | |
668 return filelist_sort_compare_filedata(a, b); | |
669 } | |
670 | |
671 GList *filelist_sort_full(GList *list, SortType method, gint ascend, GCompareFunc cb) | |
672 { | |
673 filelist_sort_method = method; | |
674 filelist_sort_ascend = ascend; | |
675 return g_list_sort(list, cb); | |
676 } | |
677 | |
678 GList *filelist_insert_sort_full(GList *list, void *data, SortType method, gint ascend, GCompareFunc cb) | |
679 { | |
680 filelist_sort_method = method; | |
681 filelist_sort_ascend = ascend; | |
682 return g_list_insert_sorted(list, data, cb); | |
683 } | |
684 | |
685 GList *filelist_sort(GList *list, SortType method, gint ascend) | |
686 { | |
687 return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb); | |
688 } | |
689 | |
690 GList *filelist_insert_sort(GList *list, FileData *fd, SortType method, gint ascend) | |
691 { | |
692 return filelist_insert_sort_full(list, fd, method, ascend, (GCompareFunc) filelist_sort_file_cb); | |
693 } | |
694 | |
695 | |
696 static GList *filelist_filter_out_sidecars(GList *flist) | |
697 { | |
698 GList *work = flist; | |
699 GList *flist_filtered = NULL; | |
700 | |
701 while (work) | |
702 { | |
703 FileData *fd = work->data; | |
704 work = work->next; | |
705 if (fd->parent) /* remove fd's that are children */ | |
706 file_data_unref(fd); | |
707 else | |
708 flist_filtered = g_list_prepend(flist_filtered, fd); | |
709 } | |
710 g_list_free(flist); | |
711 return flist_filtered; | |
712 } | |
713 | |
783 | 714 static gint filelist_read_real(FileData *dir_fd, GList **files, GList **dirs, gint follow_symlinks) |
586 | 715 { |
716 DIR *dp; | |
717 struct dirent *dir; | |
718 gchar *pathl; | |
779 | 719 GList *dlist = NULL; |
720 GList *flist = NULL; | |
721 int (*stat_func)(const char *path, struct stat *buf); | |
586 | 722 |
779 | 723 g_assert(files || dirs); |
724 | |
725 if (files) *files = NULL; | |
726 if (dirs) *dirs = NULL; | |
586 | 727 |
783 | 728 pathl = path_from_utf8(dir_fd->path); |
779 | 729 if (!pathl) return FALSE; |
730 | |
731 dp = opendir(pathl); | |
732 if (dp == NULL) | |
586 | 733 { |
734 g_free(pathl); | |
735 return FALSE; | |
736 } | |
737 | |
779 | 738 if (follow_symlinks) |
739 stat_func = stat; | |
740 else | |
741 stat_func = lstat; | |
742 | |
586 | 743 while ((dir = readdir(dp)) != NULL) |
744 { | |
779 | 745 struct stat ent_sbuf; |
746 const gchar *name = dir->d_name; | |
747 gchar *filepath; | |
748 | |
749 if (!options->file_filter.show_hidden_files && ishidden(name)) | |
750 continue; | |
751 | |
752 filepath = g_build_filename(pathl, name, NULL); | |
753 if (stat_func(filepath, &ent_sbuf) >= 0) | |
586 | 754 { |
779 | 755 if (S_ISDIR(ent_sbuf.st_mode)) |
586 | 756 { |
779 | 757 /* we ignore the .thumbnails dir for cleanliness */ |
758 if (dirs && | |
759 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) && | |
760 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 && | |
761 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 && | |
762 strcmp(name, THUMB_FOLDER_LOCAL) != 0) | |
586 | 763 { |
779 | 764 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE)); |
586 | 765 } |
766 } | |
779 | 767 else |
768 { | |
769 if (files && filter_name_exists(name)) | |
770 { | |
771 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE)); | |
772 } | |
773 } | |
586 | 774 } |
779 | 775 g_free(filepath); |
586 | 776 } |
777 | |
778 closedir(dp); | |
779 | 779 |
586 | 780 g_free(pathl); |
781 | |
782 if (dirs) *dirs = dlist; | |
779 | 783 if (files) *files = filelist_filter_out_sidecars(flist); |
586 | 784 |
785 return TRUE; | |
786 } | |
787 | |
783 | 788 gint filelist_read(FileData *dir_fd, GList **files, GList **dirs) |
586 | 789 { |
783 | 790 return filelist_read_real(dir_fd, files, dirs, TRUE); |
586 | 791 } |
792 | |
783 | 793 gint filelist_read_lstat(FileData *dir_fd, GList **files, GList **dirs) |
586 | 794 { |
783 | 795 return filelist_read_real(dir_fd, files, dirs, FALSE); |
586 | 796 } |
797 | |
798 void filelist_free(GList *list) | |
799 { | |
800 GList *work; | |
801 | |
802 work = list; | |
803 while (work) | |
804 { | |
805 file_data_unref((FileData *)work->data); | |
806 work = work->next; | |
807 } | |
808 | |
809 g_list_free(list); | |
810 } | |
811 | |
812 | |
813 GList *filelist_copy(GList *list) | |
814 { | |
815 GList *new_list = NULL; | |
816 GList *work; | |
817 | |
818 work = list; | |
819 while (work) | |
820 { | |
821 FileData *fd; | |
822 | |
823 fd = work->data; | |
824 work = work->next; | |
825 | |
826 new_list = g_list_prepend(new_list, file_data_ref(fd)); | |
827 } | |
828 | |
829 return g_list_reverse(new_list); | |
830 } | |
831 | |
832 GList *filelist_from_path_list(GList *list) | |
833 { | |
834 GList *new_list = NULL; | |
835 GList *work; | |
836 | |
837 work = list; | |
838 while (work) | |
839 { | |
840 gchar *path; | |
841 | |
842 path = work->data; | |
843 work = work->next; | |
844 | |
845 new_list = g_list_prepend(new_list, file_data_new_simple(path)); | |
846 } | |
847 | |
848 return g_list_reverse(new_list); | |
849 } | |
850 | |
851 GList *filelist_to_path_list(GList *list) | |
852 { | |
853 GList *new_list = NULL; | |
854 GList *work; | |
855 | |
856 work = list; | |
857 while (work) | |
858 { | |
859 FileData *fd; | |
860 | |
861 fd = work->data; | |
862 work = work->next; | |
863 | |
864 new_list = g_list_prepend(new_list, g_strdup(fd->path)); | |
865 } | |
866 | |
867 return g_list_reverse(new_list); | |
868 } | |
869 | |
870 GList *filelist_filter(GList *list, gint is_dir_list) | |
871 { | |
872 GList *work; | |
873 | |
874 if (!is_dir_list && options->file_filter.disable && options->file_filter.show_hidden_files) return list; | |
875 | |
876 work = list; | |
877 while (work) | |
878 { | |
879 FileData *fd = (FileData *)(work->data); | |
880 const gchar *name = fd->name; | |
881 | |
882 if ((!options->file_filter.show_hidden_files && ishidden(name)) || | |
883 (!is_dir_list && !filter_name_exists(name)) || | |
884 (is_dir_list && name[0] == '.' && (strcmp(name, GQ_CACHE_LOCAL_THUMB) == 0 || | |
885 strcmp(name, GQ_CACHE_LOCAL_METADATA) == 0)) ) | |
886 { | |
887 GList *link = work; | |
888 work = work->next; | |
889 list = g_list_remove_link(list, link); | |
890 file_data_unref(fd); | |
891 g_list_free(link); | |
892 } | |
893 else | |
894 { | |
895 work = work->next; | |
896 } | |
897 } | |
898 | |
899 return list; | |
900 } | |
901 | |
902 /* | |
903 *----------------------------------------------------------------------------- | |
904 * filelist recursive | |
905 *----------------------------------------------------------------------------- | |
906 */ | |
907 | |
908 static gint filelist_sort_path_cb(gconstpointer a, gconstpointer b) | |
909 { | |
910 return CASE_SORT(((FileData *)a)->path, ((FileData *)b)->path); | |
911 } | |
912 | |
913 GList *filelist_sort_path(GList *list) | |
914 { | |
915 return g_list_sort(list, filelist_sort_path_cb); | |
916 } | |
917 | |
918 static void filelist_recursive_append(GList **list, GList *dirs) | |
919 { | |
920 GList *work; | |
921 | |
922 work = dirs; | |
923 while (work) | |
924 { | |
925 FileData *fd = (FileData *)(work->data); | |
780
44128da39e13
Drop initialization to NULL since filelist_read() will take care of it.
zas_
parents:
779
diff
changeset
|
926 GList *f; |
44128da39e13
Drop initialization to NULL since filelist_read() will take care of it.
zas_
parents:
779
diff
changeset
|
927 GList *d; |
586 | 928 |
783 | 929 if (filelist_read(fd, &f, &d)) |
586 | 930 { |
931 f = filelist_filter(f, FALSE); | |
932 f = filelist_sort_path(f); | |
933 *list = g_list_concat(*list, f); | |
934 | |
935 d = filelist_filter(d, TRUE); | |
936 d = filelist_sort_path(d); | |
937 filelist_recursive_append(list, d); | |
938 filelist_free(d); | |
939 } | |
940 | |
941 work = work->next; | |
942 } | |
943 } | |
944 | |
783 | 945 GList *filelist_recursive(FileData *dir_fd) |
586 | 946 { |
780
44128da39e13
Drop initialization to NULL since filelist_read() will take care of it.
zas_
parents:
779
diff
changeset
|
947 GList *list; |
44128da39e13
Drop initialization to NULL since filelist_read() will take care of it.
zas_
parents:
779
diff
changeset
|
948 GList *d; |
586 | 949 |
783 | 950 if (!filelist_read(dir_fd, &list, &d)) return NULL; |
586 | 951 list = filelist_filter(list, FALSE); |
952 list = filelist_sort_path(list); | |
953 | |
954 d = filelist_filter(d, TRUE); | |
955 d = filelist_sort_path(d); | |
956 filelist_recursive_append(&list, d); | |
957 filelist_free(d); | |
958 | |
959 return list; | |
960 } | |
590 | 961 |
962 | |
800 | 963 /* |
964 * marks and orientation | |
965 */ | |
966 | |
967 | |
968 gboolean file_data_get_mark(FileData *fd, gint n) | |
969 { | |
970 return !!(fd->marks & (1 << n)); | |
971 } | |
972 | |
973 void file_data_set_mark(FileData *fd, gint n, gboolean value) | |
974 { | |
975 if (!value == !(fd->marks & (1 << n))) return; | |
976 | |
977 fd->marks = fd->marks ^ (1 << n); | |
978 file_data_increment_version(fd); | |
979 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL); | |
980 | |
981 } | |
982 | |
983 gint file_data_get_user_orientation(FileData *fd) | |
984 { | |
985 return fd->user_orientation; | |
986 } | |
987 | |
988 void file_data_set_user_orientation(FileData *fd, gint value) | |
989 { | |
990 if (fd->user_orientation == value) return; | |
991 | |
992 fd->user_orientation = value; | |
993 file_data_increment_version(fd); | |
994 file_data_send_notification(fd, NOTIFY_TYPE_INTERNAL); | |
995 } | |
996 | |
997 | |
590 | 998 |
999 /* | |
1000 * file_data - operates on the given fd | |
1001 * file_data_sc - operates on the given fd + sidecars - all fds linked via fd->sidecar_files or fd->parent | |
1002 */ | |
1003 | |
1004 | |
1005 /* return list of sidecar file extensions in a string */ | |
596 | 1006 gchar *file_data_sc_list_to_string(FileData *fd) |
1007 { | |
1008 GList *work; | |
1009 GString *result = g_string_new(""); | |
1010 | |
1011 work = fd->sidecar_files; | |
1012 while (work) | |
1013 { | |
1014 FileData *sfd = work->data; | |
1015 result = g_string_append(result, "+ "); | |
1016 result = g_string_append(result, sfd->extension); | |
1017 work = work->next; | |
1018 if (work) result = g_string_append_c(result, ' '); | |
1019 } | |
1020 | |
1021 return g_string_free(result, FALSE); | |
1022 } | |
590 | 1023 |
1024 | |
1025 /* disables / enables grouping for particular file, sends UPDATE notification */ | |
1026 void file_data_disable_grouping(FileData *fd); // now file_data_disconnect_sidecar_file, broken | |
1027 void file_data_disable_grouping(FileData *fd); | |
1028 | |
1029 /* runs stat on a file and sends UPDATE notification if it has been changed */ | |
1030 void file_data_sc_update(FileData *fd); | |
1031 | |
1032 | |
1033 | |
1034 | |
1035 /* | |
1036 * add FileDataChangeInfo (see typedefs.h) for the given operation | |
1037 * uses file_data_add_change_info | |
1038 * | |
1039 * fails if the fd->change already exists - change operations can't run in parallel | |
1040 * fd->change_info works as a lock | |
1041 * | |
1042 * dest can be NULL - in this case the current name is used for now, it will | |
1043 * be changed later | |
1044 */ | |
1045 | |
1046 /* | |
1047 FileDataChangeInfo types: | |
1048 COPY | |
1049 MOVE - patch is changed, name may be changed too | |
1050 RENAME - path remains unchanged, name is changed | |
1051 extension should remain (FIXME should we allow editing extension? it will make problems wth grouping) | |
1052 sidecar names are changed too, extensions are not changed | |
1053 DELETE | |
1054 UPDATE - file size, date or grouping has been changed | |
1055 */ | |
1056 | |
1057 gboolean file_data_add_ci(FileData *fd, FileDataChangeType type, const gchar *src, const gchar *dest) | |
1058 { | |
1059 | |
1060 FileDataChangeInfo *fdci; | |
1061 | |
1062 if (fd->change) return FALSE; | |
1063 | |
1064 fdci = g_new0(FileDataChangeInfo, 1); | |
1065 | |
1066 fdci->type = type; | |
1067 | |
1068 if (src) | |
1069 fdci->source = g_strdup(src); | |
1070 else | |
1071 fdci->source = g_strdup(fd->path); | |
1072 | |
1073 if (dest) | |
1074 fdci->dest = g_strdup(dest); | |
1075 | |
1076 fd->change = fdci; | |
1077 | |
1078 return TRUE; | |
1079 } | |
1080 | |
1081 void file_data_free_ci(FileData *fd) | |
1082 { | |
1083 FileDataChangeInfo *fdci = fd->change; | |
1084 | |
1085 if (!fdci) | |
1086 return; | |
1087 | |
1088 g_free(fdci->source); | |
1089 g_free(fdci->dest); | |
1090 | |
1091 g_free(fdci); | |
1092 | |
1093 fd->change = NULL; | |
1094 } | |
1095 | |
1096 | |
1097 static gboolean file_data_sc_add_ci(FileData *fd, FileDataChangeType type) | |
1098 { | |
1099 GList *work; | |
1100 if (fd->parent) fd = fd->parent; | |
1101 | |
1102 if (fd->change) return FALSE; | |
1103 work = fd->sidecar_files; | |
1104 while (work) | |
1105 { | |
1106 FileData *sfd = work->data; | |
1107 if (sfd->change) return FALSE; | |
1108 work = work->next; | |
1109 } | |
1110 | |
1111 file_data_add_ci(fd, type, NULL, NULL); | |
1112 | |
1113 work = fd->sidecar_files; | |
1114 while (work) | |
1115 { | |
1116 FileData *sfd = work->data; | |
1117 file_data_add_ci(sfd, type, NULL, NULL); | |
1118 work = work->next; | |
1119 } | |
1120 | |
1121 return TRUE; | |
1122 } | |
1123 | |
1124 static gboolean file_data_sc_check_ci(FileData *fd, FileDataChangeType type) | |
1125 { | |
1126 GList *work; | |
1127 if (fd->parent) fd = fd->parent; | |
1128 | |
1129 if (!fd->change) return FALSE; | |
1130 if (fd->change->type != type) return FALSE; | |
1131 work = fd->sidecar_files; | |
1132 while (work) | |
1133 { | |
1134 FileData *sfd = work->data; | |
1135 if (!sfd->change) return FALSE; | |
1136 if (sfd->change->type != type) return FALSE; | |
1137 work = work->next; | |
1138 } | |
1139 return TRUE; | |
1140 } | |
1141 | |
1142 | |
751 | 1143 gboolean file_data_sc_add_ci_copy(FileData *fd, const gchar *dest_path) |
590 | 1144 { |
1145 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE; | |
1146 file_data_sc_update_ci_copy(fd, dest_path); | |
1147 return TRUE; | |
1148 } | |
1149 | |
751 | 1150 gboolean file_data_sc_add_ci_move(FileData *fd, const gchar *dest_path) |
590 | 1151 { |
1152 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE; | |
1153 file_data_sc_update_ci_move(fd, dest_path); | |
1154 return TRUE; | |
1155 } | |
1156 | |
751 | 1157 gboolean file_data_sc_add_ci_rename(FileData *fd, const gchar *dest_path) |
590 | 1158 { |
1159 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE; | |
1160 file_data_sc_update_ci_rename(fd, dest_path); | |
1161 return TRUE; | |
1162 } | |
1163 | |
1164 gboolean file_data_sc_add_ci_delete(FileData *fd) | |
1165 { | |
1166 return file_data_sc_add_ci(fd, FILEDATA_CHANGE_DELETE); | |
1167 } | |
1168 | |
753 | 1169 gboolean file_data_sc_add_ci_unspecified(FileData *fd, const gchar *dest_path) |
590 | 1170 { |
753 | 1171 if (!file_data_sc_add_ci(fd, FILEDATA_CHANGE_UNSPECIFIED)) return FALSE; |
1172 file_data_sc_update_ci_unspecified(fd, dest_path); | |
1173 return TRUE; | |
590 | 1174 } |
1175 | |
1176 void file_data_sc_free_ci(FileData *fd) | |
1177 { | |
1178 GList *work; | |
1179 if (fd->parent) fd = fd->parent; | |
1180 | |
1181 file_data_free_ci(fd); | |
1182 | |
1183 work = fd->sidecar_files; | |
1184 while (work) | |
1185 { | |
1186 FileData *sfd = work->data; | |
1187 file_data_free_ci(sfd); | |
1188 work = work->next; | |
1189 } | |
1190 } | |
1191 | |
751 | 1192 gboolean file_data_sc_add_ci_delete_list(GList *fd_list) |
1193 { | |
1194 GList *work; | |
1195 gboolean ret = TRUE; | |
1196 work = fd_list; | |
1197 while (work) | |
1198 { | |
1199 FileData *fd = work->data; | |
1200 if (!file_data_sc_add_ci_delete(fd)) ret = FALSE; | |
1201 work = work->next; | |
1202 } | |
1203 return ret; | |
1204 } | |
1205 | |
1206 gboolean file_data_sc_add_ci_copy_list(GList *fd_list, const gchar *dest) | |
1207 { | |
1208 GList *work; | |
1209 gboolean ret = TRUE; | |
1210 work = fd_list; | |
1211 while (work) | |
1212 { | |
1213 FileData *fd = work->data; | |
1214 if (!file_data_sc_add_ci_copy(fd, dest)) ret = FALSE; | |
1215 work = work->next; | |
1216 } | |
1217 return ret; | |
1218 } | |
1219 | |
1220 gboolean file_data_sc_add_ci_move_list(GList *fd_list, const gchar *dest) | |
1221 { | |
1222 GList *work; | |
1223 gboolean ret = TRUE; | |
1224 work = fd_list; | |
1225 while (work) | |
1226 { | |
1227 FileData *fd = work->data; | |
1228 if (!file_data_sc_add_ci_move(fd, dest)) ret = FALSE; | |
1229 work = work->next; | |
1230 } | |
1231 return ret; | |
1232 } | |
1233 | |
1234 gboolean file_data_sc_add_ci_rename_list(GList *fd_list, const gchar *dest) | |
1235 { | |
1236 GList *work; | |
1237 gboolean ret = TRUE; | |
1238 work = fd_list; | |
1239 while (work) | |
1240 { | |
1241 FileData *fd = work->data; | |
1242 if (!file_data_sc_add_ci_rename(fd, dest)) ret = FALSE; | |
1243 work = work->next; | |
1244 } | |
1245 return ret; | |
1246 } | |
1247 | |
753 | 1248 gboolean file_data_sc_add_ci_unspecified_list(GList *fd_list, const gchar *dest) |
1249 { | |
1250 GList *work; | |
1251 gboolean ret = TRUE; | |
1252 work = fd_list; | |
1253 while (work) | |
1254 { | |
1255 FileData *fd = work->data; | |
1256 if (!file_data_sc_add_ci_unspecified(fd, dest)) ret = FALSE; | |
1257 work = work->next; | |
1258 } | |
1259 return ret; | |
1260 } | |
1261 | |
751 | 1262 void file_data_sc_free_ci_list(GList *fd_list) |
1263 { | |
1264 GList *work; | |
1265 work = fd_list; | |
1266 while (work) | |
1267 { | |
1268 FileData *fd = work->data; | |
1269 file_data_sc_free_ci(fd); | |
1270 work = work->next; | |
1271 } | |
1272 } | |
590 | 1273 |
1274 /* | |
1275 * update existing fd->change, it will be used from dialog callbacks for interactive editing | |
1276 * fails if fd->change does not exist or the change type does not match | |
1277 */ | |
1278 | |
751 | 1279 static void file_data_update_ci_dest(FileData *fd, const gchar *dest_path) |
590 | 1280 { |
1281 g_free(fd->change->dest); | |
1282 fd->change->dest = g_strdup(dest_path); | |
1283 } | |
1284 | |
751 | 1285 static void file_data_update_ci_dest_preserve_ext(FileData *fd, const gchar *dest_path) |
590 | 1286 { |
1287 const char *extension = extension_from_path(fd->change->source); | |
751 | 1288 gchar *base = remove_extension_from_path(dest_path); |
590 | 1289 g_free(fd->change->dest); |
751 | 1290 fd->change->dest = g_strdup_printf("%s%s", base, extension); |
1291 g_free(base); | |
590 | 1292 } |
1293 | |
751 | 1294 static void file_data_sc_update_ci(FileData *fd, const gchar *dest_path) |
590 | 1295 { |
1296 GList *work; | |
751 | 1297 gchar *dest_path_full = NULL; |
590 | 1298 if (fd->parent) fd = fd->parent; |
1299 | |
751 | 1300 if (!dest_path) dest_path = fd->path; |
1301 | |
1302 if (!strchr(dest_path, G_DIR_SEPARATOR)) /* we got only filename, not a full path */ | |
1303 { | |
1304 gchar *dir = remove_level_from_path(fd->path); | |
1305 dest_path_full = g_build_filename(dir, dest_path, NULL); | |
1306 g_free(dir); | |
1307 dest_path = dest_path_full; | |
1308 } | |
1309 | |
1310 if (isdir(dest_path)) | |
1311 { | |
1312 dest_path_full = g_build_filename(dest_path, fd->name, NULL); | |
1313 dest_path = dest_path_full; | |
1314 } | |
1315 | |
1316 | |
590 | 1317 file_data_update_ci_dest(fd, dest_path); |
1318 work = fd->sidecar_files; | |
1319 while (work) | |
1320 { | |
1321 FileData *sfd = work->data; | |
1322 file_data_update_ci_dest_preserve_ext(sfd, dest_path); | |
1323 work = work->next; | |
1324 } | |
751 | 1325 g_free(dest_path_full); |
590 | 1326 } |
1327 | |
751 | 1328 gint file_data_sc_update_ci_copy(FileData *fd, const gchar *dest_path) |
590 | 1329 { |
1330 if (!file_data_sc_check_ci(fd, FILEDATA_CHANGE_COPY)) return FALSE; | |
1331 file_data_sc_update_ci(fd, dest_path); | |
1332 return TRUE; | |
1333 } | |
1334 | |
751 | 1335 gint file_data_sc_update_ci_move(FileData *fd, const gchar *dest_path) |
590 | 1336 { |
1337 if (!file_data_sc_check_ci(fd, FILEDATA_CHANGE_MOVE)) return FALSE; | |
1338 file_data_sc_update_ci(fd, dest_path); | |
1339 return TRUE; | |
1340 } | |
1341 | |
751 | 1342 gint file_data_sc_update_ci_rename(FileData *fd, const gchar *dest_path) |
590 | 1343 { |
1344 if (!file_data_sc_check_ci(fd, FILEDATA_CHANGE_RENAME)) return FALSE; | |
1345 file_data_sc_update_ci(fd, dest_path); | |
1346 return TRUE; | |
1347 } | |
1348 | |
753 | 1349 gint file_data_sc_update_ci_unspecified(FileData *fd, const gchar *dest_path) |
1350 { | |
1351 if (!file_data_sc_check_ci(fd, FILEDATA_CHANGE_UNSPECIFIED)) return FALSE; | |
1352 file_data_sc_update_ci(fd, dest_path); | |
1353 return TRUE; | |
1354 } | |
1355 | |
590 | 1356 |
751 | 1357 gboolean file_data_sc_update_ci_move_list(GList *fd_list, const gchar *dest) |
1358 { | |
1359 GList *work; | |
1360 gboolean ret = TRUE; | |
1361 work = fd_list; | |
1362 while (work) | |
1363 { | |
1364 FileData *fd = work->data; | |
1365 if (!file_data_sc_update_ci_move(fd, dest)) ret = FALSE; | |
1366 work = work->next; | |
1367 } | |
1368 return ret; | |
1369 } | |
1370 | |
1371 gboolean file_data_sc_update_ci_copy_list(GList *fd_list, const gchar *dest) | |
1372 { | |
1373 GList *work; | |
1374 gboolean ret = TRUE; | |
1375 work = fd_list; | |
1376 while (work) | |
1377 { | |
1378 FileData *fd = work->data; | |
1379 if (!file_data_sc_update_ci_copy(fd, dest)) ret = FALSE; | |
1380 work = work->next; | |
1381 } | |
1382 return ret; | |
1383 } | |
1384 | |
753 | 1385 gboolean file_data_sc_update_ci_unspecified_list(GList *fd_list, const gchar *dest) |
1386 { | |
1387 GList *work; | |
1388 gboolean ret = TRUE; | |
1389 work = fd_list; | |
1390 while (work) | |
1391 { | |
1392 FileData *fd = work->data; | |
1393 if (!file_data_sc_update_ci_unspecified(fd, dest)) ret = FALSE; | |
1394 work = work->next; | |
1395 } | |
1396 return ret; | |
1397 } | |
1398 | |
590 | 1399 |
1400 /* | |
1401 * check dest paths - dest image exists, etc. | |
1402 * returns FIXME | |
1403 * it should detect all possible problems with the planned operation | |
1404 */ | |
1405 | |
1406 gint file_data_sc_check_ci_dest(FileData *fd) | |
1407 { | |
1408 } | |
1409 | |
1410 | |
1411 | |
1412 | |
1413 /* | |
1414 * perform the change described by FileFataChangeInfo | |
1415 * it is used for internal operations, | |
1416 * this function actually operates with files on the filesystem | |
1417 * it should implement safe delete | |
1418 */ | |
1419 | |
1420 static gboolean file_data_perform_move(FileData *fd) | |
1421 { | |
1422 g_assert(!strcmp(fd->change->source, fd->path)); | |
1423 return move_file(fd->change->source, fd->change->dest); | |
1424 } | |
1425 | |
1426 static gboolean file_data_perform_copy(FileData *fd) | |
1427 { | |
1428 g_assert(!strcmp(fd->change->source, fd->path)); | |
1429 return copy_file(fd->change->source, fd->change->dest); | |
1430 } | |
1431 | |
1432 static gboolean file_data_perform_delete(FileData *fd) | |
1433 { | |
1434 return unlink_file(fd->path); | |
1435 } | |
1436 | |
1437 static gboolean file_data_perform_ci(FileData *fd) | |
1438 { | |
1439 FileDataChangeType type = fd->change->type; | |
1440 switch (type) | |
1441 { | |
1442 case FILEDATA_CHANGE_MOVE: | |
1443 return file_data_perform_move(fd); | |
1444 case FILEDATA_CHANGE_COPY: | |
1445 return file_data_perform_copy(fd); | |
1446 case FILEDATA_CHANGE_RENAME: | |
1447 return file_data_perform_move(fd); /* the same as move */ | |
1448 case FILEDATA_CHANGE_DELETE: | |
1449 return file_data_perform_delete(fd); | |
753 | 1450 case FILEDATA_CHANGE_UNSPECIFIED: |
596 | 1451 /* nothing to do here */ |
590 | 1452 break; |
1453 } | |
1454 return TRUE; | |
1455 } | |
1456 | |
1457 | |
1458 | |
1459 gboolean file_data_sc_perform_ci(FileData *fd) | |
1460 { | |
1461 GList *work; | |
1462 gboolean ret = TRUE; | |
1463 FileDataChangeType type = fd->change->type; | |
1464 if (!file_data_sc_check_ci(fd, type)) return FALSE; | |
1465 | |
1466 work = fd->sidecar_files; | |
1467 while (work) | |
1468 { | |
1469 FileData *sfd = work->data; | |
1470 if (!file_data_perform_ci(sfd)) ret = FALSE; | |
1471 work = work->next; | |
1472 } | |
1473 if (!file_data_perform_ci(fd)) ret = FALSE; | |
1474 return ret; | |
1475 } | |
1476 | |
1477 /* | |
1478 * updates FileData structure according to FileDataChangeInfo | |
1479 */ | |
1480 | |
1481 static void file_data_apply_ci(FileData *fd) | |
1482 { | |
1483 FileDataChangeType type = fd->change->type; | |
1484 /* FIXME delete ?*/ | |
773 | 1485 if (type == FILEDATA_CHANGE_MOVE || type == FILEDATA_CHANGE_RENAME) |
590 | 1486 { |
1487 file_data_set_path(fd, fd->change->dest); | |
1488 } | |
763
81f9e8dbb4bf
improved infrastructure for tracing changes, optimized vflist_populate_view
nadvornik
parents:
753
diff
changeset
|
1489 file_data_increment_version(fd); |
800 | 1490 file_data_send_notification(fd, NOTIFY_TYPE_CHANGE); |
590 | 1491 } |
1492 | |
596 | 1493 gint file_data_sc_apply_ci(FileData *fd) |
590 | 1494 { |
1495 GList *work; | |
1496 FileDataChangeType type = fd->change->type; | |
1497 if (!file_data_sc_check_ci(fd, type)) return FALSE; | |
1498 | |
1499 work = fd->sidecar_files; | |
1500 while (work) | |
1501 { | |
1502 FileData *sfd = work->data; | |
1503 file_data_apply_ci(sfd); | |
1504 work = work->next; | |
1505 } | |
1506 file_data_apply_ci(fd); | |
1507 return TRUE; | |
1508 } | |
1509 | |
1510 | |
1511 /* | |
1512 * notify other modules about the change described by FileFataChangeInfo | |
1513 */ | |
1514 | |
1515 /* might use file_maint_ functions for now, later it should be changed to a system of callbacks | |
1516 FIXME do we need the ignore_list? It looks like a workaround for ineffective | |
1517 implementation in view_file_list.c */ | |
1518 | |
784
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1519 |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1520 |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1521 |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1522 typedef struct _NotifyData NotifyData; |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1523 |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1524 struct _NotifyData { |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1525 FileDataNotifyFunc func; |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1526 gpointer data; |
791 | 1527 NotifyPriority priority; |
784
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1528 }; |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1529 |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1530 static GList *notify_func_list = NULL; |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1531 |
791 | 1532 static gint file_data_notify_sort(gconstpointer a, gconstpointer b) |
1533 { | |
1534 NotifyData *nda = (NotifyData *)a; | |
1535 NotifyData *ndb = (NotifyData *)b; | |
1536 if (nda->priority < ndb->priority) return -1; | |
1537 if (nda->priority > ndb->priority) return 1; | |
1538 return 0; | |
1539 } | |
1540 | |
1541 gint file_data_register_notify_func(FileDataNotifyFunc func, gpointer data, NotifyPriority priority) | |
784
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1542 { |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1543 NotifyData *nd = g_new(NotifyData, 1); |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1544 nd->func = func; |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1545 nd->data = data; |
791 | 1546 nd->priority = priority; |
1547 notify_func_list = g_list_insert_sorted(notify_func_list, nd, file_data_notify_sort); | |
1548 DEBUG_1("Notify func registered: %p", nd); | |
784
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1549 return TRUE; |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1550 } |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1551 |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1552 gint file_data_unregister_notify_func(FileDataNotifyFunc func, gpointer data) |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1553 { |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1554 GList *work = notify_func_list; |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1555 |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1556 while(work) |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1557 { |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1558 NotifyData *nd = (NotifyData *)work->data; |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1559 if (nd->func == func && nd->data == data) |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1560 { |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1561 notify_func_list = g_list_delete_link(notify_func_list, work); |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1562 g_free(nd); |
791 | 1563 DEBUG_1("Notify func unregistered: %p", nd); |
784
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1564 return TRUE; |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1565 } |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1566 work = work->next; |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1567 } |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1568 return FALSE; |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1569 } |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1570 |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1571 |
792 | 1572 void file_data_send_notification(FileData *fd, NotifyType type) |
784
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1573 { |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1574 GList *work = notify_func_list; |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1575 while(work) |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1576 { |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1577 NotifyData *nd = (NotifyData *)work->data; |
791 | 1578 DEBUG_1("Notify func calling: %p %s", nd, fd->path); |
792 | 1579 nd->func(fd, type, nd->data); |
784
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1580 work = work->next; |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1581 } |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1582 } |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1583 |
791 | 1584 static GHashTable *file_data_monitor_pool = NULL; |
1585 static gint realtime_monitor_id = -1; | |
1586 | |
1587 static void realtime_monitor_check_cb(gpointer key, gpointer value, gpointer data) | |
1588 { | |
1589 FileData *fd = key; | |
1590 | |
801 | 1591 file_data_check_changed_files(fd); |
791 | 1592 |
1593 DEBUG_1("monitor %s", fd->path); | |
1594 } | |
1595 | |
1596 static gboolean realtime_monitor_cb(gpointer data) | |
1597 { | |
1598 g_hash_table_foreach(file_data_monitor_pool, realtime_monitor_check_cb, NULL); | |
1599 return TRUE; | |
1600 } | |
1601 | |
1602 gint file_data_register_real_time_monitor(FileData *fd) | |
1603 { | |
1604 gint count = 0; | |
1605 | |
1606 file_data_ref(fd); | |
1607 | |
1608 if (!file_data_monitor_pool) | |
1609 file_data_monitor_pool = g_hash_table_new(g_direct_hash, g_direct_equal); | |
1610 | |
1611 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd)); | |
1612 | |
1613 DEBUG_1("Register realtime %d %s", count, fd->path); | |
1614 | |
1615 count++; | |
1616 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count)); | |
1617 | |
1618 if (realtime_monitor_id == -1) | |
1619 { | |
1620 realtime_monitor_id = g_timeout_add(5000, realtime_monitor_cb, NULL); | |
1621 } | |
1622 return TRUE; | |
1623 } | |
1624 | |
1625 gint file_data_unregister_real_time_monitor(FileData *fd) | |
1626 { | |
1627 gint count; | |
1628 g_assert(file_data_monitor_pool); | |
1629 | |
1630 count = GPOINTER_TO_INT(g_hash_table_lookup(file_data_monitor_pool, fd)); | |
1631 | |
1632 DEBUG_1("Unregister realtime %d %s", count, fd->path); | |
1633 | |
1634 g_assert(count > 0); | |
1635 | |
1636 count--; | |
1637 | |
1638 if (count == 0) | |
1639 g_hash_table_remove(file_data_monitor_pool, fd); | |
1640 else | |
1641 g_hash_table_insert(file_data_monitor_pool, fd, GINT_TO_POINTER(count)); | |
1642 | |
1643 file_data_unref(fd); | |
1644 | |
1645 if (g_hash_table_size(file_data_monitor_pool) == 0) | |
1646 { | |
1647 g_source_remove(realtime_monitor_id); | |
1648 realtime_monitor_id = -1; | |
1649 return FALSE; | |
1650 } | |
1651 return TRUE; | |
1652 } | |
784
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1653 |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1654 |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1655 |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1656 |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1657 |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1658 |
16b3a5c8aedc
new notification system (used only in vflist for now)
nadvornik
parents:
783
diff
changeset
|
1659 |