fix for crash getting GROUP_OT_objects_remove's props without a context (own fault)
[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 static int objects_add_active_exec(bContext *C, wmOperator *op)
63 {
64         Main *bmain = CTX_data_main(C);
65         Scene *scene = CTX_data_scene(C);
66         Object *ob = OBACT;
67         Group *group;
68         int ok = 0, cycle = 0;
69         
70         if (!ob) return OPERATOR_CANCELLED;
71         
72         /* linking to same group requires its own loop so we can avoid
73          * looking up the active objects groups each time */
74
75         for (group = bmain->group.first; group; group = group->id.next) {
76                 if (object_in_group(ob, group)) {
77                         /* Assign groups to selected objects */
78                         CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
79                         {
80                                 if (base->object->dup_group != group)
81                                         add_to_group(group, base->object, scene, base);
82                                 else
83                                         cycle = 1;
84                                 ok = 1;
85                         }
86                         CTX_DATA_END;
87                 }
88         }
89         
90         if (!ok) BKE_report(op->reports, RPT_ERROR, "Active Object contains no groups");
91         if (cycle)
92                 BKE_report(op->reports, RPT_WARNING, "Skipped some groups because of cycle detected");
93         
94         DAG_scene_sort(bmain, scene);
95         WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
96         
97         return OPERATOR_FINISHED;
98 }
99
100 void GROUP_OT_objects_add_active(wmOperatorType *ot)
101 {
102         /* identifiers */
103         ot->name = "Add Selected To Active Group";
104         ot->description = "Add the object to an object group that contains the active object";
105         ot->idname = "GROUP_OT_objects_add_active";
106         
107         /* api callbacks */
108         ot->exec = objects_add_active_exec;     
109         ot->poll = ED_operator_objectmode;
110
111         /* flags */
112         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
113 }
114
115 static int objects_remove_active_exec(bContext *C, wmOperator *op)
116 {
117         Main *bmain = CTX_data_main(C);
118         Scene *scene = CTX_data_scene(C);
119         Object *ob = OBACT;
120         Group *group;
121         int ok = 0;
122         
123         if (!ob) return OPERATOR_CANCELLED;
124         
125         /* linking to same group requires its own loop so we can avoid
126          * looking up the active objects groups each time */
127
128         for (group = bmain->group.first; group; group = group->id.next) {
129                 if (object_in_group(ob, group)) {
130                         /* Assign groups to selected objects */
131                         CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
132                         {
133                                 rem_from_group(group, base->object, scene, base);
134                                 ok = 1;
135                         }
136                         CTX_DATA_END;
137                 }
138         }
139         
140         if (!ok) BKE_report(op->reports, RPT_ERROR, "Active Object contains no groups");
141         
142         DAG_scene_sort(bmain, scene);
143         WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
144         
145         return OPERATOR_FINISHED;
146 }
147
148 void GROUP_OT_objects_remove_active(wmOperatorType *ot)
149 {
150         /* identifiers */
151         ot->name = "Remove Selected From Active Group";
152         ot->description = "Remove the object from an object group that contains the active object";
153         ot->idname = "GROUP_OT_objects_remove_active";
154         
155         /* api callbacks */
156         ot->exec = objects_remove_active_exec;  
157         ot->poll = ED_operator_objectmode;
158         
159         /* flags */
160         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
161 }
162
163 static int group_objects_remove_all_exec(bContext *C, wmOperator *UNUSED(op))
164 {
165         Main *bmain = CTX_data_main(C);
166         Scene *scene = CTX_data_scene(C);
167         Group *group = NULL;
168
169         CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
170         {
171                 group = NULL;
172                 while ((group = find_group(base->object, group)))
173                         rem_from_group(group, base->object, scene, base);
174         }
175         CTX_DATA_END;
176
177         DAG_scene_sort(bmain, scene);
178         WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
179         
180         return OPERATOR_FINISHED;
181 }
182
183 void GROUP_OT_objects_remove_all(wmOperatorType *ot)
184 {
185         /* identifiers */
186         ot->name = "Remove From All Groups";
187         ot->description = "Remove selected objects from all groups or a selected group";
188         ot->idname = "GROUP_OT_objects_remove_all";
189         
190         /* api callbacks */
191         ot->exec = group_objects_remove_all_exec;
192         ot->poll = ED_operator_objectmode;
193         
194         /* flags */
195         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
196 }
197
198 static int group_objects_remove_exec(bContext *C, wmOperator *op)
199 {
200         Object *ob = ED_object_context(C);
201         Main *bmain = CTX_data_main(C);
202         Scene *scene = CTX_data_scene(C);
203         int group_object_index = RNA_enum_get(op->ptr, "group");
204
205         /* first get the group back from the enum index, quite awkward and UI spesific */
206         if (ob) {
207                 Group *group = NULL;
208                 int i = 0;
209
210                 while ((group = find_group(ob, group))) {
211                         if (i == group_object_index) {
212                                 break;
213                         }
214                         i++;
215                 }
216
217                 /* now remove all selected objects from the group */
218                 if (group) {
219
220                         CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
221                         {
222                                 rem_from_group(group, base->object, scene, base);
223                         }
224                         CTX_DATA_END;
225
226                         DAG_scene_sort(bmain, scene);
227                         WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
228
229                         return OPERATOR_FINISHED;
230                 }
231         }
232
233         return OPERATOR_CANCELLED;
234 }
235
236
237 /* can be called with C == NULL */
238 static EnumPropertyItem *group_objects_remove_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), int *free)
239 {
240         Object *ob;
241         EnumPropertyItem *item = NULL, item_tmp = {0};
242         int totitem = 0;
243
244         if (C == NULL) {
245                 return DummyRNA_NULL_items;
246         }
247
248         ob = ED_object_context(C);
249
250         /* check that the action exists */
251         if (ob) {
252                 Group *group = NULL;
253                 int i = 0;
254
255                 while ((group = find_group(ob, group))) {
256                         item_tmp.identifier = item_tmp.name = group->id.name + 2;
257                         /* item_tmp.icon = ICON_ARMATURE_DATA; */
258                         item_tmp.value = i;
259                         RNA_enum_item_add(&item, &totitem, &item_tmp);
260                         i++;
261                 }
262         }
263
264         RNA_enum_item_end(&item, &totitem);
265         *free = 1;
266
267         return item;
268 }
269
270 void GROUP_OT_objects_remove(wmOperatorType *ot)
271 {
272         PropertyRNA *prop;
273
274         /* identifiers */
275         ot->name = "Remove From Group";
276         ot->description = "Remove selected objects from all groups or a selected group";
277         ot->idname = "GROUP_OT_objects_remove";
278
279         /* api callbacks */
280         ot->exec = group_objects_remove_exec;
281         ot->invoke = WM_menu_invoke;
282         ot->poll = ED_operator_objectmode;
283
284         /* flags */
285         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
286
287         /* properties */
288         prop = RNA_def_enum(ot->srna, "group", DummyRNA_NULL_items, 0, "Group", "The group to remove this object from");
289         RNA_def_enum_funcs(prop, group_objects_remove_itemf);
290         ot->prop = prop;
291 }
292
293 static int group_create_exec(bContext *C, wmOperator *op)
294 {
295         Main *bmain = CTX_data_main(C);
296         Scene *scene = CTX_data_scene(C);
297         Group *group = NULL;
298         char name[MAX_ID_NAME - 2]; /* id name */
299         
300         RNA_string_get(op->ptr, "name", name);
301         
302         group = add_group(name);
303                 
304         CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases)
305         {
306                 add_to_group(group, base->object, scene, base);
307         }
308         CTX_DATA_END;
309
310         DAG_scene_sort(bmain, scene);
311         WM_event_add_notifier(C, NC_GROUP | NA_EDITED, NULL);
312         
313         return OPERATOR_FINISHED;
314 }
315
316 void GROUP_OT_create(wmOperatorType *ot)
317 {
318         /* identifiers */
319         ot->name = "Create New Group";
320         ot->description = "Create an object group from selected objects";
321         ot->idname = "GROUP_OT_create";
322         
323         /* api callbacks */
324         ot->exec = group_create_exec;   
325         ot->poll = ED_operator_objectmode;
326         
327         /* flags */
328         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
329         
330         RNA_def_string(ot->srna, "name", "Group", MAX_ID_NAME - 2, "Name", "Name of the new group");
331 }
332
333 /****************** properties window operators *********************/
334
335 static int group_add_exec(bContext *C, wmOperator *UNUSED(op))
336 {
337         Scene *scene = CTX_data_scene(C);
338         Object *ob = ED_object_context(C);
339         Group *group;
340
341         if (ob == NULL)
342                 return OPERATOR_CANCELLED;
343
344         group = add_group("Group");
345         add_to_group(group, ob, scene, NULL);
346
347         WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
348
349         return OPERATOR_FINISHED;
350 }
351
352 void OBJECT_OT_group_add(wmOperatorType *ot)
353 {
354         /* identifiers */
355         ot->name = "Add to Group";
356         ot->idname = "OBJECT_OT_group_add";
357         ot->description = "Add an object to a new group";
358         
359         /* api callbacks */
360         ot->exec = group_add_exec;
361
362         /* flags */
363         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
364 }
365
366 static int group_link_exec(bContext *C, wmOperator *op)
367 {
368         Scene *scene = CTX_data_scene(C);
369         Object *ob = ED_object_context(C);
370         Group *group = BLI_findlink(&CTX_data_main(C)->group, RNA_enum_get(op->ptr, "group"));
371
372         if (ELEM(NULL, ob, group))
373                 return OPERATOR_CANCELLED;
374
375         add_to_group(group, ob, scene, NULL);
376
377         WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
378
379         return OPERATOR_FINISHED;
380 }
381
382 void OBJECT_OT_group_link(wmOperatorType *ot)
383 {
384         PropertyRNA *prop;
385
386         /* identifiers */
387         ot->name = "Link to Group";
388         ot->idname = "OBJECT_OT_group_link";
389         ot->description = "Add an object to an existing group";
390         
391         /* api callbacks */
392         ot->exec = group_link_exec;
393         ot->invoke = WM_enum_search_invoke;
394
395         /* flags */
396         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
397
398         /* properties */
399         prop = RNA_def_enum(ot->srna, "group", DummyRNA_NULL_items, 0, "Group", "");
400         RNA_def_enum_funcs(prop, RNA_group_local_itemf);
401         ot->prop = prop;
402 }
403
404 static int group_remove_exec(bContext *C, wmOperator *UNUSED(op))
405 {
406         Scene *scene = CTX_data_scene(C);
407         Object *ob = ED_object_context(C);
408         Group *group = CTX_data_pointer_get_type(C, "group", &RNA_Group).data;
409
410         if (!ob || !group)
411                 return OPERATOR_CANCELLED;
412
413         rem_from_group(group, ob, scene, NULL); /* base will be used if found */
414
415         WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
416         
417         return OPERATOR_FINISHED;
418 }
419
420 void OBJECT_OT_group_remove(wmOperatorType *ot)
421 {
422         /* identifiers */
423         ot->name = "Remove Group";
424         ot->idname = "OBJECT_OT_group_remove";
425         ot->description = "Remove the active object from this group";
426         
427         /* api callbacks */
428         ot->exec = group_remove_exec;
429
430         /* flags */
431         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
432 }
433