Cleanup: comment line length (editors)
[blender.git] / source / blender / editors / space_outliner / outliner_tools.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) 2004 Blender Foundation.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Joshua Leung
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/space_outliner/outliner_tools.c
29  *  \ingroup spoutliner
30  */
31
32 #include "MEM_guardedalloc.h"
33
34 #include "DNA_anim_types.h"
35 #include "DNA_armature_types.h"
36 #include "DNA_gpencil_types.h"
37 #include "DNA_group_types.h"
38 #include "DNA_lamp_types.h"
39 #include "DNA_linestyle_types.h"
40 #include "DNA_material_types.h"
41 #include "DNA_mesh_types.h"
42 #include "DNA_meta_types.h"
43 #include "DNA_scene_types.h"
44 #include "DNA_sequence_types.h"
45 #include "DNA_world_types.h"
46 #include "DNA_object_types.h"
47 #include "DNA_constraint_types.h"
48 #include "DNA_modifier_types.h"
49
50 #include "BLI_blenlib.h"
51 #include "BLI_utildefines.h"
52
53 #include "BKE_animsys.h"
54 #include "BKE_context.h"
55 #include "BKE_constraint.h"
56 #include "BKE_depsgraph.h"
57 #include "BKE_fcurve.h"
58 #include "BKE_group.h"
59 #include "BKE_library.h"
60 #include "BKE_library_query.h"
61 #include "BKE_library_remap.h"
62 #include "BKE_main.h"
63 #include "BKE_report.h"
64 #include "BKE_scene.h"
65 #include "BKE_sequencer.h"
66
67 #include "ED_armature.h"
68 #include "ED_object.h"
69 #include "ED_screen.h"
70 #include "ED_sequencer.h"
71 #include "ED_undo.h"
72
73 #include "WM_api.h"
74 #include "WM_types.h"
75
76 #include "UI_interface.h"
77 #include "UI_view2d.h"
78 #include "UI_resources.h"
79
80 #include "RNA_access.h"
81 #include "RNA_define.h"
82 #include "RNA_enum_types.h"
83
84 #include "outliner_intern.h"
85
86
87 /* ****************************************************** */
88
89 /* ************ SELECTION OPERATIONS ********* */
90
91 static void set_operation_types(SpaceOops *soops, ListBase *lb,
92                                 int *scenelevel,
93                                 int *objectlevel,
94                                 int *idlevel,
95                                 int *datalevel)
96 {
97         TreeElement *te;
98         TreeStoreElem *tselem;
99
100         for (te = lb->first; te; te = te->next) {
101                 tselem = TREESTORE(te);
102                 if (tselem->flag & TSE_SELECTED) {
103                         if (tselem->type) {
104                                 if (*datalevel == 0)
105                                         *datalevel = tselem->type;
106                                 else if (*datalevel != tselem->type)
107                                         *datalevel = -1;
108                         }
109                         else {
110                                 int idcode = GS(tselem->id->name);
111                                 switch (idcode) {
112                                         case ID_SCE:
113                                                 *scenelevel = 1;
114                                                 break;
115                                         case ID_OB:
116                                                 *objectlevel = 1;
117                                                 break;
118
119                                         case ID_ME: case ID_CU: case ID_MB: case ID_LT:
120                                         case ID_LA: case ID_AR: case ID_CA: case ID_SPK:
121                                         case ID_MA: case ID_TE: case ID_IP: case ID_IM:
122                                         case ID_SO: case ID_KE: case ID_WO: case ID_AC:
123                                         case ID_NLA: case ID_TXT: case ID_GR: case ID_LS:
124                                         case ID_LI:
125                                                 if (*idlevel == 0) *idlevel = idcode;
126                                                 else if (*idlevel != idcode) *idlevel = -1;
127                                                 break;
128                                 }
129                         }
130                 }
131                 if (TSELEM_OPEN(tselem, soops)) {
132                         set_operation_types(soops, &te->subtree,
133                                             scenelevel, objectlevel, idlevel, datalevel);
134                 }
135         }
136 }
137
138 static void unlink_action_cb(
139         bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te),
140         TreeStoreElem *tsep, TreeStoreElem *UNUSED(tselem), void *UNUSED(user_data))
141 {
142         /* just set action to NULL */
143         BKE_animdata_set_action(CTX_wm_reports(C), tsep->id, NULL);
144 }
145
146 static void unlink_material_cb(
147         bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *te,
148         TreeStoreElem *tsep, TreeStoreElem *UNUSED(tselem), void *UNUSED(user_data))
149 {
150         Material **matar = NULL;
151         int a, totcol = 0;
152
153         if (GS(tsep->id->name) == ID_OB) {
154                 Object *ob = (Object *)tsep->id;
155                 totcol = ob->totcol;
156                 matar = ob->mat;
157         }
158         else if (GS(tsep->id->name) == ID_ME) {
159                 Mesh *me = (Mesh *)tsep->id;
160                 totcol = me->totcol;
161                 matar = me->mat;
162         }
163         else if (GS(tsep->id->name) == ID_CU) {
164                 Curve *cu = (Curve *)tsep->id;
165                 totcol = cu->totcol;
166                 matar = cu->mat;
167         }
168         else if (GS(tsep->id->name) == ID_MB) {
169                 MetaBall *mb = (MetaBall *)tsep->id;
170                 totcol = mb->totcol;
171                 matar = mb->mat;
172         }
173         else {
174                 BLI_assert(0);
175         }
176
177         if (LIKELY(matar != NULL)) {
178                 for (a = 0; a < totcol; a++) {
179                         if (a == te->index && matar[a]) {
180                                 id_us_min(&matar[a]->id);
181                                 matar[a] = NULL;
182                         }
183                 }
184         }
185 }
186
187 static void unlink_texture_cb(
188         bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *te,
189         TreeStoreElem *tsep, TreeStoreElem *UNUSED(tselem), void *UNUSED(user_data))
190 {
191         MTex **mtex = NULL;
192         int a;
193
194         if (GS(tsep->id->name) == ID_MA) {
195                 Material *ma = (Material *)tsep->id;
196                 mtex = ma->mtex;
197         }
198         else if (GS(tsep->id->name) == ID_LA) {
199                 Lamp *la = (Lamp *)tsep->id;
200                 mtex = la->mtex;
201         }
202         else if (GS(tsep->id->name) == ID_WO) {
203                 World *wrld = (World *)tsep->id;
204                 mtex = wrld->mtex;
205         }
206         else if (GS(tsep->id->name) == ID_LS) {
207                 FreestyleLineStyle *ls = (FreestyleLineStyle *)tsep->id;
208                 mtex = ls->mtex;
209         }
210         else {
211                 return;
212         }
213
214         for (a = 0; a < MAX_MTEX; a++) {
215                 if (a == te->index && mtex[a]) {
216                         if (mtex[a]->tex) {
217                                 id_us_min(&mtex[a]->tex->id);
218                                 mtex[a]->tex = NULL;
219                         }
220                 }
221         }
222 }
223
224 static void unlink_group_cb(
225         bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te),
226         TreeStoreElem *tsep, TreeStoreElem *tselem, void *UNUSED(user_data))
227 {
228         Group *group = (Group *)tselem->id;
229
230         if (tsep) {
231                 if (GS(tsep->id->name) == ID_OB) {
232                         Object *ob = (Object *)tsep->id;
233                         ob->dup_group = NULL;
234                 }
235         }
236         else {
237                 Main *bmain = CTX_data_main(C);
238                 BKE_libblock_delete(bmain, group);
239         }
240 }
241
242 static void unlink_world_cb(
243         bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te),
244         TreeStoreElem *tsep, TreeStoreElem *tselem, void *UNUSED(user_data))
245 {
246         Scene *parscene = (Scene *)tsep->id;
247         World *wo = (World *)tselem->id;
248
249         /* need to use parent scene not just scene, otherwise may end up getting wrong one */
250         id_us_min(&wo->id);
251         parscene->world = NULL;
252 }
253
254 static void outliner_do_libdata_operation(
255         bContext *C, ReportList *reports, Scene *scene, SpaceOops *soops, ListBase *lb,
256         outliner_operation_cb operation_cb,
257         void *user_data)
258 {
259         TreeElement *te;
260         TreeStoreElem *tselem;
261
262         for (te = lb->first; te; te = te->next) {
263                 tselem = TREESTORE(te);
264                 if (tselem->flag & TSE_SELECTED) {
265                         if (tselem->type == 0) {
266                                 TreeStoreElem *tsep = te->parent ? TREESTORE(te->parent) : NULL;
267                                 operation_cb(C, reports, scene, te, tsep, tselem, user_data);
268                         }
269                 }
270                 if (TSELEM_OPEN(tselem, soops)) {
271                         outliner_do_libdata_operation(C, reports, scene, soops, &te->subtree, operation_cb, user_data);
272                 }
273         }
274 }
275
276 /* ******************************************** */
277 typedef enum eOutliner_PropSceneOps {
278         OL_SCENE_OP_DELETE = 1
279 } eOutliner_PropSceneOps;
280
281 static const EnumPropertyItem prop_scene_op_types[] = {
282         {OL_SCENE_OP_DELETE, "DELETE", ICON_X, "Delete", ""},
283         {0, NULL, 0, NULL, NULL}
284 };
285
286 static bool outliner_do_scene_operation(
287         bContext *C, eOutliner_PropSceneOps event, ListBase *lb,
288         bool (*operation_cb)(bContext *, eOutliner_PropSceneOps, TreeElement *, TreeStoreElem *))
289 {
290         TreeElement *te;
291         TreeStoreElem *tselem;
292         bool success = false;
293
294         for (te = lb->first; te; te = te->next) {
295                 tselem = TREESTORE(te);
296                 if (tselem->flag & TSE_SELECTED) {
297                         if (operation_cb(C, event, te, tselem)) {
298                                 success = true;
299                         }
300                 }
301         }
302
303         return success;
304 }
305
306 static bool scene_cb(bContext *C, eOutliner_PropSceneOps event, TreeElement *UNUSED(te), TreeStoreElem *tselem)
307 {
308         Scene *scene = (Scene *)tselem->id;
309
310         if (event == OL_SCENE_OP_DELETE) {
311                 if (ED_screen_delete_scene(C, scene)) {
312                         WM_event_add_notifier(C, NC_SCENE | NA_REMOVED, scene);
313                 }
314                 else {
315                         return false;
316                 }
317         }
318
319         return true;
320 }
321
322 static int outliner_scene_operation_exec(bContext *C, wmOperator *op)
323 {
324         SpaceOops *soops = CTX_wm_space_outliner(C);
325         const eOutliner_PropSceneOps event = RNA_enum_get(op->ptr, "type");
326
327         if (outliner_do_scene_operation(C, event, &soops->tree, scene_cb) == false) {
328                 return OPERATOR_CANCELLED;
329         }
330
331         if (event == OL_SCENE_OP_DELETE) {
332                 outliner_cleanup_tree(soops);
333                 ED_undo_push(C, "Delete Scene(s)");
334         }
335         else {
336                 BLI_assert(0);
337                 return OPERATOR_CANCELLED;
338         }
339
340         return OPERATOR_FINISHED;
341 }
342
343 void OUTLINER_OT_scene_operation(wmOperatorType *ot)
344 {
345         /* identifiers */
346         ot->name = "Outliner Scene Operation";
347         ot->idname = "OUTLINER_OT_scene_operation";
348         ot->description = "Context menu for scene operations";
349
350         /* callbacks */
351         ot->invoke = WM_menu_invoke;
352         ot->exec = outliner_scene_operation_exec;
353         ot->poll = ED_operator_outliner_active;
354
355         ot->flag = 0;
356
357         ot->prop = RNA_def_enum(ot->srna, "type", prop_scene_op_types, 0, "Scene Operation", "");
358 }
359 /* ******************************************** */
360
361 static void object_select_cb(
362         bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *scene, TreeElement *te,
363         TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
364 {
365         Base *base = (Base *)te->directdata;
366
367         if (base == NULL) base = BKE_scene_base_find(scene, (Object *)tselem->id);
368         if (base && ((base->object->restrictflag & OB_RESTRICT_VIEW) == 0)) {
369                 base->flag |= SELECT;
370                 base->object->flag |= SELECT;
371         }
372 }
373
374 static void object_select_hierarchy_cb(
375         bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *te,
376         TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
377 {
378         /* Don't extend because this toggles, which is nice for Ctrl-Click but not for a menu item.
379          * it's especially confusing when multiple items are selected since some toggle on/off. */
380         outliner_item_do_activate_from_tree_element(C, te, tselem, false, true);
381 }
382
383 static void object_deselect_cb(
384         bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *scene, TreeElement *te,
385         TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
386 {
387         Base *base = (Base *)te->directdata;
388
389         if (base == NULL) base = BKE_scene_base_find(scene, (Object *)tselem->id);
390         if (base) {
391                 base->flag &= ~SELECT;
392                 base->object->flag &= ~SELECT;
393         }
394 }
395
396 static void object_delete_cb(
397         bContext *C, ReportList *reports, Scene *scene, TreeElement *te,
398         TreeStoreElem *tsep, TreeStoreElem *tselem, void *user_data)
399 {
400         Base *base = (Base *)te->directdata;
401
402         if (base == NULL)
403                 base = BKE_scene_base_find(scene, (Object *)tselem->id);
404         if (base) {
405                 Main *bmain = CTX_data_main(C);
406                 if (base->object->id.tag & LIB_TAG_INDIRECT) {
407                         BKE_reportf(reports, RPT_WARNING, "Cannot delete indirectly linked object '%s'", base->object->id.name + 2);
408                         return;
409                 }
410                 else if (BKE_library_ID_is_indirectly_used(bmain, base->object) &&
411                          ID_REAL_USERS(base->object) <= 1 && ID_EXTRA_USERS(base->object) == 0)
412                 {
413                         BKE_reportf(reports, RPT_WARNING,
414                                     "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user",
415                                     base->object->id.name + 2, scene->id.name + 2);
416                         return;
417                 }
418
419                 // check also library later
420                 if (scene->obedit == base->object) {
421                         ED_object_editmode_exit(C, EM_FREEDATA | EM_WAITCURSOR);
422                 }
423
424                 ED_base_object_free_and_unlink(CTX_data_main(C), scene, base);
425                 /* leave for ED_outliner_id_unref to handle */
426 #if 0
427                 te->directdata = NULL;
428                 tselem->id = NULL;
429 #endif
430         }
431         else {
432                 /* No base, means object is no more instantiated in any scene.
433                  * Should not happen ideally, but does happens, see T51625.
434                  * Rather than twisting in all kind of ways to address all possible cases leading to that situation, simpler
435                  * to allow deleting such object as a mere generic data-block. */
436                 id_delete_cb(C, reports, scene, te, tsep, tselem, user_data);
437         }
438 }
439
440 static void id_local_cb(
441         bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te),
442         TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
443 {
444         if (ID_IS_LINKED(tselem->id) && (tselem->id->tag & LIB_TAG_EXTERN)) {
445                 Main *bmain = CTX_data_main(C);
446                 /* if the ID type has no special local function,
447                  * just clear the lib */
448                 if (id_make_local(bmain, tselem->id, false, false) == false) {
449                         id_clear_lib_data(bmain, tselem->id);
450                 }
451                 else {
452                         BKE_main_id_clear_newpoins(bmain);
453                 }
454         }
455 }
456
457 static void id_fake_user_set_cb(
458         bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te),
459         TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
460 {
461         ID *id = tselem->id;
462
463         id_fake_user_set(id);
464 }
465
466 static void id_fake_user_clear_cb(
467         bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te),
468         TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
469 {
470         ID *id = tselem->id;
471
472         id_fake_user_clear(id);
473 }
474
475 static void id_select_linked_cb(
476         bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te),
477         TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
478 {
479         ID *id = tselem->id;
480
481         ED_object_select_linked_by_id(C, id);
482 }
483
484 static void singleuser_action_cb(
485         bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te),
486         TreeStoreElem *tsep, TreeStoreElem *tselem, void *UNUSED(user_data))
487 {
488         ID *id = tselem->id;
489
490         if (id) {
491                 IdAdtTemplate *iat = (IdAdtTemplate *)tsep->id;
492                 PointerRNA ptr = {{NULL}};
493                 PropertyRNA *prop;
494
495                 RNA_pointer_create(&iat->id, &RNA_AnimData, iat->adt, &ptr);
496                 prop = RNA_struct_find_property(&ptr, "action");
497
498                 id_single_user(C, id, &ptr, prop);
499         }
500 }
501
502 static void singleuser_world_cb(
503         bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te),
504         TreeStoreElem *tsep, TreeStoreElem *tselem, void *UNUSED(user_data))
505 {
506         ID *id = tselem->id;
507
508         /* need to use parent scene not just scene, otherwise may end up getting wrong one */
509         if (id) {
510                 Scene *parscene = (Scene *)tsep->id;
511                 PointerRNA ptr = {{NULL}};
512                 PropertyRNA *prop;
513
514                 RNA_id_pointer_create(&parscene->id, &ptr);
515                 prop = RNA_struct_find_property(&ptr, "world");
516
517                 id_single_user(C, id, &ptr, prop);
518         }
519 }
520
521 static void group_linkobs2scene_cb(
522         bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *scene, TreeElement *UNUSED(te),
523         TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
524 {
525         Group *group = (Group *)tselem->id;
526         GroupObject *gob;
527         Base *base;
528
529         for (gob = group->gobject.first; gob; gob = gob->next) {
530                 base = BKE_scene_base_find(scene, gob->ob);
531                 if (!base) {
532                         /* link to scene */
533                         base = BKE_scene_base_add(scene, gob->ob);
534                         id_us_plus(&gob->ob->id);
535                 }
536                 base->object->flag |= SELECT;
537                 base->flag |= SELECT;
538         }
539 }
540
541 static void group_instance_cb(
542         bContext *C, ReportList *UNUSED(reports), Scene *scene, TreeElement *UNUSED(te),
543         TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
544 {
545         Group *group = (Group *)tselem->id;
546
547         Object *ob = ED_object_add_type(C, OB_EMPTY, group->id.name + 2, scene->cursor, NULL, false, scene->layact);
548         ob->dup_group = group;
549         ob->transflag |= OB_DUPLIGROUP;
550         id_lib_extern(&group->id);
551 }
552
553 /**
554  * \param select_recurse: Set to false for operations which are already recursively operating on their children.
555  */
556 void outliner_do_object_operation_ex(
557         bContext *C, ReportList *reports, Scene *scene_act, SpaceOops *soops, ListBase *lb,
558         outliner_operation_cb operation_cb, bool select_recurse)
559 {
560         TreeElement *te;
561
562         for (te = lb->first; te; te = te->next) {
563                 TreeStoreElem *tselem = TREESTORE(te);
564                 bool select_handled = false;
565                 if (tselem->flag & TSE_SELECTED) {
566                         if (tselem->type == 0 && te->idcode == ID_OB) {
567                                 // when objects selected in other scenes... dunno if that should be allowed
568                                 Scene *scene_owner = (Scene *)outliner_search_back(soops, te, ID_SCE);
569                                 if (scene_owner && scene_act != scene_owner) {
570                                         ED_screen_set_scene(C, CTX_wm_screen(C), scene_owner);
571                                 }
572                                 /* important to use 'scene_owner' not scene_act else deleting objects can crash.
573                                  * only use 'scene_act' when 'scene_owner' is NULL, which can happen when the
574                                  * outliner isn't showing scenes: Visible Layer draw mode for eg. */
575                                 operation_cb(C, reports, scene_owner ? scene_owner : scene_act, te, NULL, tselem, NULL);
576                                 select_handled = true;
577                         }
578                 }
579                 if (TSELEM_OPEN(tselem, soops)) {
580                         if ((select_handled == false) || select_recurse) {
581                                 outliner_do_object_operation_ex(
582                                             C, reports, scene_act, soops, &te->subtree, operation_cb, select_recurse);
583                         }
584                 }
585         }
586 }
587
588 void outliner_do_object_operation(
589         bContext *C, ReportList *reports, Scene *scene_act, SpaceOops *soops, ListBase *lb,
590         outliner_operation_cb operation_cb)
591 {
592         outliner_do_object_operation_ex(C, reports, scene_act, soops, lb, operation_cb, true);
593 }
594
595 /* ******************************************** */
596
597 static void clear_animdata_cb(int UNUSED(event), TreeElement *UNUSED(te),
598                               TreeStoreElem *tselem, void *UNUSED(arg))
599 {
600         BKE_animdata_free(tselem->id, true);
601 }
602
603
604 static void unlinkact_animdata_cb(int UNUSED(event), TreeElement *UNUSED(te),
605                                   TreeStoreElem *tselem, void *UNUSED(arg))
606 {
607         /* just set action to NULL */
608         BKE_animdata_set_action(NULL, tselem->id, NULL);
609 }
610
611 static void cleardrivers_animdata_cb(int UNUSED(event), TreeElement *UNUSED(te),
612                                      TreeStoreElem *tselem, void *UNUSED(arg))
613 {
614         IdAdtTemplate *iat = (IdAdtTemplate *)tselem->id;
615
616         /* just free drivers - stored as a list of F-Curves */
617         free_fcurves(&iat->adt->drivers);
618 }
619
620 static void refreshdrivers_animdata_cb(int UNUSED(event), TreeElement *UNUSED(te),
621                                        TreeStoreElem *tselem, void *UNUSED(arg))
622 {
623         IdAdtTemplate *iat = (IdAdtTemplate *)tselem->id;
624         FCurve *fcu;
625
626         /* loop over drivers, performing refresh (i.e. check graph_buttons.c and rna_fcurve.c for details) */
627         for (fcu = iat->adt->drivers.first; fcu; fcu = fcu->next) {
628                 fcu->flag &= ~FCURVE_DISABLED;
629
630                 if (fcu->driver)
631                         fcu->driver->flag &= ~DRIVER_FLAG_INVALID;
632         }
633 }
634
635 /* --------------------------------- */
636
637 typedef enum eOutliner_PropDataOps {
638         OL_DOP_SELECT = 1,
639         OL_DOP_DESELECT,
640         OL_DOP_HIDE,
641         OL_DOP_UNHIDE,
642         OL_DOP_SELECT_LINKED,
643 } eOutliner_PropDataOps;
644
645 typedef enum eOutliner_PropConstraintOps {
646         OL_CONSTRAINTOP_ENABLE = 1,
647         OL_CONSTRAINTOP_DISABLE,
648         OL_CONSTRAINTOP_DELETE
649 } eOutliner_PropConstraintOps;
650
651 typedef enum eOutliner_PropModifierOps {
652         OL_MODIFIER_OP_TOGVIS = 1,
653         OL_MODIFIER_OP_TOGREN,
654         OL_MODIFIER_OP_DELETE
655 } eOutliner_PropModifierOps;
656
657 static void pchan_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *UNUSED(arg))
658 {
659         bPoseChannel *pchan = (bPoseChannel *)te->directdata;
660
661         if (event == OL_DOP_SELECT)
662                 pchan->bone->flag |= BONE_SELECTED;
663         else if (event == OL_DOP_DESELECT)
664                 pchan->bone->flag &= ~BONE_SELECTED;
665         else if (event == OL_DOP_HIDE) {
666                 pchan->bone->flag |= BONE_HIDDEN_P;
667                 pchan->bone->flag &= ~BONE_SELECTED;
668         }
669         else if (event == OL_DOP_UNHIDE)
670                 pchan->bone->flag &= ~BONE_HIDDEN_P;
671 }
672
673 static void bone_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *UNUSED(arg))
674 {
675         Bone *bone = (Bone *)te->directdata;
676
677         if (event == OL_DOP_SELECT)
678                 bone->flag |= BONE_SELECTED;
679         else if (event == OL_DOP_DESELECT)
680                 bone->flag &= ~BONE_SELECTED;
681         else if (event == OL_DOP_HIDE) {
682                 bone->flag |= BONE_HIDDEN_P;
683                 bone->flag &= ~BONE_SELECTED;
684         }
685         else if (event == OL_DOP_UNHIDE)
686                 bone->flag &= ~BONE_HIDDEN_P;
687 }
688
689 static void ebone_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *UNUSED(arg))
690 {
691         EditBone *ebone = (EditBone *)te->directdata;
692
693         if (event == OL_DOP_SELECT)
694                 ebone->flag |= BONE_SELECTED;
695         else if (event == OL_DOP_DESELECT)
696                 ebone->flag &= ~BONE_SELECTED;
697         else if (event == OL_DOP_HIDE) {
698                 ebone->flag |= BONE_HIDDEN_A;
699                 ebone->flag &= ~BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL;
700         }
701         else if (event == OL_DOP_UNHIDE)
702                 ebone->flag &= ~BONE_HIDDEN_A;
703 }
704
705 static void sequence_cb(int event, TreeElement *te, TreeStoreElem *tselem, void *scene_ptr)
706 {
707         Sequence *seq = (Sequence *)te->directdata;
708         if (event == OL_DOP_SELECT) {
709                 Scene *scene = (Scene *)scene_ptr;
710                 Editing *ed = BKE_sequencer_editing_get(scene, false);
711                 if (BLI_findindex(ed->seqbasep, seq) != -1) {
712                         ED_sequencer_select_sequence_single(scene, seq, true);
713                 }
714         }
715
716         (void)tselem;
717 }
718
719 static void gp_layer_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *UNUSED(arg))
720 {
721         bGPDlayer *gpl = (bGPDlayer *)te->directdata;
722
723         if (event == OL_DOP_SELECT)
724                 gpl->flag |= GP_LAYER_SELECT;
725         else if (event == OL_DOP_DESELECT)
726                 gpl->flag &= ~GP_LAYER_SELECT;
727         else if (event == OL_DOP_HIDE)
728                 gpl->flag |= GP_LAYER_HIDE;
729         else if (event == OL_DOP_UNHIDE)
730                 gpl->flag &= ~GP_LAYER_HIDE;
731 }
732
733 static void data_select_linked_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *C_v)
734 {
735         if (event == OL_DOP_SELECT_LINKED) {
736                 if (RNA_struct_is_ID(te->rnaptr.type)) {
737                         bContext *C = (bContext *) C_v;
738                         ID *id = te->rnaptr.data;
739
740                         ED_object_select_linked_by_id(C, id);
741                 }
742         }
743 }
744
745 static void constraint_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *C_v)
746 {
747         bContext *C = C_v;
748         Main *bmain = CTX_data_main(C);
749         SpaceOops *soops = CTX_wm_space_outliner(C);
750         bConstraint *constraint = (bConstraint *)te->directdata;
751         Object *ob = (Object *)outliner_search_back(soops, te, ID_OB);
752
753         if (event == OL_CONSTRAINTOP_ENABLE) {
754                 constraint->flag &= ~CONSTRAINT_OFF;
755                 ED_object_constraint_update(bmain, ob);
756                 WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
757         }
758         else if (event == OL_CONSTRAINTOP_DISABLE) {
759                 constraint->flag = CONSTRAINT_OFF;
760                 ED_object_constraint_update(bmain, ob);
761                 WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
762         }
763         else if (event == OL_CONSTRAINTOP_DELETE) {
764                 ListBase *lb = NULL;
765
766                 if (TREESTORE(te->parent->parent)->type == TSE_POSE_CHANNEL) {
767                         lb = &((bPoseChannel *)te->parent->parent->directdata)->constraints;
768                 }
769                 else {
770                         lb = &ob->constraints;
771                 }
772
773                 if (BKE_constraint_remove_ex(lb, ob, constraint, true)) {
774                         /* there's no active constraint now, so make sure this is the case */
775                         BKE_constraints_active_set(&ob->constraints, NULL);
776
777                         /* needed to set the flags on posebones correctly */
778                         ED_object_constraint_update(bmain, ob);
779
780                         WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob);
781                         te->store_elem->flag &= ~TSE_SELECTED;
782                 }
783         }
784 }
785
786 static void modifier_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *Carg)
787 {
788         bContext *C = (bContext *)Carg;
789         Main *bmain = CTX_data_main(C);
790         SpaceOops *soops = CTX_wm_space_outliner(C);
791         ModifierData *md = (ModifierData *)te->directdata;
792         Object *ob = (Object *)outliner_search_back(soops, te, ID_OB);
793
794         if (event == OL_MODIFIER_OP_TOGVIS) {
795                 md->mode ^= eModifierMode_Realtime;
796                 DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
797                 WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
798         }
799         else if (event == OL_MODIFIER_OP_TOGREN) {
800                 md->mode ^= eModifierMode_Render;
801                 DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
802                 WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
803         }
804         else if (event == OL_MODIFIER_OP_DELETE) {
805                 ED_object_modifier_remove(NULL, bmain, ob, md);
806                 WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER | NA_REMOVED, ob);
807                 te->store_elem->flag &= ~TSE_SELECTED;
808         }
809 }
810
811 static void outliner_do_data_operation(SpaceOops *soops, int type, int event, ListBase *lb,
812                                        void (*operation_cb)(int, TreeElement *, TreeStoreElem *, void *),
813                                        void *arg)
814 {
815         TreeElement *te;
816         TreeStoreElem *tselem;
817
818         for (te = lb->first; te; te = te->next) {
819                 tselem = TREESTORE(te);
820                 if (tselem->flag & TSE_SELECTED) {
821                         if (tselem->type == type) {
822                                 operation_cb(event, te, tselem, arg);
823                         }
824                 }
825                 if (TSELEM_OPEN(tselem, soops)) {
826                         outliner_do_data_operation(soops, type, event, &te->subtree, operation_cb, arg);
827                 }
828         }
829 }
830
831 static Base *outline_delete_hierarchy(bContext *C, ReportList *reports, Scene *scene, Base *base)
832 {
833         Base *child_base, *base_next;
834         Object *parent;
835
836         if (!base) {
837                 return NULL;
838         }
839
840         for (child_base = scene->base.first; child_base; child_base = base_next) {
841                 base_next = child_base->next;
842                 for (parent = child_base->object->parent; parent && (parent != base->object); parent = parent->parent);
843                 if (parent) {
844                         base_next = outline_delete_hierarchy(C, reports, scene, child_base);
845                 }
846         }
847
848         base_next = base->next;
849
850         Main *bmain = CTX_data_main(C);
851         if (base->object->id.tag & LIB_TAG_INDIRECT) {
852                 BKE_reportf(reports, RPT_WARNING, "Cannot delete indirectly linked object '%s'", base->object->id.name + 2);
853                 return base_next;
854         }
855         else if (BKE_library_ID_is_indirectly_used(bmain, base->object) &&
856                  ID_REAL_USERS(base->object) <= 1 && ID_EXTRA_USERS(base->object) == 0)
857         {
858                 BKE_reportf(reports, RPT_WARNING,
859                             "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user",
860                             base->object->id.name + 2, scene->id.name + 2);
861                 return base_next;
862         }
863         ED_base_object_free_and_unlink(CTX_data_main(C), scene, base);
864         return base_next;
865 }
866
867 static void object_delete_hierarchy_cb(
868         bContext *C, ReportList *reports, Scene *scene,
869         TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
870 {
871         Base *base = (Base *)te->directdata;
872         Object *obedit = scene->obedit;
873
874         if (!base) {
875                 base = BKE_scene_base_find(scene, (Object *)tselem->id);
876         }
877         if (base) {
878                 /* Check also library later. */
879                 for (; obedit && (obedit != base->object); obedit = obedit->parent);
880                 if (obedit == base->object) {
881                         ED_object_editmode_exit(C, EM_FREEDATA | EM_WAITCURSOR);
882                 }
883
884                 outline_delete_hierarchy(C, reports, scene, base);
885                 /* leave for ED_outliner_id_unref to handle */
886 #if 0
887                 te->directdata = NULL;
888                 tselem->id = NULL;
889 #endif
890         }
891
892         WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
893 }
894
895 /* **************************************** */
896
897 enum {
898         OL_OP_SELECT = 1,
899         OL_OP_DESELECT,
900         OL_OP_SELECT_HIERARCHY,
901         OL_OP_DELETE,
902         OL_OP_DELETE_HIERARCHY,
903         OL_OP_REMAP,
904         OL_OP_LOCALIZED,  /* disabled, see below */
905         OL_OP_TOGVIS,
906         OL_OP_TOGSEL,
907         OL_OP_TOGREN,
908         OL_OP_RENAME,
909 };
910
911 static const EnumPropertyItem prop_object_op_types[] = {
912         {OL_OP_SELECT, "SELECT", 0, "Select", ""},
913         {OL_OP_DESELECT, "DESELECT", 0, "Deselect", ""},
914         {OL_OP_SELECT_HIERARCHY, "SELECT_HIERARCHY", 0, "Select Hierarchy", ""},
915         {OL_OP_DELETE, "DELETE", 0, "Delete", ""},
916         {OL_OP_DELETE_HIERARCHY, "DELETE_HIERARCHY", 0, "Delete Hierarchy", ""},
917         {OL_OP_REMAP, "REMAP",   0, "Remap Users",
918          "Make all users of selected data-blocks to use instead a new chosen one"},
919         {OL_OP_TOGVIS, "TOGVIS", 0, "Toggle Visible", ""},
920         {OL_OP_TOGSEL, "TOGSEL", 0, "Toggle Selectable", ""},
921         {OL_OP_TOGREN, "TOGREN", 0, "Toggle Renderable", ""},
922         {OL_OP_RENAME, "RENAME", 0, "Rename", ""},
923         {0, NULL, 0, NULL, NULL}
924 };
925
926 static int outliner_object_operation_exec(bContext *C, wmOperator *op)
927 {
928         Main *bmain = CTX_data_main(C);
929         Scene *scene = CTX_data_scene(C);
930         SpaceOops *soops = CTX_wm_space_outliner(C);
931         int event;
932         const char *str = NULL;
933
934         /* check for invalid states */
935         if (soops == NULL)
936                 return OPERATOR_CANCELLED;
937
938         event = RNA_enum_get(op->ptr, "type");
939
940         if (event == OL_OP_SELECT) {
941                 Scene *sce = scene;  // to be able to delete, scenes are set...
942                 outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_select_cb);
943                 if (scene != sce) {
944                         ED_screen_set_scene(C, CTX_wm_screen(C), sce);
945                 }
946
947                 str = "Select Objects";
948                 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
949         }
950         else if (event == OL_OP_SELECT_HIERARCHY) {
951                 Scene *sce = scene;  // to be able to delete, scenes are set...
952                 outliner_do_object_operation_ex(C, op->reports, scene, soops, &soops->tree, object_select_hierarchy_cb, false);
953                 if (scene != sce) {
954                         ED_screen_set_scene(C, CTX_wm_screen(C), sce);
955                 }
956                 str = "Select Object Hierarchy";
957                 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
958         }
959         else if (event == OL_OP_DESELECT) {
960                 outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_deselect_cb);
961                 str = "Deselect Objects";
962                 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
963         }
964         else if (event == OL_OP_DELETE) {
965                 outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_delete_cb);
966
967                 /* XXX: tree management normally happens from draw_outliner(), but when
968                  *      you're clicking to fast on Delete object from context menu in
969                  *      outliner several mouse events can be handled in one cycle without
970                  *      handling notifiers/redraw which leads to deleting the same object twice.
971                  *      cleanup tree here to prevent such cases. */
972                 outliner_cleanup_tree(soops);
973
974                 DAG_relations_tag_update(bmain);
975                 str = "Delete Objects";
976                 WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
977         }
978         else if (event == OL_OP_DELETE_HIERARCHY) {
979                 outliner_do_object_operation_ex(C, op->reports, scene, soops, &soops->tree, object_delete_hierarchy_cb, false);
980
981                 /* XXX: See OL_OP_DELETE comment above. */
982                 outliner_cleanup_tree(soops);
983
984                 DAG_relations_tag_update(bmain);
985                 str = "Delete Object Hierarchy";
986                 WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
987         }
988         else if (event == OL_OP_REMAP) {
989                 outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_remap_cb, NULL);
990                 str = "Remap ID";
991         }
992         else if (event == OL_OP_LOCALIZED) {    /* disabled, see above enum (ton) */
993                 outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, id_local_cb);
994                 str = "Localized Objects";
995         }
996         else if (event == OL_OP_TOGVIS) {
997                 outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_toggle_visibility_cb);
998                 str = "Toggle Visibility";
999                 WM_event_add_notifier(C, NC_SCENE | ND_OB_VISIBLE, scene);
1000         }
1001         else if (event == OL_OP_TOGSEL) {
1002                 outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_toggle_selectability_cb);
1003                 str = "Toggle Selectability";
1004                 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1005         }
1006         else if (event == OL_OP_TOGREN) {
1007                 outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, object_toggle_renderability_cb);
1008                 str = "Toggle Renderability";
1009                 WM_event_add_notifier(C, NC_SCENE | ND_OB_RENDER, scene);
1010         }
1011         else if (event == OL_OP_RENAME) {
1012                 outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, item_rename_cb);
1013                 str = "Rename Object";
1014         }
1015         else {
1016                 BLI_assert(0);
1017                 return OPERATOR_CANCELLED;
1018         }
1019
1020         ED_undo_push(C, str);
1021
1022         return OPERATOR_FINISHED;
1023 }
1024
1025
1026 void OUTLINER_OT_object_operation(wmOperatorType *ot)
1027 {
1028         /* identifiers */
1029         ot->name = "Outliner Object Operation";
1030         ot->idname = "OUTLINER_OT_object_operation";
1031
1032         /* callbacks */
1033         ot->invoke = WM_menu_invoke;
1034         ot->exec = outliner_object_operation_exec;
1035         ot->poll = ED_operator_outliner_active;
1036
1037         ot->flag = 0;
1038
1039         ot->prop = RNA_def_enum(ot->srna, "type", prop_object_op_types, 0, "Object Operation", "");
1040 }
1041
1042 /* **************************************** */
1043
1044 typedef enum eOutliner_PropGroupOps {
1045         OL_GROUPOP_UNLINK = 1,
1046         OL_GROUPOP_LOCAL,
1047         OL_GROUPOP_LINK,
1048         OL_GROUPOP_DELETE,
1049         OL_GROUPOP_REMAP,
1050         OL_GROUPOP_INSTANCE,
1051         OL_GROUPOP_TOGVIS,
1052         OL_GROUPOP_TOGSEL,
1053         OL_GROUPOP_TOGREN,
1054         OL_GROUPOP_RENAME,
1055 } eOutliner_PropGroupOps;
1056
1057 static const EnumPropertyItem prop_group_op_types[] = {
1058         {OL_GROUPOP_UNLINK, "UNLINK",     0, "Unlink Group", ""},
1059         {OL_GROUPOP_LOCAL, "LOCAL",       0, "Make Local Group", ""},
1060         {OL_GROUPOP_LINK, "LINK",         0, "Link Group Objects to Scene", ""},
1061         {OL_GROUPOP_DELETE, "DELETE",     0, "Delete Group", ""},
1062         {OL_GROUPOP_REMAP, "REMAP",       0, "Remap Users",
1063          "Make all users of selected data-blocks to use instead current (clicked) one"},
1064         {OL_GROUPOP_INSTANCE, "INSTANCE", 0, "Instance Groups in Scene", ""},
1065         {OL_GROUPOP_TOGVIS, "TOGVIS",     0, "Toggle Visible Group", ""},
1066         {OL_GROUPOP_TOGSEL, "TOGSEL",     0, "Toggle Selectable", ""},
1067         {OL_GROUPOP_TOGREN, "TOGREN",     0, "Toggle Renderable", ""},
1068         {OL_GROUPOP_RENAME, "RENAME",     0, "Rename", ""},
1069         {0, NULL, 0, NULL, NULL}
1070 };
1071
1072 static int outliner_group_operation_exec(bContext *C, wmOperator *op)
1073 {
1074         Scene *scene = CTX_data_scene(C);
1075         SpaceOops *soops = CTX_wm_space_outliner(C);
1076         int event;
1077
1078         /* check for invalid states */
1079         if (soops == NULL)
1080                 return OPERATOR_CANCELLED;
1081
1082         event = RNA_enum_get(op->ptr, "type");
1083
1084         switch (event) {
1085                 case OL_GROUPOP_UNLINK:
1086                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, unlink_group_cb, NULL);
1087                         break;
1088                 case OL_GROUPOP_LOCAL:
1089                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_local_cb, NULL);
1090                         break;
1091                 case OL_GROUPOP_LINK:
1092                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, group_linkobs2scene_cb, NULL);
1093                         break;
1094                 case OL_GROUPOP_INSTANCE:
1095                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, group_instance_cb, NULL);
1096                         /* works without this except if you try render right after, see: 22027 */
1097                         DAG_relations_tag_update(CTX_data_main(C));
1098                         break;
1099                 case OL_GROUPOP_DELETE:
1100                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_delete_cb, NULL);
1101                         break;
1102                 case OL_GROUPOP_REMAP:
1103                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_remap_cb, NULL);
1104                         break;
1105                 case OL_GROUPOP_TOGVIS:
1106                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, group_toggle_visibility_cb, NULL);
1107                         break;
1108                 case OL_GROUPOP_TOGSEL:
1109                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, group_toggle_selectability_cb, NULL);
1110                         break;
1111                 case OL_GROUPOP_TOGREN:
1112                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, group_toggle_renderability_cb, NULL);
1113                         break;
1114                 case OL_GROUPOP_RENAME:
1115                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, item_rename_cb, NULL);
1116                         break;
1117                 default:
1118                         BLI_assert(0);
1119         }
1120
1121         ED_undo_push(C, prop_group_op_types[event - 1].name);
1122         WM_event_add_notifier(C, NC_GROUP, NULL);
1123
1124         return OPERATOR_FINISHED;
1125 }
1126
1127
1128 void OUTLINER_OT_group_operation(wmOperatorType *ot)
1129 {
1130         /* identifiers */
1131         ot->name = "Outliner Group Operation";
1132         ot->idname = "OUTLINER_OT_group_operation";
1133
1134         /* callbacks */
1135         ot->invoke = WM_menu_invoke;
1136         ot->exec = outliner_group_operation_exec;
1137         ot->poll = ED_operator_outliner_active;
1138
1139         ot->flag = 0;
1140
1141         ot->prop = RNA_def_enum(ot->srna, "type", prop_group_op_types, 0, "Group Operation", "");
1142 }
1143
1144 /* **************************************** */
1145
1146 typedef enum eOutlinerIdOpTypes {
1147         OUTLINER_IDOP_INVALID = 0,
1148
1149         OUTLINER_IDOP_UNLINK,
1150         OUTLINER_IDOP_LOCAL,
1151         OUTLINER_IDOP_SINGLE,
1152         OUTLINER_IDOP_DELETE,
1153         OUTLINER_IDOP_REMAP,
1154
1155         OUTLINER_IDOP_FAKE_ADD,
1156         OUTLINER_IDOP_FAKE_CLEAR,
1157         OUTLINER_IDOP_RENAME,
1158
1159         OUTLINER_IDOP_SELECT_LINKED
1160 } eOutlinerIdOpTypes;
1161
1162 // TODO: implement support for changing the ID-block used
1163 static const EnumPropertyItem prop_id_op_types[] = {
1164         {OUTLINER_IDOP_UNLINK, "UNLINK", 0, "Unlink", ""},
1165         {OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""},
1166         {OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""},
1167         {OUTLINER_IDOP_DELETE, "DELETE", 0, "Delete", "WARNING: no undo"},
1168         {OUTLINER_IDOP_REMAP, "REMAP", 0, "Remap Users",
1169          "Make all users of selected data-blocks to use instead current (clicked) one"},
1170         {OUTLINER_IDOP_FAKE_ADD, "ADD_FAKE", 0, "Add Fake User",
1171          "Ensure data-block gets saved even if it isn't in use (e.g. for motion and material libraries)"},
1172         {OUTLINER_IDOP_FAKE_CLEAR, "CLEAR_FAKE", 0, "Clear Fake User", ""},
1173         {OUTLINER_IDOP_RENAME, "RENAME", 0, "Rename", ""},
1174         {OUTLINER_IDOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""},
1175         {0, NULL, 0, NULL, NULL}
1176 };
1177
1178 static int outliner_id_operation_exec(bContext *C, wmOperator *op)
1179 {
1180         Scene *scene = CTX_data_scene(C);
1181         SpaceOops *soops = CTX_wm_space_outliner(C);
1182         int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1183         eOutlinerIdOpTypes event;
1184
1185         /* check for invalid states */
1186         if (soops == NULL)
1187                 return OPERATOR_CANCELLED;
1188
1189         set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1190
1191         event = RNA_enum_get(op->ptr, "type");
1192
1193         switch (event) {
1194                 case OUTLINER_IDOP_UNLINK:
1195                 {
1196                         /* unlink datablock from its parent */
1197                         switch (idlevel) {
1198                                 case ID_AC:
1199                                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, unlink_action_cb, NULL);
1200
1201                                         WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
1202                                         ED_undo_push(C, "Unlink action");
1203                                         break;
1204                                 case ID_MA:
1205                                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, unlink_material_cb, NULL);
1206
1207                                         WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, NULL);
1208                                         ED_undo_push(C, "Unlink material");
1209                                         break;
1210                                 case ID_TE:
1211                                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, unlink_texture_cb, NULL);
1212
1213                                         WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, NULL);
1214                                         ED_undo_push(C, "Unlink texture");
1215                                         break;
1216                                 case ID_WO:
1217                                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, unlink_world_cb, NULL);
1218
1219                                         WM_event_add_notifier(C, NC_SCENE | ND_WORLD, NULL);
1220                                         ED_undo_push(C, "Unlink world");
1221                                         break;
1222                                 default:
1223                                         BKE_report(op->reports, RPT_WARNING, "Not yet implemented");
1224                                         break;
1225                         }
1226                         break;
1227                 }
1228                 case OUTLINER_IDOP_LOCAL:
1229                 {
1230                         /* make local */
1231                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_local_cb, NULL);
1232                         ED_undo_push(C, "Localized Data");
1233                         break;
1234                 }
1235                 case OUTLINER_IDOP_SINGLE:
1236                 {
1237                         /* make single user */
1238                         switch (idlevel) {
1239                                 case ID_AC:
1240                                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, singleuser_action_cb, NULL);
1241
1242                                         WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
1243                                         ED_undo_push(C, "Single-User Action");
1244                                         break;
1245
1246                                 case ID_WO:
1247                                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, singleuser_world_cb, NULL);
1248
1249                                         WM_event_add_notifier(C, NC_SCENE | ND_WORLD, NULL);
1250                                         ED_undo_push(C, "Single-User World");
1251                                         break;
1252
1253                                 default:
1254                                         BKE_report(op->reports, RPT_WARNING, "Not yet implemented");
1255                                         break;
1256                         }
1257                         break;
1258                 }
1259                 case OUTLINER_IDOP_DELETE:
1260                 {
1261                         if (idlevel > 0) {
1262                                 outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_delete_cb, NULL);
1263                                 ED_undo_push(C, "Delete");
1264                         }
1265                         break;
1266                 }
1267                 case OUTLINER_IDOP_REMAP:
1268                 {
1269                         if (idlevel > 0) {
1270                                 outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_remap_cb, NULL);
1271                                 ED_undo_push(C, "Remap");
1272                         }
1273                         break;
1274                 }
1275                 case OUTLINER_IDOP_FAKE_ADD:
1276                 {
1277                         /* set fake user */
1278                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_fake_user_set_cb, NULL);
1279
1280                         WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
1281                         ED_undo_push(C, "Add Fake User");
1282                         break;
1283                 }
1284                 case OUTLINER_IDOP_FAKE_CLEAR:
1285                 {
1286                         /* clear fake user */
1287                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_fake_user_clear_cb, NULL);
1288
1289                         WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
1290                         ED_undo_push(C, "Clear Fake User");
1291                         break;
1292                 }
1293                 case OUTLINER_IDOP_RENAME:
1294                 {
1295                         /* rename */
1296                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, item_rename_cb, NULL);
1297
1298                         WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
1299                         ED_undo_push(C, "Rename");
1300                         break;
1301                 }
1302                 case OUTLINER_IDOP_SELECT_LINKED:
1303                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_select_linked_cb, NULL);
1304                         ED_undo_push(C, "Select");
1305                         break;
1306
1307                 default:
1308                         // invalid - unhandled
1309                         break;
1310         }
1311
1312         /* wrong notifier still... */
1313         WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
1314
1315         // XXX: this is just so that outliner is always up to date
1316         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, NULL);
1317
1318         return OPERATOR_FINISHED;
1319 }
1320
1321
1322 void OUTLINER_OT_id_operation(wmOperatorType *ot)
1323 {
1324         /* identifiers */
1325         ot->name = "Outliner ID data Operation";
1326         ot->idname = "OUTLINER_OT_id_operation";
1327
1328         /* callbacks */
1329         ot->invoke = WM_menu_invoke;
1330         ot->exec = outliner_id_operation_exec;
1331         ot->poll = ED_operator_outliner_active;
1332
1333         ot->flag = 0;
1334
1335         ot->prop = RNA_def_enum(ot->srna, "type", prop_id_op_types, 0, "ID data Operation", "");
1336 }
1337
1338 /* **************************************** */
1339
1340 typedef enum eOutlinerLibOpTypes {
1341         OL_LIB_INVALID = 0,
1342
1343         OL_LIB_RENAME,
1344         OL_LIB_DELETE,
1345         OL_LIB_RELOCATE,
1346         OL_LIB_RELOAD,
1347 } eOutlinerLibOpTypes;
1348
1349 static const EnumPropertyItem outliner_lib_op_type_items[] = {
1350         {OL_LIB_RENAME, "RENAME", 0, "Rename", ""},
1351         {OL_LIB_DELETE, "DELETE", 0, "Delete", "Delete this library and all its item from Blender - WARNING: no undo"},
1352         {OL_LIB_RELOCATE, "RELOCATE", 0, "Relocate", "Select a new path for this library, and reload all its data"},
1353         {OL_LIB_RELOAD, "RELOAD", 0, "Reload", "Reload all data from this library"},
1354         {0, NULL, 0, NULL, NULL}
1355 };
1356
1357 static int outliner_lib_operation_exec(bContext *C, wmOperator *op)
1358 {
1359         Scene *scene = CTX_data_scene(C);
1360         SpaceOops *soops = CTX_wm_space_outliner(C);
1361         int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1362         eOutlinerLibOpTypes event;
1363
1364         /* check for invalid states */
1365         if (soops == NULL)
1366                 return OPERATOR_CANCELLED;
1367
1368         set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1369
1370         event = RNA_enum_get(op->ptr, "type");
1371
1372         switch (event) {
1373                 case OL_LIB_RENAME:
1374                 {
1375                         /* rename */
1376                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, item_rename_cb, NULL);
1377
1378                         WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
1379                         ED_undo_push(C, "Rename Library");
1380                         break;
1381                 }
1382                 case OL_LIB_DELETE:
1383                 {
1384                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_delete_cb, NULL);
1385                         ED_undo_push(C, "Delete Library");
1386                         break;
1387                 }
1388                 case OL_LIB_RELOCATE:
1389                 {
1390                         /* rename */
1391                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, lib_relocate_cb, NULL);
1392                         ED_undo_push(C, "Relocate Library");
1393                         break;
1394                 }
1395                 case OL_LIB_RELOAD:
1396                 {
1397                         /* rename */
1398                         outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, lib_reload_cb, NULL);
1399                         break;
1400                 }
1401                 default:
1402                         /* invalid - unhandled */
1403                         break;
1404         }
1405
1406         /* wrong notifier still... */
1407         WM_event_add_notifier(C, NC_ID | NA_EDITED, NULL);
1408
1409         /* XXX: this is just so that outliner is always up to date */
1410         WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, NULL);
1411
1412         return OPERATOR_FINISHED;
1413 }
1414
1415
1416 void OUTLINER_OT_lib_operation(wmOperatorType *ot)
1417 {
1418         /* identifiers */
1419         ot->name = "Outliner Library Operation";
1420         ot->idname = "OUTLINER_OT_lib_operation";
1421
1422         /* callbacks */
1423         ot->invoke = WM_menu_invoke;
1424         ot->exec = outliner_lib_operation_exec;
1425         ot->poll = ED_operator_outliner_active;
1426
1427         ot->prop = RNA_def_enum(ot->srna, "type", outliner_lib_op_type_items, 0, "Library Operation", "");
1428 }
1429
1430 /* **************************************** */
1431
1432 static void outliner_do_id_set_operation(SpaceOops *soops, int type, ListBase *lb, ID *newid,
1433                                          void (*operation_cb)(TreeElement *, TreeStoreElem *, TreeStoreElem *, ID *))
1434 {
1435         TreeElement *te;
1436         TreeStoreElem *tselem;
1437
1438         for (te = lb->first; te; te = te->next) {
1439                 tselem = TREESTORE(te);
1440                 if (tselem->flag & TSE_SELECTED) {
1441                         if (tselem->type == type) {
1442                                 TreeStoreElem *tsep = te->parent ? TREESTORE(te->parent) : NULL;
1443                                 operation_cb(te, tselem, tsep, newid);
1444                         }
1445                 }
1446                 if (TSELEM_OPEN(tselem, soops)) {
1447                         outliner_do_id_set_operation(soops, type, &te->subtree, newid, operation_cb);
1448                 }
1449         }
1450 }
1451
1452 /* ------------------------------------------ */
1453
1454 static void actionset_id_cb(TreeElement *UNUSED(te), TreeStoreElem *tselem, TreeStoreElem *tsep, ID *actId)
1455 {
1456         bAction *act = (bAction *)actId;
1457
1458         if (tselem->type == TSE_ANIM_DATA) {
1459                 /* "animation" entries - action is child of this */
1460                 BKE_animdata_set_action(NULL, tselem->id, act);
1461         }
1462         /* TODO: if any other "expander" channels which own actions need to support this menu,
1463          * add: tselem->type = ...
1464          */
1465         else if (tsep && (tsep->type == TSE_ANIM_DATA)) {
1466                 /* "animation" entries case again */
1467                 BKE_animdata_set_action(NULL, tsep->id, act);
1468         }
1469         // TODO: other cases not supported yet
1470 }
1471
1472 static int outliner_action_set_exec(bContext *C, wmOperator *op)
1473 {
1474         SpaceOops *soops = CTX_wm_space_outliner(C);
1475         int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1476
1477         bAction *act;
1478
1479         /* check for invalid states */
1480         if (soops == NULL)
1481                 return OPERATOR_CANCELLED;
1482         set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1483
1484         /* get action to use */
1485         act = BLI_findlink(&CTX_data_main(C)->action, RNA_enum_get(op->ptr, "action"));
1486
1487         if (act == NULL) {
1488                 BKE_report(op->reports, RPT_ERROR, "No valid action to add");
1489                 return OPERATOR_CANCELLED;
1490         }
1491         else if (act->idroot == 0) {
1492                 /* hopefully in this case (i.e. library of userless actions), the user knows what they're doing... */
1493                 BKE_reportf(op->reports, RPT_WARNING,
1494                             "Action '%s' does not specify what data-blocks it can be used on "
1495                             "(try setting the 'ID Root Type' setting from the data-blocks editor "
1496                             "for this action to avoid future problems)",
1497                             act->id.name + 2);
1498         }
1499
1500         /* perform action if valid channel */
1501         if (datalevel == TSE_ANIM_DATA)
1502                 outliner_do_id_set_operation(soops, datalevel, &soops->tree, (ID *)act, actionset_id_cb);
1503         else if (idlevel == ID_AC)
1504                 outliner_do_id_set_operation(soops, idlevel, &soops->tree, (ID *)act, actionset_id_cb);
1505         else
1506                 return OPERATOR_CANCELLED;
1507
1508         /* set notifier that things have changed */
1509         WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
1510         ED_undo_push(C, "Set action");
1511
1512         /* done */
1513         return OPERATOR_FINISHED;
1514 }
1515
1516 void OUTLINER_OT_action_set(wmOperatorType *ot)
1517 {
1518         PropertyRNA *prop;
1519
1520         /* identifiers */
1521         ot->name = "Outliner Set Action";
1522         ot->idname = "OUTLINER_OT_action_set";
1523         ot->description = "Change the active action used";
1524
1525         /* api callbacks */
1526         ot->invoke = WM_enum_search_invoke;
1527         ot->exec = outliner_action_set_exec;
1528         ot->poll = ED_operator_outliner_active;
1529
1530         /* flags */
1531         ot->flag = 0;
1532
1533         /* props */
1534         // TODO: this would be nicer as an ID-pointer...
1535         prop = RNA_def_enum(ot->srna, "action", DummyRNA_NULL_items, 0, "Action", "");
1536         RNA_def_enum_funcs(prop, RNA_action_itemf);
1537         RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
1538         ot->prop = prop;
1539 }
1540
1541 /* **************************************** */
1542
1543 typedef enum eOutliner_AnimDataOps {
1544         OUTLINER_ANIMOP_INVALID = 0,
1545
1546         OUTLINER_ANIMOP_CLEAR_ADT,
1547
1548         OUTLINER_ANIMOP_SET_ACT,
1549         OUTLINER_ANIMOP_CLEAR_ACT,
1550
1551         OUTLINER_ANIMOP_REFRESH_DRV,
1552         OUTLINER_ANIMOP_CLEAR_DRV
1553
1554         //OUTLINER_ANIMOP_COPY_DRIVERS,
1555         //OUTLINER_ANIMOP_PASTE_DRIVERS
1556 } eOutliner_AnimDataOps;
1557
1558 static const EnumPropertyItem prop_animdata_op_types[] = {
1559         {OUTLINER_ANIMOP_CLEAR_ADT, "CLEAR_ANIMDATA", 0, "Clear Animation Data", "Remove this animation data container"},
1560         {OUTLINER_ANIMOP_SET_ACT, "SET_ACT", 0, "Set Action", ""},
1561         {OUTLINER_ANIMOP_CLEAR_ACT, "CLEAR_ACT", 0, "Unlink Action", ""},
1562         {OUTLINER_ANIMOP_REFRESH_DRV, "REFRESH_DRIVERS", 0, "Refresh Drivers", ""},
1563         //{OUTLINER_ANIMOP_COPY_DRIVERS, "COPY_DRIVERS", 0, "Copy Drivers", ""},
1564         //{OUTLINER_ANIMOP_PASTE_DRIVERS, "PASTE_DRIVERS", 0, "Paste Drivers", ""},
1565         {OUTLINER_ANIMOP_CLEAR_DRV, "CLEAR_DRIVERS", 0, "Clear Drivers", ""},
1566         {0, NULL, 0, NULL, NULL}
1567 };
1568
1569 static int outliner_animdata_operation_exec(bContext *C, wmOperator *op)
1570 {
1571         SpaceOops *soops = CTX_wm_space_outliner(C);
1572         int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1573         eOutliner_AnimDataOps event;
1574         short updateDeps = 0;
1575
1576         /* check for invalid states */
1577         if (soops == NULL)
1578                 return OPERATOR_CANCELLED;
1579
1580         event = RNA_enum_get(op->ptr, "type");
1581         set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1582
1583         if (datalevel != TSE_ANIM_DATA)
1584                 return OPERATOR_CANCELLED;
1585
1586         /* perform the core operation */
1587         switch (event) {
1588                 case OUTLINER_ANIMOP_CLEAR_ADT:
1589                         /* Remove Animation Data - this may remove the active action, in some cases... */
1590                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, clear_animdata_cb, NULL);
1591
1592                         WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
1593                         ED_undo_push(C, "Clear Animation Data");
1594                         break;
1595
1596                 case OUTLINER_ANIMOP_SET_ACT:
1597                         /* delegate once again... */
1598                         WM_operator_name_call(C, "OUTLINER_OT_action_set", WM_OP_INVOKE_REGION_WIN, NULL);
1599                         break;
1600
1601                 case OUTLINER_ANIMOP_CLEAR_ACT:
1602                         /* clear active action - using standard rules */
1603                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, unlinkact_animdata_cb, NULL);
1604
1605                         WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL);
1606                         ED_undo_push(C, "Unlink action");
1607                         break;
1608
1609                 case OUTLINER_ANIMOP_REFRESH_DRV:
1610                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, refreshdrivers_animdata_cb, NULL);
1611
1612                         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, NULL);
1613                         //ED_undo_push(C, "Refresh Drivers"); /* no undo needed - shouldn't have any impact? */
1614                         updateDeps = 1;
1615                         break;
1616
1617                 case OUTLINER_ANIMOP_CLEAR_DRV:
1618                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, cleardrivers_animdata_cb, NULL);
1619
1620                         WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, NULL);
1621                         ED_undo_push(C, "Clear Drivers");
1622                         updateDeps = 1;
1623                         break;
1624
1625                 default: // invalid
1626                         break;
1627         }
1628
1629         /* update dependencies */
1630         if (updateDeps) {
1631                 /* rebuild depsgraph for the new deps */
1632                 DAG_relations_tag_update(CTX_data_main(C));
1633         }
1634
1635         return OPERATOR_FINISHED;
1636 }
1637
1638
1639 void OUTLINER_OT_animdata_operation(wmOperatorType *ot)
1640 {
1641         /* identifiers */
1642         ot->name = "Outliner Animation Data Operation";
1643         ot->idname = "OUTLINER_OT_animdata_operation";
1644
1645         /* callbacks */
1646         ot->invoke = WM_menu_invoke;
1647         ot->exec = outliner_animdata_operation_exec;
1648         ot->poll = ED_operator_outliner_active;
1649
1650         ot->flag = 0;
1651
1652         ot->prop = RNA_def_enum(ot->srna, "type", prop_animdata_op_types, 0, "Animation Operation", "");
1653 }
1654
1655 /* **************************************** */
1656
1657 static const EnumPropertyItem prop_constraint_op_types[] = {
1658         {OL_CONSTRAINTOP_ENABLE, "ENABLE", ICON_RESTRICT_VIEW_OFF, "Enable", ""},
1659         {OL_CONSTRAINTOP_DISABLE, "DISABLE", ICON_RESTRICT_VIEW_ON, "Disable", ""},
1660         {OL_CONSTRAINTOP_DELETE, "DELETE", ICON_X, "Delete", ""},
1661         {0, NULL, 0, NULL, NULL}
1662 };
1663
1664 static int outliner_constraint_operation_exec(bContext *C, wmOperator *op)
1665 {
1666         SpaceOops *soops = CTX_wm_space_outliner(C);
1667         int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1668         eOutliner_PropConstraintOps event;
1669
1670         event = RNA_enum_get(op->ptr, "type");
1671         set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1672
1673         outliner_do_data_operation(soops, datalevel, event, &soops->tree, constraint_cb, C);
1674
1675         if (event == OL_CONSTRAINTOP_DELETE) {
1676                 outliner_cleanup_tree(soops);
1677         }
1678
1679         ED_undo_push(C, "Constraint operation");
1680
1681         return OPERATOR_FINISHED;
1682 }
1683
1684 void OUTLINER_OT_constraint_operation(wmOperatorType *ot)
1685 {
1686         /* identifiers */
1687         ot->name = "Outliner Constraint Operation";
1688         ot->idname = "OUTLINER_OT_constraint_operation";
1689
1690         /* callbacks */
1691         ot->invoke = WM_menu_invoke;
1692         ot->exec = outliner_constraint_operation_exec;
1693         ot->poll = ED_operator_outliner_active;
1694
1695         ot->flag = 0;
1696
1697         ot->prop = RNA_def_enum(ot->srna, "type", prop_constraint_op_types, 0, "Constraint Operation", "");
1698 }
1699
1700 /* ******************** */
1701
1702 static const EnumPropertyItem prop_modifier_op_types[] = {
1703         {OL_MODIFIER_OP_TOGVIS, "TOGVIS", ICON_RESTRICT_VIEW_OFF, "Toggle viewport use", ""},
1704         {OL_MODIFIER_OP_TOGREN, "TOGREN", ICON_RESTRICT_RENDER_OFF, "Toggle render use", ""},
1705         {OL_MODIFIER_OP_DELETE, "DELETE", ICON_X, "Delete", ""},
1706         {0, NULL, 0, NULL, NULL}
1707 };
1708
1709 static int outliner_modifier_operation_exec(bContext *C, wmOperator *op)
1710 {
1711         SpaceOops *soops = CTX_wm_space_outliner(C);
1712         int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1713         eOutliner_PropModifierOps event;
1714
1715         event = RNA_enum_get(op->ptr, "type");
1716         set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1717
1718         outliner_do_data_operation(soops, datalevel, event, &soops->tree, modifier_cb, C);
1719
1720         if (event == OL_MODIFIER_OP_DELETE) {
1721                 outliner_cleanup_tree(soops);
1722         }
1723
1724         ED_undo_push(C, "Modifier operation");
1725
1726         return OPERATOR_FINISHED;
1727 }
1728
1729 void OUTLINER_OT_modifier_operation(wmOperatorType *ot)
1730 {
1731         /* identifiers */
1732         ot->name = "Outliner Modifier Operation";
1733         ot->idname = "OUTLINER_OT_modifier_operation";
1734
1735         /* callbacks */
1736         ot->invoke = WM_menu_invoke;
1737         ot->exec = outliner_modifier_operation_exec;
1738         ot->poll = ED_operator_outliner_active;
1739
1740         ot->flag = 0;
1741
1742         ot->prop = RNA_def_enum(ot->srna, "type", prop_modifier_op_types, 0, "Modifier Operation", "");
1743 }
1744
1745 /* ******************** */
1746
1747 // XXX: select linked is for RNA structs only
1748 static const EnumPropertyItem prop_data_op_types[] = {
1749         {OL_DOP_SELECT, "SELECT", 0, "Select", ""},
1750         {OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""},
1751         {OL_DOP_HIDE, "HIDE", 0, "Hide", ""},
1752         {OL_DOP_UNHIDE, "UNHIDE", 0, "Unhide", ""},
1753         {OL_DOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""},
1754         {0, NULL, 0, NULL, NULL}
1755 };
1756
1757 static int outliner_data_operation_exec(bContext *C, wmOperator *op)
1758 {
1759         SpaceOops *soops = CTX_wm_space_outliner(C);
1760         int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1761         eOutliner_PropDataOps event;
1762
1763         /* check for invalid states */
1764         if (soops == NULL)
1765                 return OPERATOR_CANCELLED;
1766
1767         event = RNA_enum_get(op->ptr, "type");
1768         set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1769
1770         switch (datalevel) {
1771                 case TSE_POSE_CHANNEL:
1772                 {
1773                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, pchan_cb, NULL);
1774                         WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
1775                         ED_undo_push(C, "PoseChannel operation");
1776
1777                         break;
1778                 }
1779                 case TSE_BONE:
1780                 {
1781                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, bone_cb, NULL);
1782                         WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
1783                         ED_undo_push(C, "Bone operation");
1784
1785                         break;
1786                 }
1787                 case TSE_EBONE:
1788                 {
1789                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, ebone_cb, NULL);
1790                         WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
1791                         ED_undo_push(C, "EditBone operation");
1792
1793                         break;
1794                 }
1795                 case TSE_SEQUENCE:
1796                 {
1797                         Scene *scene = CTX_data_scene(C);
1798                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, sequence_cb, scene);
1799
1800                         break;
1801                 }
1802                 case TSE_GP_LAYER:
1803                 {
1804                         outliner_do_data_operation(soops, datalevel, event, &soops->tree, gp_layer_cb, NULL);
1805                         WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL);
1806                         ED_undo_push(C, "Grease Pencil Layer operation");
1807
1808                         break;
1809                 }
1810                 case TSE_RNA_STRUCT:
1811                         if (event == OL_DOP_SELECT_LINKED) {
1812                                 outliner_do_data_operation(soops, datalevel, event, &soops->tree, data_select_linked_cb, C);
1813                         }
1814
1815                         break;
1816
1817                 default:
1818                         BKE_report(op->reports, RPT_WARNING, "Not yet implemented");
1819                         break;
1820         }
1821
1822         return OPERATOR_FINISHED;
1823 }
1824
1825
1826 void OUTLINER_OT_data_operation(wmOperatorType *ot)
1827 {
1828         /* identifiers */
1829         ot->name = "Outliner Data Operation";
1830         ot->idname = "OUTLINER_OT_data_operation";
1831
1832         /* callbacks */
1833         ot->invoke = WM_menu_invoke;
1834         ot->exec = outliner_data_operation_exec;
1835         ot->poll = ED_operator_outliner_active;
1836
1837         ot->flag = 0;
1838
1839         ot->prop = RNA_def_enum(ot->srna, "type", prop_data_op_types, 0, "Data Operation", "");
1840 }
1841
1842
1843 /* ******************** */
1844
1845
1846 static int do_outliner_operation_event(bContext *C, ARegion *ar, SpaceOops *soops,
1847                                        TreeElement *te, const float mval[2])
1848 {
1849         ReportList *reports = CTX_wm_reports(C); // XXX...
1850
1851         if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
1852                 int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
1853                 TreeStoreElem *tselem = TREESTORE(te);
1854
1855                 /* select object that's clicked on and popup context menu */
1856                 if (!(tselem->flag & TSE_SELECTED)) {
1857
1858                         if (outliner_flag_is_any_test(&soops->tree, TSE_SELECTED, 1))
1859                                 outliner_flag_set(&soops->tree, TSE_SELECTED, 0);
1860
1861                         tselem->flag |= TSE_SELECTED;
1862                         /* redraw, same as outliner_select function */
1863                         soops->storeflag |= SO_TREESTORE_REDRAW;
1864                         ED_region_tag_redraw(ar);
1865                 }
1866
1867                 set_operation_types(soops, &soops->tree, &scenelevel, &objectlevel, &idlevel, &datalevel);
1868
1869                 if (scenelevel) {
1870                         if (objectlevel || datalevel || idlevel) {
1871                                 BKE_report(reports, RPT_WARNING, "Mixed selection");
1872                         }
1873                         else {
1874                                 WM_operator_name_call(C, "OUTLINER_OT_scene_operation", WM_OP_INVOKE_REGION_WIN, NULL);
1875                         }
1876                 }
1877                 else if (objectlevel) {
1878                         WM_operator_name_call(C, "OUTLINER_OT_object_operation", WM_OP_INVOKE_REGION_WIN, NULL);
1879                 }
1880                 else if (idlevel) {
1881                         if (idlevel == -1 || datalevel) {
1882                                 BKE_report(reports, RPT_WARNING, "Mixed selection");
1883                         }
1884                         else {
1885                                 switch (idlevel) {
1886                                         case ID_GR:
1887                                                 WM_operator_name_call(C, "OUTLINER_OT_group_operation", WM_OP_INVOKE_REGION_WIN, NULL);
1888                                                 break;
1889                                         case ID_LI:
1890                                                 WM_operator_name_call(C, "OUTLINER_OT_lib_operation", WM_OP_INVOKE_REGION_WIN, NULL);
1891                                                 break;
1892                                         default:
1893                                                 WM_operator_name_call(C, "OUTLINER_OT_id_operation", WM_OP_INVOKE_REGION_WIN, NULL);
1894                                                 break;
1895                                 }
1896                         }
1897                 }
1898                 else if (datalevel) {
1899                         if (datalevel == -1) {
1900                                 BKE_report(reports, RPT_WARNING, "Mixed selection");
1901                         }
1902                         else {
1903                                 if (datalevel == TSE_ANIM_DATA)
1904                                         WM_operator_name_call(C, "OUTLINER_OT_animdata_operation", WM_OP_INVOKE_REGION_WIN, NULL);
1905                                 else if (datalevel == TSE_DRIVER_BASE) {
1906                                         /* do nothing... no special ops needed yet */
1907                                 }
1908                                 else if (ELEM(datalevel, TSE_R_LAYER_BASE, TSE_R_LAYER, TSE_R_PASS)) {
1909                                         /*WM_operator_name_call(C, "OUTLINER_OT_renderdata_operation", WM_OP_INVOKE_REGION_WIN, NULL)*/
1910                                 }
1911                                 else if (datalevel == TSE_ID_BASE) {
1912                                         /* do nothing... there are no ops needed here yet */
1913                                 }
1914                                 else if (datalevel == TSE_CONSTRAINT) {
1915                                         WM_operator_name_call(C, "OUTLINER_OT_constraint_operation", WM_OP_INVOKE_REGION_WIN, NULL);
1916                                 }
1917                                 else if (datalevel == TSE_MODIFIER) {
1918                                         WM_operator_name_call(C, "OUTLINER_OT_modifier_operation", WM_OP_INVOKE_REGION_WIN, NULL);
1919                                 }
1920                                 else {
1921                                         WM_operator_name_call(C, "OUTLINER_OT_data_operation", WM_OP_INVOKE_REGION_WIN, NULL);
1922                                 }
1923                         }
1924                 }
1925
1926                 return 1;
1927         }
1928
1929         for (te = te->subtree.first; te; te = te->next) {
1930                 if (do_outliner_operation_event(C, ar, soops, te, mval))
1931                         return 1;
1932         }
1933         return 0;
1934 }
1935
1936
1937 static int outliner_operation(bContext *C, wmOperator *UNUSED(op), const wmEvent *event)
1938 {
1939         ARegion *ar = CTX_wm_region(C);
1940         SpaceOops *soops = CTX_wm_space_outliner(C);
1941         uiBut *but = UI_context_active_but_get(C);
1942         TreeElement *te;
1943         float fmval[2];
1944
1945         if (but) {
1946                 UI_but_tooltip_timer_remove(C, but);
1947         }
1948
1949         UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
1950
1951         for (te = soops->tree.first; te; te = te->next) {
1952                 if (do_outliner_operation_event(C, ar, soops, te, fmval)) {
1953                         break;
1954                 }
1955         }
1956
1957         return OPERATOR_FINISHED;
1958 }
1959
1960 /* Menu only! Calls other operators */
1961 void OUTLINER_OT_operation(wmOperatorType *ot)
1962 {
1963         ot->name = "Execute Operation";
1964         ot->idname = "OUTLINER_OT_operation";
1965         ot->description = "Context menu for item operations";
1966
1967         ot->invoke = outliner_operation;
1968
1969         ot->poll = ED_operator_outliner_active;
1970 }
1971
1972 /* ****************************************************** */