Beispiel #1
0
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 "";
}
Beispiel #2
0
/* 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);
}
Beispiel #3
0
/* 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?
	}
}
Beispiel #4
0
/* 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);
	}
}
Beispiel #5
0
/* 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;
}
Beispiel #6
0
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 */
}
Beispiel #7
0
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;
}