Mercurial > geeqie
comparison src/filedata.c @ 1728:94ced97edf0b
improved the sidecar grouping algorithm
author | nadvornik |
---|---|
date | Fri, 28 Aug 2009 20:18:46 +0000 |
parents | ea4effc8398c |
children | 672ee190869e |
comparison
equal
deleted
inserted
replaced
1727:68b791050958 | 1728:94ced97edf0b |
---|---|
23 #include "histogram.h" | 23 #include "histogram.h" |
24 | 24 |
25 | 25 |
26 static GHashTable *file_data_pool = NULL; | 26 static GHashTable *file_data_pool = NULL; |
27 static GHashTable *file_data_planned_change_hash = NULL; | 27 static GHashTable *file_data_planned_change_hash = NULL; |
28 static GHashTable *file_data_basename_hash = NULL; | |
29 | 28 |
30 static gint sidecar_file_priority(const gchar *path); | 29 static gint sidecar_file_priority(const gchar *path); |
31 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars, gboolean stat_sidecars); | 30 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars, GHashTable *basename_hash); |
32 | 31 |
33 | 32 |
34 /* | 33 /* |
35 *----------------------------------------------------------------------------- | 34 *----------------------------------------------------------------------------- |
36 * text conversion utils | 35 * text conversion utils |
136 * file info struct | 135 * file info struct |
137 *----------------------------------------------------------------------------- | 136 *----------------------------------------------------------------------------- |
138 */ | 137 */ |
139 | 138 |
140 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source); | 139 FileData *file_data_merge_sidecar_files(FileData *target, FileData *source); |
141 static void file_data_check_sidecars(FileData *fd, gboolean stat_sidecars); | 140 static void file_data_check_sidecars(FileData *fd, GHashTable *basename_hash); |
142 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd); | 141 FileData *file_data_disconnect_sidecar_file(FileData *target, FileData *sfd); |
143 | 142 |
144 | 143 |
145 void file_data_increment_version(FileData *fd) | 144 void file_data_increment_version(FileData *fd) |
146 { | 145 { |
159 const FileData *fdb = b; | 158 const FileData *fdb = b; |
160 | 159 |
161 return strcmp(fdb->extension, fda->extension); | 160 return strcmp(fdb->extension, fda->extension); |
162 } | 161 } |
163 | 162 |
164 static void file_data_basename_hash_insert(FileData *fd) | 163 static GHashTable *file_data_basename_hash_new(void) |
164 { | |
165 return g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); | |
166 } | |
167 | |
168 static void file_data_basename_hash_insert(GHashTable *basename_hash, FileData *fd) | |
165 { | 169 { |
166 GList *list; | 170 GList *list; |
167 const gchar *ext = extension_from_path(fd->path); | 171 const gchar *ext = extension_from_path(fd->path); |
168 gchar *basename = ext ? g_strndup(fd->path, ext - fd->path) : g_strdup(fd->path); | 172 gchar *basename = ext ? g_strndup(fd->path, ext - fd->path) : g_strdup(fd->path); |
169 if (!file_data_basename_hash) | 173 |
170 file_data_basename_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); | 174 list = g_hash_table_lookup(basename_hash, basename); |
171 | |
172 list = g_hash_table_lookup(file_data_basename_hash, basename); | |
173 | 175 |
174 if (!g_list_find(list, fd)) | 176 if (!g_list_find(list, fd)) |
175 { | 177 { |
176 list = g_list_insert_sorted(list, fd, file_data_sort_by_ext); | 178 list = g_list_insert_sorted(list, file_data_ref(fd), file_data_sort_by_ext); |
177 g_hash_table_insert(file_data_basename_hash, basename, list); | 179 g_hash_table_insert(basename_hash, basename, list); |
178 } | 180 } |
179 else | 181 else |
180 { | 182 { |
181 g_free(basename); | 183 g_free(basename); |
182 } | 184 } |
183 } | 185 } |
184 | 186 |
185 static void file_data_basename_hash_remove(FileData *fd) | 187 static void file_data_basename_hash_remove(GHashTable *basename_hash, FileData *fd) |
186 { | 188 { |
187 GList *list; | 189 GList *list; |
188 const gchar *ext = extension_from_path(fd->path); | 190 const gchar *ext = extension_from_path(fd->path); |
189 gchar *basename = ext ? g_strndup(fd->path, ext - fd->path) : g_strdup(fd->path); | 191 gchar *basename = ext ? g_strndup(fd->path, ext - fd->path) : g_strdup(fd->path); |
190 if (!file_data_basename_hash) | 192 |
191 file_data_basename_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); | 193 list = g_hash_table_lookup(basename_hash, basename); |
192 | 194 |
193 list = g_hash_table_lookup(file_data_basename_hash, basename); | 195 if (!g_list_find(list, fd)) return; |
194 | 196 |
195 list = g_list_remove(list, fd); | 197 list = g_list_remove(list, fd); |
198 file_data_unref(fd); | |
196 | 199 |
197 if (list) | 200 if (list) |
198 { | 201 { |
199 g_hash_table_insert(file_data_basename_hash, basename, list); | 202 g_hash_table_insert(basename_hash, basename, list); |
200 } | 203 } |
201 else | 204 else |
202 { | 205 { |
203 g_hash_table_remove(file_data_basename_hash, basename); | 206 g_hash_table_remove(basename_hash, basename); |
204 g_free(basename); | 207 g_free(basename); |
205 } | 208 } |
209 } | |
210 | |
211 static void file_data_basename_hash_remove_list(gpointer key, gpointer value, gpointer data) | |
212 { | |
213 filelist_free((GList *)value); | |
214 } | |
215 | |
216 static void file_data_basename_hash_free(GHashTable *basename_hash) | |
217 { | |
218 g_hash_table_foreach(basename_hash, file_data_basename_hash_remove_list, NULL); | |
219 g_hash_table_destroy(basename_hash); | |
206 } | 220 } |
207 | 221 |
208 static void file_data_set_collate_keys(FileData *fd) | 222 static void file_data_set_collate_keys(FileData *fd) |
209 { | 223 { |
210 gchar *caseless_name; | 224 gchar *caseless_name; |
222 fd->collate_key_name_nocase = g_utf8_collate_key(caseless_name, -1); | 236 fd->collate_key_name_nocase = g_utf8_collate_key(caseless_name, -1); |
223 #endif | 237 #endif |
224 g_free(caseless_name); | 238 g_free(caseless_name); |
225 } | 239 } |
226 | 240 |
227 static void file_data_set_path(FileData *fd, const gchar *path) | 241 static void file_data_set_path(FileData *fd, const gchar *path, GHashTable *basename_hash) |
228 { | 242 { |
229 g_assert(path /* && *path*/); /* view_dir_tree uses FileData with zero length path */ | 243 g_assert(path /* && *path*/); /* view_dir_tree uses FileData with zero length path */ |
230 g_assert(file_data_pool); | 244 g_assert(file_data_pool); |
231 | 245 |
232 if (fd->path) file_data_basename_hash_remove(fd); | 246 if (basename_hash && fd->path) file_data_basename_hash_remove(basename_hash, fd); |
233 | 247 |
234 g_free(fd->path); | 248 g_free(fd->path); |
235 | 249 |
236 if (fd->original_path) | 250 if (fd->original_path) |
237 { | 251 { |
281 if (fd->extension == NULL) | 295 if (fd->extension == NULL) |
282 { | 296 { |
283 fd->extension = fd->name + strlen(fd->name); | 297 fd->extension = fd->name + strlen(fd->name); |
284 } | 298 } |
285 | 299 |
286 file_data_basename_hash_insert(fd); /* we can ignore the special cases above - they don't have extensions */ | 300 if (basename_hash) file_data_basename_hash_insert(basename_hash, fd); /* we can ignore the special cases above - they don't have extensions */ |
287 | 301 |
288 file_data_set_collate_keys(fd); | 302 file_data_set_collate_keys(fd); |
289 } | 303 } |
290 | 304 |
291 static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st) | 305 static gboolean file_data_check_changed_files_recursive(FileData *fd, struct stat *st) |
362 } | 376 } |
363 | 377 |
364 return ret; | 378 return ret; |
365 } | 379 } |
366 | 380 |
367 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars, gboolean stat_sidecars) | 381 static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean check_sidecars, GHashTable *basename_hash) |
368 { | 382 { |
369 FileData *fd; | 383 FileData *fd; |
370 | 384 |
371 DEBUG_2("file_data_new: '%s' %d %d", path_utf8, check_sidecars, stat_sidecars); | 385 DEBUG_2("file_data_new: '%s' %d %d", path_utf8, check_sidecars, !!basename_hash); |
372 | 386 |
373 if (!file_data_pool) | 387 if (!file_data_pool) |
374 file_data_pool = g_hash_table_new(g_str_hash, g_str_equal); | 388 file_data_pool = g_hash_table_new(g_str_hash, g_str_equal); |
375 | 389 |
376 fd = g_hash_table_lookup(file_data_pool, path_utf8); | 390 fd = g_hash_table_lookup(file_data_pool, path_utf8); |
397 if (fd->parent) | 411 if (fd->parent) |
398 changed = file_data_check_changed_files(fd); | 412 changed = file_data_check_changed_files(fd); |
399 else | 413 else |
400 changed = file_data_check_changed_files_recursive(fd, st); | 414 changed = file_data_check_changed_files_recursive(fd, st); |
401 if (changed && check_sidecars && sidecar_file_priority(fd->extension)) | 415 if (changed && check_sidecars && sidecar_file_priority(fd->extension)) |
402 file_data_check_sidecars(fd, stat_sidecars); | 416 file_data_check_sidecars(fd, basename_hash); |
403 DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : ""); | 417 DEBUG_2("file_data_pool hit: '%s' %s", fd->path, changed ? "(changed)" : ""); |
404 | 418 |
405 return fd; | 419 return fd; |
406 } | 420 } |
407 | 421 |
411 fd->date = st->st_mtime; | 425 fd->date = st->st_mtime; |
412 fd->mode = st->st_mode; | 426 fd->mode = st->st_mode; |
413 fd->ref = 1; | 427 fd->ref = 1; |
414 fd->magick = 0x12345678; | 428 fd->magick = 0x12345678; |
415 | 429 |
416 file_data_set_path(fd, path_utf8); /* set path, name, collate_key_*, original_path */ | 430 file_data_set_path(fd, path_utf8, basename_hash); /* set path, name, collate_key_*, original_path */ |
417 | 431 |
418 if (check_sidecars) | 432 if (check_sidecars) |
419 file_data_check_sidecars(fd, stat_sidecars); | 433 file_data_check_sidecars(fd, basename_hash); |
420 | 434 |
421 return fd; | 435 return fd; |
422 } | 436 } |
423 | 437 |
424 /* extension must contain only ASCII characters */ | 438 /* extension must contain only ASCII characters */ |
457 g_free(sl); | 471 g_free(sl); |
458 | 472 |
459 return list; | 473 return list; |
460 } | 474 } |
461 | 475 |
462 static void file_data_check_sidecars(FileData *fd, gboolean stat_sidecars) | 476 static void file_data_check_sidecars(FileData *fd, GHashTable *basename_hash) |
463 { | 477 { |
464 gint base_len; | 478 gint base_len; |
465 GString *fname; | 479 GString *fname; |
466 FileData *parent_fd = NULL; | 480 FileData *parent_fd = NULL; |
467 GList *work; | 481 GList *work; |
471 return; | 485 return; |
472 | 486 |
473 base_len = fd->extension - fd->path; | 487 base_len = fd->extension - fd->path; |
474 fname = g_string_new_len(fd->path, base_len); | 488 fname = g_string_new_len(fd->path, base_len); |
475 | 489 |
476 if (!stat_sidecars) | 490 if (basename_hash) |
477 { | 491 { |
478 basename_list = g_hash_table_lookup(file_data_basename_hash, fname->str); | 492 basename_list = g_hash_table_lookup(basename_hash, fname->str); |
479 } | 493 } |
480 | 494 |
481 | 495 |
482 /* check for possible sidecar files; | 496 /* check for possible sidecar files; |
483 the sidecar files created here are referenced only via fd->sidecar_files or fd->parent, | 497 the sidecar files created here are referenced only via fd->sidecar_files or fd->parent, |
493 while (work) | 507 while (work) |
494 { | 508 { |
495 gchar *ext = work->data; | 509 gchar *ext = work->data; |
496 work = work->next; | 510 work = work->next; |
497 | 511 |
498 if (stat_sidecars) | 512 if (!basename_hash) |
499 { | 513 { |
500 GList *new_list; | 514 GList *new_list; |
501 g_string_truncate(fname, base_len); | 515 g_string_truncate(fname, base_len); |
502 g_string_append(fname, ext); | 516 g_string_append(fname, ext); |
503 new_list = check_case_insensitive_ext(fname->str); | 517 new_list = check_case_insensitive_ext(fname->str); |
507 { | 521 { |
508 const GList *work2 = basename_list; | 522 const GList *work2 = basename_list; |
509 | 523 |
510 while (work2) | 524 while (work2) |
511 { | 525 { |
512 struct stat nst; | |
513 FileData *sfd = work2->data; | 526 FileData *sfd = work2->data; |
514 | 527 |
515 if (g_ascii_strcasecmp(ext, sfd->extension) == 0 && | 528 if (g_ascii_strcasecmp(ext, sfd->extension) == 0) |
516 (sfd == fd || stat_utf8(sfd->path, &nst))) | |
517 /* basename list can contain deleted files, fd was recently stat'd by caller */ | |
518 { | 529 { |
519 group_list = g_list_append(group_list, file_data_ref(sfd)); | 530 group_list = g_list_append(group_list, file_data_ref(sfd)); |
520 } | 531 } |
521 work2 = work2->next; | 532 work2 = work2->next; |
522 } | 533 } |
546 } | 557 } |
547 g_list_free(group_list); | 558 g_list_free(group_list); |
548 } | 559 } |
549 | 560 |
550 | 561 |
551 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars, gboolean stat_sidecars) | 562 static FileData *file_data_new_local(const gchar *path, struct stat *st, gboolean check_sidecars, GHashTable *basename_hash) |
552 { | 563 { |
553 gchar *path_utf8 = path_to_utf8(path); | 564 gchar *path_utf8 = path_to_utf8(path); |
554 FileData *ret = file_data_new(path_utf8, st, check_sidecars, stat_sidecars); | 565 FileData *ret = file_data_new(path_utf8, st, check_sidecars, basename_hash); |
555 | 566 |
556 g_free(path_utf8); | 567 g_free(path_utf8); |
557 return ret; | 568 return ret; |
558 } | 569 } |
559 | 570 |
565 { | 576 { |
566 st.st_size = 0; | 577 st.st_size = 0; |
567 st.st_mtime = 0; | 578 st.st_mtime = 0; |
568 } | 579 } |
569 | 580 |
570 return file_data_new(path_utf8, &st, TRUE, TRUE); | 581 return file_data_new(path_utf8, &st, TRUE, NULL); |
571 } | 582 } |
572 | 583 |
573 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd) | 584 FileData *file_data_add_sidecar_file(FileData *target, FileData *sfd) |
574 { | 585 { |
575 sfd->parent = target; | 586 sfd->parent = target; |
627 static void file_data_free(FileData *fd) | 638 static void file_data_free(FileData *fd) |
628 { | 639 { |
629 g_assert(fd->magick == 0x12345678); | 640 g_assert(fd->magick == 0x12345678); |
630 g_assert(fd->ref == 0); | 641 g_assert(fd->ref == 0); |
631 | 642 |
632 if (fd->path) file_data_basename_hash_remove(fd); | |
633 g_hash_table_remove(file_data_pool, fd->original_path); | 643 g_hash_table_remove(file_data_pool, fd->original_path); |
634 | 644 |
635 g_free(fd->path); | 645 g_free(fd->path); |
636 g_free(fd->original_path); | 646 g_free(fd->original_path); |
637 g_free(fd->collate_key_name); | 647 g_free(fd->collate_key_name); |
989 struct dirent *dir; | 999 struct dirent *dir; |
990 gchar *pathl; | 1000 gchar *pathl; |
991 GList *dlist = NULL; | 1001 GList *dlist = NULL; |
992 GList *flist = NULL; | 1002 GList *flist = NULL; |
993 gint (*stat_func)(const gchar *path, struct stat *buf); | 1003 gint (*stat_func)(const gchar *path, struct stat *buf); |
1004 GHashTable *basename_hash = NULL; | |
994 | 1005 |
995 g_assert(files || dirs); | 1006 g_assert(files || dirs); |
996 | 1007 |
997 if (files) *files = NULL; | 1008 if (files) *files = NULL; |
998 if (dirs) *dirs = NULL; | 1009 if (dirs) *dirs = NULL; |
1004 if (dp == NULL) | 1015 if (dp == NULL) |
1005 { | 1016 { |
1006 g_free(pathl); | 1017 g_free(pathl); |
1007 return FALSE; | 1018 return FALSE; |
1008 } | 1019 } |
1020 | |
1021 if (files) basename_hash = file_data_basename_hash_new(); | |
1009 | 1022 |
1010 if (follow_symlinks) | 1023 if (follow_symlinks) |
1011 stat_func = stat; | 1024 stat_func = stat; |
1012 else | 1025 else |
1013 stat_func = lstat; | 1026 stat_func = lstat; |
1031 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) && | 1044 !(name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'))) && |
1032 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 && | 1045 strcmp(name, GQ_CACHE_LOCAL_THUMB) != 0 && |
1033 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 && | 1046 strcmp(name, GQ_CACHE_LOCAL_METADATA) != 0 && |
1034 strcmp(name, THUMB_FOLDER_LOCAL) != 0) | 1047 strcmp(name, THUMB_FOLDER_LOCAL) != 0) |
1035 { | 1048 { |
1036 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE, FALSE)); | 1049 dlist = g_list_prepend(dlist, file_data_new_local(filepath, &ent_sbuf, FALSE, NULL)); |
1037 } | 1050 } |
1038 } | 1051 } |
1039 else | 1052 else |
1040 { | 1053 { |
1041 if (files && filter_name_exists(name)) | 1054 if (files && filter_name_exists(name)) |
1042 { | 1055 { |
1043 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE, FALSE)); | 1056 flist = g_list_prepend(flist, file_data_new_local(filepath, &ent_sbuf, TRUE, basename_hash)); |
1044 } | 1057 } |
1045 } | 1058 } |
1046 } | 1059 } |
1047 g_free(filepath); | 1060 g_free(filepath); |
1048 } | 1061 } |
1049 | 1062 |
1050 closedir(dp); | 1063 closedir(dp); |
1051 | 1064 |
1052 g_free(pathl); | 1065 g_free(pathl); |
1066 if (basename_hash) file_data_basename_hash_free(basename_hash); | |
1053 | 1067 |
1054 if (dirs) *dirs = dlist; | 1068 if (dirs) *dirs = dlist; |
1055 if (files) *files = filelist_filter_out_sidecars(flist); | 1069 if (files) *files = filelist_filter_out_sidecars(flist); |
1056 | 1070 |
1057 return TRUE; | 1071 return TRUE; |
2382 */ | 2396 */ |
2383 DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path); | 2397 DEBUG_1("can't rename fd, target exists %s -> %s", fd->change->dest, fd->path); |
2384 } | 2398 } |
2385 else | 2399 else |
2386 { | 2400 { |
2387 file_data_set_path(fd, fd->change->dest); | 2401 file_data_set_path(fd, fd->change->dest, NULL); |
2388 } | 2402 } |
2389 } | 2403 } |
2390 file_data_increment_version(fd); | 2404 file_data_increment_version(fd); |
2391 file_data_send_notification(fd, NOTIFY_CHANGE); | 2405 file_data_send_notification(fd, NOTIFY_CHANGE); |
2392 | 2406 |