Fix #35704: Simplify on scene with dupli recursion crashes
[blender.git] / source / blender / editors / object / object_group.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) Blender Foundation
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/object/object_group.c
29  *  \ingroup edobj
30  */
31
32
33 #include <string.h>
34
35 #include "BLI_blenlib.h"
36 #include "BLI_utildefines.h"
37
38 #include "DNA_group_types.h"
39 #include "DNA_object_types.h"
40 #include "DNA_scene_types.h"
41
42 #include "BKE_context.h"
43 #include "BKE_depsgraph.h"
44 #include "BKE_group.h"
45 #include "BKE_library.h"
46 #include "BKE_main.h"
47 #include "BKE_report.h"
48 #include "BKE_object.h"
49
50 #include "ED_screen.h"
51 #include "ED_object.h"
52
53 #include "WM_api.h"
54 #include "WM_types.h"
55
56 #include "RNA_access.h"
57 #include "RNA_define.h"
58 #include "RNA_enum_types.h"
59
60 #include "object_intern.h"
61
62 /********************* 3d view operators ***********************/
63
64 /* can be called with C == NULL */
65 static EnumPropertyItem *group_object_active_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), int *free)
66 {
67         Object *ob;
68         EnumPropertyItem *item = NULL, item_tmp = {0};
69         int totitem = 0;
70
71         if (C == NULL) {
72                 return DummyRNA_NULL_items;
73         }
74
75         ob = ED_object_context(C);
76
77         /* check that the action exists */
78         if (ob) {
79                 Group *group = NULL;
80                 int i = 0;
81
82                 while ((group = BKE_group_object_find(group, ob))) {
83                         item_tmp.identifier = item_tmp.name = group->id.name + 2;
84                         /* item_tmp.icon = ICON_ARMATURE_DATA; */
85                         item_tmp.value = i;
86                         RNA_enum_item_add(&item, &totitem, &item_tmp);
87                         i++;
88                 }
89         }
90
91         RNA_enum_item_end(&item, &totitem);
92         *free = 1;
93
94         return item;
95 }
96
97 /* get the group back from the enum index, quite awkward and UI specific */
98 static Group *group_object_active_find_index(Object *ob, const int group_object_index)
99 {
100         Group *group = NULL;
101         int i = 0;
102         while ((group = BKE_group_object_find(group, ob))) {
103                 if (i == group_object_index) {
104                         break;
105                 }
106                 i++;
107         }
108
109         return group;
110 }
111
112 static int objects_add_active_exec(bContext *C, wmOperator *op)
113 {
114         Object *ob = ED_object_context(C);
115         Main *bmain = CTX_data_main(C);
116         Scene *scene = CTX_data_scene(C);
117         int group_object_index = RNA_enum_get(op->ptr, "group");
118         int is_cycle = FALSE;
119
120         if (ob) {
121                 Group *group = group_object_active_find_index(ob, group_object_index);
122
123                 /* now add all selected objects from the group */
124                 if (group) {
125
126                         CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
127                         {
128                                 if (base->object->dup_group != group) {
129                                         BKE_group_object_add(group, base->object, scene, base);
130                                 }
131                                 else {
132                                         is_cycle = TRUE;
133                                 }
134                         }
135                         CTX_DATA_END;
136
137                         if (is_cycle) {
138                                 BKE_report(op->reports, RPT_WARNING, "Skipped some groups because of cycle detected");
139                         }
140
141                         DAG_relations_tag_update(bmain);
142                         WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
143
144                         return OPERATOR_FINISHED;
145                 }
146         }
147
148         return OPERATOR_CANCELLED;
149 }
150
151 void GROUP_OT_objects_add_active(wmOperatorType *ot)
152 {
153         PropertyRNA *prop;
154
155         /* identifiers */
156         ot->name = "Add Selected To Active Group";
157         ot->description = "Add the object to an object group that contains the active object";
158         ot->idname = "GROUP_OT_objects_add_active";
159         
160         /* api callbacks */
161         ot->exec = objects_add_active_exec;
162         ot->invoke = WM_menu_invoke;
163         ot->poll = ED_operator_objectmode;
164
165         /* flags */
166         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
167
168         /* properties */
169         prop = RNA_def_enum(ot->srna, "group", DummyRNA_NULL_items, 0, "Group", "The group to add other selected objects to");
170         RNA_def_enum_funcs(prop, group_object_active_itemf);
171         ot->prop = prop;
172 }
173
174 static int objects_remove_active_exec(bContext *C, wmOperator *op)
175 {
176         Main *bmain = CTX_data_main(C);
177         Scene *scene = CTX_data_scene(C);
178         Object *ob = OBACT;
179         Group *group;
180         int ok = 0;
181         
182         if (!ob) return OPERATOR_CANCELLED;
183         
184         /* linking to same group requires its own loop so we can avoid
185          * looking up the active objects groups each time */
186
187         for (group = bmain->group.first; group; group = group->id.next) {
188                 if (BKE_group_object_exists(group, ob)) {
189                         /* Assign groups to selected objects */
190                         CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
191                         {
192                                 BKE_group_object_unlink(group, base->object, scene, base);
193                                 ok = 1;
194                         }
195                         CTX_DATA_END;
196                 }
197         }
198         
199         if (!ok) BKE_report(op->reports, RPT_ERROR, "Active object contains no groups");
200         
201         DAG_relations_tag_update(bmain);
202         WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
203         
204         return OPERATOR_FINISHED;
205 }
206
207 void GROUP_OT_objects_remove_active(wmOperatorType *ot)
208 {
209         /* identifiers */
210         ot->name = "Remove Selected From Active Group";
211         ot->description = "Remove the object from an object group that contains the active object";
212         ot->idname = "GROUP_OT_objects_remove_active";
213         
214         /* api callbacks */
215         ot->exec = objects_remove_active_exec;
216         ot->poll = ED_operator_objectmode;
217         
218         /* flags */
219         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
220 }
221
222 static int group_objects_remove_all_exec(bContext *C, wmOperator *UNUSED(op))
223 {
224         Main *bmain = CTX_data_main(C);
225         Scene *scene = CTX_data_scene(C);
226
227         CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
228         {
229                 BKE_object_groups_clear(scene, base, base->object);
230         }
231         CTX_DATA_END;
232
233         DAG_relations_tag_update(bmain);
234         WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
235         
236         return OPERATOR_FINISHED;
237 }
238
239 void GROUP_OT_objects_remove_all(wmOperatorType *ot)
240 {
241         /* identifiers */
242         ot->name = "Remove From All Groups";
243         ot->description = "Remove selected objects from all groups or a selected group";
244         ot->idname = "GROUP_OT_objects_remove_all";
245         
246         /* api callbacks */
247         ot->exec = group_objects_remove_all_exec;
248         ot->poll = ED_operator_objectmode;
249         
250         /* flags */
251         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
252 }
253
254 static int group_objects_remove_exec(bContext *C, wmOperator *op)
255 {
256         Object *ob = ED_object_context(C);
257         Main *bmain = CTX_data_main(C);
258         Scene *scene = CTX_data_scene(C);
259         int group_object_index = RNA_enum_get(op->ptr, "group");
260
261         if (ob) {
262                 Group *group = group_object_active_find_index(ob, group_object_index);
263
264                 /* now remove all selected objects from the group */
265                 if (group) {
266
267                         CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
268                         {
269                                 BKE_group_object_unlink(group, base->object, scene, base);
270                         }
271                         CTX_DATA_END;
272
273                         DAG_relations_tag_update(bmain);
274                         WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
275
276                         return OPERATOR_FINISHED;
277                 }
278         }
279
280         return OPERATOR_CANCELLED;
281 }
282
283 void GROUP_OT_objects_remove(wmOperatorType *ot)
284 {
285         PropertyRNA *prop;
286
287         /* identifiers */
288         ot->name = "Remove From Group";
289         ot->description = "Remove selected objects from all groups or a selected group";
290         ot->idname = "GROUP_OT_objects_remove";
291
292         /* api callbacks */
293         ot->exec = group_objects_remove_exec;
294         ot->invoke = WM_menu_invoke;
295         ot->poll = ED_operator_objectmode;
296
297         /* flags */
298         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
299
300         /* properties */
301         prop = RNA_def_enum(ot->srna, "group", DummyRNA_NULL_items, 0, "Group", "The group to remove this object from");
302         RNA_def_enum_funcs(prop, group_object_active_itemf);
303         ot->prop = prop;
304 }
305
306 static int group_create_exec(bContext *C, wmOperator *op)
307 {
308         Main *bmain = CTX_data_main(C);
309         Scene *scene = CTX_data_scene(C);
310         Group *group = NULL;
311         char name[MAX_ID_NAME - 2]; /* id name */
312         
313         RNA_string_get(op->ptr, "name", name);
314         
315         group = BKE_group_add(bmain, name);
316                 
317         CTX_DATA_BEGIN (C, Base *, base, selected_bases)
318         {
319                 BKE_group_object_add(group, base->object, scene, base);
320         }
321         CTX_DATA_END;
322
323         DAG_relations_tag_update(bmain);
324         WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
325         
326         return OPERATOR_FINISHED;
327 }
328
329 void GROUP_OT_create(wmOperatorType *ot)
330 {
331         /* identifiers */
332         ot->name = "Create New Group";
333         ot->description = "Create an object group from selected objects";
334         ot->idname = "GROUP_OT_create";
335         
336         /* api callbacks */
337         ot->exec = group_create_exec;
338         ot->poll = ED_operator_objectmode;
339         
340         /* flags */
341         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
342         
343         RNA_def_string(ot->srna, "name", "Group", MAX_ID_NAME - 2, "Name", "Name of the new group");
344 }
345
346 /****************** properties window operators *********************/
347
348 static int group_add_exec(bContext *C, wmOperator *UNUSED(op))
349 {
350         Scene *scene = CTX_data_scene(C);
351         Object *ob = ED_object_context(C);
352         Main *bmain = CTX_data_main(C);
353         Group *group;
354
355         if (ob == NULL)
356                 return OPERATOR_CANCELLED;
357
358         group = BKE_group_add(bmain, "Group");
359         BKE_group_object_add(group, ob, scene, NULL);
360
361         WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
362
363         return OPERATOR_FINISHED;
364 }
365
366 void OBJECT_OT_group_add(wmOperatorType *ot)
367 {
368         /* identifiers */
369         ot->name = "Add to Group";
370         ot->idname = "OBJECT_OT_group_add";
371         ot->description = "Add an object to a new group";
372         
373         /* api callbacks */
374         ot->exec = group_add_exec;
375         ot->poll = ED_operator_objectmode;
376
377         /* flags */
378         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
379 }
380
381 static bool group_link_early_exit_check(Group *group, Object *object)
382 {
383         GroupObject *group_object;
384
385         for (group_object = group->gobject.first; group_object; group_object = group_object->next) {
386                 if (group_object->ob == object) {
387                         return true;
388                 }
389         }
390
391         return false;
392 }
393
394 static bool check_group_contains_object_recursive(Group *group, Object *object)
395 {
396         GroupObject *group_object;
397
398         if ((group->id.flag & LIB_DOIT) == 0) {
399                 /* Cycle already exists in groups, let's prevent further crappyness */
400                 return true;
401         }
402
403         group->id.flag &= ~LIB_DOIT;
404
405         for (group_object = group->gobject.first; group_object; group_object = group_object->next) {
406                 Object *current_object = group_object->ob;
407
408                 if (current_object == object) {
409                         return true;
410                 }
411
412                 if (current_object->dup_group) {
413                         if (check_group_contains_object_recursive(current_object->dup_group, object)) {
414                                 return true;
415                         }
416                 }
417         }
418
419         return false;
420 }
421
422 static int group_link_exec(bContext *C, wmOperator *op)
423 {
424         Main *bmain = CTX_data_main(C);
425         Scene *scene = CTX_data_scene(C);
426         Object *ob = ED_object_context(C);
427         Group *group = BLI_findlink(&CTX_data_main(C)->group, RNA_enum_get(op->ptr, "group"));
428
429         if (ELEM(NULL, ob, group))
430                 return OPERATOR_CANCELLED;
431
432         /* Early return check, if the object is already in group
433          * we could sckip all the dependency check and just consider
434          * operator is finished.
435          */
436         if (group_link_early_exit_check(group, ob)) {
437                 return OPERATOR_FINISHED;
438         }
439
440         /* Adding object to group which is used as dupligroup for self is bad idea.
441          *
442          * It is also  bad idea to add object to group which is in group which
443          * contains our current object.
444          */
445         tag_main_lb(&bmain->group, TRUE);
446         if (ob->dup_group == group || check_group_contains_object_recursive(group, ob)) {
447                 BKE_report(op->reports, RPT_ERROR, "Could not add the group because of dependency cycle detected");
448                 return OPERATOR_CANCELLED;
449         }
450
451         BKE_group_object_add(group, ob, scene, NULL);
452
453         WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
454
455         return OPERATOR_FINISHED;
456 }
457
458 void OBJECT_OT_group_link(wmOperatorType *ot)
459 {
460         PropertyRNA *prop;
461
462         /* identifiers */
463         ot->name = "Link to Group";
464         ot->idname = "OBJECT_OT_group_link";
465         ot->description = "Add an object to an existing group";
466         
467         /* api callbacks */
468         ot->exec = group_link_exec;
469         ot->invoke = WM_enum_search_invoke;
470         ot->poll = ED_operator_objectmode;
471
472         /* flags */
473         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
474
475         /* properties */
476         prop = RNA_def_enum(ot->srna, "group", DummyRNA_NULL_items, 0, "Group", "");
477         RNA_def_enum_funcs(prop, RNA_group_local_itemf);
478         ot->prop = prop;
479 }
480
481 static int group_remove_exec(bContext *C, wmOperator *UNUSED(op))
482 {
483         Scene *scene = CTX_data_scene(C);
484         Object *ob = ED_object_context(C);
485         Group *group = CTX_data_pointer_get_type(C, "group", &RNA_Group).data;
486
487         if (!ob || !group)
488                 return OPERATOR_CANCELLED;
489
490         BKE_group_object_unlink(group, ob, scene, NULL); /* base will be used if found */
491
492         WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
493         
494         return OPERATOR_FINISHED;
495 }
496
497 void OBJECT_OT_group_remove(wmOperatorType *ot)
498 {
499         /* identifiers */
500         ot->name = "Remove Group";
501         ot->idname = "OBJECT_OT_group_remove";
502         ot->description = "Remove the active object from this group";
503         
504         /* api callbacks */
505         ot->exec = group_remove_exec;
506         ot->poll = ED_operator_objectmode;
507
508         /* flags */
509         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
510 }
511