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