static char *rna_NlaStrip_path(PointerRNA *ptr) { NlaStrip *strip= (NlaStrip *)ptr->data; AnimData *adt= BKE_animdata_from_id(ptr->id.data); /* if we're attached to AnimData, try to resolve path back to AnimData */ if (adt) { NlaTrack *nlt; NlaStrip *nls; for (nlt= adt->nla_tracks.first; nlt; nlt= nlt->next) { for (nls = nlt->strips.first; nls; nls = nls->next) { if (nls == strip) { // XXX but if we animate like this, the control will never work... return BLI_sprintfN("animation_data.nla_tracks[\"%s\"].strips[\"%s\"]", nlt->name, strip->name); } } } } /* no path */ return ""; }
/* this is called in main loop, doing tagged updates before redraw */ void BKE_scene_update_tagged(Main *bmain, Scene *scene) { /* keep this first */ BLI_callback_exec(bmain, &scene->id, BLI_CB_EVT_SCENE_UPDATE_PRE); /* flush recalc flags to dependencies */ DAG_ids_flush_tagged(bmain); scene->physics_settings.quick_cache_step = 0; /* update all objects: drivers, matrices, displists, etc. flags set * by depgraph or manual, no layer check here, gets correct flushed * * in the future this should handle updates for all datablocks, not * only objects and scenes. - brecht */ scene_update_tagged_recursive(bmain, scene, scene); /* extra call here to recalc scene animation (for sequencer) */ { AnimData *adt = BKE_animdata_from_id(&scene->id); float ctime = BKE_scene_frame_get(scene); if (adt && (adt->recalc & ADT_RECALC_ANIM)) BKE_animsys_evaluate_animdata(scene, &scene->id, adt, ctime, 0); } /* quick point cache updates */ if (scene->physics_settings.quick_cache_step) BKE_ptcache_quick_cache_all(bmain, scene); /* notify editors and python about recalc */ BLI_callback_exec(bmain, &scene->id, BLI_CB_EVT_SCENE_UPDATE_POST); DAG_ids_check_recalc(bmain, scene, FALSE); /* clear recalc flags */ DAG_ids_clear_recalc(bmain); }
/* tags the given anim list element for refreshes (if applicable) * due to Animation Editor editing */ void ANIM_list_elem_update(Scene *scene, bAnimListElem *ale) { ID *id; FCurve *fcu; AnimData *adt; id = ale->id; if (!id) return; /* tag AnimData for refresh so that other views will update in realtime with these changes */ adt = BKE_animdata_from_id(id); if (adt) adt->recalc |= ADT_RECALC_ANIM; /* update data */ fcu = (ale->datatype == ALE_FCURVE) ? ale->key_data : NULL; if (fcu && fcu->rna_path) { /* if we have an fcurve, call the update for the property we * are editing, this is then expected to do the proper redraws * and depsgraph updates */ PointerRNA id_ptr, ptr; PropertyRNA *prop; RNA_id_pointer_create(id, &id_ptr); if (RNA_path_resolve_property(&id_ptr, fcu->rna_path, &ptr, &prop)) RNA_property_update_main(G.main, scene, &ptr, prop); } else { /* in other case we do standard depsgraph update, ideally * we'd be calling property update functions here too ... */ DAG_id_tag_update(id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); // XXX or do we want something more restrictive? } }
/* Draw the given motion path for an Object or a Bone * - assumes that the viewport has already been initialized properly * i.e. draw_motion_paths_init() has been called */ void draw_motion_path_instance(Scene *scene, Object *ob, bPoseChannel *pchan, bAnimVizSettings *avs, bMotionPath *mpath) { //RegionView3D *rv3d = ar->regiondata; bMotionPathVert *mpv, *mpv_start; int i, stepsize = avs->path_step; int sfra, efra, sind, len; /* get frame ranges */ if (avs->path_type == MOTIONPATH_TYPE_ACFRA) { /* With "Around Current", we only choose frames from around * the current frame to draw. */ sfra = CFRA - avs->path_bc; efra = CFRA + avs->path_ac; } else { /* Use the current display range */ sfra = avs->path_sf; efra = avs->path_ef; } /* no matter what, we can only show what is in the cache and no more * - abort if whole range is past ends of path * - otherwise clamp endpoints to extents of path */ if (sfra < mpath->start_frame) { /* start clamp */ sfra = mpath->start_frame; } if (efra > mpath->end_frame) { /* end clamp */ efra = mpath->end_frame; } if ((sfra > mpath->end_frame) || (efra < mpath->start_frame)) { /* whole path is out of bounds */ return; } len = efra - sfra; if ((len <= 0) || (mpath->points == NULL)) { return; } /* get pointers to parts of path */ sind = sfra - mpath->start_frame; mpv_start = (mpath->points + sind); /* draw curve-line of path */ glShadeModel(GL_SMOOTH); glBegin(GL_LINE_STRIP); for (i = 0, mpv = mpv_start; i < len; i++, mpv++) { short sel = (pchan) ? (pchan->bone->flag & BONE_SELECTED) : (ob->flag & SELECT); float intensity; /* how faint */ int frame = sfra + i; int blend_base = (abs(frame - CFRA) == 1) ? TH_CFRAME : TH_BACK; /* "bleed" cframe color to ease color blending */ /* set color * - more intense for active/selected bones, less intense for unselected bones * - black for before current frame, green for current frame, blue for after current frame * - intensity decreases as distance from current frame increases */ #define SET_INTENSITY(A, B, C, min, max) (((1.0f - ((C - B) / (C - A))) * (max - min)) + min) if (frame < CFRA) { /* black - before cfra */ if (sel) { /* intensity = 0.5f; */ intensity = SET_INTENSITY(sfra, i, CFRA, 0.25f, 0.75f); } else { /* intensity = 0.8f; */ intensity = SET_INTENSITY(sfra, i, CFRA, 0.68f, 0.92f); } UI_ThemeColorBlend(TH_WIRE, blend_base, intensity); } else if (frame > CFRA) { /* blue - after cfra */ if (sel) { /* intensity = 0.5f; */ intensity = SET_INTENSITY(CFRA, i, efra, 0.25f, 0.75f); } else { /* intensity = 0.8f; */ intensity = SET_INTENSITY(CFRA, i, efra, 0.68f, 0.92f); } UI_ThemeColorBlend(TH_BONE_POSE, blend_base, intensity); } else { /* green - on cfra */ if (sel) { intensity = 0.5f; } else { intensity = 0.99f; } UI_ThemeColorBlendShade(TH_CFRAME, TH_BACK, intensity, 10); } #undef SET_INTENSITY /* draw a vertex with this color */ glVertex3fv(mpv->co); } glEnd(); glShadeModel(GL_FLAT); glPointSize(1.0); /* draw little black point at each frame * NOTE: this is not really visible/noticeable */ glBegin(GL_POINTS); for (i = 0, mpv = mpv_start; i < len; i++, mpv++) glVertex3fv(mpv->co); glEnd(); /* Draw little white dots at each framestep value */ UI_ThemeColor(TH_TEXT_HI); glBegin(GL_POINTS); for (i = 0, mpv = mpv_start; i < len; i += stepsize, mpv += stepsize) glVertex3fv(mpv->co); glEnd(); /* Draw big green dot where the current frame is * NOTE: this is only done when keyframes are shown, since this adds similar types of clutter */ if ((avs->path_viewflag & MOTIONPATH_VIEW_KFRAS) && (sfra < CFRA) && (CFRA <= efra)) { UI_ThemeColor(TH_CFRAME); glPointSize(6.0f); glBegin(GL_POINTS); mpv = mpv_start + (CFRA - sfra); glVertex3fv(mpv->co); glEnd(); UI_ThemeColor(TH_TEXT_HI); } /* XXX, this isn't up to date but probably should be kept so. */ invert_m4_m4(ob->imat, ob->obmat); /* Draw frame numbers at each framestep value */ if (avs->path_viewflag & MOTIONPATH_VIEW_FNUMS) { unsigned char col[4]; UI_GetThemeColor3ubv(TH_TEXT_HI, col); col[3] = 255; for (i = 0, mpv = mpv_start; i < len; i += stepsize, mpv += stepsize) { int frame = sfra + i; char numstr[32]; size_t numstr_len; float co[3]; /* only draw framenum if several consecutive highlighted points don't occur on same point */ if (i == 0) { numstr_len = sprintf(numstr, " %d", frame); mul_v3_m4v3(co, ob->imat, mpv->co); view3d_cached_text_draw_add(co, numstr, numstr_len, 0, V3D_CACHE_TEXT_WORLDSPACE | V3D_CACHE_TEXT_ASCII, col); } else if ((i >= stepsize) && (i < len - stepsize)) { bMotionPathVert *mpvP = (mpv - stepsize); bMotionPathVert *mpvN = (mpv + stepsize); if ((equals_v3v3(mpv->co, mpvP->co) == 0) || (equals_v3v3(mpv->co, mpvN->co) == 0)) { numstr_len = sprintf(numstr, " %d", frame); mul_v3_m4v3(co, ob->imat, mpv->co); view3d_cached_text_draw_add(co, numstr, numstr_len, 0, V3D_CACHE_TEXT_WORLDSPACE | V3D_CACHE_TEXT_ASCII, col); } } } } /* Keyframes - dots and numbers */ if (avs->path_viewflag & MOTIONPATH_VIEW_KFRAS) { unsigned char col[4]; AnimData *adt = BKE_animdata_from_id(&ob->id); DLRBT_Tree keys; /* build list of all keyframes in active action for object or pchan */ BLI_dlrbTree_init(&keys); if (adt) { /* it is assumed that keyframes for bones are all grouped in a single group * unless an option is set to always use the whole action */ if ((pchan) && (avs->path_viewflag & MOTIONPATH_VIEW_KFACT) == 0) { bActionGroup *agrp = BKE_action_group_find_name(adt->action, pchan->name); if (agrp) { agroup_to_keylist(adt, agrp, &keys, NULL); BLI_dlrbTree_linkedlist_sync(&keys); } } else { action_to_keylist(adt, adt->action, &keys, NULL); BLI_dlrbTree_linkedlist_sync(&keys); } } /* Draw slightly-larger yellow dots at each keyframe */ UI_GetThemeColor3ubv(TH_VERTEX_SELECT, col); col[3] = 255; glPointSize(4.0f); glColor3ubv(col); glBegin(GL_POINTS); for (i = 0, mpv = mpv_start; i < len; i++, mpv++) { int frame = sfra + i; float mframe = (float)(frame); if (BLI_dlrbTree_search_exact(&keys, compare_ak_cfraPtr, &mframe)) glVertex3fv(mpv->co); } glEnd(); /* Draw frame numbers of keyframes */ if (avs->path_viewflag & MOTIONPATH_VIEW_KFNOS) { float co[3]; for (i = 0, mpv = mpv_start; i < len; i++, mpv++) { float mframe = (float)(sfra + i); if (BLI_dlrbTree_search_exact(&keys, compare_ak_cfraPtr, &mframe)) { char numstr[32]; size_t numstr_len; numstr_len = sprintf(numstr, " %d", (sfra + i)); mul_v3_m4v3(co, ob->imat, mpv->co); view3d_cached_text_draw_add(co, numstr, numstr_len, 0, V3D_CACHE_TEXT_WORLDSPACE | V3D_CACHE_TEXT_ASCII, col); } } } BLI_dlrbTree_free(&keys); } }
/* Get (or add relevant data to be able to do so) F-Curve from the driver stack, * for the given Animation Data block. This assumes that all the destinations are valid. * * - add: 0 - don't add anything if not found, * 1 - add new Driver FCurve (with keyframes for visual tweaking), * 2 - add new Driver FCurve (with generator, for script backwards compatibility) * -1 - add new Driver FCurve without driver stuff (for pasting) */ FCurve *verify_driver_fcurve(ID *id, const char rna_path[], const int array_index, short add) { AnimData *adt; FCurve *fcu; /* sanity checks */ if (ELEM(NULL, id, rna_path)) return NULL; /* init animdata if none available yet */ adt = BKE_animdata_from_id(id); if ((adt == NULL) && (add)) adt = BKE_id_add_animdata(id); if (adt == NULL) { /* if still none (as not allowed to add, or ID doesn't have animdata for some reason) */ return NULL; } /* try to find f-curve matching for this setting * - add if not found and allowed to add one * TODO: add auto-grouping support? how this works will need to be resolved */ fcu = list_find_fcurve(&adt->drivers, rna_path, array_index); if ((fcu == NULL) && (add)) { /* use default settings to make a F-Curve */ fcu = MEM_callocN(sizeof(FCurve), "FCurve"); fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED); /* store path - make copy, and store that */ fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path)); fcu->array_index = array_index; /* if add is negative, don't init this data yet, since it will be filled in by the pasted driver */ if (add > 0) { BezTriple *bezt; size_t i; /* add some new driver data */ fcu->driver = MEM_callocN(sizeof(ChannelDriver), "ChannelDriver"); fcu->driver->flag |= DRIVER_FLAG_SHOWDEBUG; /* F-Modifier or Keyframes? */ // FIXME: replace these magic numbers with defines if (add == 2) { /* Python API Backwards compatibility hack: * Create FModifier so that old scripts won't break * for now before 2.7 series -- (September 4, 2013) */ add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR); } else { /* add 2 keyframes so that user has something to work with * - These are configured to 0,0 and 1,1 to give a 1-1 mapping * which can be easily tweaked from there. */ insert_vert_fcurve(fcu, 0.0f, 0.0f, INSERTKEY_FAST); insert_vert_fcurve(fcu, 1.0f, 1.0f, INSERTKEY_FAST); /* configure this curve to extrapolate */ for (i = 0, bezt = fcu->bezt; (i < fcu->totvert) && bezt; i++, bezt++) { bezt->h1 = bezt->h2 = HD_VECT; } fcu->extend = FCURVE_EXTRAPOLATE_LINEAR; calchandles_fcurve(fcu); } } /* just add F-Curve to end of driver list */ BLI_addtail(&adt->drivers, fcu); } /* return the F-Curve */ return fcu; }
void BKE_object_handle_data_update(EvaluationContext *eval_ctx, Scene *scene, Object *ob) { ID *data_id = (ID *)ob->data; AnimData *adt = BKE_animdata_from_id(data_id); Key *key; float ctime = BKE_scene_frame_get(scene); if (G.debug & G_DEBUG_DEPSGRAPH) printf("recalcdata %s\n", ob->id.name + 2); /* TODO(sergey): Only used by legacy depsgraph. */ if (adt) { /* evaluate drivers - datalevel */ /* XXX: for mesh types, should we push this to derivedmesh instead? */ BKE_animsys_evaluate_animdata(scene, data_id, adt, ctime, ADT_RECALC_DRIVERS); } /* TODO(sergey): Only used by legacy depsgraph. */ key = BKE_key_from_object(ob); if (key && key->block.first) { if (!(ob->shapeflag & OB_SHAPE_LOCK)) BKE_animsys_evaluate_animdata(scene, &key->id, key->adt, ctime, ADT_RECALC_DRIVERS); } /* includes all keys and modifiers */ switch (ob->type) { case OB_MESH: { BMEditMesh *em = (ob == scene->obedit) ? BKE_editmesh_from_object(ob) : NULL; uint64_t data_mask = scene->customdata_mask | CD_MASK_BAREMESH; #ifdef WITH_FREESTYLE /* make sure Freestyle edge/face marks appear in DM for render (see T40315) */ if (eval_ctx->mode != DAG_EVAL_VIEWPORT) { data_mask |= CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE; } #endif if (em) { makeDerivedMesh(scene, ob, em, data_mask, false); /* was CD_MASK_BAREMESH */ } else { makeDerivedMesh(scene, ob, NULL, data_mask, false); } break; } case OB_ARMATURE: if (ob->id.lib && ob->proxy_from) { if (BKE_pose_copy_result(ob->pose, ob->proxy_from->pose) == false) { printf("Proxy copy error, lib Object: %s proxy Object: %s\n", ob->id.name + 2, ob->proxy_from->id.name + 2); } } else { BKE_pose_where_is(scene, ob); } break; case OB_MBALL: BKE_displist_make_mball(eval_ctx, scene, ob); break; case OB_CURVE: case OB_SURF: case OB_FONT: BKE_displist_make_curveTypes(scene, ob, 0); break; case OB_LATTICE: BKE_lattice_modifiers_calc(scene, ob); break; case OB_EMPTY: if (ob->empty_drawtype == OB_EMPTY_IMAGE && ob->data) if (BKE_image_is_animated(ob->data)) BKE_image_user_check_frame_calc(ob->iuser, (int)ctime, 0); break; } /* related materials */ /* XXX: without depsgraph tagging, this will always need to be run, which will be slow! * However, not doing anything (or trying to hack around this lack) is not an option * anymore, especially due to Cycles [#31834] */ if (ob->totcol) { int a; if (ob->totcol != 0) { BLI_mutex_lock(&material_lock); for (a = 1; a <= ob->totcol; a++) { Material *ma = give_current_material(ob, a); if (ma) { /* recursively update drivers for this material */ material_drivers_update(scene, ma, ctime); } } BLI_mutex_unlock(&material_lock); } } else if (ob->type == OB_LAMP) lamp_drivers_update(scene, ob->data, ctime); /* particles */ if (ob != scene->obedit && ob->particlesystem.first) { ParticleSystem *tpsys, *psys; DerivedMesh *dm; ob->transflag &= ~OB_DUPLIPARTS; psys = ob->particlesystem.first; while (psys) { /* ensure this update always happens even if psys is disabled */ if (psys->recalc & PSYS_RECALC_TYPE) { psys_changed_type(ob, psys); } if (psys_check_enabled(ob, psys)) { /* check use of dupli objects here */ if (psys->part && (psys->part->draw_as == PART_DRAW_REND || eval_ctx->mode == DAG_EVAL_RENDER) && ((psys->part->ren_as == PART_DRAW_OB && psys->part->dup_ob) || (psys->part->ren_as == PART_DRAW_GR && psys->part->dup_group))) { ob->transflag |= OB_DUPLIPARTS; } particle_system_update(scene, ob, psys); psys = psys->next; } else if (psys->flag & PSYS_DELETE) { tpsys = psys->next; BLI_remlink(&ob->particlesystem, psys); psys_free(ob, psys); psys = tpsys; } else psys = psys->next; } if (eval_ctx->mode == DAG_EVAL_RENDER && ob->transflag & OB_DUPLIPARTS) { /* this is to make sure we get render level duplis in groups: * the derivedmesh must be created before init_render_mesh, * since object_duplilist does dupliparticles before that */ CustomDataMask data_mask = CD_MASK_BAREMESH | CD_MASK_MFACE | CD_MASK_MTFACE | CD_MASK_MCOL; dm = mesh_create_derived_render(scene, ob, data_mask); dm->release(dm); for (psys = ob->particlesystem.first; psys; psys = psys->next) psys_get_modifier(ob, psys)->flag &= ~eParticleSystemFlag_psys_updated; } } /* quick cache removed */ }
static int mouse_nla_channels(bContext *C, bAnimContext *ac, float x, int channel_index, short selectmode) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; View2D *v2d = &ac->ar->v2d; int notifierFlags = 0; /* get the channel that was clicked on */ /* filter channels */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* get channel from index */ ale = BLI_findlink(&anim_data, channel_index); if (ale == NULL) { /* channel not found */ if (G.debug & G_DEBUG) printf("Error: animation channel (index = %d) not found in mouse_anim_channels()\n", channel_index); ANIM_animdata_freelist(&anim_data); return 0; } /* action to take depends on what channel we've got */ // WARNING: must keep this in sync with the equivalent function in anim_channels_edit.c switch (ale->type) { case ANIMTYPE_SCENE: { Scene *sce = (Scene *)ale->data; AnimData *adt = sce->adt; /* set selection status */ if (selectmode == SELECT_INVERT) { /* swap select */ sce->flag ^= SCE_DS_SELECTED; if (adt) adt->flag ^= ADT_UI_SELECTED; } else { sce->flag |= SCE_DS_SELECTED; if (adt) adt->flag |= ADT_UI_SELECTED; } notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); break; } case ANIMTYPE_OBJECT: { bDopeSheet *ads = (bDopeSheet *)ac->data; Scene *sce = (Scene *)ads->source; Base *base = (Base *)ale->data; Object *ob = base->object; AnimData *adt = ob->adt; if (nlaedit_is_tweakmode_on(ac) == 0) { /* set selection status */ if (selectmode == SELECT_INVERT) { /* swap select */ base->flag ^= SELECT; ob->flag = base->flag; if (adt) adt->flag ^= ADT_UI_SELECTED; } else { Base *b; /* deselect all */ /* TODO: should this deselect all other types of channels too? */ for (b = sce->base.first; b; b = b->next) { b->flag &= ~SELECT; b->object->flag = b->flag; if (b->object->adt) b->object->adt->flag &= ~(ADT_UI_SELECTED | ADT_UI_ACTIVE); } /* select object now */ base->flag |= SELECT; ob->flag |= SELECT; if (adt) adt->flag |= ADT_UI_SELECTED; } /* change active object - regardless of whether it is now selected [T37883] */ ED_base_object_activate(C, base); /* adds notifier */ if ((adt) && (adt->flag & ADT_UI_SELECTED)) adt->flag |= ADT_UI_ACTIVE; /* notifiers - channel was selected */ notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); } break; } case ANIMTYPE_FILLACTD: /* Action Expander */ case ANIMTYPE_DSMAT: /* Datablock AnimData Expanders */ case ANIMTYPE_DSLAM: case ANIMTYPE_DSCAM: case ANIMTYPE_DSCACHEFILE: case ANIMTYPE_DSCUR: case ANIMTYPE_DSSKEY: case ANIMTYPE_DSWOR: case ANIMTYPE_DSNTREE: case ANIMTYPE_DSPART: case ANIMTYPE_DSMBALL: case ANIMTYPE_DSARM: case ANIMTYPE_DSMESH: case ANIMTYPE_DSTEX: case ANIMTYPE_DSLAT: case ANIMTYPE_DSLINESTYLE: case ANIMTYPE_DSSPK: case ANIMTYPE_DSGPENCIL: { /* sanity checking... */ if (ale->adt) { /* select/deselect */ if (selectmode == SELECT_INVERT) { /* inverse selection status of this AnimData block only */ ale->adt->flag ^= ADT_UI_SELECTED; } else { /* select AnimData block by itself */ ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); ale->adt->flag |= ADT_UI_SELECTED; } /* set active? */ if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED)) ale->adt->flag |= ADT_UI_ACTIVE; } notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); break; } case ANIMTYPE_NLATRACK: { NlaTrack *nlt = (NlaTrack *)ale->data; AnimData *adt = ale->adt; short offset; /* offset for start of channel (on LHS of channel-list) */ if (ale->id) { /* special exception for materials and particles */ if (ELEM(GS(ale->id->name), ID_MA, ID_PA)) offset = 21 + NLACHANNEL_BUTTON_WIDTH; else offset = 14; } else offset = 0; if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) { /* toggle protection (only if there's a toggle there) */ nlt->flag ^= NLATRACK_PROTECTED; /* notifier flags - channel was edited */ notifierFlags |= (ND_ANIMCHAN | NA_EDITED); } else if (x >= (v2d->cur.xmax - 2 * NLACHANNEL_BUTTON_WIDTH)) { /* toggle mute */ nlt->flag ^= NLATRACK_MUTED; /* notifier flags - channel was edited */ notifierFlags |= (ND_ANIMCHAN | NA_EDITED); } else if (x <= ((NLACHANNEL_BUTTON_WIDTH * 2) + offset)) { /* toggle 'solo' */ BKE_nlatrack_solo_toggle(adt, nlt); /* notifier flags - channel was edited */ notifierFlags |= (ND_ANIMCHAN | NA_EDITED); } else if (nlaedit_is_tweakmode_on(ac) == 0) { /* set selection */ if (selectmode == SELECT_INVERT) { /* inverse selection status of this F-Curve only */ nlt->flag ^= NLATRACK_SELECTED; } else { /* select F-Curve by itself */ ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); nlt->flag |= NLATRACK_SELECTED; } /* if NLA-Track is selected now, make NLA-Track the 'active' one in the visible list */ if (nlt->flag & NLATRACK_SELECTED) ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nlt, ANIMTYPE_NLATRACK); /* notifier flags - channel was selected */ notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); } break; } case ANIMTYPE_NLAACTION: { AnimData *adt = BKE_animdata_from_id(ale->id); /* button region... */ if (x >= (v2d->cur.xmax - NLACHANNEL_BUTTON_WIDTH)) { if (nlaedit_is_tweakmode_on(ac) == 0) { /* 'push-down' action - only usable when not in TweakMode */ /* TODO: make this use the operator instead of calling the function directly * however, calling the operator requires that we supply the args, and that works with proper buttons only */ BKE_nla_action_pushdown(adt); } else { /* when in tweakmode, this button becomes the toggle for mapped editing */ adt->flag ^= ADT_NLA_EDIT_NOMAP; } /* changes to NLA-Action occurred */ notifierFlags |= ND_NLA_ACTCHANGE; } /* OR rest of name... */ else { /* NOTE: rest of NLA-Action name doubles for operating on the AnimData block * - this is useful when there's no clear divider, and makes more sense in * the case of users trying to use this to change actions * - in tweakmode, clicking here gets us out of tweakmode, as changing selection * while in tweakmode is really evil! * - we disable "solo" flags too, to make it easier to work with stashed actions * with less trouble */ if (nlaedit_is_tweakmode_on(ac)) { /* exit tweakmode immediately */ nlaedit_disable_tweakmode(ac, true); /* changes to NLA-Action occurred */ notifierFlags |= ND_NLA_ACTCHANGE; } else { /* select/deselect */ if (selectmode == SELECT_INVERT) { /* inverse selection status of this AnimData block only */ adt->flag ^= ADT_UI_SELECTED; } else { /* select AnimData block by itself */ ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); adt->flag |= ADT_UI_SELECTED; } /* set active? */ if (adt->flag & ADT_UI_SELECTED) adt->flag |= ADT_UI_ACTIVE; notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); } } break; } default: if (G.debug & G_DEBUG) printf("Error: Invalid channel type in mouse_nla_channels()\n"); break; } /* free channels */ ANIM_animdata_freelist(&anim_data); /* return the notifier-flags set */ return notifierFlags; }