Blender V2.61 - r43446
|
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 }