Blender V2.61 - r43446

outliner_edit.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version. 
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2004 Blender Foundation.
00019  * All rights reserved.
00020  *
00021  * The Original Code is: all of this file.
00022  *
00023  * Contributor(s): Joshua Leung
00024  *
00025  * ***** END GPL LICENSE BLOCK *****
00026  */
00027 
00032 #include <math.h>
00033 #include <string.h>
00034 #include <stdlib.h>
00035 #include <stddef.h>
00036 
00037 #include "MEM_guardedalloc.h"
00038 
00039 #include "DNA_anim_types.h"
00040 #include "DNA_armature_types.h"
00041 #include "DNA_constraint_types.h"
00042 #include "DNA_camera_types.h"
00043 #include "DNA_group_types.h"
00044 #include "DNA_key_types.h"
00045 #include "DNA_lamp_types.h"
00046 #include "DNA_material_types.h"
00047 #include "DNA_mesh_types.h"
00048 #include "DNA_meta_types.h"
00049 #include "DNA_particle_types.h"
00050 #include "DNA_scene_types.h"
00051 #include "DNA_world_types.h"
00052 #include "DNA_sequence_types.h"
00053 #include "DNA_object_types.h"
00054 
00055 #include "BLI_blenlib.h"
00056 #include "BLI_utildefines.h"
00057 #include "BLI_math_base.h"
00058 
00059 #if defined WIN32 && !defined _LIBC
00060 # include "BLI_fnmatch.h" /* use fnmatch included in blenlib */
00061 #else
00062 #  ifndef _GNU_SOURCE
00063 #    define _GNU_SOURCE
00064 #  endif
00065 # include <fnmatch.h>
00066 #endif
00067 
00068 
00069 #include "BKE_animsys.h"
00070 #include "BKE_context.h"
00071 #include "BKE_deform.h"
00072 #include "BKE_depsgraph.h"
00073 #include "BKE_fcurve.h"
00074 #include "BKE_global.h"
00075 #include "BKE_group.h"
00076 #include "BKE_library.h"
00077 #include "BKE_main.h"
00078 #include "BKE_modifier.h"
00079 #include "BKE_report.h"
00080 #include "BKE_scene.h"
00081 #include "BKE_sequencer.h"
00082 
00083 #include "ED_armature.h"
00084 #include "ED_object.h"
00085 #include "ED_screen.h"
00086 #include "ED_util.h"
00087 
00088 #include "WM_api.h"
00089 #include "WM_types.h"
00090 
00091 #include "BIF_gl.h"
00092 #include "BIF_glutil.h"
00093 
00094 #include "UI_interface.h"
00095 #include "UI_interface_icons.h"
00096 #include "UI_resources.h"
00097 #include "UI_view2d.h"
00098 
00099 #include "RNA_access.h"
00100 #include "RNA_define.h"
00101 #include "RNA_enum_types.h"
00102 
00103 #include "ED_keyframing.h"
00104 
00105 #include "outliner_intern.h"
00106 
00107 /* ************************************************************** */
00108 /* Unused Utilities */
00109 // XXX: where to place these?
00110 
00111 /* This is not used anywhere at the moment */
00112 #if 0
00113 /* return 1 when levels were opened */
00114 static int outliner_open_back(SpaceOops *soops, TreeElement *te)
00115 {
00116     TreeStoreElem *tselem;
00117     int retval= 0;
00118     
00119     for (te= te->parent; te; te= te->parent) {
00120         tselem= TREESTORE(te);
00121         if (tselem->flag & TSE_CLOSED) { 
00122             tselem->flag &= ~TSE_CLOSED;
00123             retval= 1;
00124         }
00125     }
00126     return retval;
00127 }
00128 
00129 static void outliner_open_reveal(SpaceOops *soops, ListBase *lb, TreeElement *teFind, int *found)
00130 {
00131     TreeElement *te;
00132     TreeStoreElem *tselem;
00133     
00134     for (te= lb->first; te; te= te->next) {
00135         /* check if this tree-element was the one we're seeking */
00136         if (te == teFind) {
00137             *found= 1;
00138             return;
00139         }
00140         
00141         /* try to see if sub-tree contains it then */
00142         outliner_open_reveal(soops, &te->subtree, teFind, found);
00143         if (*found) {
00144             tselem= TREESTORE(te);
00145             if (tselem->flag & TSE_CLOSED) 
00146                 tselem->flag &= ~TSE_CLOSED;
00147             return;
00148         }
00149     }
00150 }
00151 #endif
00152 
00153 /* ************************************************************** */
00154 /* Click Activated */
00155 
00156 /* Toggle Open/Closed ------------------------------------------- */
00157 
00158 static int do_outliner_item_openclose(bContext *C, SpaceOops *soops, TreeElement *te, int all, const float mval[2])
00159 {
00160     
00161     if(mval[1]>te->ys && mval[1]<te->ys+UI_UNIT_Y) {
00162         TreeStoreElem *tselem= TREESTORE(te);
00163         
00164         /* all below close/open? */
00165         if(all) {
00166             tselem->flag &= ~TSE_CLOSED;
00167             outliner_set_flag(soops, &te->subtree, TSE_CLOSED, !outliner_has_one_flag(soops, &te->subtree, TSE_CLOSED, 1));
00168         }
00169         else {
00170             if(tselem->flag & TSE_CLOSED) tselem->flag &= ~TSE_CLOSED;
00171             else tselem->flag |= TSE_CLOSED;
00172         }
00173         
00174         return 1;
00175     }
00176     
00177     for(te= te->subtree.first; te; te= te->next) {
00178         if(do_outliner_item_openclose(C, soops, te, all, mval)) 
00179             return 1;
00180     }
00181     return 0;
00182     
00183 }
00184 
00185 /* event can enterkey, then it opens/closes */
00186 static int outliner_item_openclose(bContext *C, wmOperator *op, wmEvent *event)
00187 {
00188     ARegion *ar= CTX_wm_region(C);
00189     SpaceOops *soops= CTX_wm_space_outliner(C);
00190     TreeElement *te;
00191     float fmval[2];
00192     int all= RNA_boolean_get(op->ptr, "all");
00193     
00194     UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], fmval, fmval+1);
00195     
00196     for(te= soops->tree.first; te; te= te->next) {
00197         if(do_outliner_item_openclose(C, soops, te, all, fmval)) 
00198             break;
00199     }
00200 
00201     ED_region_tag_redraw(ar);
00202     
00203     return OPERATOR_FINISHED;
00204 }
00205 
00206 void OUTLINER_OT_item_openclose(wmOperatorType *ot)
00207 {
00208     ot->name= "Open/Close Item";
00209     ot->idname= "OUTLINER_OT_item_openclose";
00210     ot->description= "Toggle whether item under cursor is enabled or closed";
00211     
00212     ot->invoke= outliner_item_openclose;
00213     
00214     ot->poll= ED_operator_outliner_active;
00215     
00216     RNA_def_boolean(ot->srna, "all", 1, "All", "Close or open all items");
00217 }
00218 
00219 /* Rename --------------------------------------------------- */
00220 
00221 static void do_item_rename(ARegion *ar, TreeElement *te, TreeStoreElem *tselem, ReportList *reports)
00222 {
00223     /* can't rename rna datablocks entries */
00224     if(ELEM3(tselem->type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) {
00225             /* do nothing */;
00226     }
00227     else if(ELEM10(tselem->type, TSE_ANIM_DATA, TSE_NLA, TSE_DEFGROUP_BASE, TSE_CONSTRAINT_BASE, TSE_MODIFIER_BASE,
00228                                  TSE_SCRIPT_BASE, TSE_POSE_BASE, TSE_POSEGRP_BASE, TSE_R_LAYER_BASE, TSE_R_PASS))
00229     {
00230             BKE_report(reports, RPT_WARNING, "Cannot edit builtin name");
00231     }
00232     else if(ELEM3(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) {
00233         BKE_report(reports, RPT_WARNING, "Cannot edit sequence name");
00234     }
00235     else if(tselem->id->lib) {
00236         // XXX                      error_libdata();
00237     } 
00238     else if(te->idcode == ID_LI && te->parent) {
00239         BKE_report(reports, RPT_WARNING, "Cannot edit the path of an indirectly linked library");
00240     } 
00241     else {
00242         tselem->flag |= TSE_TEXTBUT;
00243         ED_region_tag_redraw(ar);
00244     }
00245 }
00246 
00247 void item_rename_cb(bContext *C, Scene *UNUSED(scene), TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
00248 {
00249     ARegion *ar= CTX_wm_region(C);
00250     ReportList *reports= CTX_wm_reports(C); // XXX
00251     do_item_rename(ar, te, tselem, reports) ;
00252 }
00253 
00254 static int do_outliner_item_rename(bContext *C, ARegion *ar, SpaceOops *soops, TreeElement *te, const float mval[2])
00255 {   
00256     ReportList *reports= CTX_wm_reports(C); // XXX
00257     
00258     if(mval[1]>te->ys && mval[1]<te->ys+UI_UNIT_Y) {
00259         TreeStoreElem *tselem= TREESTORE(te);
00260         
00261         /* name and first icon */
00262         if(mval[0]>te->xs+UI_UNIT_X && mval[0]<te->xend) {
00263             
00264             do_item_rename(ar, te, tselem, reports) ;
00265         }
00266         return 1;
00267     }
00268     
00269     for(te= te->subtree.first; te; te= te->next) {
00270         if(do_outliner_item_rename(C, ar, soops, te, mval)) return 1;
00271     }
00272     return 0;
00273 }
00274 
00275 static int outliner_item_rename(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
00276 {
00277     ARegion *ar= CTX_wm_region(C);
00278     SpaceOops *soops= CTX_wm_space_outliner(C);
00279     TreeElement *te;
00280     float fmval[2];
00281     
00282     UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], fmval, fmval+1);
00283     
00284     for(te= soops->tree.first; te; te= te->next) {
00285         if(do_outliner_item_rename(C, ar, soops, te, fmval)) break;
00286     }
00287     
00288     return OPERATOR_FINISHED;
00289 }
00290 
00291 
00292 void OUTLINER_OT_item_rename(wmOperatorType *ot)
00293 {
00294     ot->name= "Rename Item";
00295     ot->idname= "OUTLINER_OT_item_rename";
00296     ot->description= "Rename item under cursor";
00297     
00298     ot->invoke= outliner_item_rename;
00299     
00300     ot->poll= ED_operator_outliner_active;
00301 }
00302 
00303 /* ************************************************************** */
00304 /* Setting Toggling Operators */
00305 
00306 /* =============================================== */
00307 /* Toggling Utilities (Exported) */
00308 
00309 /* Apply Settings ------------------------------- */
00310 
00311 static int outliner_count_levels(SpaceOops *soops, ListBase *lb, int curlevel)
00312 {
00313     TreeElement *te;
00314     int level=curlevel, lev;
00315     
00316     for(te= lb->first; te; te= te->next) {
00317         
00318         lev= outliner_count_levels(soops, &te->subtree, curlevel+1);
00319         if(lev>level) level= lev;
00320     }
00321     return level;
00322 }
00323 
00324 int outliner_has_one_flag(SpaceOops *soops, ListBase *lb, short flag, short curlevel)
00325 {
00326     TreeElement *te;
00327     TreeStoreElem *tselem;
00328     int level;
00329     
00330     for(te= lb->first; te; te= te->next) {
00331         tselem= TREESTORE(te);
00332         if(tselem->flag & flag) return curlevel;
00333         
00334         level= outliner_has_one_flag(soops, &te->subtree, flag, curlevel+1);
00335         if(level) return level;
00336     }
00337     return 0;
00338 }
00339 
00340 void outliner_set_flag(SpaceOops *soops, ListBase *lb, short flag, short set)
00341 {
00342     TreeElement *te;
00343     TreeStoreElem *tselem;
00344     
00345     for(te= lb->first; te; te= te->next) {
00346         tselem= TREESTORE(te);
00347         if(set==0) tselem->flag &= ~flag;
00348         else tselem->flag |= flag;
00349         outliner_set_flag(soops, &te->subtree, flag, set);
00350     }
00351 }
00352 
00353 /* Restriction Columns ------------------------------- */
00354 
00355 /* same check needed for both object operation and restrict column button func
00356  * return 0 when in edit mode (cannot restrict view or select)
00357  * otherwise return 1 */
00358 int common_restrict_check(bContext *C, Object *ob)
00359 {
00360     /* Don't allow hide an object in edit mode,
00361      * check the bug #22153 and #21609, #23977
00362      */
00363     Object *obedit= CTX_data_edit_object(C);
00364     if (obedit && obedit == ob) {
00365         /* found object is hidden, reset */
00366         if (ob->restrictflag & OB_RESTRICT_VIEW)
00367             ob->restrictflag &= ~OB_RESTRICT_VIEW;
00368         /* found object is unselectable, reset */
00369         if (ob->restrictflag & OB_RESTRICT_SELECT)
00370             ob->restrictflag &= ~OB_RESTRICT_SELECT;
00371         return 0;
00372     }
00373     
00374     return 1;
00375 }
00376 
00377 /* =============================================== */
00378 /* Restriction toggles */
00379 
00380 /* Toggle Visibility ---------------------------------------- */
00381 
00382 void object_toggle_visibility_cb(bContext *C, Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
00383 {
00384     Base *base= (Base *)te->directdata;
00385     Object *ob = (Object *)tselem->id;
00386     
00387     /* add check for edit mode */
00388     if(!common_restrict_check(C, ob)) return;
00389     
00390     if(base || (base= object_in_scene(ob, scene))) {
00391         if((base->object->restrictflag ^= OB_RESTRICT_VIEW)) {
00392             ED_base_object_select(base, BA_DESELECT);
00393         }
00394     }
00395 }
00396 
00397 void group_toggle_visibility_cb(bContext *UNUSED(C), Scene *scene, TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
00398 {
00399     Group *group= (Group *)tselem->id;
00400     restrictbutton_gr_restrict_flag(scene, group, OB_RESTRICT_VIEW);
00401 }
00402 
00403 static int outliner_toggle_visibility_exec(bContext *C, wmOperator *UNUSED(op))
00404 {
00405     SpaceOops *soops= CTX_wm_space_outliner(C);
00406     Scene *scene= CTX_data_scene(C);
00407     ARegion *ar= CTX_wm_region(C);
00408     
00409     outliner_do_object_operation(C, scene, soops, &soops->tree, object_toggle_visibility_cb);
00410     
00411     WM_event_add_notifier(C, NC_SCENE|ND_OB_VISIBLE, scene);
00412     ED_region_tag_redraw(ar);
00413     
00414     return OPERATOR_FINISHED;
00415 }
00416 
00417 void OUTLINER_OT_visibility_toggle(wmOperatorType *ot)
00418 {
00419     /* identifiers */
00420     ot->name= "Toggle Visibility";
00421     ot->idname= "OUTLINER_OT_visibility_toggle";
00422     ot->description= "Toggle the visibility of selected items";
00423     
00424     /* callbacks */
00425     ot->exec= outliner_toggle_visibility_exec;
00426     ot->poll= ED_operator_outliner_active_no_editobject;
00427     
00428     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00429 }
00430 
00431 /* Toggle Selectability ---------------------------------------- */
00432 
00433 void object_toggle_selectability_cb(bContext *UNUSED(C), Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
00434 {
00435     Base *base= (Base *)te->directdata;
00436     
00437     if(base==NULL) base= object_in_scene((Object *)tselem->id, scene);
00438     if(base) {
00439         base->object->restrictflag^=OB_RESTRICT_SELECT;
00440     }
00441 }
00442 
00443 void group_toggle_selectability_cb(bContext *UNUSED(C), Scene *scene, TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
00444 {
00445     Group *group= (Group *)tselem->id;
00446     restrictbutton_gr_restrict_flag(scene, group, OB_RESTRICT_SELECT);
00447 }
00448 
00449 static int outliner_toggle_selectability_exec(bContext *C, wmOperator *UNUSED(op))
00450 {
00451     SpaceOops *soops= CTX_wm_space_outliner(C);
00452     Scene *scene= CTX_data_scene(C);
00453     ARegion *ar= CTX_wm_region(C);
00454     
00455     outliner_do_object_operation(C, scene, soops, &soops->tree, object_toggle_selectability_cb);
00456     
00457     WM_event_add_notifier(C, NC_SCENE|ND_OB_SELECT, scene);
00458     ED_region_tag_redraw(ar);
00459     
00460     return OPERATOR_FINISHED;
00461 }
00462 
00463 void OUTLINER_OT_selectability_toggle(wmOperatorType *ot)
00464 {
00465     /* identifiers */
00466     ot->name= "Toggle Selectability";
00467     ot->idname= "OUTLINER_OT_selectability_toggle";
00468     ot->description= "Toggle the selectability";
00469     
00470     /* callbacks */
00471     ot->exec= outliner_toggle_selectability_exec;
00472     ot->poll= ED_operator_outliner_active_no_editobject;
00473     
00474     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00475 }
00476 
00477 /* Toggle Renderability ---------------------------------------- */
00478 
00479 void object_toggle_renderability_cb(bContext *UNUSED(C), Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
00480 {
00481     Base *base= (Base *)te->directdata;
00482     
00483     if(base==NULL) base= object_in_scene((Object *)tselem->id, scene);
00484     if(base) {
00485         base->object->restrictflag^=OB_RESTRICT_RENDER;
00486     }
00487 }
00488 
00489 void group_toggle_renderability_cb(bContext *UNUSED(C), Scene *scene, TreeElement *UNUSED(te), TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem)
00490 {
00491     Group *group= (Group *)tselem->id;
00492     restrictbutton_gr_restrict_flag(scene, group, OB_RESTRICT_RENDER);
00493 }
00494 
00495 static int outliner_toggle_renderability_exec(bContext *C, wmOperator *UNUSED(op))
00496 {
00497     SpaceOops *soops= CTX_wm_space_outliner(C);
00498     Scene *scene= CTX_data_scene(C);
00499     ARegion *ar= CTX_wm_region(C);
00500     
00501     outliner_do_object_operation(C, scene, soops, &soops->tree, object_toggle_renderability_cb);
00502     
00503     ED_region_tag_redraw(ar);
00504     
00505     return OPERATOR_FINISHED;
00506 }
00507 
00508 void OUTLINER_OT_renderability_toggle(wmOperatorType *ot)
00509 {
00510     /* identifiers */
00511     ot->name= "Toggle Renderability";
00512     ot->idname= "OUTLINER_OT_renderability_toggle";
00513     ot->description= "Toggle the renderability of selected items";
00514     
00515     /* callbacks */
00516     ot->exec= outliner_toggle_renderability_exec;
00517     ot->poll= ED_operator_outliner_active;
00518     
00519     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00520 }
00521 
00522 /* =============================================== */
00523 /* Outliner setting toggles */
00524 
00525 /* Toggle Expanded (Outliner) ---------------------------------------- */
00526 
00527 static int outliner_toggle_expanded_exec(bContext *C, wmOperator *UNUSED(op))
00528 {
00529     SpaceOops *soops= CTX_wm_space_outliner(C);
00530     ARegion *ar= CTX_wm_region(C);
00531     
00532     if (outliner_has_one_flag(soops, &soops->tree, TSE_CLOSED, 1))
00533         outliner_set_flag(soops, &soops->tree, TSE_CLOSED, 0);
00534     else 
00535         outliner_set_flag(soops, &soops->tree, TSE_CLOSED, 1);
00536     
00537     ED_region_tag_redraw(ar);
00538     
00539     return OPERATOR_FINISHED;
00540 }
00541 
00542 void OUTLINER_OT_expanded_toggle(wmOperatorType *ot)
00543 {
00544     /* identifiers */
00545     ot->name= "Expand/Collapse All";
00546     ot->idname= "OUTLINER_OT_expanded_toggle";
00547     ot->description= "Expand/Collapse all items";
00548     
00549     /* callbacks */
00550     ot->exec= outliner_toggle_expanded_exec;
00551     ot->poll= ED_operator_outliner_active;
00552     
00553     /* no undo or registry, UI option */
00554 }
00555 
00556 /* Toggle Selected (Outliner) ---------------------------------------- */
00557 
00558 static int outliner_toggle_selected_exec(bContext *C, wmOperator *UNUSED(op))
00559 {
00560     SpaceOops *soops= CTX_wm_space_outliner(C);
00561     ARegion *ar= CTX_wm_region(C);
00562     Scene *scene= CTX_data_scene(C);
00563     
00564     if (outliner_has_one_flag(soops, &soops->tree, TSE_SELECTED, 1))
00565         outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
00566     else 
00567         outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 1);
00568     
00569     soops->storeflag |= SO_TREESTORE_REDRAW;
00570     
00571     WM_event_add_notifier(C, NC_SCENE|ND_OB_SELECT, scene);
00572     ED_region_tag_redraw(ar);
00573     
00574     return OPERATOR_FINISHED;
00575 }
00576 
00577 void OUTLINER_OT_selected_toggle(wmOperatorType *ot)
00578 {
00579     /* identifiers */
00580     ot->name= "Toggle Selected";
00581     ot->idname= "OUTLINER_OT_selected_toggle";
00582     ot->description= "Toggle the Outliner selection of items";
00583     
00584     /* callbacks */
00585     ot->exec= outliner_toggle_selected_exec;
00586     ot->poll= ED_operator_outliner_active;
00587     
00588     /* no undo or registry, UI option */
00589 }
00590 
00591 /* ************************************************************** */
00592 /* Hotkey Only Operators */
00593 
00594 /* Show Active --------------------------------------------------- */
00595 
00596 static int outliner_show_active_exec(bContext *C, wmOperator *UNUSED(op))
00597 {
00598     SpaceOops *so= CTX_wm_space_outliner(C);
00599     Scene *scene= CTX_data_scene(C);
00600     ARegion *ar= CTX_wm_region(C);
00601     View2D *v2d= &ar->v2d;
00602     
00603     TreeElement *te;
00604     int xdelta, ytop;
00605     
00606     // TODO: make this get this info from context instead...
00607     if (OBACT == NULL) 
00608         return OPERATOR_CANCELLED;
00609     
00610     te= outliner_find_id(so, &so->tree, (ID *)OBACT);
00611     if (te) {
00612         /* make te->ys center of view */
00613         ytop= (int)(te->ys + (v2d->mask.ymax - v2d->mask.ymin)/2);
00614         if (ytop>0) ytop= 0;
00615         
00616         v2d->cur.ymax= (float)ytop;
00617         v2d->cur.ymin= (float)(ytop-(v2d->mask.ymax - v2d->mask.ymin));
00618         
00619         /* make te->xs ==> te->xend center of view */
00620         xdelta = (int)(te->xs - v2d->cur.xmin);
00621         v2d->cur.xmin += xdelta;
00622         v2d->cur.xmax += xdelta;
00623         
00624         so->storeflag |= SO_TREESTORE_REDRAW;
00625     }
00626     
00627     ED_region_tag_redraw(ar);
00628     
00629     return OPERATOR_FINISHED;
00630 }
00631 
00632 void OUTLINER_OT_show_active(wmOperatorType *ot)
00633 {
00634     /* identifiers */
00635     ot->name= "Show Active";
00636     ot->idname= "OUTLINER_OT_show_active";
00637     ot->description= "Adjust the view so that the active Object is shown centered";
00638     
00639     /* callbacks */
00640     ot->exec= outliner_show_active_exec;
00641     ot->poll= ED_operator_outliner_active;
00642 }
00643 
00644 /* View Panning --------------------------------------------------- */
00645 
00646 static int outliner_scroll_page_exec(bContext *C, wmOperator *op)
00647 {
00648     ARegion *ar= CTX_wm_region(C);
00649     int dy= ar->v2d.mask.ymax - ar->v2d.mask.ymin;
00650     int up= 0;
00651     
00652     if(RNA_boolean_get(op->ptr, "up"))
00653         up= 1;
00654 
00655     if(up == 0) dy= -dy;
00656     ar->v2d.cur.ymin+= dy;
00657     ar->v2d.cur.ymax+= dy;
00658     
00659     ED_region_tag_redraw(ar);
00660     
00661     return OPERATOR_FINISHED;
00662 }
00663 
00664 
00665 void OUTLINER_OT_scroll_page(wmOperatorType *ot)
00666 {
00667     /* identifiers */
00668     ot->name= "Scroll Page";
00669     ot->idname= "OUTLINER_OT_scroll_page";
00670     ot->description= "Scroll page up or down";
00671     
00672     /* callbacks */
00673     ot->exec= outliner_scroll_page_exec;
00674     ot->poll= ED_operator_outliner_active;
00675     
00676     /* properties */
00677     RNA_def_boolean(ot->srna, "up", 0, "Up", "Scroll up one page");
00678 }
00679 
00680 /* Search ------------------------------------------------------- */
00681 // TODO: probably obsolete now with filtering?
00682 
00683 #if 0
00684 
00685 /* recursive helper for function below */
00686 static void outliner_set_coordinates_element(SpaceOops *soops, TreeElement *te, int startx, int *starty)
00687 {
00688     TreeStoreElem *tselem= TREESTORE(te);
00689     
00690     /* store coord and continue, we need coordinates for elements outside view too */
00691     te->xs= (float)startx;
00692     te->ys= (float)(*starty);
00693     *starty-= UI_UNIT_Y;
00694     
00695     if(TSELEM_OPEN(tselem,soops)) {
00696         TreeElement *ten;
00697         for(ten= te->subtree.first; ten; ten= ten->next) {
00698             outliner_set_coordinates_element(soops, ten, startx+UI_UNIT_X, starty);
00699         }
00700     }
00701     
00702 }
00703 
00704 /* to retrieve coordinates with redrawing the entire tree */
00705 static void outliner_set_coordinates(ARegion *ar, SpaceOops *soops)
00706 {
00707     TreeElement *te;
00708     int starty= (int)(ar->v2d.tot.ymax)-UI_UNIT_Y;
00709     int startx= 0;
00710     
00711     for(te= soops->tree.first; te; te= te->next) {
00712         outliner_set_coordinates_element(soops, te, startx, &starty);
00713     }
00714 }
00715 
00716 /* find next element that has this name */
00717 static TreeElement *outliner_find_named(SpaceOops *soops, ListBase *lb, char *name, int flags, TreeElement *prev, int *prevFound)
00718 {
00719     TreeElement *te, *tes;
00720     
00721     for (te= lb->first; te; te= te->next) {
00722         int found = outliner_filter_has_name(te, name, flags);
00723         
00724         if(found) {
00725             /* name is right, but is element the previous one? */
00726             if (prev) {
00727                 if ((te != prev) && (*prevFound)) 
00728                     return te;
00729                 if (te == prev) {
00730                     *prevFound = 1;
00731                 }
00732             }
00733             else
00734                 return te;
00735         }
00736         
00737         tes= outliner_find_named(soops, &te->subtree, name, flags, prev, prevFound);
00738         if(tes) return tes;
00739     }
00740 
00741     /* nothing valid found */
00742     return NULL;
00743 }
00744 
00745 static void outliner_find_panel(Scene *UNUSED(scene), ARegion *ar, SpaceOops *soops, int again, int flags) 
00746 {
00747     ReportList *reports = NULL; // CTX_wm_reports(C);
00748     TreeElement *te= NULL;
00749     TreeElement *last_find;
00750     TreeStoreElem *tselem;
00751     int ytop, xdelta, prevFound=0;
00752     char name[sizeof(soops->search_string)];
00753     
00754     /* get last found tree-element based on stored search_tse */
00755     last_find= outliner_find_tse(soops, &soops->search_tse);
00756     
00757     /* determine which type of search to do */
00758     if (again && last_find) {
00759         /* no popup panel - previous + user wanted to search for next after previous */     
00760         BLI_strncpy(name, soops->search_string, sizeof(name));
00761         flags= soops->search_flags;
00762         
00763         /* try to find matching element */
00764         te= outliner_find_named(soops, &soops->tree, name, flags, last_find, &prevFound);
00765         if (te==NULL) {
00766             /* no more matches after previous, start from beginning again */
00767             prevFound= 1;
00768             te= outliner_find_named(soops, &soops->tree, name, flags, last_find, &prevFound);
00769         }
00770     }
00771     else {
00772         /* pop up panel - no previous, or user didn't want search after previous */
00773         name[0]= '\0';
00774 // XXX      if (sbutton(name, 0, sizeof(name)-1, "Find: ") && name[0]) {
00775 //          te= outliner_find_named(soops, &soops->tree, name, flags, NULL, &prevFound);
00776 //      }
00777 //      else return; /* XXX RETURN! XXX */
00778     }
00779 
00780     /* do selection and reveal */
00781     if (te) {
00782         tselem= TREESTORE(te);
00783         if (tselem) {
00784             /* expand branches so that it will be visible, we need to get correct coordinates */
00785             if( outliner_open_back(soops, te))
00786                 outliner_set_coordinates(ar, soops);
00787             
00788             /* deselect all visible, and select found element */
00789             outliner_set_flag(soops, &soops->tree, TSE_SELECTED, 0);
00790             tselem->flag |= TSE_SELECTED;
00791             
00792             /* make te->ys center of view */
00793             ytop= (int)(te->ys + (ar->v2d.mask.ymax-ar->v2d.mask.ymin)/2);
00794             if(ytop>0) ytop= 0;
00795             ar->v2d.cur.ymax= (float)ytop;
00796             ar->v2d.cur.ymin= (float)(ytop-(ar->v2d.mask.ymax-ar->v2d.mask.ymin));
00797             
00798             /* make te->xs ==> te->xend center of view */
00799             xdelta = (int)(te->xs - ar->v2d.cur.xmin);
00800             ar->v2d.cur.xmin += xdelta;
00801             ar->v2d.cur.xmax += xdelta;
00802             
00803             /* store selection */
00804             soops->search_tse= *tselem;
00805             
00806             BLI_strncpy(soops->search_string, name, sizeof(soops->search_string));
00807             soops->search_flags= flags;
00808             
00809             /* redraw */
00810             soops->storeflag |= SO_TREESTORE_REDRAW;
00811         }
00812     }
00813     else {
00814         /* no tree-element found */
00815         BKE_report(reports, RPT_WARNING, "Not found: %s", name);
00816     }
00817 }
00818 #endif
00819 
00820 /* Show One Level ----------------------------------------------- */
00821 
00822 /* helper function for Show/Hide one level operator */
00823 static void outliner_openclose_level(SpaceOops *soops, ListBase *lb, int curlevel, int level, int open)
00824 {
00825     TreeElement *te;
00826     TreeStoreElem *tselem;
00827     
00828     for(te= lb->first; te; te= te->next) {
00829         tselem= TREESTORE(te);
00830         
00831         if(open) {
00832             if(curlevel<=level) tselem->flag &= ~TSE_CLOSED;
00833         }
00834         else {
00835             if(curlevel>=level) tselem->flag |= TSE_CLOSED;
00836         }
00837         
00838         outliner_openclose_level(soops, &te->subtree, curlevel+1, level, open);
00839     }
00840 }
00841 
00842 static int outliner_one_level_exec(bContext *C, wmOperator *op)
00843 {
00844     SpaceOops *soops= CTX_wm_space_outliner(C);
00845     ARegion *ar= CTX_wm_region(C);
00846     int add= RNA_boolean_get(op->ptr, "open");
00847     int level;
00848     
00849     level= outliner_has_one_flag(soops, &soops->tree, TSE_CLOSED, 1);
00850     if(add==1) {
00851         if(level) outliner_openclose_level(soops, &soops->tree, 1, level, 1);
00852     }
00853     else {
00854         if(level==0) level= outliner_count_levels(soops, &soops->tree, 0);
00855         if(level) outliner_openclose_level(soops, &soops->tree, 1, level-1, 0);
00856     }
00857     
00858     ED_region_tag_redraw(ar);
00859     
00860     return OPERATOR_FINISHED;
00861 }
00862 
00863 void OUTLINER_OT_show_one_level(wmOperatorType *ot)
00864 {
00865     /* identifiers */
00866     ot->name= "Show/Hide One Level";
00867     ot->idname= "OUTLINER_OT_show_one_level";
00868     ot->description= "Expand/collapse all entries by one level";
00869     
00870     /* callbacks */
00871     ot->exec= outliner_one_level_exec;
00872     ot->poll= ED_operator_outliner_active;
00873     
00874     /* no undo or registry, UI option */
00875     
00876     /* properties */
00877     RNA_def_boolean(ot->srna, "open", 1, "Open", "Expand all entries one level deep");
00878 }
00879 
00880 /* Show Hierarchy ----------------------------------------------- */
00881 
00882 /* helper function for tree_element_shwo_hierarchy() - recursively checks whether subtrees have any objects*/
00883 static int subtree_has_objects(SpaceOops *soops, ListBase *lb)
00884 {
00885     TreeElement *te;
00886     TreeStoreElem *tselem;
00887     
00888     for(te= lb->first; te; te= te->next) {
00889         tselem= TREESTORE(te);
00890         if(tselem->type==0 && te->idcode==ID_OB) return 1;
00891         if( subtree_has_objects(soops, &te->subtree)) return 1;
00892     }
00893     return 0;
00894 }
00895 
00896 /* recursive helper function for Show Hierarchy operator */
00897 static void tree_element_show_hierarchy(Scene *scene, SpaceOops *soops, ListBase *lb)
00898 {
00899     TreeElement *te;
00900     TreeStoreElem *tselem;
00901 
00902     /* open all object elems, close others */
00903     for(te= lb->first; te; te= te->next) {
00904         tselem= TREESTORE(te);
00905         
00906         if(tselem->type==0) {
00907             if(te->idcode==ID_SCE) {
00908                 if(tselem->id!=(ID *)scene) tselem->flag |= TSE_CLOSED;
00909                     else tselem->flag &= ~TSE_CLOSED;
00910             }
00911             else if(te->idcode==ID_OB) {
00912                 if(subtree_has_objects(soops, &te->subtree)) tselem->flag &= ~TSE_CLOSED;
00913                 else tselem->flag |= TSE_CLOSED;
00914             }
00915         }
00916         else tselem->flag |= TSE_CLOSED;
00917         
00918         if(TSELEM_OPEN(tselem,soops)) tree_element_show_hierarchy(scene, soops, &te->subtree);
00919     }
00920 }
00921 
00922 /* show entire object level hierarchy */
00923 static int outliner_show_hierarchy_exec(bContext *C, wmOperator *UNUSED(op))
00924 {
00925     SpaceOops *soops= CTX_wm_space_outliner(C);
00926     ARegion *ar= CTX_wm_region(C);
00927     Scene *scene= CTX_data_scene(C);
00928     
00929     /* recursively open/close levels */
00930     tree_element_show_hierarchy(scene, soops, &soops->tree);
00931     
00932     ED_region_tag_redraw(ar);
00933     
00934     return OPERATOR_FINISHED;
00935 }
00936 
00937 void OUTLINER_OT_show_hierarchy(wmOperatorType *ot)
00938 {
00939     /* identifiers */
00940     ot->name= "Show Hierarchy";
00941     ot->idname= "OUTLINER_OT_show_hierarchy";
00942     ot->description= "Open all object entries and close all others";
00943     
00944     /* callbacks */
00945     ot->exec= outliner_show_hierarchy_exec;
00946     ot->poll= ED_operator_outliner_active; //  TODO: shouldn't be allowed in RNA views...
00947     
00948     /* no undo or registry, UI option */
00949 }
00950 
00951 /* ************************************************************** */
00952 /* ANIMATO OPERATIONS */
00953 /* KeyingSet and Driver Creation - Helper functions */
00954 
00955 /* specialized poll callback for these operators to work in Datablocks view only */
00956 static int ed_operator_outliner_datablocks_active(bContext *C)
00957 {
00958     ScrArea *sa= CTX_wm_area(C);
00959     if ((sa) && (sa->spacetype==SPACE_OUTLINER)) {
00960         SpaceOops *so= CTX_wm_space_outliner(C);
00961         return (so->outlinevis == SO_DATABLOCKS);
00962     }
00963     return 0;
00964 }
00965 
00966 
00967 /* Helper func to extract an RNA path from selected tree element 
00968  * NOTE: the caller must zero-out all values of the pointers that it passes here first, as
00969  * this function does not do that yet 
00970  */
00971 static void tree_element_to_path(SpaceOops *soops, TreeElement *te, TreeStoreElem *tselem, 
00972                             ID **id, char **path, int *array_index, short *flag, short *UNUSED(groupmode))
00973 {
00974     ListBase hierarchy = {NULL, NULL};
00975     LinkData *ld;
00976     TreeElement *tem, *temnext, *temsub;
00977     TreeStoreElem *tse /* , *tsenext */ /* UNUSED */;
00978     PointerRNA *ptr, *nextptr;
00979     PropertyRNA *prop;
00980     char *newpath=NULL;
00981     
00982     /* optimise tricks:
00983      *  - Don't do anything if the selected item is a 'struct', but arrays are allowed
00984      */
00985     if (tselem->type == TSE_RNA_STRUCT)
00986         return;
00987     
00988     /* Overview of Algorithm:
00989      *  1. Go up the chain of parents until we find the 'root', taking note of the 
00990      *     levels encountered in reverse-order (i.e. items are added to the start of the list
00991      *      for more convenient looping later)
00992      *  2. Walk down the chain, adding from the first ID encountered 
00993      *     (which will become the 'ID' for the KeyingSet Path), and build a  
00994      *      path as we step through the chain
00995      */
00996      
00997     /* step 1: flatten out hierarchy of parents into a flat chain */
00998     for (tem= te->parent; tem; tem= tem->parent) {
00999         ld= MEM_callocN(sizeof(LinkData), "LinkData for tree_element_to_path()");
01000         ld->data= tem;
01001         BLI_addhead(&hierarchy, ld);
01002     }
01003     
01004     /* step 2: step down hierarchy building the path (NOTE: addhead in previous loop was needed so that we can loop like this) */
01005     for (ld= hierarchy.first; ld; ld= ld->next) {
01006         /* get data */
01007         tem= (TreeElement *)ld->data;
01008         tse= TREESTORE(tem);
01009         ptr= &tem->rnaptr;
01010         prop= tem->directdata;
01011         
01012         /* check if we're looking for first ID, or appending to path */
01013         if (*id) {
01014             /* just 'append' property to path 
01015              *  - to prevent memory leaks, we must write to newpath not path, then free old path + swap them
01016              */
01017             if(tse->type == TSE_RNA_PROPERTY) {
01018                 if(RNA_property_type(prop) == PROP_POINTER) {
01019                     /* for pointer we just append property name */
01020                     newpath= RNA_path_append(*path, ptr, prop, 0, NULL);
01021                 }
01022                 else if(RNA_property_type(prop) == PROP_COLLECTION) {
01023                     char buf[128], *name;
01024                     
01025                     temnext= (TreeElement*)(ld->next->data);
01026                     /* tsenext= TREESTORE(temnext); */ /* UNUSED */
01027                     
01028                     nextptr= &temnext->rnaptr;
01029                     name= RNA_struct_name_get_alloc(nextptr, buf, sizeof(buf), NULL);
01030                     
01031                     if(name) {
01032                         /* if possible, use name as a key in the path */
01033                         newpath= RNA_path_append(*path, NULL, prop, 0, name);
01034                         
01035                         if(name != buf)
01036                             MEM_freeN(name);
01037                     }
01038                     else {
01039                         /* otherwise use index */
01040                         int index= 0;
01041                         
01042                         for(temsub=tem->subtree.first; temsub; temsub=temsub->next, index++)
01043                             if(temsub == temnext)
01044                                 break;
01045                         
01046                         newpath= RNA_path_append(*path, NULL, prop, index, NULL);
01047                     }
01048                     
01049                     ld= ld->next;
01050                 }
01051             }
01052             
01053             if(newpath) {
01054                 if (*path) MEM_freeN(*path);
01055                 *path= newpath;
01056                 newpath= NULL;
01057             }
01058         }
01059         else {
01060             /* no ID, so check if entry is RNA-struct, and if that RNA-struct is an ID datablock to extract info from */
01061             if (tse->type == TSE_RNA_STRUCT) {
01062                 /* ptr->data not ptr->id.data seems to be the one we want, since ptr->data is sometimes the owner of this ID? */
01063                 if(RNA_struct_is_ID(ptr->type)) {
01064                     *id= (ID *)ptr->data;
01065                     
01066                     /* clear path */
01067                     if(*path) {
01068                         MEM_freeN(*path);
01069                         path= NULL;
01070                     }
01071                 }
01072             }
01073         }
01074     }
01075 
01076     /* step 3: if we've got an ID, add the current item to the path */
01077     if (*id) {
01078         /* add the active property to the path */
01079         ptr= &te->rnaptr;
01080         prop= te->directdata;
01081         
01082         /* array checks */
01083         if (tselem->type == TSE_RNA_ARRAY_ELEM) {
01084             /* item is part of an array, so must set the array_index */
01085             *array_index= te->index;
01086         }
01087         else if (RNA_property_array_length(ptr, prop)) {
01088             /* entire array was selected, so keyframe all */
01089             *flag |= KSP_FLAG_WHOLE_ARRAY;
01090         }
01091         
01092         /* path */
01093         newpath= RNA_path_append(*path, NULL, prop, 0, NULL);
01094         if (*path) MEM_freeN(*path);
01095         *path= newpath;
01096     }
01097 
01098     /* free temp data */
01099     BLI_freelistN(&hierarchy);
01100 }
01101 
01102 /* =============================================== */
01103 /* Driver Operations */
01104 
01105 /* These operators are only available in databrowser mode for now, as
01106  * they depend on having RNA paths and/or hierarchies available.
01107  */
01108 enum {
01109     DRIVERS_EDITMODE_ADD    = 0,
01110     DRIVERS_EDITMODE_REMOVE,
01111 } /*eDrivers_EditModes*/;
01112 
01113 /* Utilities ---------------------------------- */ 
01114 
01115 /* Recursively iterate over tree, finding and working on selected items */
01116 static void do_outliner_drivers_editop(SpaceOops *soops, ListBase *tree, ReportList *reports, short mode)
01117 {
01118     TreeElement *te;
01119     TreeStoreElem *tselem;
01120     
01121     for (te= tree->first; te; te=te->next) {
01122         tselem= TREESTORE(te);
01123         
01124         /* if item is selected, perform operation */
01125         if (tselem->flag & TSE_SELECTED) {
01126             ID *id= NULL;
01127             char *path= NULL;
01128             int array_index= 0;
01129             short flag= 0;
01130             short groupmode= KSP_GROUP_KSNAME;
01131             
01132             /* check if RNA-property described by this selected element is an animateable prop */
01133             if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) && RNA_property_animateable(&te->rnaptr, te->directdata)) {
01134                 /* get id + path + index info from the selected element */
01135                 tree_element_to_path(soops, te, tselem, 
01136                         &id, &path, &array_index, &flag, &groupmode);
01137             }
01138             
01139             /* only if ID and path were set, should we perform any actions */
01140             if (id && path) {
01141                 short dflags = CREATEDRIVER_WITH_DEFAULT_DVAR;
01142                 int arraylen = 1;
01143                 
01144                 /* array checks */
01145                 if (flag & KSP_FLAG_WHOLE_ARRAY) {
01146                     /* entire array was selected, so add drivers for all */
01147                     arraylen= RNA_property_array_length(&te->rnaptr, te->directdata);
01148                 }
01149                 else
01150                     arraylen= array_index;
01151                 
01152                 /* we should do at least one step */
01153                 if (arraylen == array_index)
01154                     arraylen++;
01155                 
01156                 /* for each array element we should affect, add driver */
01157                 for (; array_index < arraylen; array_index++) {
01158                     /* action depends on mode */
01159                     switch (mode) {
01160                         case DRIVERS_EDITMODE_ADD:
01161                         {
01162                             /* add a new driver with the information obtained (only if valid) */
01163                             ANIM_add_driver(reports, id, path, array_index, dflags, DRIVER_TYPE_PYTHON);
01164                         }
01165                             break;
01166                         case DRIVERS_EDITMODE_REMOVE:
01167                         {
01168                             /* remove driver matching the information obtained (only if valid) */
01169                             ANIM_remove_driver(reports, id, path, array_index, dflags);
01170                         }
01171                             break;
01172                     }
01173                 }
01174                 
01175                 /* free path, since it had to be generated */
01176                 MEM_freeN(path);
01177             }
01178             
01179             
01180         }
01181         
01182         /* go over sub-tree */
01183         if (TSELEM_OPEN(tselem,soops))
01184             do_outliner_drivers_editop(soops, &te->subtree, reports, mode);
01185     }
01186 }
01187 
01188 /* Add Operator ---------------------------------- */
01189 
01190 static int outliner_drivers_addsel_exec(bContext *C, wmOperator *op)
01191 {
01192     SpaceOops *soutliner= CTX_wm_space_outliner(C);
01193     
01194     /* check for invalid states */
01195     if (soutliner == NULL)
01196         return OPERATOR_CANCELLED;
01197     
01198     /* recursively go into tree, adding selected items */
01199     do_outliner_drivers_editop(soutliner, &soutliner->tree, op->reports, DRIVERS_EDITMODE_ADD);
01200     
01201     /* send notifiers */
01202     WM_event_add_notifier(C, NC_ANIMATION|ND_FCURVES_ORDER, NULL); // XXX
01203     
01204     return OPERATOR_FINISHED;
01205 }
01206 
01207 void OUTLINER_OT_drivers_add_selected(wmOperatorType *ot)
01208 {
01209     /* api callbacks */
01210     ot->idname= "OUTLINER_OT_drivers_add_selected";
01211     ot->name= "Add Drivers for Selected";
01212     ot->description= "Add drivers to selected items";
01213     
01214     /* api callbacks */
01215     ot->exec= outliner_drivers_addsel_exec;
01216     ot->poll= ed_operator_outliner_datablocks_active;
01217     
01218     /* flags */
01219     ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
01220 }
01221 
01222 
01223 /* Remove Operator ---------------------------------- */
01224 
01225 static int outliner_drivers_deletesel_exec(bContext *C, wmOperator *op)
01226 {
01227     SpaceOops *soutliner= CTX_wm_space_outliner(C);
01228     
01229     /* check for invalid states */
01230     if (soutliner == NULL)
01231         return OPERATOR_CANCELLED;
01232     
01233     /* recursively go into tree, adding selected items */
01234     do_outliner_drivers_editop(soutliner, &soutliner->tree, op->reports, DRIVERS_EDITMODE_REMOVE);
01235     
01236     /* send notifiers */
01237     WM_event_add_notifier(C, ND_KEYS, NULL); // XXX
01238     
01239     return OPERATOR_FINISHED;
01240 }
01241 
01242 void OUTLINER_OT_drivers_delete_selected(wmOperatorType *ot)
01243 {
01244     /* identifiers */
01245     ot->idname= "OUTLINER_OT_drivers_delete_selected";
01246     ot->name= "Delete Drivers for Selected";
01247     ot->description= "Delete drivers assigned to selected items";
01248     
01249     /* api callbacks */
01250     ot->exec= outliner_drivers_deletesel_exec;
01251     ot->poll= ed_operator_outliner_datablocks_active;
01252     
01253     /* flags */
01254     ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
01255 }
01256 
01257 /* =============================================== */
01258 /* Keying Set Operations */
01259 
01260 /* These operators are only available in databrowser mode for now, as
01261  * they depend on having RNA paths and/or hierarchies available.
01262  */
01263 enum {
01264     KEYINGSET_EDITMODE_ADD  = 0,
01265     KEYINGSET_EDITMODE_REMOVE,
01266 } /*eKeyingSet_EditModes*/;
01267 
01268 /* Utilities ---------------------------------- */ 
01269  
01270 /* find the 'active' KeyingSet, and add if not found (if adding is allowed) */
01271 // TODO: should this be an API func?
01272 static KeyingSet *verify_active_keyingset(Scene *scene, short add)
01273 {
01274     KeyingSet *ks= NULL;
01275     
01276     /* sanity check */
01277     if (scene == NULL)
01278         return NULL;
01279     
01280     /* try to find one from scene */
01281     if (scene->active_keyingset > 0)
01282         ks= BLI_findlink(&scene->keyingsets, scene->active_keyingset-1);
01283         
01284     /* add if none found */
01285     // XXX the default settings have yet to evolve
01286     if ((add) && (ks==NULL)) {
01287         ks= BKE_keyingset_add(&scene->keyingsets, NULL, KEYINGSET_ABSOLUTE, 0);
01288         scene->active_keyingset= BLI_countlist(&scene->keyingsets);
01289     }
01290     
01291     return ks;
01292 }
01293 
01294 /* Recursively iterate over tree, finding and working on selected items */
01295 static void do_outliner_keyingset_editop(SpaceOops *soops, KeyingSet *ks, ListBase *tree, short mode)
01296 {
01297     TreeElement *te;
01298     TreeStoreElem *tselem;
01299     
01300     for (te= tree->first; te; te=te->next) {
01301         tselem= TREESTORE(te);
01302         
01303         /* if item is selected, perform operation */
01304         if (tselem->flag & TSE_SELECTED) {
01305             ID *id= NULL;
01306             char *path= NULL;
01307             int array_index= 0;
01308             short flag= 0;
01309             short groupmode= KSP_GROUP_KSNAME;
01310             
01311             /* check if RNA-property described by this selected element is an animateable prop */
01312             if (ELEM(tselem->type, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM) && RNA_property_animateable(&te->rnaptr, te->directdata)) {
01313                 /* get id + path + index info from the selected element */
01314                 tree_element_to_path(soops, te, tselem, 
01315                         &id, &path, &array_index, &flag, &groupmode);
01316             }
01317             
01318             /* only if ID and path were set, should we perform any actions */
01319             if (id && path) {
01320                 /* action depends on mode */
01321                 switch (mode) {
01322                     case KEYINGSET_EDITMODE_ADD:
01323                     {
01324                         /* add a new path with the information obtained (only if valid) */
01325                         // TODO: what do we do with group name? for now, we don't supply one, and just let this use the KeyingSet name
01326                         BKE_keyingset_add_path(ks, id, NULL, path, array_index, flag, groupmode);
01327                         ks->active_path= BLI_countlist(&ks->paths);
01328                     }
01329                         break;
01330                     case KEYINGSET_EDITMODE_REMOVE:
01331                     {
01332                         /* find the relevant path, then remove it from the KeyingSet */
01333                         KS_Path *ksp= BKE_keyingset_find_path(ks, id, NULL, path, array_index, groupmode);
01334                         
01335                         if (ksp) {
01336                             /* free path's data */
01337                             BKE_keyingset_free_path(ks, ksp);
01338 
01339                             ks->active_path= 0;
01340                         }
01341                     }
01342                         break;
01343                 }
01344                 
01345                 /* free path, since it had to be generated */
01346                 MEM_freeN(path);
01347             }
01348         }
01349         
01350         /* go over sub-tree */
01351         if (TSELEM_OPEN(tselem,soops))
01352             do_outliner_keyingset_editop(soops, ks, &te->subtree, mode);
01353     }
01354 }
01355 
01356 /* Add Operator ---------------------------------- */
01357 
01358 static int outliner_keyingset_additems_exec(bContext *C, wmOperator *op)
01359 {
01360     SpaceOops *soutliner= CTX_wm_space_outliner(C);
01361     Scene *scene= CTX_data_scene(C);
01362     KeyingSet *ks= verify_active_keyingset(scene, 1);
01363     
01364     /* check for invalid states */
01365     if (ks == NULL) {
01366         BKE_report(op->reports, RPT_ERROR, "Operation requires an Active Keying Set");
01367         return OPERATOR_CANCELLED;
01368     }
01369     if (soutliner == NULL)
01370         return OPERATOR_CANCELLED;
01371     
01372     /* recursively go into tree, adding selected items */
01373     do_outliner_keyingset_editop(soutliner, ks, &soutliner->tree, KEYINGSET_EDITMODE_ADD);
01374     
01375     /* send notifiers */
01376     WM_event_add_notifier(C, NC_SCENE|ND_KEYINGSET, NULL);
01377     
01378     return OPERATOR_FINISHED;
01379 }
01380 
01381 void OUTLINER_OT_keyingset_add_selected(wmOperatorType *ot)
01382 {
01383     /* identifiers */
01384     ot->idname= "OUTLINER_OT_keyingset_add_selected";
01385     ot->name= "Keying Set Add Selected";
01386     ot->description= "Add selected items (blue-grey rows) to active Keying Set";
01387     
01388     /* api callbacks */
01389     ot->exec= outliner_keyingset_additems_exec;
01390     ot->poll= ed_operator_outliner_datablocks_active;
01391     
01392     /* flags */
01393     ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
01394 }
01395 
01396 
01397 /* Remove Operator ---------------------------------- */
01398 
01399 static int outliner_keyingset_removeitems_exec(bContext *C, wmOperator *UNUSED(op))
01400 {
01401     SpaceOops *soutliner= CTX_wm_space_outliner(C);
01402     Scene *scene= CTX_data_scene(C);
01403     KeyingSet *ks= verify_active_keyingset(scene, 1);
01404     
01405     /* check for invalid states */
01406     if (soutliner == NULL)
01407         return OPERATOR_CANCELLED;
01408     
01409     /* recursively go into tree, adding selected items */
01410     do_outliner_keyingset_editop(soutliner, ks, &soutliner->tree, KEYINGSET_EDITMODE_REMOVE);
01411     
01412     /* send notifiers */
01413     WM_event_add_notifier(C, NC_SCENE|ND_KEYINGSET, NULL);
01414     
01415     return OPERATOR_FINISHED;
01416 }
01417 
01418 void OUTLINER_OT_keyingset_remove_selected(wmOperatorType *ot)
01419 {
01420     /* identifiers */
01421     ot->idname= "OUTLINER_OT_keyingset_remove_selected";
01422     ot->name= "Keying Set Remove Selected";
01423     ot->description = "Remove selected items (blue-grey rows) from active Keying Set";
01424     
01425     /* api callbacks */
01426     ot->exec= outliner_keyingset_removeitems_exec;
01427     ot->poll= ed_operator_outliner_datablocks_active;
01428     
01429     /* flags */
01430     ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
01431 }