void UFaceFXMatineeControlInst::InitTrackInst(UInterpTrack* Track)
{
	UInterpGroupInst* GrInst = CastChecked<UInterpGroupInst>( GetOuter() );
	AMatineeActor* MatineeActor = CastChecked<AMatineeActor>( GrInst->GetOuter() );

	LastUpdatePosition = MatineeActor->InterpPosition;
}
bool UMatineeTrackVectorPropHelper::PreCreateTrack( UInterpGroup* Group, const UInterpTrack *TrackDef, bool bDuplicatingTrack, bool bAllowPrompts ) const
{
	bool bResult = true;

	if( bAllowPrompts && bDuplicatingTrack == false )
	{
		bResult = false;

		// For Property tracks - pop up a dialog to choose property name.
		TrackAddPropName = NAME_None;

		FEdModeInterpEdit* Mode = (FEdModeInterpEdit*)GEditorModeTools().GetActiveMode( FBuiltinEditorModes::EM_InterpEdit );
		check(Mode != NULL);

		IMatineeBase* InterpEd = Mode->InterpEd;
		check(InterpEd != NULL);

		UInterpGroupInst* GrInst = InterpEd->GetMatineeActor()->FindFirstGroupInst(Group);
		check(GrInst);

		AActor* Actor = GrInst->GetGroupActor();
		if ( Actor != NULL )
		{
			TArray<FName> PropNames;
			FMatineeUtils::GetInterpVectorPropertyNames(Actor, PropNames);
			bResult = ChooseProperty(PropNames);
		}
	}

	return bResult;
}
void UFaceFXMatineeControl::PreviewUpdateTrack( float NewPosition, UInterpTrackInst* TrackInst ) 
{
	UInterpGroupInst* GrInst = CastChecked<UInterpGroupInst>( TrackInst->GetOuter() );
	AMatineeActor* MatineeActor = CastChecked<AMatineeActor>( GrInst->GetOuter() );

	const bool bJump = !(MatineeActor->bIsPlaying);

	UpdateTrack(NewPosition, TrackInst, bJump);

	if(AActor* Actor = TrackInst->GetGroupActor())
	{
		TInlineComponentArray<USkeletalMeshComponent*> SkelMeshComps;
		Actor->GetComponents(SkelMeshComps);

		const UFaceFXMatineeControlInst* TrackFaceFX = CastChecked<UFaceFXMatineeControlInst>(TrackInst);
		const float TimeElapsed = (bJump || NewPosition < TrackFaceFX->LastUpdatePosition) ? 0.F : NewPosition - TrackFaceFX->LastUpdatePosition;

		for(USkeletalMeshComponent* SkelMeshComp : SkelMeshComps)
		{
			// Update space bases so new animation position has an effect.
			if(SkelMeshComp->AnimScriptInstance)
			{
				SkelMeshComp->UpdateMaterialParameters();
			}
			SkelMeshComp->RefreshBoneTransforms();
			SkelMeshComp->RefreshSlaveComponents();
			SkelMeshComp->UpdateComponentToWorld();
		}
	}
}
void UInterpTrackAkAudioEvent::PreviewUpdateTrack(float NewPosition, UInterpTrackInst* TrInst)
{
	UInterpGroupInst* GrInst = CastChecked<UInterpGroupInst>( TrInst->GetOuter() );
	AMatineeActor* MatineeActor = CastChecked<AMatineeActor>( GrInst->GetOuter() );
	UInterpTrackInstAkAudioEvent* AkEventInst = CastChecked<UInterpTrackInstAkAudioEvent>( TrInst );
	UInterpGroup* Group = CastChecked<UInterpGroup>( GetOuter() );
	UInterpData* IData = CastChecked<UInterpData>( Group->GetOuter() );

	// Dont play sounds unless we are preview playback (ie not scrubbing).
	bool bJump = !(MatineeActor->bIsPlaying);
	UpdateTrack(NewPosition, TrInst, bJump);
}
void UFaceFXMatineeControlInst::RestoreActorState(UInterpTrack* Track)
{
	UInterpGroupInst* GrInst = CastChecked<UInterpGroupInst>( GetOuter() );

	//called when matinee closes. In that case we stop any running animation/sounds
	if(const AActor* GroupActor = GrInst->GetGroupActor())
	{
		if(UFaceFXComponent* FaceFXComp = GroupActor->FindComponentByClass<UFaceFXComponent>())
		{
			FaceFXComp->StopAll();
		}
	}
}
bool UCameraAnim::CreateFromInterpGroup(class UInterpGroup* SrcGroup, class AMatineeActor* InMatineeActor)
{
	// assert we're controlling a camera actor
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
	{
		UInterpGroupInst* GroupInst = InMatineeActor ? InMatineeActor->FindFirstGroupInst(SrcGroup) : NULL;
		if (GroupInst)
		{
			check( GroupInst->GetGroupActor()->IsA(ACameraActor::StaticClass()) );
		}
	}
#endif
	
	// copy length information
	AnimLength = (InMatineeActor && InMatineeActor->MatineeData) ? InMatineeActor->MatineeData->InterpLength : 0.f;

	UInterpGroup* OldGroup = CameraInterpGroup;

	if (CameraInterpGroup != SrcGroup)
	{
		// copy the source interp group for use in the CameraAnim
		// @fixme jf: fixed this potentially creating an object of UInterpGroup and raw casting it to InterpGroupCamera.  No source data in UE4 to test though.
		CameraInterpGroup = Cast<UInterpGroupCamera>(StaticDuplicateObject(SrcGroup, this, NAME_None, RF_AllFlags, UInterpGroupCamera::StaticClass()));

		if (CameraInterpGroup)
		{
			// delete the old one, if it exists
			if (OldGroup)
			{
				OldGroup->MarkPendingKill();
			}

			// success!
			return true;
		}
		else
		{
			// creation of new one failed somehow, restore the old one
			CameraInterpGroup = OldGroup;
		}
	}
	else
	{
		// no need to perform work above, but still a "success" case
		return true;
	}

	// failed creation
	return false;
}
AActor* UInterpTrackHelper::GetGroupActor(const UInterpTrack* Track) const
{
	FEdModeInterpEdit* mode = (FEdModeInterpEdit*)GEditorModeTools().GetActiveMode( FBuiltinEditorModes::EM_InterpEdit );
	check(mode != NULL);

	IMatineeBase* InterpEd = mode->InterpEd;
	check(InterpEd != NULL);

	UInterpGroupInst* GrInst = NULL;

	// Traverse through the selected tracks in hopes of finding the associated group. 
	for( FSelectedTrackIterator TrackIt(InterpEd->GetSelectedTrackIterator()); TrackIt; ++TrackIt )
	{
		if( (*TrackIt) == Track )
		{
			GrInst = InterpEd->GetMatineeActor()->FindFirstGroupInst(TrackIt.GetGroup());
			break;
		}
	}
	
	return ( GrInst != NULL ) ? GrInst->GetGroupActor() : NULL;
}
bool UMatineeTrackColorPropHelper::PreCreateTrack( UInterpGroup* Group, const UInterpTrack *TrackDef, bool bDuplicatingTrack, bool bAllowPrompts ) const
{
	bool bResult = true;

	if( bAllowPrompts && bDuplicatingTrack == false )
	{
		bResult = false;

		// For Property tracks - pop up a dialog to choose property name.
		TrackAddPropName = NAME_None;

		FEdModeInterpEdit* Mode = (FEdModeInterpEdit*)GEditorModeTools().GetActiveMode( FBuiltinEditorModes::EM_InterpEdit );
		check(Mode != NULL);

		IMatineeBase* InterpEd = Mode->InterpEd;
		check(InterpEd != NULL);

		UInterpGroupInst* GrInst = InterpEd->GetMatineeActor()->FindFirstGroupInst(Group);
		check(GrInst);

		AActor* Actor = GrInst->GetGroupActor();
		if ( Actor != NULL )
		{
			TArray<FName> PropNames;
			FMatineeUtils::GetInterpColorPropertyNames(Actor, PropNames);
			bResult = PropNames.Num() > 0 ? ChooseProperty(PropNames) : false;

			if( !bResult )
			{
				FMessageDialog::Open( EAppMsgType::Ok, LOCTEXT( "MatineeColorTrackHelper_NoProperties", "No Color track properties are available for this actor" ) );
			}
		}
	}

	return bResult;
}
bool UMatineeTrackAnimControlHelper::PreCreateTrack( UInterpGroup* Group, const UInterpTrack *TrackDef, bool bDuplicatingTrack, bool bAllowPrompts ) const
{
	// For AnimControl tracks - pop up a dialog to choose slot name.
	AnimSlotName = NAME_None;

	FEdModeInterpEdit* Mode = (FEdModeInterpEdit*)GEditorModeTools().GetActiveMode( FBuiltinEditorModes::EM_InterpEdit );
	check(Mode != NULL);

	IMatineeBase* InterpEd = Mode->InterpEd;
	check(InterpEd != NULL);

	UInterpGroupInst* GrInst = InterpEd->GetMatineeActor()->FindFirstGroupInst(Group);
	check(GrInst);

	AActor* Actor = GrInst->GetGroupActor();
	if ( Actor != NULL )
	{
		IMatineeAnimInterface* MatineeAnimInterface = InterfaceCast<IMatineeAnimInterface>(Actor);
		if (!MatineeAnimInterface)
		{
			UE_LOG(LogSlateMatinee, Log, TEXT("IntepGroup : MatineeAnimInterface is missing for (%s)"), *Actor->GetName());
			return false;
		}

		// If this is the first AnimControlTrack, then init anim control now.
		// We need that before calling GetAnimControlSlotDesc
		if( !Group->HasAnimControlTrack() )
		{
			MatineeAnimInterface->PreviewBeginAnimControl( Group );
		}

		if( bAllowPrompts )
		{
			TArray<FAnimSlotDesc> SlotDescs;
			MatineeAnimInterface->GetAnimControlSlotDesc(SlotDescs);

			// If we get no information - just allow it to be created with empty slot.
			if( SlotDescs.Num() == 0 )
			{
				return true;
			}

			// Build combo to let you pick a slot. Don't put any names in that have already used all their channels. */

			TArray<FString> SlotStrings;
			for(int32 i=0; i<SlotDescs.Num(); i++)
			{
				int32 ChannelsUsed = GrInst->Group->GetAnimTracksUsingSlot( SlotDescs[i].SlotName );
				if(ChannelsUsed < SlotDescs[i].NumChannels)
				{
					SlotStrings.Add(*SlotDescs[i].SlotName.ToString());
				}
			}

			// If no slots free - we fail to create track.
			if(SlotStrings.Num() == 0)
			{
				FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "Error_NoAnimChannelsLeft", "This Actor Has No AnimControl Channels Left.") );
				return false;
			}

			TSharedRef<SWindow> NewWindow = SNew(SWindow)
				.Title(NSLOCTEXT("Matinee.Popups", "ChooseAnimSlotTitle", "Choose Anim Slot..."))
				.SizingRule( ESizingRule::Autosized )
				.SupportsMinimize(false)
				.SupportsMaximize(false);

			FString Result;
			TSharedRef<STextComboPopup> TextEntryPopup = 
				SNew(STextComboPopup)
				.Label(NSLOCTEXT("Matinee.Popups", "ChooseAnimSlot", "Choose Anim Slot..."))
				.TextOptions(SlotStrings)
				.OnTextChosen_UObject(this, &UMatineeTrackAnimControlHelper::OnCreateTrackTextEntry, NewWindow, (FString *)&Result)
				;

			NewWindow->SetContent(TextEntryPopup);
			GEditor->EditorAddModalWindow(NewWindow);
			if (!Result.IsEmpty())
			{			
				AnimSlotName = FName( *Result );
				if ( AnimSlotName != NAME_None )
				{
					return true;
				}
			}
		}
		else
		{
			// Prompts aren't allowed, so just succeed with defaults
			return true;
		}
	}

	return false;
}
bool UMatineeTrackBoolPropHelper::PreCreateTrack( UInterpGroup* Group, const UInterpTrack* TrackDef, bool bDuplicatingTrack, bool bAllowPrompts ) const
{
	if( bAllowPrompts && bDuplicatingTrack == false )
	{
		// For Property tracks - pop up a dialog to choose property name.
		TrackAddPropName = NAME_None;

		FEdModeInterpEdit* Mode = (FEdModeInterpEdit*)GEditorModeTools().GetActiveMode( FBuiltinEditorModes::EM_InterpEdit );
		check(Mode != NULL);

		IMatineeBase* InterpEd = Mode->InterpEd;
		check(InterpEd != NULL);

		UInterpGroupInst* GrInst = InterpEd->GetMatineeActor()->FindFirstGroupInst(Group);
		check(GrInst);

		AActor* Actor = GrInst->GetGroupActor();
		if ( Actor != NULL )
		{
			TArray<FName> PropNames;
			FMatineeUtils::GetInterpBoolPropertyNames(Actor, PropNames);

			if( PropNames.Num() > 0 )
			{
				TArray<FString> PropStrings;
				PropStrings.AddZeroed( PropNames.Num() );
				for(int32 i=0; i<PropNames.Num(); i++)
				{
					PropStrings[i] = PropNames[i].ToString();
				}

				TSharedRef<SWindow> NewWindow = SNew(SWindow)
					.Title(NSLOCTEXT("Matinee.Popups", "ChooseProperty", "Choose Property..."))
					.SizingRule( ESizingRule::Autosized )
					.SupportsMinimize(false)
					.SupportsMaximize(false);

				FString Result;
				TSharedRef<STextComboPopup> TextEntryPopup = 
					SNew(STextComboPopup)
					.Label(NSLOCTEXT("Matinee.Popups", "PropertyName", "Property Name"))
					.TextOptions(PropStrings)
					.OnTextChosen_UObject(this, &UMatineeTrackBoolPropHelper::OnCreateTrackTextEntry, TWeakPtr<SWindow>(NewWindow), (FString *)&Result);

				NewWindow->SetContent(TextEntryPopup);
				GEditor->EditorAddModalWindow(NewWindow);
				if (!Result.IsEmpty())
				{
					TrackAddPropName = FName( *Result );
					if ( TrackAddPropName != NAME_None )
					{
						// Check we don't already have a track controlling this property.
						for(int32 i=0; i<Group->InterpTracks.Num(); i++)
						{
							UInterpTrackFloatProp* TestFloatTrack = Cast<UInterpTrackFloatProp>( Group->InterpTracks[i] );
							if(TestFloatTrack && TestFloatTrack->PropertyName == TrackAddPropName)
							{
								FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "Error_PropertyAlreadyControlled", "Already a FloatProp track controlling this property.") );
								return false;
							}

							UInterpTrackBoolProp* TestBoolTrack = Cast<UInterpTrackBoolProp>( Group->InterpTracks[i] );
							if(TestBoolTrack && TestBoolTrack->PropertyName == TrackAddPropName)
							{
								FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "Error_BoolPropertyAlreadyControlled", "Already a BoolProp track controlling this property.") );
								return false;
							}

							UInterpTrackVectorProp* TestVectorTrack = Cast<UInterpTrackVectorProp>( Group->InterpTracks[i] );
							if(TestVectorTrack && TestVectorTrack->PropertyName == TrackAddPropName)
							{
								FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "Error_VectorPropertyAlreadyControlled", "Already a VectorProp track controlling this property.") );
								return false;
							}
							UInterpTrackLinearColorProp* TestLinearColorTrack = Cast<UInterpTrackLinearColorProp>( Group->InterpTracks[i] );
							if(TestLinearColorTrack && TestLinearColorTrack->PropertyName == TrackAddPropName)
							{
								FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "Error_LinearPropertyAlreadyControlled", "Already a LinearProp track controlling this property.") );
								return false;
							}					
						}

						return true;
					}
				}
			}
			else
			{
				FMessageDialog::Open( EAppMsgType::Ok, LOCTEXT( "MatineeBoolTrackHelper_NoProperties", "No Bool track properties are available for this actor" ) );
			}
		}

		return false;
	}
	else
	{
		return true;
	}
}
UInterpGroup* FAssetTypeActions_CameraAnim::CreateInterpGroup(UCameraAnim* InCameraAnim, FCameraPreviewInfo& PreviewInfo)
{
	check(InCameraAnim);

	CreatePreviewPawn(InCameraAnim, PreviewInfo.PawnClass, PreviewInfo.Location, PreviewInfo.Rotation);

	PreviewInfo.PawnInst = PreviewPawn.Get();

	if (PreviewInfo.PawnInst)
	{
		// create InterpGroup so that we can play animation to this pawn
		check(PreviewMatineeActor.Get()->MatineeData);
		UInterpGroup* NewGroup = ConstructObject<UInterpGroup>(UInterpGroup::StaticClass(), PreviewMatineeActor.Get()->MatineeData, NAME_None, RF_Transient);
		NewGroup->GroupName = FName(TEXT("Preview Pawn"));
		NewGroup->EnsureUniqueName();

		PreviewMatineeActor.Get()->MatineeData->InterpGroups.Add(NewGroup);

		// now add group inst
		UInterpGroupInst* NewGroupInst = ConstructObject<UInterpGroupInst>(UInterpGroupInst::StaticClass(), PreviewMatineeActor.Get(), NAME_None, RF_Transient);
		// Initialise group instance, saving ref to actor it works on.
		NewGroupInst->InitGroupInst(NewGroup, PreviewInfo.PawnInst);
		const int32 NewGroupInstIndex = PreviewMatineeActor.Get()->GroupInst.Add(NewGroupInst);

		//Link group with actor
		PreviewMatineeActor.Get()->InitGroupActorForGroup(NewGroup, PreviewInfo.PawnInst);

		// Now time to add AnimTrack so that we can play animation
		int32 AnimTrackIndex = INDEX_NONE;
		// add anim track but do not use addtotrack function that does too many things 
		// Construct track and track instance objects.
		UInterpTrackAnimControl* AnimTrack = ConstructObject<UInterpTrackAnimControl>( UInterpTrackAnimControl::StaticClass(), NewGroup, NAME_None, RF_Transient );
		check(AnimTrack);

		NewGroup->InterpTracks.Add(AnimTrack);
		
		// use config anim slot
		AnimTrack->SlotName = FName(*GConfig->GetStr(TEXT("MatineePreview"), TEXT("DefaultAnimSlotName"), GEditorIni));
		UInterpTrackInst* NewTrackInst = ConstructObject<UInterpTrackInst>( AnimTrack->TrackInstClass, NewGroupInst, NAME_None, RF_Transient );
		check(NewTrackInst);

		NewGroupInst->TrackInst.Add(NewTrackInst);

		// Initialize track, giving selected object.
		NewTrackInst->InitTrackInst(AnimTrack);

		// Save state into new track before doing anything else (because we didn't do it on ed mode change).
		NewTrackInst->SaveActorState(AnimTrack);
		check(NewGroupInst->TrackInst.Num() > 0);

		// add default anim curve weights to be 1
		int32 KeyIndex = AnimTrack->CreateNewKey(0.0f);
		AnimTrack->SetKeyOut(0, KeyIndex, 1.0f);

		if(PreviewInfo.AnimSeq != NULL)
		{
			KeyIndex = AnimTrack->AddKeyframe(0.0f, NewGroupInst->TrackInst[0], CIM_Linear);
			FAnimControlTrackKey& NewSeqKey = AnimTrack->AnimSeqs[KeyIndex];
			NewSeqKey.AnimSeq = PreviewInfo.AnimSeq;	
		}

		PreviewPawn = PreviewInfo.PawnInst;

		return NewGroup;
	}
	return NULL;
}
void UFaceFXMatineeControl::DrawTrack( FCanvas* Canvas, UInterpGroup* Group, const FInterpTrackDrawParams& Params )
{
	static const FColor	KeySelectedColor(255,128,0);
	static const FColor KeyLabelColor(225,225,225);
	static const FColor KeyBackgroundColor(0,150,200);
	static const FColor KeyColorBlack(0,0,0);
	static const int32	KeyVertOffset = 3;

	const bool bHitTesting = Canvas->IsHitTesting();
	const bool bAllowBarSelection = bHitTesting && Params.bAllowKeyframeBarSelection;
	const bool bAllowTextSelection = bHitTesting && Params.bAllowKeyframeTextSelection;

	AMatineeActor* MatineeActor = CastChecked<AMatineeActor>(Group->GetOuter()->GetOuter());
	UInterpGroupInst* GroupInst = MatineeActor->FindFirstGroupInst(Group);
	const AActor* GroupActor = GroupInst ? GroupInst->GetGroupActor() : nullptr;

	//cached animation ids
	TArray<FFaceFXAnimId> AnimIds;
	AnimIds.AddUninitialized(Keys.Num());
	
	//Draw the tiles for each animation
	for (int32 i = 0; i < Keys.Num(); i++)
	{
		const FFaceFXTrackKey& AnimKey = Keys[i];

		FFaceFXAnimId AnimationId;
		if(AnimKey.AnimationId.IsValid())
		{
			AnimationId = AnimKey.AnimationId;
		}
		else if(const UFaceFXAnim* Animation = GetAnimation(AnimKey, this))
		{
			AnimationId = Animation->GetId();
		}

		AnimIds[i] = AnimationId;

		//determine animation duration
		const float AnimStartTime = AnimKey.Time;
		const float AnimEndTime = AnimStartTime + AnimKey.GetAnimationDuration(GroupActor);

		const int32 StartPixelPos = FMath::TruncToInt((AnimStartTime - Params.StartTime) * Params.PixelsPerSec);
		const int32 EndPixelPos = FMath::TruncToInt((AnimEndTime - Params.StartTime) * Params.PixelsPerSec);

		// Find if this sound is one of the selected ones.
		bool bKeySelected = false;
		for (const FInterpEdSelKey& SelectedKey : Params.SelectedKeys)
		{
			if(SelectedKey.Group == Group && SelectedKey.Track == this && SelectedKey.KeyIndex == i)
			{
				bKeySelected = true;
				break;
			}
		}

		const FColor& BorderColor = bKeySelected ? KeySelectedColor : KeyColorBlack;

		if(bAllowBarSelection) 
		{
			Canvas->SetHitProxy(new HInterpTrackKeypointProxy(Group, this, i));
		}
		Canvas->DrawTile(StartPixelPos, KeyVertOffset, EndPixelPos - StartPixelPos + 1, FMath::TruncToFloat(Params.TrackHeight - 2.f*KeyVertOffset), 0.f, 0.f, 1.f, 1.f, BorderColor);
		Canvas->DrawTile(StartPixelPos+1, KeyVertOffset+1, EndPixelPos - StartPixelPos - 1, FMath::TruncToFloat(Params.TrackHeight - 2.f*KeyVertOffset) - 2, 0.f, 0.f, 1.f, 1.f, KeyBackgroundColor);
		if(bAllowBarSelection)
		{
			Canvas->SetHitProxy(nullptr);
		}
	}
	
	// Use base-class to draw key triangles
	Super::DrawTrack( Canvas, Group, Params );

	//Draw animation names on top
	for (int32 i = 0; i < Keys.Num(); i++)
	{
		const FFaceFXTrackKey& AnimKey = Keys[i];
		
		//fetch the cached animation id for the key
		const FFaceFXAnimId& AnimId = AnimIds[i];

		FString AnimName(TEXT("Unknown"));
		if(AnimId.IsValid())
		{
			AnimName = AnimId.Group.IsNone() ? TEXT("") : AnimId.Group.ToString() + TEXT(" / ");
			AnimName += AnimId.Name.ToString();
		}

		if(AnimKey.bLoop)
		{
			AnimName += TEXT(" [Looping]");
		}

		int32 XL, YL;
		StringSize(GEngine->GetSmallFont(), XL, YL, *AnimName);

		if (bAllowTextSelection)
		{
			Canvas->SetHitProxy(new HInterpTrackKeypointProxy(Group, this, i));
		}

		const int32 PixelPos = FMath::TruncToInt((AnimKey.Time - Params.StartTime) * Params.PixelsPerSec);

		Canvas->DrawShadowedString((PixelPos + 2), Params.TrackHeight - YL - KeyVertOffset, *AnimName, GEngine->GetSmallFont(), KeyLabelColor);
		if (bAllowTextSelection)
		{
			Canvas->SetHitProxy(nullptr);
		}
	}
}