229 lines
7.7 KiB
C#
229 lines
7.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Drawing;
|
|
using System.Linq;
|
|
using System.Windows.Forms;
|
|
using JD.Application.Utils;
|
|
using JD.Application.View.Misc;
|
|
using JD.Application.View.Timeline;
|
|
using JD.Audio.Annotations;
|
|
using JD.Audio.SoundPlayer;
|
|
using JD.Collections.Extensions;
|
|
using JD.Maths.Utils;
|
|
using JD.MessageService;
|
|
using JD.Timeline.Model.Clips;
|
|
using JD.Timeline.View.Actions.Refactor;
|
|
using JD.Timeline.View.ClipView;
|
|
using TeaBox;
|
|
using TeaBox.Gui;
|
|
using JD.Serialization;
|
|
using JD.WinFormExtensions;
|
|
using TeaBox.Core.Source.Gui;
|
|
using TeaBox.Core.Source.Gui.Misc;
|
|
using FrameConverter = JD.Core.FrameConverter;
|
|
|
|
namespace JD.Timeline.View.Actions
|
|
{
|
|
/// <summary>
|
|
/// Provides the different menu actions to create a new clip.
|
|
/// </summary>
|
|
public class AddNewClipActionHandler : TapeActionHandlerBase
|
|
{
|
|
protected virtual IEnumerable<System.Type> ClipTypes()
|
|
{
|
|
return Track != null ? Track.ClipTypes() : Enumerable.Empty<System.Type>();
|
|
}
|
|
|
|
public AddNewClipActionHandler()
|
|
{
|
|
ClipLimit = 500;
|
|
}
|
|
|
|
protected ClipViewControl CurrentControl
|
|
{
|
|
get
|
|
{
|
|
var view = CurrentView;
|
|
return view != null ? view.ClipView : null;
|
|
}
|
|
}
|
|
|
|
protected TapeTrack Track
|
|
{
|
|
get; set;
|
|
}
|
|
|
|
public override bool UpdateArray(ActionContext context, ActionArrayInfo info)
|
|
{
|
|
if (CurrentView == null || SelectedClips.Any())
|
|
{
|
|
return true;
|
|
}
|
|
|
|
Point currentLocation = context.Location;
|
|
if (currentLocation == Point.Empty)
|
|
{
|
|
currentLocation = Control.MousePosition;
|
|
}
|
|
|
|
var dropLocation = CurrentControl.PointToClient(currentLocation);
|
|
Track = CurrentControl.TrackFromPoint(dropLocation.Y);
|
|
|
|
if (Track != null)
|
|
{
|
|
foreach (var clipType in ClipTypes().AsSorted((x1, x2) => System.String.Compare(x1.Name, x2.Name, System.StringComparison.Ordinal)))
|
|
{
|
|
var action = new ActionInfoElement("Add " + clipType.GetStyle().DisplayName)
|
|
{
|
|
Icon = new ImageOrIcon(clipType.GetStyle().Icon),
|
|
State = clipType
|
|
};
|
|
info.Add(action);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public override void Execute(ActionExecutionContext context)
|
|
{
|
|
if (CurrentTape == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var type = (System.Type)context.State;
|
|
|
|
// From the shortcut.
|
|
Clip newClip;
|
|
if (type == null)
|
|
{
|
|
newClip = InstantiateDefaultClip();
|
|
}
|
|
else
|
|
{
|
|
var dropLocation = CurrentControl.PointToClient(context.Location);
|
|
newClip = Instantiate(type, dropLocation);
|
|
}
|
|
|
|
if (newClip != null)
|
|
{
|
|
CurrentTape.Clips.Add(newClip);
|
|
var newWidget = CurrentControl.ClipWidgets.FirstOrDefault(x => x.Model == newClip);
|
|
newWidget.OnInserted();
|
|
}
|
|
}
|
|
|
|
private Clip InstantiateDefaultClip()
|
|
{
|
|
var trackWidget = CurrentView.ClipView.TrackView.ActiveTrack as TapeTrackWidget;
|
|
if (trackWidget != null)
|
|
{
|
|
var track = trackWidget.Track;
|
|
var clipType = track.ClipTypes().SingleOrDefault();
|
|
var curentBeat = CurrentControl.Timeline.ConvertTo(CurrentControl.Timeline.TimeCursor.Time, TimeUnit.Beat);
|
|
|
|
if (clipType == null)
|
|
{
|
|
// When the track is not of a fixed type
|
|
// we assume the user might want to create the same type of clip he just created before.
|
|
// so we find a type that is near the actual time cursor on the same track.
|
|
|
|
clipType = (from clip in track.Tape.Clips
|
|
where clip.Track == track
|
|
orderby Math.Abs(clip.StartBeat - curentBeat)
|
|
select clip.GetType()).FirstOrDefault();
|
|
}
|
|
|
|
if (clipType != null && ValidateCanAddClip(clipType))
|
|
{
|
|
curentBeat = CurrentTape.TryGetWave().UnitConverter.Snap(new BeatUnit(curentBeat), EditionOptions.CurrentSnappingUnit).Value;
|
|
|
|
var newClip = Clip.Instantiate(clipType);
|
|
newClip.InitializeDefaultValues();
|
|
newClip.StartFrame = FindBestStartFrame(track, FrameConverter.FrameFromTimeInteger(curentBeat));
|
|
newClip.TrackId = track.Id;
|
|
return newClip;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
protected virtual Clip Instantiate(System.Type clipType, Point dropLocation)
|
|
{
|
|
var track = CurrentControl.TrackFromPoint(dropLocation.Y);
|
|
|
|
if (track != null && ValidateCanAddClip(clipType))
|
|
{
|
|
var newEvent = Clip.Instantiate(clipType);
|
|
newEvent.InitializeDefaultValues();
|
|
newEvent.StartFrame = FindBestStartFrame(track, (int) CurrentControl.FrameFromPoint(dropLocation.X));
|
|
newEvent.TrackId = track.Id;
|
|
|
|
if (newEvent.DefaultDuration > 0)
|
|
{
|
|
newEvent.Duration = newEvent.DefaultDuration;
|
|
}
|
|
else
|
|
{
|
|
newEvent.Duration = (int)CurrentControl.Timeline.FrameFromPixelDuration(200);
|
|
}
|
|
return newEvent;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the starting frame to make sure it does not overlap an existing clip
|
|
/// </summary>
|
|
private int FindBestStartFrame(TapeTrack track, int frame)
|
|
{
|
|
Clip collidingClip;
|
|
|
|
while (TryGetCollidingClip(track, frame, out collidingClip))
|
|
{
|
|
frame = collidingClip.StopFrame;
|
|
}
|
|
|
|
return frame;
|
|
}
|
|
|
|
private bool TryGetCollidingClip(TapeTrack track, int frame, out Clip collision)
|
|
{
|
|
collision = track.Clips().FirstOrDefault(x => frame.IsBetween(x.StartFrame, x.StopFrame - 1));
|
|
return collision != null;
|
|
}
|
|
|
|
protected int ClipLimit
|
|
{
|
|
get; set;
|
|
}
|
|
|
|
private bool ValidateCanAddClip(System.Type type)
|
|
{
|
|
var clipCount = CurrentControl.Tape.Clips.Count;
|
|
if (type != typeof(TapeReferenceClip) && clipCount > ClipLimit)
|
|
{
|
|
var dialog = new MessageDialog
|
|
{
|
|
MainIcon = MessageDialogIcon.Warning,
|
|
Content =
|
|
$"Clip limit reached ({clipCount} clips instead of 300). Are you sure you want to continue?"
|
|
};
|
|
|
|
var yes = dialog.AddButton("Add clip anyway");
|
|
yes.Content = "We suggest you start to reduce the clip count before adding new clips.";
|
|
var help = dialog.AddButton("Help");
|
|
var cancel = dialog.AddButton("Cancel");
|
|
cancel.IsDefault = true;
|
|
|
|
var result = dialog.ShowDialog();
|
|
if (result == help)
|
|
{
|
|
ClipLimitWarning.OpenHelpFile();
|
|
}
|
|
return result == yes;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|