Cleanup: link/append: get rid of booleans in func parameters.
[blender.git] / source / blender / windowmanager / intern / wm_files_link.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version. 
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2007 Blender Foundation.
19  * All rights reserved.
20  *
21  * 
22  * Contributor(s): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/windowmanager/intern/wm_files_link.c
28  *  \ingroup wm
29  *
30  * Functions for dealing with append/link operators and helpers.
31  */
32
33
34 #include <float.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <stdio.h>
38 #include <stddef.h>
39 #include <assert.h>
40 #include <errno.h>
41
42 #include "MEM_guardedalloc.h"
43
44 #include "DNA_ID.h"
45 #include "DNA_screen_types.h"
46 #include "DNA_scene_types.h"
47 #include "DNA_windowmanager_types.h"
48
49
50
51 #include "BLI_blenlib.h"
52 #include "BLI_bitmap.h"
53 #include "BLI_linklist.h"
54 #include "BLI_math.h"
55 #include "BLI_memarena.h"
56 #include "BLI_utildefines.h"
57 #include "BLI_ghash.h"
58
59 #include "BLO_readfile.h"
60
61 #include "BKE_context.h"
62 #include "BKE_depsgraph.h"
63 #include "BKE_library.h"
64 #include "BKE_library_remap.h"
65 #include "BKE_global.h"
66 #include "BKE_main.h"
67 #include "BKE_report.h"
68 #include "BKE_scene.h"
69
70 #include "BKE_idcode.h"
71
72
73 #include "IMB_colormanagement.h"
74
75 #include "ED_screen.h"
76
77 #include "GPU_material.h"
78
79 #include "RNA_access.h"
80 #include "RNA_define.h"
81
82
83 #include "WM_api.h"
84 #include "WM_types.h"
85
86 #include "wm_files.h"
87
88 /* **************** link/append *************** */
89
90 static int wm_link_append_poll(bContext *C)
91 {
92         if (WM_operator_winactive(C)) {
93                 /* linking changes active object which is pretty useful in general,
94                  * but which totally confuses edit mode (i.e. it becoming not so obvious
95                  * to leave from edit mode and invalid tools in toolbar might be displayed)
96                  * so disable link/append when in edit mode (sergey) */
97                 if (CTX_data_edit_object(C))
98                         return 0;
99
100                 return 1;
101         }
102
103         return 0;
104 }
105
106 static int wm_link_append_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
107 {
108         if (RNA_struct_property_is_set(op->ptr, "filepath")) {
109                 return WM_operator_call_notest(C, op);
110         }
111         else {
112                 /* XXX TODO solve where to get last linked library from */
113                 if (G.lib[0] != '\0') {
114                         RNA_string_set(op->ptr, "filepath", G.lib);
115                 }
116                 else if (G.relbase_valid) {
117                         char path[FILE_MAX];
118                         BLI_strncpy(path, G.main->name, sizeof(G.main->name));
119                         BLI_parent_dir(path);
120                         RNA_string_set(op->ptr, "filepath", path);
121                 }
122                 WM_event_add_fileselect(C, op);
123                 return OPERATOR_RUNNING_MODAL;
124         }
125 }
126
127 static short wm_link_append_flag(wmOperator *op)
128 {
129         PropertyRNA *prop;
130         short flag = 0;
131
132         if (RNA_boolean_get(op->ptr, "autoselect"))
133                 flag |= FILE_AUTOSELECT;
134         if (RNA_boolean_get(op->ptr, "active_layer"))
135                 flag |= FILE_ACTIVELAY;
136         if ((prop = RNA_struct_find_property(op->ptr, "relative_path")) && RNA_property_boolean_get(op->ptr, prop))
137                 flag |= FILE_RELPATH;
138         if (RNA_boolean_get(op->ptr, "link"))
139                 flag |= FILE_LINK;
140         if (RNA_boolean_get(op->ptr, "instance_groups"))
141                 flag |= FILE_GROUP_INSTANCE;
142
143         return flag;
144 }
145
146 typedef struct WMLinkAppendDataItem {
147         char *name;
148         BLI_bitmap *libraries;  /* All libs (from WMLinkAppendData.libraries) to try to load this ID from. */
149         short idcode;
150
151         ID *new_id;
152         void *customdata;
153 } WMLinkAppendDataItem;
154
155 typedef struct WMLinkAppendData {
156         LinkNodePair libraries;
157         LinkNodePair items;
158         int num_libraries;
159         int num_items;
160         int flag;  /* Combines eFileSel_Params_Flag from DNA_space_types.h and BLO_LibLinkFlags from BLO_readfile.h */
161
162         /* Internal 'private' data */
163         MemArena *memarena;
164 } WMLinkAppendData;
165
166 static WMLinkAppendData *wm_link_append_data_new(const int flag)
167 {
168         MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
169         WMLinkAppendData *lapp_data = BLI_memarena_calloc(ma, sizeof(*lapp_data));
170
171         lapp_data->flag = flag;
172         lapp_data->memarena = ma;
173
174         return lapp_data;
175 }
176
177 static void wm_link_append_data_free(WMLinkAppendData *lapp_data)
178 {
179         BLI_memarena_free(lapp_data->memarena);
180 }
181
182 /* WARNING! *Never* call wm_link_append_data_library_add() after having added some items! */
183
184 static void wm_link_append_data_library_add(WMLinkAppendData *lapp_data, const char *libname)
185 {
186         size_t len = strlen(libname) + 1;
187         char *libpath = BLI_memarena_alloc(lapp_data->memarena, len);
188
189         BLI_strncpy(libpath, libname, len);
190         BLI_linklist_append_arena(&lapp_data->libraries, libpath, lapp_data->memarena);
191         lapp_data->num_libraries++;
192 }
193
194 static WMLinkAppendDataItem *wm_link_append_data_item_add(
195         WMLinkAppendData *lapp_data, const char *idname, const short idcode, void *customdata)
196 {
197         WMLinkAppendDataItem *item = BLI_memarena_alloc(lapp_data->memarena, sizeof(*item));
198         size_t len = strlen(idname) + 1;
199
200         item->name = BLI_memarena_alloc(lapp_data->memarena, len);
201         BLI_strncpy(item->name, idname, len);
202         item->idcode = idcode;
203         item->libraries = BLI_BITMAP_NEW_MEMARENA(lapp_data->memarena, lapp_data->num_libraries);
204
205         item->new_id = NULL;
206         item->customdata = customdata;
207
208         BLI_linklist_append_arena(&lapp_data->items, item, lapp_data->memarena);
209         lapp_data->num_items++;
210
211         return item;
212 }
213
214 static void wm_link_do(WMLinkAppendData *lapp_data, ReportList *reports, Main *bmain, Scene *scene, View3D *v3d)
215 {
216         Main *mainl;
217         BlendHandle *bh;
218         Library *lib;
219
220         const int flag = lapp_data->flag;
221
222         LinkNode *liblink, *itemlink;
223         int lib_idx, item_idx;
224
225         BLI_assert(lapp_data->num_items && lapp_data->num_libraries);
226
227         for (lib_idx = 0, liblink = lapp_data->libraries.list; liblink; lib_idx++, liblink = liblink->next) {
228                 char *libname = liblink->link;
229
230                 bh = BLO_blendhandle_from_file(libname, reports);
231
232                 if (bh == NULL) {
233                         /* Unlikely since we just browsed it, but possible
234                          * Error reports will have been made by BLO_blendhandle_from_file() */
235                         continue;
236                 }
237
238                 /* here appending/linking starts */
239                 mainl = BLO_library_link_begin(bmain, &bh, libname);
240                 lib = mainl->curlib;
241                 BLI_assert(lib);
242                 UNUSED_VARS_NDEBUG(lib);
243
244                 if (mainl->versionfile < 250) {
245                         BKE_reportf(reports, RPT_WARNING,
246                                     "Linking or appending from a very old .blend file format (%d.%d), no animation conversion will "
247                                     "be done! You may want to re-save your lib file with current Blender",
248                                     mainl->versionfile, mainl->subversionfile);
249                 }
250
251                 /* For each lib file, we try to link all items belonging to that lib,
252                  * and tag those successful to not try to load them again with the other libs. */
253                 for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) {
254                         WMLinkAppendDataItem *item = itemlink->link;
255                         ID *new_id;
256
257                         if (!BLI_BITMAP_TEST(item->libraries, lib_idx)) {
258                                 continue;
259                         }
260
261                         new_id = BLO_library_link_named_part_ex(mainl, &bh, item->idcode, item->name, flag, scene, v3d);
262
263                         if (new_id) {
264                                 /* If the link is successful, clear item's libs 'todo' flags.
265                                  * This avoids trying to link same item with other libraries to come. */
266                                 BLI_BITMAP_SET_ALL(item->libraries, false, lapp_data->num_libraries);
267                                 item->new_id = new_id;
268                         }
269                 }
270
271                 BLO_library_link_end(mainl, &bh, flag, scene, v3d);
272                 BLO_blendhandle_close(bh);
273         }
274 }
275
276 static int wm_link_append_exec(bContext *C, wmOperator *op)
277 {
278         Main *bmain = CTX_data_main(C);
279         Scene *scene = CTX_data_scene(C);
280         PropertyRNA *prop;
281         WMLinkAppendData *lapp_data;
282         char path[FILE_MAX_LIBEXTRA], root[FILE_MAXDIR], libname[FILE_MAX_LIBEXTRA], relname[FILE_MAX];
283         char *group, *name;
284         int totfiles = 0;
285         short flag;
286
287         RNA_string_get(op->ptr, "filename", relname);
288         RNA_string_get(op->ptr, "directory", root);
289
290         BLI_join_dirfile(path, sizeof(path), root, relname);
291
292         /* test if we have a valid data */
293         if (!BLO_library_path_explode(path, libname, &group, &name)) {
294                 BKE_reportf(op->reports, RPT_ERROR, "'%s': not a library", path);
295                 return OPERATOR_CANCELLED;
296         }
297         else if (!group) {
298                 BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path);
299                 return OPERATOR_CANCELLED;
300         }
301         else if (BLI_path_cmp(bmain->name, libname) == 0) {
302                 BKE_reportf(op->reports, RPT_ERROR, "'%s': cannot use current file as library", path);
303                 return OPERATOR_CANCELLED;
304         }
305
306         /* check if something is indicated for append/link */
307         prop = RNA_struct_find_property(op->ptr, "files");
308         if (prop) {
309                 totfiles = RNA_property_collection_length(op->ptr, prop);
310                 if (totfiles == 0) {
311                         if (!name) {
312                                 BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path);
313                                 return OPERATOR_CANCELLED;
314                         }
315                 }
316         }
317         else if (!name) {
318                 BKE_reportf(op->reports, RPT_ERROR, "'%s': nothing indicated", path);
319                 return OPERATOR_CANCELLED;
320         }
321
322         flag = wm_link_append_flag(op);
323
324         /* sanity checks for flag */
325         if (scene && scene->id.lib) {
326                 BKE_reportf(op->reports, RPT_WARNING,
327                             "Scene '%s' is linked, instantiation of objects & groups is disabled", scene->id.name + 2);
328                 flag &= ~FILE_GROUP_INSTANCE;
329                 scene = NULL;
330         }
331
332         /* We need to add nothing from BLO_LibLinkFlags to flag here. */
333
334         /* from here down, no error returns */
335
336         if (scene && RNA_boolean_get(op->ptr, "autoselect")) {
337                 BKE_scene_base_deselect_all(scene);
338         }
339         
340         /* tag everything, all untagged data can be made local
341          * its also generally useful to know what is new
342          *
343          * take extra care BKE_main_id_flag_all(bmain, LIB_TAG_PRE_EXISTING, false) is called after! */
344         BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true);
345
346         /* We define our working data...
347          * Note that here, each item 'uses' one library, and only one. */
348         lapp_data = wm_link_append_data_new(flag);
349         if (totfiles != 0) {
350                 GHash *libraries = BLI_ghash_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, __func__);
351                 int lib_idx = 0;
352
353                 RNA_BEGIN (op->ptr, itemptr, "files")
354                 {
355                         RNA_string_get(&itemptr, "name", relname);
356
357                         BLI_join_dirfile(path, sizeof(path), root, relname);
358
359                         if (BLO_library_path_explode(path, libname, &group, &name)) {
360                                 if (!group || !name) {
361                                         continue;
362                                 }
363
364                                 if (!BLI_ghash_haskey(libraries, libname)) {
365                                         BLI_ghash_insert(libraries, BLI_strdup(libname), SET_INT_IN_POINTER(lib_idx));
366                                         lib_idx++;
367                                         wm_link_append_data_library_add(lapp_data, libname);
368                                 }
369                         }
370                 }
371                 RNA_END;
372
373                 RNA_BEGIN (op->ptr, itemptr, "files")
374                 {
375                         RNA_string_get(&itemptr, "name", relname);
376
377                         BLI_join_dirfile(path, sizeof(path), root, relname);
378
379                         if (BLO_library_path_explode(path, libname, &group, &name)) {
380                                 WMLinkAppendDataItem *item;
381                                 if (!group || !name) {
382                                         printf("skipping %s\n", path);
383                                         continue;
384                                 }
385
386                                 lib_idx = GET_INT_FROM_POINTER(BLI_ghash_lookup(libraries, libname));
387
388                                 item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL);
389                                 BLI_BITMAP_ENABLE(item->libraries, lib_idx);
390                         }
391                 }
392                 RNA_END;
393
394                 BLI_ghash_free(libraries, MEM_freeN, NULL);
395         }
396         else {
397                 WMLinkAppendDataItem *item;
398
399                 wm_link_append_data_library_add(lapp_data, libname);
400                 item = wm_link_append_data_item_add(lapp_data, name, BKE_idcode_from_name(group), NULL);
401                 BLI_BITMAP_ENABLE(item->libraries, 0);
402         }
403
404         /* XXX We'd need re-entrant locking on Main for this to work... */
405         /* BKE_main_lock(bmain); */
406
407         wm_link_do(lapp_data, op->reports, bmain, scene, CTX_wm_view3d(C));
408
409         /* BKE_main_unlock(bmain); */
410
411         /* mark all library linked objects to be updated */
412         BKE_main_lib_objects_recalc_all(bmain);
413         IMB_colormanagement_check_file_config(bmain);
414
415         /* append, rather than linking */
416         if ((flag & FILE_LINK) == 0) {
417                 const bool set_fake = RNA_boolean_get(op->ptr, "set_fake");
418                 const bool use_recursive = RNA_boolean_get(op->ptr, "use_recursive");
419
420                 if (use_recursive) {
421                         BKE_library_make_local(bmain, NULL, NULL, true, set_fake);
422                 }
423                 else {
424                         LinkNode *itemlink;
425                         GSet *done_libraries = BLI_gset_new_ex(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp,
426                                                                __func__, lapp_data->num_libraries);
427
428                         for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) {
429                                 ID *new_id = ((WMLinkAppendDataItem *)(itemlink->link))->new_id;
430
431                                 if (new_id && !BLI_gset_haskey(done_libraries, new_id->lib)) {
432                                         BKE_library_make_local(bmain, new_id->lib, NULL, true, set_fake);
433                                         BLI_gset_insert(done_libraries, new_id->lib);
434                                 }
435                         }
436
437                         BLI_gset_free(done_libraries, NULL);
438                 }
439         }
440
441         wm_link_append_data_free(lapp_data);
442
443         /* important we unset, otherwise these object wont
444          * link into other scenes from this blend file */
445         BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false);
446
447         /* recreate dependency graph to include new objects */
448         if (scene) {
449                 DAG_scene_relations_rebuild(bmain, scene);
450         }
451         
452         /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */
453         GPU_materials_free();
454
455         /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */
456         BLI_strncpy(G.lib, root, FILE_MAX);
457
458         WM_event_add_notifier(C, NC_WINDOW, NULL);
459
460         return OPERATOR_FINISHED;
461 }
462
463 static void wm_link_append_properties_common(wmOperatorType *ot, bool is_link)
464 {
465         PropertyRNA *prop;
466
467         /* better not save _any_ settings for this operator */
468         /* properties */
469         prop = RNA_def_boolean(ot->srna, "link", is_link,
470                                "Link", "Link the objects or data-blocks rather than appending");
471         RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
472         prop = RNA_def_boolean(ot->srna, "autoselect", true,
473                                "Select", "Select new objects");
474         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
475         prop = RNA_def_boolean(ot->srna, "active_layer", true,
476                                "Active Layer", "Put new objects on the active layer");
477         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
478         prop = RNA_def_boolean(ot->srna, "instance_groups", is_link,
479                                "Instance Groups", "Create Dupli-Group instances for each group");
480         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
481 }
482
483 void WM_OT_link(wmOperatorType *ot)
484 {
485         ot->name = "Link from Library";
486         ot->idname = "WM_OT_link";
487         ot->description = "Link from a Library .blend file";
488         
489         ot->invoke = wm_link_append_invoke;
490         ot->exec = wm_link_append_exec;
491         ot->poll = wm_link_append_poll;
492         
493         ot->flag |= OPTYPE_UNDO;
494
495         WM_operator_properties_filesel(
496                 ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE,
497                 WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_RELPATH | WM_FILESEL_FILES,
498                 FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
499         
500         wm_link_append_properties_common(ot, true);
501 }
502
503 void WM_OT_append(wmOperatorType *ot)
504 {
505         ot->name = "Append from Library";
506         ot->idname = "WM_OT_append";
507         ot->description = "Append from a Library .blend file";
508
509         ot->invoke = wm_link_append_invoke;
510         ot->exec = wm_link_append_exec;
511         ot->poll = wm_link_append_poll;
512
513         ot->flag |= OPTYPE_UNDO;
514
515         WM_operator_properties_filesel(
516                 ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER | FILE_TYPE_BLENDERLIB, FILE_LOADLIB, FILE_OPENFILE,
517                 WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_FILES,
518                 FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
519
520         wm_link_append_properties_common(ot, false);
521         RNA_def_boolean(ot->srna, "set_fake", false, "Fake User",
522                         "Set Fake User for appended items (except Objects and Groups)");
523         RNA_def_boolean(ot->srna, "use_recursive", true, "Localize All",
524                         "Localize all appended data, including those indirectly linked from other libraries");
525 }
526
527 /** \name Reload/relocate libraries.
528  *
529  * \{ */
530
531 static int wm_lib_relocate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
532 {
533         Library *lib;
534         char lib_name[MAX_NAME];
535
536         RNA_string_get(op->ptr, "library", lib_name);
537         lib = (Library *)BKE_libblock_find_name_ex(CTX_data_main(C), ID_LI, lib_name);
538
539         if (lib) {
540                 if (lib->parent) {
541                         BKE_reportf(op->reports, RPT_ERROR_INVALID_INPUT,
542                                     "Cannot relocate indirectly linked library '%s'", lib->filepath);
543                         return OPERATOR_CANCELLED;
544                 }
545                 RNA_string_set(op->ptr, "filepath", lib->filepath);
546
547                 WM_event_add_fileselect(C, op);
548
549                 return OPERATOR_RUNNING_MODAL;
550         }
551
552         return OPERATOR_CANCELLED;
553 }
554
555 static void lib_relocate_do(
556         Main *bmain, Scene *scene,
557         Library *library, WMLinkAppendData *lapp_data, ReportList *reports, const bool do_reload)
558 {
559         ListBase *lbarray[MAX_LIBARRAY];
560         int lba_idx;
561
562         LinkNode *itemlink;
563         int item_idx;
564
565         /* Remove all IDs to be reloaded from Main. */
566         lba_idx = set_listbasepointers(bmain, lbarray);
567         while (lba_idx--) {
568                 ID *id = lbarray[lba_idx]->first;
569                 const short idcode = id ? GS(id->name) : 0;
570
571                 if (!id || !BKE_idcode_is_linkable(idcode)) {
572                         /* No need to reload non-linkable datatypes, those will get relinked with their 'users ID'. */
573                         continue;
574                 }
575
576                 for (; id; id = id->next) {
577                         if (id->lib == library) {
578                                 WMLinkAppendDataItem *item;
579
580                                 /* We remove it from current Main, and add it to items to link... */
581                                 /* Note that non-linkable IDs (like e.g. shapekeys) are also explicitly linked here... */
582                                 BLI_remlink(lbarray[lba_idx], id);
583                                 item = wm_link_append_data_item_add(lapp_data, id->name + 2, idcode, id);
584                                 BLI_BITMAP_SET_ALL(item->libraries, true, lapp_data->num_libraries);
585
586 #ifdef PRINT_DEBUG
587                                 printf("\tdatablock to seek for: %s\n", id->name);
588 #endif
589                         }
590                 }
591         }
592
593         BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true);
594
595         /* We do not want any instanciation here! */
596         wm_link_do(lapp_data, reports, bmain, NULL, NULL);
597
598         BKE_main_lock(bmain);
599
600         /* We add back old id to bmain.
601          * We need to do this in a first, separated loop, otherwise some of those may not be handled by
602          * ID remapping, which means they would still reference old data to be deleted... */
603         for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) {
604                 WMLinkAppendDataItem *item = itemlink->link;
605                 ID *old_id = item->customdata;
606
607                 BLI_assert(old_id);
608                 BLI_addtail(which_libbase(bmain, GS(old_id->name)), old_id);
609         }
610
611         /* Note that in reload case, we also want to replace indirect usages. */
612         const short remap_flags = ID_REMAP_SKIP_NEVER_NULL_USAGE | ID_REMAP_NO_INDIRECT_PROXY_DATA_USAGE |
613                                   (do_reload ? 0 : ID_REMAP_SKIP_INDIRECT_USAGE);
614         for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) {
615                 WMLinkAppendDataItem *item = itemlink->link;
616                 ID *old_id = item->customdata;
617                 ID *new_id = item->new_id;
618
619                 BLI_assert(old_id);
620                 if (do_reload) {
621                         /* Since we asked for placeholders in case of missing IDs, we expect to always get a valid one. */
622                         BLI_assert(new_id);
623                 }
624                 if (new_id) {
625 #ifdef PRINT_DEBUG
626                         printf("before remap, old_id users: %d, new_id users: %d\n", old_id->us, new_id->us);
627 #endif
628                         BKE_libblock_remap_locked(bmain, old_id, new_id, remap_flags);
629
630                         if (old_id->flag & LIB_FAKEUSER) {
631                                 id_fake_user_clear(old_id);
632                                 id_fake_user_set(new_id);
633                         }
634
635 #ifdef PRINT_DEBUG
636                         printf("after remap, old_id users: %d, new_id users: %d\n", old_id->us, new_id->us);
637 #endif
638
639                         /* In some cases, new_id might become direct link, remove parent of library in this case. */
640                         if (new_id->lib->parent && (new_id->tag & LIB_TAG_INDIRECT) == 0) {
641                                 if (do_reload) {
642                                         BLI_assert(0);  /* Should not happen in 'pure' reload case... */
643                                 }
644                                 new_id->lib->parent = NULL;
645                         }
646                 }
647
648                 if (old_id->us > 0 && new_id && old_id->lib == new_id->lib) {
649                         /* Note that this *should* not happen - but better be safe than sorry in this area, at least until we are
650                          * 100% sure this cannot ever happen.
651                          * Also, we can safely assume names were unique so far, so just replacing '.' by '~' should work,
652                          * but this does not totally rules out the possibility of name collision. */
653                         size_t len = strlen(old_id->name);
654                         size_t dot_pos;
655                         bool has_num = false;
656
657                         for (dot_pos = len; dot_pos--;) {
658                                 char c = old_id->name[dot_pos];
659                                 if (c == '.') {
660                                         break;
661                                 }
662                                 else if (c < '0' || c > '9') {
663                                         has_num = false;
664                                         break;
665                                 }
666                                 has_num = true;
667                         }
668
669                         if (has_num) {
670                                 old_id->name[dot_pos] = '~';
671                         }
672                         else {
673                                 len = MIN2(len, MAX_ID_NAME - 7);
674                                 BLI_strncpy(&old_id->name[len], "~000", 7);
675                         }
676
677                         id_sort_by_name(which_libbase(bmain, GS(old_id->name)), old_id);
678
679                         BKE_reportf(reports, RPT_WARNING,
680                                     "Lib Reload: Replacing all references to old data-block '%s' by reloaded one failed, "
681                                     "old one (%d remaining users) had to be kept and was renamed to '%s'",
682                                     new_id->name, old_id->us, old_id->name);
683                 }
684         }
685
686         BKE_main_unlock(bmain);
687
688         for (item_idx = 0, itemlink = lapp_data->items.list; itemlink; item_idx++, itemlink = itemlink->next) {
689                 WMLinkAppendDataItem *item = itemlink->link;
690                 ID *old_id = item->customdata;
691
692                 if (old_id->us == 0) {
693                         BKE_libblock_free(bmain, old_id);
694                 }
695         }
696
697         /* Some datablocks can get reloaded/replaced 'silently' because they are not linkable (shape keys e.g.),
698          * so we need another loop here to clear old ones if possible. */
699         lba_idx = set_listbasepointers(bmain, lbarray);
700         while (lba_idx--) {
701                 ID *id, *id_next;
702                 for (id  = lbarray[lba_idx]->first; id; id = id_next) {
703                         id_next = id->next;
704                         /* XXX That check may be a bit to generic/permissive? */
705                         if (id->lib && (id->flag & LIB_TAG_PRE_EXISTING) && id->us == 0) {
706                                 BKE_libblock_free(bmain, id);
707                         }
708                 }
709         }
710
711         /* Get rid of no more used libraries... */
712         BKE_main_id_tag_idcode(bmain, ID_LI, LIB_TAG_DOIT, true);
713         lba_idx = set_listbasepointers(bmain, lbarray);
714         while (lba_idx--) {
715                 ID *id;
716                 for (id = lbarray[lba_idx]->first; id; id = id->next) {
717                         if (id->lib) {
718                                 id->lib->id.tag &= ~LIB_TAG_DOIT;
719                         }
720                 }
721         }
722         Library *lib, *lib_next;
723         for (lib = which_libbase(bmain, ID_LI)->first; lib; lib = lib_next) {
724                 lib_next = lib->id.next;
725                 if (lib->id.tag & LIB_TAG_DOIT) {
726                         id_us_clear_real(&lib->id);
727                         if (lib->id.us == 0) {
728                                 BKE_libblock_free(bmain, (ID *)lib);
729                         }
730                 }
731         }
732
733         BKE_main_lib_objects_recalc_all(bmain);
734         IMB_colormanagement_check_file_config(bmain);
735
736         /* important we unset, otherwise these object wont
737          * link into other scenes from this blend file */
738         BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false);
739
740         /* recreate dependency graph to include new objects */
741         DAG_scene_relations_rebuild(bmain, scene);
742
743         /* free gpu materials, some materials depend on existing objects, such as lamps so freeing correctly refreshes */
744         GPU_materials_free();
745 }
746
747 void WM_lib_reload(Library *lib, bContext *C, ReportList *reports)
748 {
749         if (!BLO_has_bfile_extension(lib->filepath)) {
750                 BKE_reportf(reports, RPT_ERROR, "'%s' is not a valid library filepath", lib->filepath);
751                 return;
752         }
753
754         if (!BLI_exists(lib->filepath)) {
755                 BKE_reportf(reports, RPT_ERROR,
756                             "Trying to reload library '%s' from invalid path '%s'", lib->id.name, lib->filepath);
757                 return;
758         }
759
760         WMLinkAppendData *lapp_data = wm_link_append_data_new(BLO_LIBLINK_USE_PLACEHOLDERS | BLO_LIBLINK_FORCE_INDIRECT);
761
762         wm_link_append_data_library_add(lapp_data, lib->filepath);
763
764         lib_relocate_do(CTX_data_main(C), CTX_data_scene(C), lib, lapp_data, reports, true);
765
766         wm_link_append_data_free(lapp_data);
767
768         WM_event_add_notifier(C, NC_WINDOW, NULL);
769 }
770
771 static int wm_lib_relocate_exec_do(bContext *C, wmOperator *op, bool do_reload)
772 {
773         Library *lib;
774         char lib_name[MAX_NAME];
775
776         RNA_string_get(op->ptr, "library", lib_name);
777         lib = (Library *)BKE_libblock_find_name_ex(CTX_data_main(C), ID_LI, lib_name);
778
779         if (lib) {
780                 Main *bmain = CTX_data_main(C);
781                 Scene *scene = CTX_data_scene(C);
782                 PropertyRNA *prop;
783                 WMLinkAppendData *lapp_data;
784
785                 char path[FILE_MAX], root[FILE_MAXDIR], libname[FILE_MAX], relname[FILE_MAX];
786                 short flag = 0;
787
788                 if (RNA_boolean_get(op->ptr, "relative_path")) {
789                         flag |= FILE_RELPATH;
790                 }
791
792                 if (lib->parent && !do_reload) {
793                         BKE_reportf(op->reports, RPT_ERROR_INVALID_INPUT,
794                                     "Cannot relocate indirectly linked library '%s'", lib->filepath);
795                         return OPERATOR_CANCELLED;
796                 }
797
798                 RNA_string_get(op->ptr, "directory", root);
799                 RNA_string_get(op->ptr, "filename", libname);
800
801                 if (!BLO_has_bfile_extension(libname)) {
802                         BKE_report(op->reports, RPT_ERROR, "Not a library");
803                         return OPERATOR_CANCELLED;
804                 }
805
806                 BLI_join_dirfile(path, sizeof(path), root, libname);
807
808                 if (!BLI_exists(path)) {
809                         BKE_reportf(op->reports, RPT_ERROR_INVALID_INPUT,
810                                     "Trying to reload or relocate library '%s' to invalid path '%s'", lib->id.name, path);
811                         return OPERATOR_CANCELLED;
812                 }
813
814                 if (BLI_path_cmp(lib->filepath, path) == 0) {
815 #ifdef PRINT_DEBUG
816                         printf("We are supposed to reload '%s' lib (%d)...\n", lib->filepath, lib->id.us);
817 #endif
818
819                         do_reload = true;
820
821                         lapp_data = wm_link_append_data_new(flag);
822                         wm_link_append_data_library_add(lapp_data, path);
823                 }
824                 else {
825                         int totfiles = 0;
826
827 #ifdef PRINT_DEBUG
828                         printf("We are supposed to relocate '%s' lib to new '%s' one...\n", lib->filepath, libname);
829 #endif
830
831                         /* Check if something is indicated for relocate. */
832                         prop = RNA_struct_find_property(op->ptr, "files");
833                         if (prop) {
834                                 totfiles = RNA_property_collection_length(op->ptr, prop);
835                                 if (totfiles == 0) {
836                                         if (!libname[0]) {
837                                                 BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
838                                                 return OPERATOR_CANCELLED;
839                                         }
840                                 }
841                         }
842
843                         lapp_data = wm_link_append_data_new(flag);
844
845                         if (totfiles) {
846                                 RNA_BEGIN (op->ptr, itemptr, "files")
847                                 {
848                                         RNA_string_get(&itemptr, "name", relname);
849
850                                         BLI_join_dirfile(path, sizeof(path), root, relname);
851
852                                         if (BLI_path_cmp(path, lib->filepath) == 0 || !BLO_has_bfile_extension(relname)) {
853                                                 continue;
854                                         }
855
856 #ifdef PRINT_DEBUG
857                                         printf("\t candidate new lib to reload datablocks from: %s\n", path);
858 #endif
859                                         wm_link_append_data_library_add(lapp_data, path);
860                                 }
861                                 RNA_END;
862                         }
863                         else {
864 #ifdef PRINT_DEBUG
865                                 printf("\t candidate new lib to reload datablocks from: %s\n", path);
866 #endif
867                                 wm_link_append_data_library_add(lapp_data, path);
868                         }
869                 }
870
871                 if (do_reload) {
872                         lapp_data->flag |= BLO_LIBLINK_USE_PLACEHOLDERS | BLO_LIBLINK_FORCE_INDIRECT;
873                 }
874
875                 lib_relocate_do(bmain, scene, lib, lapp_data, op->reports, do_reload);
876
877                 wm_link_append_data_free(lapp_data);
878
879                 /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */
880                 BLI_strncpy(G.lib, root, FILE_MAX);
881
882                 WM_event_add_notifier(C, NC_WINDOW, NULL);
883
884                 return OPERATOR_FINISHED;
885         }
886
887         return OPERATOR_CANCELLED;
888 }
889
890 static int wm_lib_relocate_exec(bContext *C, wmOperator *op)
891 {
892         return wm_lib_relocate_exec_do(C, op, false);
893 }
894
895 void WM_OT_lib_relocate(wmOperatorType *ot)
896 {
897         PropertyRNA *prop;
898
899         ot->name = "Relocate Library";
900         ot->idname = "WM_OT_lib_relocate";
901         ot->description = "Relocate the given library to one or several others";
902
903         ot->invoke = wm_lib_relocate_invoke;
904         ot->exec = wm_lib_relocate_exec;
905
906         ot->flag |= OPTYPE_UNDO;
907
908         prop = RNA_def_string(ot->srna, "library", NULL, MAX_NAME, "Library", "Library to relocate");
909         RNA_def_property_flag(prop, PROP_HIDDEN);
910
911         WM_operator_properties_filesel(
912                     ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE,
913                     WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_FILES | WM_FILESEL_RELPATH,
914                     FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
915 }
916
917 static int wm_lib_reload_exec(bContext *C, wmOperator *op)
918 {
919         return wm_lib_relocate_exec_do(C, op, true);
920 }
921
922 void WM_OT_lib_reload(wmOperatorType *ot)
923 {
924         PropertyRNA *prop;
925
926         ot->name = "Reload Library";
927         ot->idname = "WM_OT_lib_reload";
928         ot->description = "Reload the given library";
929
930         ot->exec = wm_lib_reload_exec;
931
932         ot->flag |= OPTYPE_UNDO;
933
934         prop = RNA_def_string(ot->srna, "library", NULL, MAX_NAME, "Library", "Library to reload");
935         RNA_def_property_flag(prop, PROP_HIDDEN);
936
937         WM_operator_properties_filesel(
938                     ot, FILE_TYPE_FOLDER | FILE_TYPE_BLENDER, FILE_BLENDER, FILE_OPENFILE,
939                     WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_RELPATH,
940                     FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
941 }
942
943 /** \} */