using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Interop; using System.Diagnostics; using System.ComponentModel; using System.IO; using BuzzGUI.Interfaces; using BuzzGUI.Common; using BuzzGUI.Common.InterfaceExtensions; using BuzzGUI.Common.Actions; using BuzzGUI.Common.Actions.SongActions; using BuzzGUI.Common.Actions.SequenceActions; using BuzzGUI.Common.Actions.MachineActions; using BuzzGUI.SequenceEditor.Actions; namespace BuzzGUI.SequenceEditor { /// /// Interaction logic for UserControl1.xaml /// public partial class SequenceEditor : UserControl, INotifyPropertyChanged { ISong song; public ISong Song { get { return song; } set { if (song != null) { song.SequenceAdded -= song_SequenceAdded; song.SequenceRemoved -= song_SequenceRemoved; song.SequenceChanged -= song_SequenceChanged; song.PropertyChanged -= song_PropertyChanged; song.MachineAdded -= song_MachineAdded; song.MachineRemoved -= song_MachineRemoved; song.Buzz.PropertyChanged -= Buzz_PropertyChanged; song.Buzz.PatternEditorActivated -= Buzz_PatternEditorActivated; } PatternElement.InvalidateResources(); song = value; if (song != null) { song.SequenceAdded += song_SequenceAdded; song.SequenceRemoved += song_SequenceRemoved; song.SequenceChanged += song_SequenceChanged; song.PropertyChanged += song_PropertyChanged; song.MachineAdded += song_MachineAdded; song.MachineRemoved += song_MachineRemoved; song.Buzz.PropertyChanged += Buzz_PropertyChanged; song.Buzz.PatternEditorActivated += Buzz_PatternEditorActivated; if (song.Associations.ContainsKey("SequenceEditorViewSettings")) viewSettings = (ViewSettings)song.Associations["SequenceEditorViewSettings"]; else { viewSettings = new ViewSettings(this); viewSettings.TimeSignatureList.Changed += TimeSignatureListChanged; song.Associations["SequenceEditorViewSettings"] = viewSettings; } trackSV.ScrollToHorizontalOffset(0); trackSV.ScrollToVerticalOffset(0); AddAllSequences(); UpdateWidth(); cursorElement.Time = 0; cursorElement.Row = 0; cursorElement.IsActive = false; selectionLayer.KillSelection(); PropertyChanged.RaiseAll(this); } } } void song_MachineAdded(IMachine m) { PropertyChanged.Raise(this, "MachineList"); m.PropertyChanged += Machine_PropertyChanged; } void song_MachineRemoved(IMachine m) { PropertyChanged.Raise(this, "MachineList"); m.PropertyChanged -= Machine_PropertyChanged; viewSettings.PatternAssociations.Remove(k => k.Machine == m); } void song_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { switch (e.PropertyName) { case "SongEnd": UpdateWidth(); break; case "LoopStart": UpdateWidth(); break; case "LoopEnd": UpdateWidth(); break; case "PlayPosition": PlayPositionChanged(); break; } } void Buzz_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) { switch (e.PropertyName) { case "ActiveView": if (Buzz.ActiveView != BuzzView.PatternView) ClearEditContext(); break; } } void Machine_PropertyChanged(object sender, PropertyChangedEventArgs e) { var m = sender as IMachine; if (e.PropertyName == "Patterns") { viewSettings.PatternAssociations.Remove(k => k.Machine == m && !m.Patterns.Contains(k)); } } void Buzz_PatternEditorActivated() { ClearEditContext(); } void AddAllSequences() { trackStack.Children.Clear(); trackHeaderStack.Children.Clear(); for (int i = 0; i < song.Sequences.Count; i++) AddSequence(i); TrackCountChanged(); } void AddSequence(int i) { trackStack.Children.Insert(i, new TrackControl(this) { ViewSettings = viewSettings, Height = viewSettings.TrackHeight, HorizontalAlignment = HorizontalAlignment.Left, Sequence = song.Sequences[i] }); trackHeaderStack.Children.Insert(i, new TrackHeaderControl(this) { ViewSettings = viewSettings, Height = viewSettings.TrackHeight, HorizontalAlignment = HorizontalAlignment.Left, Resources = this.Resources, Sequence = song.Sequences[i] }); } void song_SequenceAdded(int i) { AddSequence(i); TrackCountChanged(); } void song_SequenceRemoved(int i) { trackStack.Children.RemoveAt(i); (trackHeaderStack.Children[i] as TrackHeaderControl).Sequence = null; trackHeaderStack.Children.RemoveAt(i); TrackCountChanged(); } void song_SequenceChanged(int i) { (trackStack.Children[i] as TrackControl).Sequence = song.Sequences[i]; (trackHeaderStack.Children[i] as TrackHeaderControl).Sequence = song.Sequences[i]; } void TrackCountChanged() { viewSettings.TrackCount = trackStack.Children.Count; int tc = trackStack.Children.Count; tc = Math.Min(tc, (int)(SystemParameters.FullPrimaryScreenHeight / viewSettings.TrackHeight * 2 / 3)); // limit to 2/3 of screen height double mh = viewSettings.TrackHeight * tc + SystemParameters.HorizontalScrollBarHeight + 3; double th = mh + 20 + 4; resizeGrid.RowDefinitions[0].MaxHeight = th; if (resizeGrid.RowDefinitions[0].Height.Value != 0) // resize if view is not hidden by the user resizeGrid.RowDefinitions[0].Height = new GridLength(th); double vh = viewSettings.TrackHeight * trackStack.Children.Count + 1; trackViewGrid.Height = vh; trackHeaderStack.Height = vh; selectionLayer.KillSelection(); clipboard.Clear(); if (cursorElement.Row >= viewSettings.TrackCount) cursorElement.Row = viewSettings.TrackCount - 1; } void SetMarkerPosition(MarkerControl m, int p) { Canvas.SetLeft(m, p * viewSettings.TickWidth - Math.Floor(playPosMarker.ActualWidth / 2)); } void PlayPositionChanged() { SetMarkerPosition(playPosMarker, song.PlayPosition); } void UpdateWidth() { viewSettings.SongEnd = song.SongEnd; SetMarkerPosition(songEndMarker, song.SongEnd); SetMarkerPosition(loopStartMarker, song.LoopStart); loopStartMarker.Visibility = song.LoopStart != 0 ? Visibility.Visible : Visibility.Hidden; SetMarkerPosition(loopEndMarker, song.LoopEnd); int maxt = Math.Max(song.SongEnd, CursorRightTime); double w = viewSettings.TickWidth * maxt + 1; timeLineElement.Width = w; trackStack.Width = w; markerCanvas.Width = w; selectionLayer.UpdateVisual(); timeLineElement.InvalidateVisual(); } TrackHeaderControl SelectedTrackHeader { get { if (trackHeaderStack.Children.Count == 0 || cursorElement.Row < 0 || cursorElement.Row >= trackHeaderStack.Children.Count) return null; return trackHeaderStack.Children[cursorElement.Row] as TrackHeaderControl; } } ISequence SelectedSequence { get { return SelectedTrackHeader != null ? SelectedTrackHeader.Sequence : null; } } int CursorRow { get { return cursorElement.Row; } } int CursorTime { get { return cursorElement.Time; } } int CursorSpan { get { return viewSettings.TimeSignatureList.GetBarLengthAt(cursorElement.Time); } } int CursorRightTime { get { return CursorTime + CursorSpan; } } IPattern GetPatternAt(int time, int row) { if (row >= song.Sequences.Count) return null; return song.Sequences[row].Events.Where(e => time >= e.Key && time < e.Key + e.Value.Span).Select(e => e.Value.Pattern).FirstOrDefault(); } IPattern CursorPattern { get { return GetPatternAt(cursorElement.Time, cursorElement.Row); } } static ViewSettings viewSettings; public static ViewSettings ViewSettings { get { return viewSettings; } } public void SetVisibility(bool visible) { // only hide the grid that contains PatternElements to keep layout working trackViewGrid.Visibility = visible ? Visibility.Visible : Visibility.Hidden; } public void PlayCursor() { song.PlayPosition = CursorTime; song.Buzz.Playing = true; } public IBuzz Buzz { get; set; } public ResourceDictionary ResourceDictionary { get; private set; } SequenceClipboard clipboard = new SequenceClipboard(); void GeneralSettings_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { case "WPFIdealFontMetrics": PropertyChanged.Raise(this, "TextFormattingMode"); break; } } public static SequenceEditorSettings Settings = new SequenceEditorSettings(); public SequenceEditorSettings BindableSettings { get { return Settings; } } void Settings_PropertyChanged(object sender, PropertyChangedEventArgs e) { switch (e.PropertyName) { case "PatternBoxLook": PatternElement.InvalidateResources(); foreach (TrackControl tc in trackStack.Children) tc.EventsChanged(); break; case "PatternBoxColors": PatternElement.InvalidateResources(); foreach (TrackControl tc in trackStack.Children) tc.EventsChanged(); break; case "TimelineNumbers": timeLineElement.InvalidateVisual(); break; case "HideEditor": if (Settings.HideEditor) resizeGrid.RowDefinitions[0].Height = new GridLength(0); else if (resizeGrid.RowDefinitions[0].Height.Value == 0) resizeGrid.RowDefinitions[0].Height = new GridLength(resizeGrid.RowDefinitions[0].MaxHeight); break; } } public ICommand AddTrackCommand { get; private set; } public ICommand DeleteTrackCommand { get; private set; } public ICommand MoveTrackUpCommand { get; private set; } public ICommand MoveTrackDownCommand { get; private set; } public ICommand SetStartCommand { get; private set; } public ICommand SetEndCommand { get; private set; } public ICommand SetTimeSignatureCommand { get; private set; } public ICommand SelectPatternCommand { get; private set; } public ICommand InsertAllCommand { get; private set; } public ICommand DeleteAllCommand { get; private set; } public ICommand CutCommand { get; private set; } public ICommand CopyCommand { get; private set; } public ICommand PasteCommand { get; private set; } public ICommand UndoCommand { get; private set; } public ICommand RedoCommand { get; private set; } public ICommand SettingsCommand { get; private set; } public ICommand SetPatternColorCommand { get; private set; } public ICommand ExportTrackMIDICommand { get; private set; } public ICommand ExportSongMIDICommand { get; private set; } public SequenceEditor(IBuzz buzz, ResourceDictionary rd) { Buzz = buzz; Global.GeneralSettings.PropertyChanged += new PropertyChangedEventHandler(GeneralSettings_PropertyChanged); Settings.PropertyChanged += Settings_PropertyChanged; SettingsWindow.AddSettings("Sequence Editor", Settings); Buzz.OpenSong += Buzz_OpenSong; Buzz.SaveSong += Buzz_SaveSong; ResourceDictionary = rd; this.DataContext = this; if (rd != null) this.Resources.MergedDictionaries.Add(rd); InitializeComponent(); loopStartMarker.rectangle.Style = TryFindResource("LoopStartMarkerRectangleStyle") as Style; loopEndMarker.rectangle.Style = TryFindResource("LoopEndMarkerRectangleStyle") as Style; songEndMarker.rectangle.Style = TryFindResource("SongEndMarkerRectangleStyle") as Style; playPosMarker.rectangle.Style = TryFindResource("PlayPositionMarkerRectangleStyle") as Style; trackViewGrid.Visibility = Visibility.Collapsed; trackSV.ScrollChanged += (sender, e) => { timelineSV.ScrollToHorizontalOffset(e.HorizontalOffset); markerSV.ScrollToHorizontalOffset(e.HorizontalOffset); trackHeaderSV.ScrollToVerticalOffset(e.VerticalOffset); }; this.GotKeyboardFocus += (sender, e) => { Buzz.EditContext = viewSettings.EditContext; buzz.NewSequenceEditorActivated(); cursorElement.IsActive = true; e.Handled = true; }; this.LostKeyboardFocus += (sender, e) => { //Buzz.EditContext = null; cursorElement.IsActive = false; e.Handled = true; }; this.PreviewKeyDown += (sender, e) => { if (SelectedSequence == null) return; if (Keyboard.Modifiers == ModifierKeys.None || Keyboard.Modifiers == ModifierKeys.Shift) { if (e.Key == Key.Right) { MoveCursorDelta(1, 0, int.MaxValue, Keyboard.Modifiers == ModifierKeys.Shift); e.Handled = true; } else if (e.Key == Key.Left) { MoveCursorDelta(-1, 0, int.MaxValue, Keyboard.Modifiers == ModifierKeys.Shift); e.Handled = true; } else if (e.Key == Key.Down) { MoveCursorDelta(0, 1, int.MaxValue, Keyboard.Modifiers == ModifierKeys.Shift); e.Handled = true; } else if (e.Key == Key.Up) { MoveCursorDelta(0, -1, int.MaxValue, Keyboard.Modifiers == ModifierKeys.Shift); e.Handled = true; } else if (e.Key == Key.PageDown) { MoveCursorDelta(16, 0, int.MaxValue, Keyboard.Modifiers == ModifierKeys.Shift); e.Handled = true; } else if (e.Key == Key.PageUp) { MoveCursorDelta(-16, 0, int.MaxValue, Keyboard.Modifiers == ModifierKeys.Shift); e.Handled = true; } else if (e.Key == Key.Home) { if (CursorTime > 0) MoveCursorDelta(-int.MaxValue, 0, int.MaxValue, Keyboard.Modifiers == ModifierKeys.Shift); else MoveCursorDelta(0, -int.MaxValue, int.MaxValue, Keyboard.Modifiers == ModifierKeys.Shift); e.Handled = true; } else if (e.Key == Key.End) { if (CursorTime != viewSettings.LastCellTime) MoveCursorDelta(int.MaxValue, 0, song.SongEnd, Keyboard.Modifiers == ModifierKeys.Shift); else MoveCursorDelta(0, int.MaxValue, song.SongEnd, Keyboard.Modifiers == ModifierKeys.Shift); e.Handled = true; } } if (Keyboard.Modifiers == ModifierKeys.None) { if (e.Key == Key.Insert) { Do(new InsertOrDeleteAction(SelectedSequence, CursorTime, CursorSpan, true)); e.Handled = true; } else if (e.Key == Key.Delete) { Do(new InsertOrDeleteAction(SelectedSequence, CursorTime, CursorSpan, false)); e.Handled = true; } else if (e.Key == Key.Return) { if (SelectPatternCommand.CanExecute(null)) SelectPatternCommand.Execute(null); e.Handled = true; } else if (e.Key == Key.Back) { int oldct = CursorTime; MoveCursorDelta(-1, 0, int.MaxValue, false); if (CursorTime < oldct) Do(new ClearAction(SelectedSequence, CursorTime, CursorSpan)); e.Handled = true; } } else if (Keyboard.Modifiers == ModifierKeys.Shift) { if (e.Key == Key.Return) { song.Buzz.ActiveView = BuzzView.SequenceView; e.Handled = true; } } else if (Keyboard.Modifiers == ModifierKeys.Control) { if (e.Key == Key.M) { SelectedSequence.Machine.IsMuted ^= true; e.Handled = true; } else if (e.Key == Key.L) { SelectedSequence.Machine.IsSoloed ^= true; e.Handled = true; } else if (e.Key == Key.Return) { mainGrid.ContextMenu.DataContext = this; mainGrid.ContextMenu.IsOpen = true; e.Handled = true; } else if (e.Key == Key.Up) { if (MoveTrackUpCommand.CanExecute(null)) MoveTrackUpCommand.Execute(null); e.Handled = true; } else if (e.Key == Key.Down) { if (MoveTrackDownCommand.CanExecute(null)) MoveTrackDownCommand.Execute(null); e.Handled = true; } } }; this.PreviewTextInput += (sender, e) => { if (e.Text.Length == 1 && SelectedTrackHeader != null) { var p = SelectedTrackHeader.GetPatternByChar(e.Text[0]); if (p != null) { Do(new SetEventAction(SelectedSequence, CursorTime, new SequenceEvent(SequenceEventType.PlayPattern, p))); MoveCursor(CursorTime + CursorPattern.Length, CursorRow); } else if (e.Text[0] == ',') { Do(new SetEventAction(SelectedSequence, CursorTime, new SequenceEvent(SequenceEventType.Break))); MoveCursorDelta(1, 0, int.MaxValue, false); } else if (e.Text[0] == '-') { Do(new SetEventAction(SelectedSequence, CursorTime, new SequenceEvent(SequenceEventType.Mute))); MoveCursorDelta(1, 0, int.MaxValue, false); } else if (e.Text[0] == '_') { Do(new SetEventAction(SelectedSequence, CursorTime, new SequenceEvent(SequenceEventType.Thru))); MoveCursorDelta(1, 0, int.MaxValue, false); } else if (e.Text[0] == '.') { Do(new ClearAction(SelectedSequence, CursorTime, CursorSpan)); MoveCursorDelta(1, 0, int.MaxValue, false); } } }; AddTrackCommand = new SimpleCommand { CanExecuteDelegate = x => true, ExecuteDelegate = machine => { Do(new AddSequenceAction(machine as IMachine)); } }; DeleteTrackCommand = new SimpleCommand { CanExecuteDelegate = x => SelectedSequence != null, ExecuteDelegate = x => { //if (MessageBox.Show("Sure?", "Delete Track " + SelectedSequence.Machine.Name, MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) { Do(new DeleteSequenceAction(SelectedSequence)); MoveCursor(CursorTime, 0); } Focus(); } }; MoveTrackUpCommand = new SimpleCommand { CanExecuteDelegate = x => SelectedSequence != null && CursorRow > 0, ExecuteDelegate = x => { Do(new SwapSequencesAction(SelectedSequence, song.Sequences[CursorRow - 1])); MoveCursorDelta(0, -1, int.MaxValue, false); } }; MoveTrackDownCommand = new SimpleCommand { CanExecuteDelegate = x => song != null && SelectedSequence != null && CursorRow < song.Sequences.Count - 1, ExecuteDelegate = x => { Do(new SwapSequencesAction(SelectedSequence, song.Sequences[CursorRow + 1])); MoveCursorDelta(0, 1, int.MaxValue, false); } }; SetStartCommand = new SimpleCommand { CanExecuteDelegate = x => true, ExecuteDelegate = x => { Do(new SetMarkerAction(song, SongMarkers.LoopStart, CursorTime)); } }; SetEndCommand = new SimpleCommand { CanExecuteDelegate = x => true, ExecuteDelegate = x => { int t = CursorRightTime; using (new ActionGroup(viewSettings.EditContext.ActionStack)) { if (song.LoopEnd != t) Do(new SetMarkerAction(song, SongMarkers.LoopEnd, t)); else Do(new SetMarkerAction(song, SongMarkers.SongEnd, t)); } timeLineElement.InvalidateVisual(); } }; SetTimeSignatureCommand = new SimpleCommand { CanExecuteDelegate = x => CursorTime < viewSettings.SongEnd, ExecuteDelegate = x => { Point p = cursorCanvas.PointToScreen(new Point(Canvas.GetLeft(cursorElement), Canvas.GetTop(cursorElement))); StepEditWindow hw = new StepEditWindow(CursorSpan) { WindowStartupLocation = WindowStartupLocation.Manual, Left = p.X, Top = p.Y }; new WindowInteropHelper(hw).Owner = ((HwndSource)PresentationSource.FromVisual(this)).Handle; if ((bool)hw.ShowDialog()) { Do(new SetTimeSignatureAction(viewSettings.TimeSignatureList, CursorTime, hw.Step)); cursorElement.Update(); cursorElement.SetBlinkAnimation(true, false); } this.Focus(); } }; SelectPatternCommand = new SimpleCommand { CanExecuteDelegate = x => SelectedSequence != null, ExecuteDelegate = noswitch => { var p = CursorPattern; if (p != null) song.Buzz.SetPatternEditorPattern(p); else if (SelectedSequence != null) song.Buzz.SetPatternEditorMachine(SelectedSequence.Machine); if (noswitch == null || !(bool)noswitch) song.Buzz.ActivatePatternEditor(); else this.Focus(); } }; InsertAllCommand = new SimpleCommand { CanExecuteDelegate = x => SelectedSequence != null, ExecuteDelegate = x => { using (new ActionGroup(viewSettings.EditContext.ActionStack)) { Do(new TSLInsertOrDeleteAction(viewSettings.TimeSignatureList, CursorTime, CursorSpan, true)); foreach (var s in song.Sequences) Do(new InsertOrDeleteAction(s, CursorTime, CursorSpan, true)); if (song.SongEnd >= CursorRightTime) Do(new SetMarkerAction(song, SongMarkers.SongEnd, song.SongEnd + CursorSpan)); if (song.LoopEnd >= CursorRightTime) Do(new SetMarkerAction(song, SongMarkers.LoopEnd, song.LoopEnd + CursorSpan)); if (song.LoopStart >= CursorRightTime) Do(new SetMarkerAction(song, SongMarkers.LoopStart, song.LoopStart + CursorSpan)); } cursorElement.Update(); } }; DeleteAllCommand = new SimpleCommand { CanExecuteDelegate = x => SelectedSequence != null, ExecuteDelegate = x => { using (new ActionGroup(viewSettings.EditContext.ActionStack)) { Do(new TSLInsertOrDeleteAction(viewSettings.TimeSignatureList, CursorTime, CursorSpan, false)); foreach (var s in song.Sequences) Do(new InsertOrDeleteAction(s, CursorTime, CursorSpan, false)); if (song.LoopEnd > CursorRightTime) Do(new SetMarkerAction(song, SongMarkers.LoopEnd, song.LoopEnd - CursorSpan)); if (song.SongEnd > CursorRightTime) Do(new SetMarkerAction(song, SongMarkers.SongEnd, song.SongEnd - CursorSpan)); if (song.LoopStart > CursorRightTime) Do(new SetMarkerAction(song, SongMarkers.LoopStart, song.LoopStart - CursorSpan)); } cursorElement.Update(); } }; CutCommand = new SimpleCommand { CanExecuteDelegate = x => selectionLayer.SelectionNotEmpty, ExecuteDelegate = x => { Do(new CutOrCopySequenceEventsAction(song, selectionLayer.Rect, clipboard, true)); } }; CopyCommand = new SimpleCommand { CanExecuteDelegate = x => selectionLayer.SelectionNotEmpty, ExecuteDelegate = x => { Do(new CutOrCopySequenceEventsAction(song, selectionLayer.Rect, clipboard, false)); } }; PasteCommand = new SimpleCommand { CanExecuteDelegate = x => clipboard.ContainsData && CursorRow == clipboard.FirstTrack, ExecuteDelegate = x => { Do(new PasteSequenceEventsAction(song, CursorTime, clipboard)); } }; UndoCommand = new SimpleCommand { CanExecuteDelegate = x => viewSettings.EditContext.ActionStack.CanUndo, ExecuteDelegate = x => viewSettings.EditContext.ActionStack.Undo() }; RedoCommand = new SimpleCommand { CanExecuteDelegate = x => viewSettings.EditContext.ActionStack.CanRedo, ExecuteDelegate = x => viewSettings.EditContext.ActionStack.Redo() }; SettingsCommand = new SimpleCommand { CanExecuteDelegate = x => true, ExecuteDelegate = x => { SettingsWindow.Show(this, "Sequence Editor"); } }; SetPatternColorCommand = new SimpleCommand { CanExecuteDelegate = x => true, ExecuteDelegate = index => { if (CursorPattern == null) return; if (!viewSettings.PatternAssociations.ContainsKey(CursorPattern)) viewSettings.PatternAssociations[CursorPattern] = new PatternEx(); viewSettings.PatternAssociations[CursorPattern].ColorIndex = (int)index; foreach (TrackControl tc in trackStack.Children) tc.EventsChanged(); } }; ExportTrackMIDICommand = new SimpleCommand { CanExecuteDelegate = x => SelectedSequence != null, ExecuteDelegate = x => MIDIExporter.ExportMIDI(SelectedSequence) }; ExportSongMIDICommand = new SimpleCommand { CanExecuteDelegate = x => Song != null, ExecuteDelegate = x => MIDIExporter.ExportMIDI(Song) }; this.InputBindings.Add(new InputBinding(DeleteTrackCommand, new KeyGesture(Key.Delete, ModifierKeys.Control))); this.InputBindings.Add(new InputBinding(SetStartCommand, new KeyGesture(Key.B, ModifierKeys.Control))); this.InputBindings.Add(new InputBinding(SetEndCommand, new KeyGesture(Key.E, ModifierKeys.Control))); this.InputBindings.Add(new InputBinding(SetTimeSignatureCommand, new KeyGesture(Key.T, ModifierKeys.Control))); this.InputBindings.Add(new InputBinding(InsertAllCommand, new KeyGesture(Key.I, ModifierKeys.Control))); this.InputBindings.Add(new InputBinding(DeleteAllCommand, new KeyGesture(Key.D, ModifierKeys.Control))); this.InputBindings.Add(new InputBinding(CutCommand, new KeyGesture(Key.X, ModifierKeys.Control))); this.InputBindings.Add(new InputBinding(CopyCommand, new KeyGesture(Key.C, ModifierKeys.Control))); this.InputBindings.Add(new InputBinding(PasteCommand, new KeyGesture(Key.V, ModifierKeys.Control))); this.InputBindings.Add(new InputBinding(UndoCommand, new KeyGesture(Key.Z, ModifierKeys.Control))); this.InputBindings.Add(new InputBinding(RedoCommand, new KeyGesture(Key.Y, ModifierKeys.Control))); new Dragger { Element = trackViewGrid, Gesture = new DragMouseGesture { Button = MouseButton.Left }, Mode = DraggerMode.Absolute, BeginDrag = (p, alt, cc) => { trackViewGrid.Focus(); int row = GetRowAt(p.Y); int time = GetTimeAt(p.X); MoveCursor(time, row); selectionLayer.BeginSelect(new Point(time, row)); }, Drag = p => { int row = GetRowAt(p.Y); int time = GetTimeAt(p.X); selectionLayer.UpdateSelect(new Point(time, row)); }, EndDrag = p => { selectionLayer.EndSelect(); } }; trackViewGrid.MouseLeftButtonDown += (sender, e) => { if (e.ClickCount == 2) { if (CursorPattern == null) { using (new ActionGroup(viewSettings.EditContext.ActionStack)) { string pname = SelectedSequence.Machine.GetNewPatternName(); Do(new CreatePatternAction(SelectedSequence.Machine, pname, 16)); Do(new SetEventAction(SelectedSequence, CursorTime, new SequenceEvent(SequenceEventType.PlayPattern, SelectedSequence.Machine.Patterns.First(p => p.Name == pname)))); } } else if (!Settings.AutoSelectPattern) { if (SelectPatternCommand.CanExecute(null)) SelectPatternCommand.Execute(null); } e.Handled = true; } }; trackViewGrid.MouseRightButtonDown += (sender, e) => { var p = e.GetPosition(trackViewGrid); trackViewGrid.Focus(); int row = GetRowAt(p.Y); int time = GetTimeAt(p.X); MoveCursor(time, row); PropertyChanged.Raise(this, "EnableColorMenu"); }; this.SizeChanged += (sender, e) => { double mh = Math.Max(0, mainGrid.RowDefinitions[0].ActualHeight + mainGrid.RowDefinitions[1].ActualHeight - SystemParameters.HorizontalScrollBarHeight - 6); markerSV.Height = mh; songEndMarker.Height = mh; loopStartMarker.Height = mh; loopEndMarker.Height = mh; playPosMarker.Height = mh; double mw = Math.Max(0, mainGrid.ColumnDefinitions[1].ActualWidth - SystemParameters.VerticalScrollBarWidth - 10); markerSV.Width = mw; }; resizeGrid.SizeChanged += (sender, e) => { Settings.Set("HideEditor", resizeGrid.RowDefinitions[0].Height.Value == 0); Settings.Save(); }; if (Settings.HideEditor) resizeGrid.RowDefinitions[0].Height = new GridLength(0); zoomSlider.ValueChanged += (sender, e) => { UpdateZoomSlider(); }; timeLineElement.MouseLeftButtonDown += (sender, e) => { int t = (int)(e.GetPosition(timeLineElement).X / viewSettings.TickWidth); song.PlayPosition = viewSettings.TimeSignatureList.Snap(t, int.MaxValue); }; } public void Release() { Global.GeneralSettings.PropertyChanged -= GeneralSettings_PropertyChanged; Settings.PropertyChanged -= Settings_PropertyChanged; Buzz.OpenSong -= Buzz_OpenSong; Buzz.SaveSong -= Buzz_SaveSong; } int DataVersion = 2; void Buzz_OpenSong(IOpenSong os) { Stream s = os.GetSubSection("SequenceEditor"); if (s == null) return; var br = new BinaryReader(s); int ver = br.ReadInt32(); if (ver > DataVersion) return; zoomSlider.Value = br.ReadDouble(); viewSettings.TimeSignatureList = new TimeSignatureList(br); viewSettings.TimeSignatureList.Changed += TimeSignatureListChanged; if (ver >= 2) { int count = br.ReadInt32(); for (int i = 0; i < count; i++) { string mname = br.ReadString(); string pname = br.ReadString(); int ci = br.ReadInt32(); var mac = song.Machines.Where(m => m.Name == mname).FirstOrDefault(); if (mac != null) { var pat = mac.Patterns.FirstOrDefault(p => p.Name == pname); if (pat != null) viewSettings.PatternAssociations[pat] = new PatternEx() { ColorIndex = ci }; } } } UpdateZoomSlider(); timeLineElement.InvalidateVisual(); UpdateWidth(); } void Buzz_SaveSong(ISaveSong ss) { Stream s = ss.CreateSubSection("SequenceEditor"); var bw = new BinaryWriter(s); bw.Write(DataVersion); bw.Write(zoomSlider.Value); viewSettings.TimeSignatureList.Write(bw); bw.Write(viewSettings.PatternAssociations.Count); foreach (var pa in viewSettings.PatternAssociations) { bw.Write(pa.Key.Machine.Name); bw.Write(pa.Key.Name); bw.Write(pa.Value.ColorIndex); } } void UpdateZoomSlider() { ViewSettings.TickWidth = zoomSlider.Value; UpdateWidth(); foreach (TrackControl tc in trackStack.Children) tc.EventsChanged(); cursorElement.Update(); } void MoveCursor(int time, int row) { cursorElement.Row = Math.Min(row, trackHeaderStack.Children.Count - 1); cursorElement.Time = viewSettings.TimeSignatureList.Snap(time, int.MaxValue); UpdateWidth(); cursorElement.BringIntoView(); UpdateSelectedRow(); } void MoveCursorDelta(int dx, int dy, int maxx, bool select) { if (select) { if (!selectionLayer.Selecting) selectionLayer.BeginSelect(new Point(CursorTime, CursorRow)); } else { selectionLayer.KillSelection(); } cursorElement.Move(dx, dy, maxx); UpdateWidth(); UpdateSelectedRow(); if (select) selectionLayer.UpdateSelect(new Point(CursorTime, CursorRow)); } public void UpdateSelectedRow() { if (cursorElement.Row < 0 || cursorElement.Row >= trackHeaderStack.Children.Count) return; for (int i = 0; i < trackHeaderStack.Children.Count; i++) (trackHeaderStack.Children[i] as TrackHeaderControl).IsSelected = i == cursorElement.Row; patternListBox.SetBinding(ListBox.ItemsSourceProperty, new Binding("PatternList") { Source = SelectedTrackHeader }); var m = SelectedSequence.Machine; if (m != null && m.DLL.Info.Type == MachineType.Generator || m.IsControlMachine) Buzz.MIDIFocusMachine = m; if (Settings.AutoSelectPattern && SelectPatternCommand.CanExecute(null)) SelectPatternCommand.Execute(true); } int GetTimeAt(double x) { return Math.Max(0, (int)(x / viewSettings.TickWidth)); } int GetRowAt(double y) { return Math.Max(0, Math.Min(trackStack.Children.Count - 1, (int)(y / viewSettings.TrackHeight))); } internal void SelectRow(TrackHeaderControl tc) { MoveCursor(CursorTime, trackHeaderStack.Children.IndexOf(tc)); } public IEnumerable MachineList { get { return song.Machines.Select(m => new MenuItemVM() { Text = m.Name, Command = AddTrackCommand, CommandParameter = m }).OrderBy(m => m.Text); } } public double ZoomLevel { get { return viewSettings != null ? viewSettings.TickWidth : 2; } set { viewSettings.TickWidth = value; PropertyChanged.Raise(this, "ZoomLevel"); } } void Do(IAction a) { ViewSettings.EditContext.ActionStack.Do(a); } void TimeSignatureListChanged() { timeLineElement.InvalidateVisual(); } void ClearEditContext() { if (Buzz.EditContext == viewSettings.EditContext) { Buzz.EditContext = null; } } public class ColorMenuVM { public string Name { get; set; } public Brush Brush { get; set; } public ICommand Command { get; set; } public object CommandParameter { get; set; } public bool IsSeparator { get; set; } } List colorMenuItems; public IEnumerable ColorMenuItems { get { if (colorMenuItems == null) { colorMenuItems = new List(); if (PatternElement.PatternBrushes != null) { colorMenuItems = PatternElement.PatternBrushes.Select((b, index) => new ColorMenuVM() { Name = b.Item1, Brush = b.Item2.Brush, Command = SetPatternColorCommand, CommandParameter = index, }).Concat(new [] { new ColorMenuVM() { IsSeparator = true }, new ColorMenuVM() { Name = "Default", Command = SetPatternColorCommand, CommandParameter = -1 } }).ToList(); } } return colorMenuItems; } } public bool EnableColorMenu { get { return CursorPattern != null; } } #region INotifyPropertyChanged Members public event PropertyChangedEventHandler PropertyChanged; #endregion private void AddTrackButtonClick(object sender, RoutedEventArgs e) { addTrackButtonContextMenu.PlacementTarget = this; addTrackButtonContextMenu.IsOpen = true; } public TextFormattingMode TextFormattingMode { get { return Global.GeneralSettings.WPFIdealFontMetrics ? TextFormattingMode.Ideal : TextFormattingMode.Display; } } } }