3b9d54d76d64a6455ebeb3ff9618c334df82dfee
[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_main.h"
46 #include "BKE_report.h"
47
48 #include "ED_screen.h"
49 #include "ED_object.h"
50
51 #include "WM_api.h"
52 #include "WM_types.h"
53
54 #include "RNA_access.h"
55 #include "RNA_define.h"
56 #include "RNA_enum_types.h"
57
58 #include "object_intern.h"
59
60 /********************* 3d view operators ***********************/
61
62 /* can be called with C == NULL */
63 static EnumPropertyItem *group_object_active_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), int *free)
64 {
65         Object *ob;
66         EnumPropertyItem *item = NULL, item_tmp = {0};
67         int totitem = 0;
68
69         if (C == NULL) {
70                 return DummyRNA_NULL_items;
71         }
72
73         ob = ED_object_context(C);
74
75         /* check that the action exists */
76         if (ob) {
77                 Group *group = NULL;
78                 int i = 0;
79
80                 while ((group = find_group(ob, group))) {
81                         item_tmp.identifier = item_tmp.name = group->id.name + 2;
82                         /* item_tmp.icon = ICON_ARMATURE_DATA; */
83                         item_tmp.value = i;
84                         RNA_enum_item_add(&item, &totitem, &item_tmp);
85                         i++;
86                 }
87         }
88
89         RNA_enum_item_end(&item, &totitem);
90         *free = 1;
91
92         return item;
93 }
94
95 /* get the group back from the enum index, quite awkward and UI specific */
96 static Group *group_object_active_find_index(Object *ob, const int group_object_index)
97 {
98         Group *group = NULL;
99         int i = 0;
100         while ((group = find_group(ob, group))) {
101                 if (i == group_object_index) {
102                         break;
103                 }
104                 i++;
105         }
106
107         return group;
108 }
109
110 static int objects_add_active_exec(bContext *C, wmOperator *op)
111 {
112         Object *ob = ED_object_context(C);
113         Main *bmain = CTX_data_main(C);
114         Scene *scene = CTX_data_scene(C);
115         int group_object_index = RNA_enum_get(op->ptr, "group");
116         int cycle = FALSE;
117
118         if (ob) {
119                 Group *group = group_object_active_find_index(ob, group_object_index);
120
121                 /* now add all selected objects from the group */
122                 if (group) {
123
124                         CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
125                         {
126                                 if (base->object->dup_group != group) {
127                                         add_to_group(group, base->object, scene, base);
128                                 }
129                                 else {
130                                         cycle = TRUE;
131                                 }
132                         }
133                         CTX_DATA_END;
134
135                         DAG_scene_sort(bmain, scene);
136                         WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
137
138                         return OPERATOR_FINISHED;
139                 }
140         }
141
142         if (cycle)
143                 BKE_report(op->reports, RPT_WARNING, "Skipped some groups because of cycle detected");
144
145         return OPERATOR_CANCELLED;
146 }
147
148 void GROUP_OT_objects_add_active(wmOperatorType *ot)
149 {
150         PropertyRNA *prop;
151
152         /* identifiers */
153         ot->name = "Add Selected To Active Group";
154         ot->description = "Add the object to an object group that contains the active object";
155         ot->idname = "GROUP_OT_objects_add_active";
156         
157         /* api callbacks */
158         ot->exec = objects_add_active_exec;
159         ot->invoke = WM_menu_invoke;
160         ot->poll = ED_operator_objectmode;
161
162         /* flags */
163         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
164
165         /* properties */
166         prop = RNA_def_enum(ot->srna, "group", DummyRNA_NULL_items, 0, "Group", "The group to add other selected objects to");
167         RNA_def_enum_funcs(prop, group_object_active_itemf);
168         ot->prop = prop;
169 }
170
171 static int objects_remove_active_exec(bContext *C, wmOperator *op)
172 {
173         Main *bmain = CTX_data_main(C);
174         Scene *scene = CTX_data_scene(C);
175         Object *ob = OBACT;
176         Group *group;
177         int ok = 0;
178         
179         if (!ob) return OPERATOR_CANCELLED;
180         
181         /* linking to same group requires its own loop so we can avoid
182          * looking up the active objects groups each time */
183
184         for (group = bmain->group.first; group; group = group->id.next) {
185                 if (object_in_group(ob, group)) {
186                         /* Assign groups to selected objects */
187                         CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
188                         {
189                                 rem_from_group(group, base->object, scene, base);
190                                 ok = 1;
191                         }
192                         CTX_DATA_END;
193                 }
194         }
195         
196         if (!ok) BKE_report(op->reports, RPT_ERROR, "Active Object contains no groups");
197         
198         DAG_scene_sort(bmain, scene);
199         WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
200         
201         return OPERATOR_FINISHED;
202 }
203
204 void GROUP_OT_objects_remove_active(wmOperatorType *ot)
205 {
206         /* identifiers */
207         ot->name = "Remove Selected From Active Group";
208         ot->description = "Remove the object from an object group that contains the active object";
209         ot->idname = "GROUP_OT_objects_remove_active";
210         
211         /* api callbacks */
212         ot->exec = objects_remove_active_exec;  
213         ot->poll = ED_operator_objectmode;
214         
215         /* flags */
216         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
217 }
218
219 static int group_objects_remove_all_exec(bContext *C, wmOperator *UNUSED(op))
220 {
221         Main *bmain = CTX_data_main(C);
222         Scene *scene = CTX_data_scene(C);
223         Group *group = NULL;
224
225         CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
226         {
227                 group = NULL;
228                 while ((group = find_group(base->object, group)))
229                         rem_from_group(group, base->object, scene, base);
230         }
231         CTX_DATA_END;
232
233         DAG_scene_sort(bmain, scene);
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                                 rem_from_group(group, base->object, scene, base);
270                         }
271                         CTX_DATA_END;
272
273                         DAG_scene_sort(bmain, scene);
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 = add_group(name);
316                 
317         CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
318         {
319                 add_to_group(group, base->object, scene, base);
320         }
321         CTX_DATA_END;
322
323         DAG_scene_sort(bmain, scene);
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         Group *group;
353
354         if (ob == NULL)
355                 return OPERATOR_CANCELLED;
356
357         group = add_group("Group");
358         add_to_group(group, ob, scene, NULL);
359
360         WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
361
362         return OPERATOR_FINISHED;
363 }
364
365 void OBJECT_OT_group_add(wmOperatorType *ot)
366 {
367         /* identifiers */
368         ot->name = "Add to Group";
369         ot->idname = "OBJECT_OT_group_add";
370         ot->description = "Add an object to a new group";
371         
372         /* api callbacks */
373         ot->exec = group_add_exec;
374
375         /* flags */
376         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
377 }
378
379 static int group_link_exec(bContext *C, wmOperator *op)
380 {
381         Scene *scene = CTX_data_scene(C);
382         Object *ob = ED_object_context(C);
383         Group *group = BLI_findlink(&CTX_data_main(C)->group, RNA_enum_get(op->ptr, "group"));
384
385         if (ELEM(NULL, ob, group))
386                 return OPERATOR_CANCELLED;
387
388         add_to_group(group, ob, scene, NULL);
389
390         WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
391
392         return OPERATOR_FINISHED;
393 }
394
395 void OBJECT_OT_group_link(wmOperatorType *ot)
396 {
397         PropertyRNA *prop;
398
399         /* identifiers */
400         ot->name = "Link to Group";
401         ot->idname = "OBJECT_OT_group_link";
402         ot->description = "Add an object to an existing group";
403         
404         /* api callbacks */
405         ot->exec = group_link_exec;
406         ot->invoke = WM_enum_search_invoke;
407
408         /* flags */
409         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
410
411         /* properties */
412         prop = RNA_def_enum(ot->srna, "group", DummyRNA_NULL_items, 0, "Group", "");
413         RNA_def_enum_funcs(prop, RNA_group_local_itemf);
414         ot->prop = prop;
415 }
416
417 static int group_remove_exec(bContext *C, wmOperator *UNUSED(op))
418 {
419         Scene *scene = CTX_data_scene(C);
420         Object *ob = ED_object_context(C);
421         Group *group = CTX_data_pointer_get_type(C, "group", &RNA_Group).data;
422
423         if (!ob || !group)
424                 return OPERATOR_CANCELLED;
425
426         rem_from_group(group, ob, scene, NULL); /* base will be used if found */
427
428         WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
429         
430         return OPERATOR_FINISHED;
431 }
432
433 void OBJECT_OT_group_remove(wmOperatorType *ot)
434 {
435         /* identifiers */
436         ot->name = "Remove Group";
437         ot->idname = "OBJECT_OT_group_remove";
438         ot->description = "Remove the active object from this group";
439         
440         /* api callbacks */
441         ot->exec = group_remove_exec;
442
443         /* flags */
444         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
445 }
446