using QuikDawEditor.EditingClasses; using QuikDawEditor.Undo; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using static QuikDawEditor.EDITING.MiscMethods; using static QuikDawEditor.EDITING.StaticProperties; namespace QuikDawEditor; public partial class AutomationLaneControl : UserControl, INotifyPropertyChanged { public string RelRes() { ReleaseResources(); return "Resources released"; } private AutomationLane thisAutoLane { get { return (AutomationLane)this.DataContext; } } public event PropertyChangedEventHandler PropertyChanged; private void NotifyPropertyChanged([CallerMemberName] String propertyName = "") { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } public AutomationLaneControl() { InitializeComponent(); } private void AutoClipControl_Loaded(object sender, RoutedEventArgs e) { } private int GetAutoLaneIndex { get { return thisAutoLane.myTrack == null ? editingProject.MasterTrack.AutomationLanes.IndexOf(thisAutoLane) : thisAutoLane.myTrack.AutomationLanes.IndexOf(thisAutoLane); } } private void AutoPointThumb_DragStarted(object sender, System.Windows.Controls.Primitives.DragStartedEventArgs e) { thisAP = (AutoPoint)((System.Windows.Controls.Primitives.Thumb)sender).DataContext; undoActions.Add(new AutoPointMovedUndoClass(thisAutoLane.myTrackID, GetAutoLaneIndex, thisAutoLane.autoPoints.IndexOf(thisAP), thisAP.sourcePointms, thisAP.AutoValue)); double thisWidthMs = PixelsToMsec(this.ActualWidth); AutoPoint prevGP = thisAutoLane.autoPoints.LastOrDefault(gp => gp.sourcePointms < thisAP.sourcePointms); AutoPoint nextGP = thisAutoLane.autoPoints.FirstOrDefault(gp => gp.sourcePointms > thisAP.sourcePointms); RightMax = thisAP.IsLeftEdgeAutoPoint ? 0 : (thisAP.IsRightEdgeAutoPoint ? thisWidthMs : nextGP.sourcePointms - 1); LeftMin = thisAP.IsRightEdgeAutoPoint ? thisWidthMs : (thisAP.IsLeftEdgeAutoPoint ? 0 : prevGP.sourcePointms + 1); thisAP.AutoPointValVisibility = Visibility.Visible; } private void AutoPointThumb_DragCompleted(object sender, System.Windows.Controls.Primitives.DragCompletedEventArgs e) { thisAP.AutoPointValVisibility = Visibility.Hidden; } AutoPoint thisAP; double LeftMin; double RightMax; private void AutoPointThumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e) { thisAP.sourcePointms = Math.Min(Math.Max(thisAP.sourcePointms + PixelsToMsec(e.HorizontalChange), LeftMin), RightMax); float aval = (float)(thisAP.AutoValue + thisAutoLane.ValueRange * (-e.VerticalChange / UnitHeight)); thisAP.AutoValue = Math.Max(Math.Min(aval, thisAutoLane.MaxValue), thisAutoLane.MinValue); thisAutoLane.NotifyAutoPointsChanged(); //Automation type-specific displaying here string prefix = ""; if (thisAutoLane.AutomationType == "Pan") prefix = Math.Abs(thisAP.AutoValue) < 0.2 ? " C" : (Math.Sign(thisAP.AutoValue) == 1 ? " R" : " L"); thisAP.UpdateAutoString(prefix); } bool mouseOverAutoPoint = false; private void AutoPointThumb_MouseEnter(object sender, MouseEventArgs e) { mouseOverAutoPoint = true; thisAP = (AutoPoint)((System.Windows.Controls.Primitives.Thumb)sender).DataContext; } private void AutoPointThumb_MouseLeave(object sender, MouseEventArgs e) { mouseOverAutoPoint = false; } private void ClearAllAutoPointsMenuItem_Click(object sender, RoutedEventArgs e) { List<AutoPoint> removedAutoPoints = new List<AutoPoint>(); for (int gpno = thisAutoLane.autoPoints.Count - 2; gpno > 0; gpno -= 1) { removedAutoPoints.Add(thisAutoLane.autoPoints[gpno]); thisAutoLane.autoPoints.RemoveAt(gpno); } undoActions.Add(new AutoPointsClearedUndoClass(thisAutoLane.myTrackID, GetAutoLaneIndex, removedAutoPoints, thisAutoLane.ClipStartAutoPoint.AutoValue, thisAutoLane.ClipEndAutoPoint.AutoValue)); thisAutoLane.ClipStartAutoPoint.AutoValue = thisAutoLane.MidValue; thisAutoLane.ClipEndAutoPoint.AutoValue = thisAutoLane.MidValue; thisAutoLane.NotifyAutoPointsChanged(); editingProject.NeedsSaving = true; } private void FadeInFromStartMenuItem_Click(object sender, RoutedEventArgs e) { double endPointms = PixelsToMsec(MouseDownX); double thisWidthMs = PixelsToMsec(this.ActualWidth) / editingProject.ViewXZoomFac; float relAutoValue = CalculateValBetweenAutoPoints(endPointms, thisAutoLane); //Remove gain points between new points List<AutoPoint> deletedAutoPoints = new List<AutoPoint>(thisAutoLane.autoPoints.Where(gp => !gp.IsLeftEdgeAutoPoint && gp.sourcePointms <= endPointms)); thisAutoLane.autoPoints = new ObservableCollection<AutoPoint>(thisAutoLane.autoPoints.Except(deletedAutoPoints)); int insertAPIdx = thisAutoLane.autoPoints.IndexOf(thisAutoLane.autoPoints.FirstOrDefault(ap=>ap.sourcePointms > endPointms)); thisAutoLane.autoPoints.Insert(insertAPIdx, new AutoPoint() { sourcePointms = endPointms, AutoValue = relAutoValue }); undoActions.Add(new AutoPointsFadeInUndoClass(thisAutoLane.myTrackID, GetAutoLaneIndex, insertAPIdx, deletedAutoPoints, thisAutoLane.ClipStartAutoPoint.AutoValue)); thisAutoLane.ClipStartAutoPoint.AutoValue = thisAutoLane.MinValue; thisAutoLane.NotifyAutoPointsChanged(); editingProject.NeedsSaving = true; } private void FadeOutToEndMenuItem_Click(object sender, RoutedEventArgs e) { double startpointms = PixelsToMsec(MouseDownX); double thisWidthMs = PixelsToMsec(this.ActualWidth) / editingProject.ViewXZoomFac; float relAutoValue = CalculateValBetweenAutoPoints(startpointms, thisAutoLane); //Remove gain points between new points List<AutoPoint> deletedAutoPoints = new List<AutoPoint>(thisAutoLane.autoPoints.Where(gp => gp.sourcePointms >= startpointms && !gp.IsRightEdgeAutoPoint)); thisAutoLane.autoPoints = new ObservableCollection<AutoPoint>(thisAutoLane.autoPoints.Except(deletedAutoPoints)); int insertAPIdx = thisAutoLane.autoPoints.IndexOf(thisAutoLane.autoPoints.FirstOrDefault(ap => ap.sourcePointms > startpointms)); thisAutoLane.autoPoints.Insert(insertAPIdx, new AutoPoint() { sourcePointms = startpointms, AutoValue =relAutoValue }); undoActions.Add(new AutoPointsFadeOutUndoClass(thisAutoLane.myTrackID, GetAutoLaneIndex, insertAPIdx, deletedAutoPoints, thisAutoLane.ClipEndAutoPoint.AutoValue)); thisAutoLane.ClipEndAutoPoint.AutoValue = thisAutoLane.MinValue; thisAutoLane.NotifyAutoPointsChanged(); editingProject.NeedsSaving = true; } private void MuteSelectionMenuItem_Click(object sender, RoutedEventArgs e) { InsertMutedAutoPoints(); } private void InsertMutedAutoPoints() { double rectStartMs = PixelsToMsec(SelectionRect.Margin.Left); double rectEndMs = PixelsToMsec(SelectionRect.Margin.Left + SelectionRect.Width); AutoPoint prevAutoPoint = thisAutoLane.autoPoints.LastOrDefault(gp => gp.sourcePointms < rectStartMs); AutoPoint nextAutoPoint = thisAutoLane.autoPoints.FirstOrDefault(gp => gp.sourcePointms > rectEndMs); if (prevAutoPoint == null) prevAutoPoint = thisAutoLane.ClipStartAutoPoint; if (nextAutoPoint == null) nextAutoPoint = thisAutoLane.ClipEndAutoPoint; List<AutoPoint> deletedAutoPoints = new List<AutoPoint>(thisAutoLane.autoPoints.Where(gp => gp.sourcePointms >= rectStartMs && gp.sourcePointms <= rectEndMs)); int deletedStart = deletedAutoPoints.Count > 0 ? thisAutoLane.autoPoints.IndexOf(deletedAutoPoints[0]) : -1; if (deletedStart > -1) for (int n = 0; n < deletedAutoPoints.Count; n++) thisAutoLane.autoPoints.RemoveAt(deletedStart); double percOfSectionStart = (rectStartMs - prevAutoPoint.sourcePointms) / (nextAutoPoint.sourcePointms - prevAutoPoint.sourcePointms); float gainValAtStart = (float)(percOfSectionStart * (nextAutoPoint.AutoValue - prevAutoPoint.AutoValue)) + prevAutoPoint.AutoValue; double percOfSectionEnd = (rectEndMs - prevAutoPoint.sourcePointms) / (nextAutoPoint.sourcePointms - prevAutoPoint.sourcePointms); float gainValAtEnd = (float)(percOfSectionEnd * (nextAutoPoint.AutoValue - prevAutoPoint.AutoValue)) + prevAutoPoint.AutoValue; int insertIdx = thisAutoLane.autoPoints.IndexOf(nextAutoPoint); AutoPoint startPair = new AutoPoint() { sourcePointms = rectStartMs, AutoValue = thisAutoLane.MinValue }; AutoPoint endPair = new AutoPoint() { sourcePointms = rectEndMs, AutoValue = thisAutoLane.MinValue }; List<AutoPoint> newAutoPoints = new List<AutoPoint>() { new AutoPoint() { sourcePointms = endPair.sourcePointms + 1, AutoValue = gainValAtEnd }, endPair, startPair, new AutoPoint() { sourcePointms = startPair.sourcePointms - 1, AutoValue = gainValAtStart } }; foreach (AutoPoint gp in newAutoPoints) thisAutoLane.autoPoints.Insert(insertIdx, gp); undoActions.Add(new AutoPointsSelectionMutedUndoClass(thisAutoLane.myTrackID, GetAutoLaneIndex, insertIdx, deletedAutoPoints)); editingProject.NeedsSaving = true; SelectionRect.Visibility = Visibility.Hidden; MouseDownOnAutomationLane = false; thisAutoLane.NotifyAutoPointsChanged(); } private void ClearSelectionGainPointsMenuItem_Click(object sender, RoutedEventArgs e) { ClearSelection(false); } private void ResetSelectionGainPointsMenuItem_Click(object sender, RoutedEventArgs e) { ClearSelection(true); } private void ClearSelection(bool addends) { double rectStartMs = PixelsToMsec(SelectionRect.Margin.Left); double rectEndMs = PixelsToMsec(SelectionRect.Margin.Left + SelectionRect.Width); //Remove gain points between new points List<AutoPoint> removeAPs = thisAutoLane.autoPoints.Where(ap => ap.sourcePointms >= rectStartMs && ap.sourcePointms <= rectEndMs).ToList(); for (int apno = removeAPs.Count - 1; apno > -1; apno -=1) thisAutoLane.autoPoints.Remove(removeAPs[apno]); int insertPtsIdx = thisAutoLane.autoPoints.IndexOf(thisAutoLane.autoPoints.FirstOrDefault(ap => ap.sourcePointms > rectEndMs)); ; if (addends) { thisAutoLane.autoPoints.Insert(insertPtsIdx, new AutoPoint() { sourcePointms = rectEndMs, AutoValue = thisAutoLane.MidValue }); thisAutoLane.autoPoints.Insert(insertPtsIdx, new AutoPoint() { sourcePointms = rectStartMs, AutoValue = thisAutoLane.MidValue }); } undoActions.Add(new AutoPointsSelectionClearedOrResetUndoClass(thisAutoLane.myTrackID, GetAutoLaneIndex, insertPtsIdx, removeAPs, addends)); thisAutoLane.NotifyAutoPointsChanged(); SelectionRect.Visibility = Visibility.Hidden; MouseDownOnAutomationLane = false; editingProject.NeedsSaving = true; } private void SelectionRect_PreviewKeyDown(object sender, KeyEventArgs e) { switch (e.Key) { //case Key.M: // InsertMutedAutoPoints(); // break; //case Key.C: // ClearSelection(false); // break; } } private void SelectionRect_PreviewMouseDown(object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Right) SelectionRect.ContextMenu.IsOpen = true; else SelectionRect.Visibility = Visibility.Hidden; e.Handled = true; } private void SelectionRect_PreviewMouseUp(object sender, MouseButtonEventArgs e) { SelectionRect.Focus(); } private void AutoPath_PreviewMouseDown(object sender, MouseButtonEventArgs e) { e.Handled = true; if (e.ChangedButton == MouseButton.Right) return; if (SelectionRect.IsVisible) return; double newPointMs = PixelsToMsec(e.GetPosition(this).X); AutoPoint lastAutoPoint = thisAutoLane.autoPoints.LastOrDefault(gp => gp.sourcePointms < newPointMs); int insertIdx = lastAutoPoint == null ? 0 : thisAutoLane.autoPoints.IndexOf(lastAutoPoint) + 1; AutoPoint newAutoPoint = new AutoPoint() { sourcePointms = newPointMs }; float aval = thisAutoLane.ValueRange + thisAutoLane.MinValue - thisAutoLane.ValueRange * (float)((e.GetPosition(this).Y - 2) / UnitHeight); //Mouse click places it too low newAutoPoint.AutoValue = aval; thisAutoLane.autoPoints.Insert(insertIdx, newAutoPoint); thisAutoLane.NotifyAutoPointsChanged(); undoActions.Add(new AutoPointAddedUndoClass(thisAutoLane.myTrackID, GetAutoLaneIndex, insertIdx)); editingProject.NeedsSaving = true; } private void AutoPath_PreviewMouseMove(object sender, MouseEventArgs e) { //Debug.WriteLine("In path: " + e.GetPosition(GainPath).X.ToString()); } private void thisAutoLaneControl_MouseEnter(object sender, MouseEventArgs e) { thisAutoLane.SetAutoPointLayoutTransform(); thisAutoLane.AutoPointsVisible = thisAutoLane.IsActive; } private void thisAutoLaneControl_MouseLeave(object sender, MouseEventArgs e) { Cursor = Cursors.Arrow; thisAutoLane.AutoPointsVisible = false; } private void AutoPointDeleteMenuItem_Click(object sender, RoutedEventArgs e) { if (thisAP.IsLeftOrRightEdgeAutoPoint) return; undoActions.Add(new AutoPointDeletedUndoClass(thisAutoLane.myTrackID, GetAutoLaneIndex, thisAutoLane.autoPoints.IndexOf(thisAP), thisAP)); thisAutoLane.autoPoints.Remove(thisAP); thisAutoLane.NotifyAutoPointsChanged(); editingProject.NeedsSaving = true; } private void ClipContextMenu_Opened(object sender, RoutedEventArgs e) { thisAutoLane.AutoPointsVisible = true; } private void AutoPath_PreviewDragOver(object sender, DragEventArgs e) { e.Effects = DragDropEffects.None; e.Handled = true; } private void thisAutoLaneControl_PreviewMouseDown(object sender, MouseButtonEventArgs e) { TrackContentControlChildBeingModified = true; if (e.ChangedButton == MouseButton.Right) return; MouseDownOnAutomationLane = true; MouseDownX = e.GetPosition(this).X; SelectionRect.Visibility = Visibility.Hidden; if (!mouseOverAutoPoint) this.CaptureMouse(); } private double MouseDownX = 0; private bool MouseDownOnAutomationLane = false; private void thisAutoLaneControl_PreviewMouseUp(object sender, MouseButtonEventArgs e) { TrackContentControlChildBeingModified = false; if (e.ChangedButton == MouseButton.Right) { if (mouseOverAutoPoint) return; e.Handled = true; MouseDownX = e.GetPosition(this).X; this.ContextMenu.IsOpen = true; } MouseDownOnAutomationLane = false; this.ReleaseMouseCapture(); } private void thisAutoLaneControl_PreviewMouseMove(object sender, MouseEventArgs e) { if (mouseOverAutoPoint) return; if (MouseDownOnAutomationLane) { double rwidth = e.GetPosition(this).X - MouseDownX; if (rwidth > 5) { SelectionRect.Margin = new Thickness(MouseDownX, 0, 0, 0); SelectionRect.Visibility = Visibility.Visible; SelectionRect.Width = rwidth; } } } private void SelRectContextMenu_Opened(object sender, RoutedEventArgs e) { thisAutoLane.AutoPointsVisible = true; } private void ThumbContextMenu_Opened(object sender, RoutedEventArgs e) { thisAutoLane.AutoPointsVisible = true; } }