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