1 /* nautilus-batch-rename-utilities.c
2 *
3 * Copyright (C) 2016 Alexandru Pandelea <alexandru.pandelea@gmail.com>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "nautilus-batch-rename-dialog.h"
20 #include "nautilus-batch-rename-utilities.h"
21 #include "nautilus-file.h"
22 #include "nautilus-tracker-utilities.h"
23
24 #include <glib.h>
25 #include <gtk/gtk.h>
26 #include <string.h>
27 #include <stdarg.h>
28 #include <eel/eel-vfs-extensions.h>
29
30 typedef struct
31 {
32 NautilusFile *file;
33 gint position;
34 } CreateDateElem;
35
36 typedef struct
37 {
38 NautilusBatchRenameDialog *dialog;
39 GHashTable *date_order_hash_table;
40
41 GList *selection_metadata;
42
43 gboolean has_metadata[G_N_ELEMENTS (metadata_tags_constants)];
44
45 GCancellable *cancellable;
46 } QueryData;
47
48 enum
49 {
50 FILE_NAME_INDEX,
51 CREATION_DATE_INDEX,
52 YEAR_INDEX,
53 MONTH_INDEX,
54 DAY_INDEX,
55 HOURS_INDEX,
56 MINUTES_INDEX,
57 SECONDS_INDEX,
58 CAMERA_MODEL_INDEX,
59 SEASON_INDEX,
60 EPISODE_NUMBER_INDEX,
61 TRACK_NUMBER_INDEX,
62 ARTIST_NAME_INDEX,
63 TITLE_INDEX,
64 ALBUM_NAME_INDEX,
65 } QueryMetadata;
66
67 static void on_cursor_callback (GObject *object,
68 GAsyncResult *result,
69 gpointer user_data);
70
71 void
string_free(gpointer mem)72 string_free (gpointer mem)
73 {
74 if (mem != NULL)
75 {
76 g_string_free (mem, TRUE);
77 }
78 }
79
80 void
conflict_data_free(gpointer mem)81 conflict_data_free (gpointer mem)
82 {
83 ConflictData *conflict_data = mem;
84
85 g_free (conflict_data->name);
86 g_free (conflict_data);
87 }
88
89 gchar *
batch_rename_get_tag_text_representation(TagConstants tag_constants)90 batch_rename_get_tag_text_representation (TagConstants tag_constants)
91 {
92 return g_strdup_printf ("[%s]", gettext (tag_constants.label));
93 }
94
95 static GString *
batch_rename_replace(gchar * string,gchar * substring,gchar * replacement)96 batch_rename_replace (gchar *string,
97 gchar *substring,
98 gchar *replacement)
99 {
100 GString *new_string;
101 gchar **splitted_string;
102 gint i, n_splits;
103
104 new_string = g_string_new ("");
105
106 if (substring == NULL || replacement == NULL)
107 {
108 g_string_append (new_string, string);
109
110 return new_string;
111 }
112
113 if (g_utf8_strlen (substring, -1) == 0)
114 {
115 g_string_append (new_string, string);
116
117 return new_string;
118 }
119
120 splitted_string = g_strsplit (string, substring, -1);
121 if (splitted_string == NULL)
122 {
123 g_string_append (new_string, string);
124
125 return new_string;
126 }
127
128 n_splits = g_strv_length (splitted_string);
129
130 for (i = 0; i < n_splits; i++)
131 {
132 g_string_append (new_string, splitted_string[i]);
133
134 if (i != n_splits - 1)
135 {
136 g_string_append (new_string, replacement);
137 }
138 }
139
140 g_strfreev (splitted_string);
141
142 return new_string;
143 }
144
145 void
batch_rename_sort_lists_for_rename(GList ** selection,GList ** new_names,GList ** old_names,GList ** new_files,GList ** old_files,gboolean is_undo_redo)146 batch_rename_sort_lists_for_rename (GList **selection,
147 GList **new_names,
148 GList **old_names,
149 GList **new_files,
150 GList **old_files,
151 gboolean is_undo_redo)
152 {
153 GList *new_names_list;
154 GList *new_names_list2;
155 GList *files;
156 GList *files2;
157 GList *old_names_list = NULL;
158 GList *new_files_list = NULL;
159 GList *old_files_list = NULL;
160 GList *old_names_list2 = NULL;
161 GList *new_files_list2 = NULL;
162 GList *old_files_list2 = NULL;
163 GString *new_file_name;
164 GString *new_name;
165 GString *old_name;
166 GFile *new_file;
167 GFile *old_file;
168 NautilusFile *file;
169 gboolean order_changed = TRUE;
170
171 /* in the following case:
172 * file1 -> file2
173 * file2 -> file3
174 * file2 must be renamed first, so because of that, the list has to be reordered
175 */
176 while (order_changed)
177 {
178 order_changed = FALSE;
179
180 if (is_undo_redo)
181 {
182 old_names_list = *old_names;
183 new_files_list = *new_files;
184 old_files_list = *old_files;
185 }
186
187 for (new_names_list = *new_names, files = *selection;
188 new_names_list != NULL && files != NULL;
189 new_names_list = new_names_list->next, files = files->next)
190 {
191 g_autofree gchar *old_file_name = NULL;
192
193 old_file_name = nautilus_file_get_name (NAUTILUS_FILE (files->data));
194 new_file_name = new_names_list->data;
195
196 if (is_undo_redo)
197 {
198 old_names_list2 = old_names_list;
199 new_files_list2 = new_files_list;
200 old_files_list2 = old_files_list;
201 }
202
203 for (files2 = files, new_names_list2 = new_names_list;
204 files2 != NULL && new_names_list2 != NULL;
205 files2 = files2->next, new_names_list2 = new_names_list2->next)
206 {
207 g_autofree gchar *file_name = NULL;
208
209 file_name = nautilus_file_get_name (NAUTILUS_FILE (files2->data));
210 new_name = new_names_list2->data;
211
212 if (files2 != files && g_strcmp0 (file_name, new_file_name->str) == 0)
213 {
214 file = NAUTILUS_FILE (files2->data);
215
216 *selection = g_list_remove_link (*selection, files2);
217 *new_names = g_list_remove_link (*new_names, new_names_list2);
218
219 *selection = g_list_prepend (*selection, file);
220 *new_names = g_list_prepend (*new_names, new_name);
221
222 if (is_undo_redo)
223 {
224 old_name = old_names_list2->data;
225 new_file = new_files_list2->data;
226 old_file = old_files_list2->data;
227
228 *old_names = g_list_remove_link (*old_names, old_names_list2);
229 *new_files = g_list_remove_link (*new_files, new_files_list2);
230 *old_files = g_list_remove_link (*old_files, old_files_list2);
231
232 *old_names = g_list_prepend (*old_names, old_name);
233 *new_files = g_list_prepend (*new_files, new_file);
234 *old_files = g_list_prepend (*old_files, old_file);
235 }
236
237 order_changed = TRUE;
238 break;
239 }
240
241 if (is_undo_redo)
242 {
243 old_names_list2 = old_names_list2->next;
244 new_files_list2 = new_files_list2->next;
245 old_files_list2 = old_files_list2->next;
246 }
247 }
248
249 if (is_undo_redo)
250 {
251 old_names_list = old_names_list->next;
252 new_files_list = new_files_list->next;
253 old_files_list = old_files_list->next;
254 }
255 }
256 }
257 }
258
259 /* This function changes the background color of the replaced part of the name */
260 GString *
batch_rename_replace_label_text(gchar * label,const gchar * substring)261 batch_rename_replace_label_text (gchar *label,
262 const gchar *substring)
263 {
264 GString *new_label;
265 gchar **splitted_string;
266 gchar *token;
267 gint i, n_splits;
268
269 new_label = g_string_new ("");
270
271 if (substring == NULL || g_strcmp0 (substring, "") == 0)
272 {
273 token = g_markup_escape_text (label, -1);
274 new_label = g_string_append (new_label, token);
275 g_free (token);
276
277 return new_label;
278 }
279
280 splitted_string = g_strsplit (label, substring, -1);
281 if (splitted_string == NULL)
282 {
283 token = g_markup_escape_text (label, -1);
284 new_label = g_string_append (new_label, token);
285 g_free (token);
286
287 return new_label;
288 }
289
290 n_splits = g_strv_length (splitted_string);
291
292 for (i = 0; i < n_splits; i++)
293 {
294 token = g_markup_escape_text (splitted_string[i], -1);
295 new_label = g_string_append (new_label, token);
296
297 g_free (token);
298
299 if (i != n_splits - 1)
300 {
301 token = g_markup_escape_text (substring, -1);
302 g_string_append_printf (new_label,
303 "<span background=\'#f57900\' color='white'>%s</span>",
304 token);
305
306 g_free (token);
307 }
308 }
309
310 g_strfreev (splitted_string);
311
312 return new_label;
313 }
314
315 static gchar *
get_metadata(GList * selection_metadata,gchar * file_name,MetadataType metadata_type)316 get_metadata (GList *selection_metadata,
317 gchar *file_name,
318 MetadataType metadata_type)
319 {
320 GList *l;
321 FileMetadata *file_metadata;
322 gchar *metadata = NULL;
323
324 for (l = selection_metadata; l != NULL; l = l->next)
325 {
326 file_metadata = l->data;
327 if (g_strcmp0 (file_name, file_metadata->file_name->str) == 0)
328 {
329 if (file_metadata->metadata[metadata_type] &&
330 file_metadata->metadata[metadata_type]->len > 0)
331 {
332 metadata = file_metadata->metadata[metadata_type]->str;
333 }
334
335 break;
336 }
337 }
338
339 return metadata;
340 }
341
342 static GString *
batch_rename_format(NautilusFile * file,GList * text_chunks,GList * selection_metadata,gint count)343 batch_rename_format (NautilusFile *file,
344 GList *text_chunks,
345 GList *selection_metadata,
346 gint count)
347 {
348 GList *l;
349 GString *tag_string;
350 GString *new_name;
351 gboolean added_tag;
352 MetadataType metadata_type;
353 g_autofree gchar *file_name = NULL;
354 g_autofree gchar *extension = NULL;
355 gint i;
356 gchar *metadata;
357
358 file_name = nautilus_file_get_display_name (file);
359 if (!nautilus_file_is_directory (file))
360 {
361 extension = nautilus_file_get_extension (file);
362 }
363
364 new_name = g_string_new ("");
365
366 for (l = text_chunks; l != NULL; l = l->next)
367 {
368 added_tag = FALSE;
369 tag_string = l->data;
370
371 for (i = 0; i < G_N_ELEMENTS (numbering_tags_constants); i++)
372 {
373 g_autofree gchar *tag_text_representation = NULL;
374
375 tag_text_representation = batch_rename_get_tag_text_representation (numbering_tags_constants[i]);
376 if (g_strcmp0 (tag_string->str, tag_text_representation) == 0)
377 {
378 switch (numbering_tags_constants[i].numbering_type)
379 {
380 case NUMBERING_NO_ZERO_PAD:
381 {
382 g_string_append_printf (new_name, "%d", count);
383 }
384 break;
385
386 case NUMBERING_ONE_ZERO_PAD:
387 {
388 g_string_append_printf (new_name, "%02d", count);
389 }
390 break;
391
392 case NUMBERING_TWO_ZERO_PAD:
393 {
394 g_string_append_printf (new_name, "%03d", count);
395 }
396 break;
397
398 default:
399 {
400 g_warn_if_reached ();
401 }
402 break;
403 }
404
405 added_tag = TRUE;
406 break;
407 }
408 }
409
410 if (added_tag)
411 {
412 continue;
413 }
414
415 for (i = 0; i < G_N_ELEMENTS (metadata_tags_constants); i++)
416 {
417 g_autofree gchar *tag_text_representation = NULL;
418
419 tag_text_representation = batch_rename_get_tag_text_representation (metadata_tags_constants[i]);
420 if (g_strcmp0 (tag_string->str, tag_text_representation) == 0)
421 {
422 metadata_type = metadata_tags_constants[i].metadata_type;
423 metadata = get_metadata (selection_metadata, file_name, metadata_type);
424
425 /* TODO: This is a hack, we should provide a cancellable for checking
426 * the metadata, and if that is happening don't enter here. We can
427 * special case original file name upper in the call stack */
428 if (!metadata && metadata_type != ORIGINAL_FILE_NAME)
429 {
430 g_warning ("Metadata not present in one file, it shouldn't have been added. File name: %s, Metadata: %s",
431 file_name, metadata_tags_constants[i].label);
432 continue;
433 }
434
435 switch (metadata_type)
436 {
437 case ORIGINAL_FILE_NAME:
438 {
439 if (nautilus_file_is_directory (file))
440 {
441 new_name = g_string_append (new_name, file_name);
442 }
443 else
444 {
445 g_autofree gchar *base_name = NULL;
446 base_name = eel_filename_strip_extension (file_name);
447 new_name = g_string_append (new_name, base_name);
448 }
449 }
450 break;
451
452 case TRACK_NUMBER:
453 {
454 g_string_append_printf (new_name, "%02d", atoi (metadata));
455 }
456 break;
457
458 default:
459 {
460 new_name = g_string_append (new_name, metadata);
461 }
462 break;
463 }
464
465 added_tag = TRUE;
466 break;
467 }
468 }
469
470 if (!added_tag)
471 {
472 new_name = g_string_append (new_name, tag_string->str);
473 }
474 }
475
476 if (g_strcmp0 (new_name->str, "") == 0)
477 {
478 new_name = g_string_append (new_name, file_name);
479 }
480 else
481 {
482 if (extension != NULL)
483 {
484 new_name = g_string_append (new_name, extension);
485 }
486 }
487
488 return new_name;
489 }
490
491 GList *
batch_rename_dialog_get_new_names_list(NautilusBatchRenameDialogMode mode,GList * selection,GList * text_chunks,GList * selection_metadata,gchar * entry_text,gchar * replace_text)492 batch_rename_dialog_get_new_names_list (NautilusBatchRenameDialogMode mode,
493 GList *selection,
494 GList *text_chunks,
495 GList *selection_metadata,
496 gchar *entry_text,
497 gchar *replace_text)
498 {
499 GList *l;
500 GList *result;
501 GString *file_name;
502 GString *new_name;
503 NautilusFile *file;
504 gchar *name;
505 gint count;
506
507 result = NULL;
508 count = 1;
509
510 for (l = selection; l != NULL; l = l->next)
511 {
512 file = NAUTILUS_FILE (l->data);
513
514 name = nautilus_file_get_name (file);
515 file_name = g_string_new (name);
516
517 /* get the new name here and add it to the list*/
518 if (mode == NAUTILUS_BATCH_RENAME_DIALOG_FORMAT)
519 {
520 new_name = batch_rename_format (file,
521 text_chunks,
522 selection_metadata,
523 count++);
524 result = g_list_prepend (result, new_name);
525 }
526
527 if (mode == NAUTILUS_BATCH_RENAME_DIALOG_REPLACE)
528 {
529 new_name = batch_rename_replace (file_name->str,
530 entry_text,
531 replace_text);
532 result = g_list_prepend (result, new_name);
533 }
534
535 g_string_free (file_name, TRUE);
536 g_free (name);
537 }
538
539 return result;
540 }
541
542 /* There is a case that a new name for a file conflicts with an existing file name
543 * in the directory but it's not a problem because the file in the directory that
544 * conflicts is part of the batch renaming selection and it's going to change the name anyway. */
545 gboolean
file_name_conflicts_with_results(GList * selection,GList * new_names,GString * old_name,gchar * parent_uri)546 file_name_conflicts_with_results (GList *selection,
547 GList *new_names,
548 GString *old_name,
549 gchar *parent_uri)
550 {
551 GList *l1;
552 GList *l2;
553 NautilusFile *selection_file;
554 GString *new_name;
555
556 for (l1 = selection, l2 = new_names; l1 != NULL && l2 != NULL; l1 = l1->next, l2 = l2->next)
557 {
558 g_autofree gchar *name1 = NULL;
559 g_autofree gchar *selection_parent_uri = NULL;
560
561 selection_file = NAUTILUS_FILE (l1->data);
562 name1 = nautilus_file_get_name (selection_file);
563
564 selection_parent_uri = nautilus_file_get_parent_uri (selection_file);
565
566 if (g_strcmp0 (name1, old_name->str) == 0)
567 {
568 new_name = l2->data;
569
570 /* if the name didn't change, then there's a conflict */
571 if (g_string_equal (old_name, new_name) &&
572 (parent_uri == NULL || g_strcmp0 (parent_uri, selection_parent_uri) == 0))
573 {
574 return FALSE;
575 }
576
577
578 /* if this file exists and it changed it's name, then there's no
579 * conflict */
580 return TRUE;
581 }
582 }
583
584 /* the case this function searched for doesn't exist, so the file
585 * has a conlfict */
586 return FALSE;
587 }
588
589 static gint
compare_files_by_name_ascending(gconstpointer a,gconstpointer b)590 compare_files_by_name_ascending (gconstpointer a,
591 gconstpointer b)
592 {
593 NautilusFile *file1;
594 NautilusFile *file2;
595
596 file1 = NAUTILUS_FILE (a);
597 file2 = NAUTILUS_FILE (b);
598
599 return nautilus_file_compare_for_sort (file1, file2,
600 NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
601 FALSE, FALSE);
602 }
603
604 static gint
compare_files_by_name_descending(gconstpointer a,gconstpointer b)605 compare_files_by_name_descending (gconstpointer a,
606 gconstpointer b)
607 {
608 NautilusFile *file1;
609 NautilusFile *file2;
610
611 file1 = NAUTILUS_FILE (a);
612 file2 = NAUTILUS_FILE (b);
613
614 return nautilus_file_compare_for_sort (file1, file2,
615 NAUTILUS_FILE_SORT_BY_DISPLAY_NAME,
616 FALSE, TRUE);
617 }
618
619 static gint
compare_files_by_first_modified(gconstpointer a,gconstpointer b)620 compare_files_by_first_modified (gconstpointer a,
621 gconstpointer b)
622 {
623 NautilusFile *file1;
624 NautilusFile *file2;
625
626 file1 = NAUTILUS_FILE (a);
627 file2 = NAUTILUS_FILE (b);
628
629 return nautilus_file_compare_for_sort (file1, file2,
630 NAUTILUS_FILE_SORT_BY_MTIME,
631 FALSE, FALSE);
632 }
633
634 static gint
compare_files_by_last_modified(gconstpointer a,gconstpointer b)635 compare_files_by_last_modified (gconstpointer a,
636 gconstpointer b)
637 {
638 NautilusFile *file1;
639 NautilusFile *file2;
640
641 file1 = NAUTILUS_FILE (a);
642 file2 = NAUTILUS_FILE (b);
643
644 return nautilus_file_compare_for_sort (file1, file2,
645 NAUTILUS_FILE_SORT_BY_MTIME,
646 FALSE, TRUE);
647 }
648
649 static gint
compare_files_by_first_created(gconstpointer a,gconstpointer b)650 compare_files_by_first_created (gconstpointer a,
651 gconstpointer b)
652 {
653 CreateDateElem *elem1;
654 CreateDateElem *elem2;
655
656 elem1 = (CreateDateElem *) a;
657 elem2 = (CreateDateElem *) b;
658
659 return elem1->position - elem2->position;
660 }
661
662 static gint
compare_files_by_last_created(gconstpointer a,gconstpointer b)663 compare_files_by_last_created (gconstpointer a,
664 gconstpointer b)
665 {
666 CreateDateElem *elem1;
667 CreateDateElem *elem2;
668
669 elem1 = (CreateDateElem *) a;
670 elem2 = (CreateDateElem *) b;
671
672 return elem2->position - elem1->position;
673 }
674
675 GList *
nautilus_batch_rename_dialog_sort(GList * selection,SortMode mode,GHashTable * creation_date_table)676 nautilus_batch_rename_dialog_sort (GList *selection,
677 SortMode mode,
678 GHashTable *creation_date_table)
679 {
680 GList *l, *l2;
681 NautilusFile *file;
682 GList *create_date_list;
683 GList *create_date_list_sorted;
684 gchar *name;
685
686 if (mode == ORIGINAL_ASCENDING)
687 {
688 return g_list_sort (selection, compare_files_by_name_ascending);
689 }
690
691 if (mode == ORIGINAL_DESCENDING)
692 {
693 return g_list_sort (selection, compare_files_by_name_descending);
694 }
695
696 if (mode == FIRST_MODIFIED)
697 {
698 return g_list_sort (selection, compare_files_by_first_modified);
699 }
700
701 if (mode == LAST_MODIFIED)
702 {
703 return g_list_sort (selection, compare_files_by_last_modified);
704 }
705
706 if (mode == FIRST_CREATED || mode == LAST_CREATED)
707 {
708 create_date_list = NULL;
709
710 for (l = selection; l != NULL; l = l->next)
711 {
712 CreateDateElem *elem;
713 elem = g_new (CreateDateElem, 1);
714
715 file = NAUTILUS_FILE (l->data);
716
717 name = nautilus_file_get_name (file);
718 elem->file = file;
719 elem->position = GPOINTER_TO_INT (g_hash_table_lookup (creation_date_table, name));
720 g_free (name);
721
722 create_date_list = g_list_prepend (create_date_list, elem);
723 }
724
725 if (mode == FIRST_CREATED)
726 {
727 create_date_list_sorted = g_list_sort (create_date_list,
728 compare_files_by_first_created);
729 }
730 else
731 {
732 create_date_list_sorted = g_list_sort (create_date_list,
733 compare_files_by_last_created);
734 }
735
736 for (l = selection, l2 = create_date_list_sorted; l2 != NULL; l = l->next, l2 = l2->next)
737 {
738 CreateDateElem *elem = l2->data;
739 l->data = elem->file;
740 }
741
742 g_list_free_full (create_date_list, g_free);
743 }
744
745 return selection;
746 }
747
748 static void
cursor_next(QueryData * query_data,TrackerSparqlCursor * cursor)749 cursor_next (QueryData *query_data,
750 TrackerSparqlCursor *cursor)
751 {
752 tracker_sparql_cursor_next_async (cursor,
753 query_data->cancellable,
754 on_cursor_callback,
755 query_data);
756 }
757
758 static void
remove_metadata(QueryData * query_data,MetadataType metadata_type)759 remove_metadata (QueryData *query_data,
760 MetadataType metadata_type)
761 {
762 GList *l;
763 FileMetadata *metadata_to_delete;
764
765 for (l = query_data->selection_metadata; l != NULL; l = l->next)
766 {
767 metadata_to_delete = l->data;
768 if (metadata_to_delete->metadata[metadata_type])
769 {
770 g_string_free (metadata_to_delete->metadata[metadata_type], TRUE);
771 metadata_to_delete->metadata[metadata_type] = NULL;
772 }
773 }
774
775 query_data->has_metadata[metadata_type] = FALSE;
776 }
777
778 static GString *
format_date_time(GDateTime * date_time)779 format_date_time (GDateTime *date_time)
780 {
781 g_autofree gchar *date = NULL;
782 GString *formated_date;
783
784 date = g_date_time_format (date_time, "%x");
785 if (strstr (date, "/") != NULL)
786 {
787 formated_date = batch_rename_replace (date, "/", "-");
788 }
789 else
790 {
791 formated_date = g_string_new (date);
792 }
793
794 return formated_date;
795 }
796
797 static void
on_cursor_callback(GObject * object,GAsyncResult * result,gpointer user_data)798 on_cursor_callback (GObject *object,
799 GAsyncResult *result,
800 gpointer user_data)
801 {
802 TrackerSparqlCursor *cursor;
803 gboolean success;
804 QueryData *query_data;
805 MetadataType metadata_type;
806 g_autoptr (GError) error = NULL;
807 GList *l;
808 FileMetadata *file_metadata;
809 GDateTime *date_time;
810 guint i;
811 const gchar *current_metadata;
812 const gchar *file_name;
813 const gchar *creation_date;
814 const gchar *year;
815 const gchar *month;
816 const gchar *day;
817 const gchar *hours;
818 const gchar *minutes;
819 const gchar *seconds;
820 const gchar *equipment;
821 const gchar *season_number;
822 const gchar *episode_number;
823 const gchar *track_number;
824 const gchar *artist_name;
825 const gchar *title;
826 const gchar *album_name;
827
828 file_metadata = NULL;
829
830 cursor = TRACKER_SPARQL_CURSOR (object);
831 query_data = user_data;
832
833 success = tracker_sparql_cursor_next_finish (cursor, result, &error);
834 if (!success)
835 {
836 if (error != NULL)
837 {
838 g_warning ("Error on batch rename tracker query cursor: %s", error->message);
839 }
840
841 g_clear_object (&cursor);
842
843 /* The dialog is going away at the time of cancellation */
844 if (error == NULL ||
845 (error != NULL && error->code != G_IO_ERROR_CANCELLED))
846 {
847 nautilus_batch_rename_dialog_query_finished (query_data->dialog,
848 query_data->date_order_hash_table,
849 query_data->selection_metadata);
850 }
851
852 g_free (query_data);
853
854 return;
855 }
856
857 creation_date = tracker_sparql_cursor_get_string (cursor, CREATION_DATE_INDEX, NULL);
858
859 year = tracker_sparql_cursor_get_string (cursor, YEAR_INDEX, NULL);
860 month = tracker_sparql_cursor_get_string (cursor, MONTH_INDEX, NULL);
861 day = tracker_sparql_cursor_get_string (cursor, DAY_INDEX, NULL);
862 hours = tracker_sparql_cursor_get_string (cursor, HOURS_INDEX, NULL);
863 minutes = tracker_sparql_cursor_get_string (cursor, MINUTES_INDEX, NULL);
864 seconds = tracker_sparql_cursor_get_string (cursor, SECONDS_INDEX, NULL);
865 equipment = tracker_sparql_cursor_get_string (cursor, CAMERA_MODEL_INDEX, NULL);
866 season_number = tracker_sparql_cursor_get_string (cursor, SEASON_INDEX, NULL);
867 episode_number = tracker_sparql_cursor_get_string (cursor, EPISODE_NUMBER_INDEX, NULL);
868 track_number = tracker_sparql_cursor_get_string (cursor, TRACK_NUMBER_INDEX, NULL);
869 artist_name = tracker_sparql_cursor_get_string (cursor, ARTIST_NAME_INDEX, NULL);
870 title = tracker_sparql_cursor_get_string (cursor, TITLE_INDEX, NULL);
871 album_name = tracker_sparql_cursor_get_string (cursor, ALBUM_NAME_INDEX, NULL);
872
873 /* Search for the metadata object corresponding to the file name */
874 file_name = tracker_sparql_cursor_get_string (cursor, FILE_NAME_INDEX, NULL);
875 for (l = query_data->selection_metadata; l != NULL; l = l->next)
876 {
877 file_metadata = l->data;
878
879 if (g_strcmp0 (file_name, file_metadata->file_name->str) == 0)
880 {
881 break;
882 }
883 }
884
885 /* Set metadata when available, and delete for the whole selection when not */
886 for (i = 0; i < G_N_ELEMENTS (metadata_tags_constants); i++)
887 {
888 if (query_data->has_metadata[i])
889 {
890 metadata_type = metadata_tags_constants[i].metadata_type;
891 current_metadata = NULL;
892 switch (metadata_type)
893 {
894 case ORIGINAL_FILE_NAME:
895 {
896 current_metadata = file_name;
897 }
898 break;
899
900 case CREATION_DATE:
901 {
902 current_metadata = creation_date;
903 }
904 break;
905
906 case EQUIPMENT:
907 {
908 current_metadata = equipment;
909 }
910 break;
911
912 case SEASON_NUMBER:
913 {
914 current_metadata = season_number;
915 }
916 break;
917
918 case EPISODE_NUMBER:
919 {
920 current_metadata = episode_number;
921 }
922 break;
923
924 case ARTIST_NAME:
925 {
926 current_metadata = artist_name;
927 }
928 break;
929
930 case ALBUM_NAME:
931 {
932 current_metadata = album_name;
933 }
934 break;
935
936 case TITLE:
937 {
938 current_metadata = title;
939 }
940 break;
941
942 case TRACK_NUMBER:
943 {
944 current_metadata = track_number;
945 }
946 break;
947
948 default:
949 {
950 g_warn_if_reached ();
951 }
952 break;
953 }
954
955 /* TODO: Figure out how to inform the user of why the metadata is
956 * unavailable when one or more contains the unallowed character "/"
957 */
958 if (!current_metadata || g_strrstr (current_metadata, "/"))
959 {
960 remove_metadata (query_data,
961 metadata_type);
962
963 if (metadata_type == CREATION_DATE &&
964 query_data->date_order_hash_table)
965 {
966 g_hash_table_destroy (query_data->date_order_hash_table);
967 query_data->date_order_hash_table = NULL;
968 }
969 }
970 else
971 {
972 if (metadata_type == CREATION_DATE)
973 {
974 /* Add the sort order to the order hash table */
975 g_hash_table_insert (query_data->date_order_hash_table,
976 g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL)),
977 GINT_TO_POINTER (g_hash_table_size (query_data->date_order_hash_table)));
978
979 date_time = g_date_time_new_local (atoi (year),
980 atoi (month),
981 atoi (day),
982 atoi (hours),
983 atoi (minutes),
984 atoi (seconds));
985
986 file_metadata->metadata[metadata_type] = format_date_time (date_time);
987 }
988 else
989 {
990 file_metadata->metadata[metadata_type] = g_string_new (current_metadata);
991 }
992 }
993 }
994 }
995
996 /* Get next */
997 cursor_next (query_data, cursor);
998 }
999
1000 static void
batch_rename_dialog_query_callback(GObject * object,GAsyncResult * result,gpointer user_data)1001 batch_rename_dialog_query_callback (GObject *object,
1002 GAsyncResult *result,
1003 gpointer user_data)
1004 {
1005 TrackerSparqlConnection *connection;
1006 TrackerSparqlCursor *cursor;
1007 QueryData *query_data;
1008 g_autoptr (GError) error = NULL;
1009
1010 connection = TRACKER_SPARQL_CONNECTION (object);
1011 query_data = user_data;
1012
1013 cursor = tracker_sparql_connection_query_finish (connection,
1014 result,
1015 &error);
1016
1017 if (error != NULL)
1018 {
1019 g_warning ("Error on batch rename query for metadata: %s", error->message);
1020
1021 /* The dialog is being finalized at this point */
1022 if (error->code != G_IO_ERROR_CANCELLED)
1023 {
1024 nautilus_batch_rename_dialog_query_finished (query_data->dialog,
1025 query_data->date_order_hash_table,
1026 query_data->selection_metadata);
1027 }
1028
1029 g_free (query_data);
1030 }
1031 else
1032 {
1033 cursor_next (query_data, cursor);
1034 }
1035 }
1036
1037 void
check_metadata_for_selection(NautilusBatchRenameDialog * dialog,GList * selection,GCancellable * cancellable)1038 check_metadata_for_selection (NautilusBatchRenameDialog *dialog,
1039 GList *selection,
1040 GCancellable *cancellable)
1041 {
1042 TrackerSparqlConnection *connection;
1043 GString *query;
1044 GList *l;
1045 NautilusFile *file;
1046 GError *error;
1047 QueryData *query_data;
1048 gchar *file_name;
1049 FileMetadata *file_metadata;
1050 GList *selection_metadata;
1051 guint i;
1052 g_autofree gchar *parent_uri = NULL;
1053 gchar *file_name_escaped;
1054
1055 error = NULL;
1056 selection_metadata = NULL;
1057
1058 query = g_string_new ("SELECT "
1059 "nfo:fileName(?file) "
1060 "nie:contentCreated(?content) "
1061 "year(nie:contentCreated(?content)) "
1062 "month(nie:contentCreated(?content)) "
1063 "day(nie:contentCreated(?content)) "
1064 "hours(nie:contentCreated(?content)) "
1065 "minutes(nie:contentCreated(?content)) "
1066 "seconds(nie:contentCreated(?content)) "
1067 "nfo:model(nfo:equipment(?content)) "
1068 "nmm:seasonNumber(?content) "
1069 "nmm:episodeNumber(?content) "
1070 "nmm:trackNumber(?content) "
1071 "nmm:artistName(nmm:performer(?content)) "
1072 "nie:title(?content) "
1073 "nie:title(nmm:musicAlbum(?content)) "
1074 "WHERE { ?file a nfo:FileDataObject. ?file nie:url ?url. ?content nie:isStoredAs ?file. ");
1075
1076 parent_uri = nautilus_file_get_parent_uri (NAUTILUS_FILE (selection->data));
1077
1078 g_string_append_printf (query,
1079 "FILTER(tracker:uri-is-parent(\"%s\", ?url)) ",
1080 parent_uri);
1081
1082 for (l = selection; l != NULL; l = l->next)
1083 {
1084 file = NAUTILUS_FILE (l->data);
1085 file_name = nautilus_file_get_name (file);
1086 file_name_escaped = tracker_sparql_escape_string (file_name);
1087
1088 if (l == selection)
1089 {
1090 g_string_append_printf (query,
1091 "FILTER (nfo:fileName(?file) IN (\"%s\", ",
1092 file_name_escaped);
1093 }
1094 else if (l->next == NULL)
1095 {
1096 g_string_append_printf (query,
1097 "\"%s\")) ",
1098 file_name_escaped);
1099 }
1100 else
1101 {
1102 g_string_append_printf (query,
1103 "\"%s\", ",
1104 file_name_escaped);
1105 }
1106
1107 file_metadata = g_new0 (FileMetadata, 1);
1108 file_metadata->file_name = g_string_new (file_name);
1109 file_metadata->metadata[ORIGINAL_FILE_NAME] = g_string_new (file_name);
1110
1111 selection_metadata = g_list_prepend (selection_metadata, file_metadata);
1112
1113 g_free (file_name);
1114 g_free (file_name_escaped);
1115 }
1116
1117 selection_metadata = g_list_reverse (selection_metadata);
1118
1119 g_string_append (query, "} ORDER BY ASC(nie:contentCreated(?content))");
1120
1121 connection = nautilus_tracker_get_miner_fs_connection (&error);
1122 if (!connection)
1123 {
1124 if (error)
1125 {
1126 g_warning ("Error on batch rename tracker connection: %s", error->message);
1127 g_error_free (error);
1128 }
1129
1130 return;
1131 }
1132
1133 query_data = g_new (QueryData, 1);
1134 query_data->date_order_hash_table = g_hash_table_new_full (g_str_hash,
1135 g_str_equal,
1136 (GDestroyNotify) g_free,
1137 NULL);
1138 query_data->dialog = dialog;
1139 query_data->selection_metadata = selection_metadata;
1140 for (i = 0; i < G_N_ELEMENTS (metadata_tags_constants); i++)
1141 {
1142 query_data->has_metadata[i] = TRUE;
1143 }
1144 query_data->cancellable = cancellable;
1145
1146 /* Make an asynchronous query to the store */
1147 tracker_sparql_connection_query_async (connection,
1148 query->str,
1149 cancellable,
1150 batch_rename_dialog_query_callback,
1151 query_data);
1152
1153 g_string_free (query, TRUE);
1154 }
1155
1156 GList *
batch_rename_files_get_distinct_parents(GList * selection)1157 batch_rename_files_get_distinct_parents (GList *selection)
1158 {
1159 GList *result;
1160 GList *l1;
1161 NautilusFile *file;
1162 NautilusDirectory *directory;
1163 NautilusFile *parent;
1164
1165 result = NULL;
1166 for (l1 = selection; l1 != NULL; l1 = l1->next)
1167 {
1168 file = NAUTILUS_FILE (l1->data);
1169 parent = nautilus_file_get_parent (file);
1170 directory = nautilus_directory_get_for_file (parent);
1171 if (!g_list_find (result, directory))
1172 {
1173 result = g_list_prepend (result, directory);
1174 }
1175
1176 nautilus_file_unref (parent);
1177 }
1178
1179 return result;
1180 }
1181