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