public void GroupSelectedBlocks() { if (!SelectedBlocks.Any()) { return; } var relatedSegments = Enumerable.Select(SelectedBlocks, b => b.SegmentContext).Distinct().ToList(); MusicSegment segmentForGroup = (relatedSegments.Count == 1 ? relatedSegments.Single().GetModel() : model.MusicSegments[0]); var group = new LoopBlock(model); group.SegmentContext = segmentForGroup; group.StartTime = SelectedBlocks.Min(b => b.StartTime); foreach (var b in SelectedBlocks) { // create an independent copy of the block so transforming its time to local reference frame does not screw up undo Block newChild = Block.FromXML(model, b.GetModel().ToXML()); group.AddChild(newChild, true); } using (ActionManager.CreateTransaction()) { DeleteSelectedBlocks(); ActionManager.RecordAdd(model.Blocks, group); } SelectBlock(BlockViewModel.FromModel(this, group), CompositionMode.None); }
public void UngroupSelectedBlocks() { using (ActionManager.CreateTransaction()) { foreach (var groupVM in SelectedBlocks.OfType <GroupBlockViewModel>().ToArray()) { SelectedBlocks.Remove(groupVM); GroupBlock group = (GroupBlock)groupVM.GetModel(); ActionManager.RecordRemove(model.Blocks, group); foreach (Block b in group.Children.ToArray()) { // create an independent copy of the block so transforming its time back to global reference frame does not screw up undo Block independentBlock = Block.FromXML(model, b.ToXML()); independentBlock.StartTime += group.StartTime; ActionManager.RecordRemove(group.Children, b); ActionManager.RecordAdd(model.Blocks, independentBlock); SelectBlock(BlockViewModel.FromModel(this, independentBlock), CompositionMode.Additive); } } } }
public TrackViewModel(SequencerViewModel sequencer, Model.Track model) { this.sequencer = sequencer; this.model = model; ForwardPropertyEvents(nameof(model.Label), model, nameof(Label)); ForwardPropertyEvents(nameof(sequencer.SelectedTrack), sequencer, nameof(IsSelected)); Blocks = model.Blocks.Select(b => BlockViewModel.FromModel(sequencer, b)); }
// type may be null, then the target will be auto-inferred based on currently selected blocks public void ConvertSelectedBlocksTo(string type) { bool rampsToColors = (type == "color"); bool colorsToRamps = (type == "ramp"); if (!rampsToColors && !colorsToRamps) { colorsToRamps = SelectedBlocks.Any(b => b is ColorBlockViewModel); rampsToColors = SelectedBlocks.Any(b => b is RampBlockViewModel); if (colorsToRamps && rampsToColors) { throw new ArgumentException("type needs to be given when both color and ramp blocks are selected"); } } using (ActionManager.CreateTransaction()) { for (int i = 0; i < SelectedBlocks.Count; i++) { var block = SelectedBlocks[i].GetModel(); Block convertedBlock; if (rampsToColors && block is RampBlock rampBlock) { convertedBlock = new ColorBlock(model, rampBlock.Tracks.ToArray()) { Color = (rampBlock.StartColor == GloColor.Black ? rampBlock.EndColor : rampBlock.StartColor) }; } else if (colorsToRamps && block is ColorBlock colorBlock) { convertedBlock = new RampBlock(model, colorBlock.Tracks.ToArray()) { StartColor = colorBlock.Color, EndColor = (colorBlock.Color == GloColor.White ? GloColor.Black : GloColor.White) }; } else { continue; } // copy generic properties convertedBlock.StartTime = block.StartTime; convertedBlock.Duration = block.Duration; convertedBlock.SegmentContext = block.SegmentContext; ActionManager.RecordReplace(model.Blocks, block, convertedBlock); SelectedBlocks[i] = BlockViewModel.FromModel(this, convertedBlock); } } }
public void SplitBlocksAtCursor() { var blocksUnderCursor = SelectedBlocks .AsEnumerable() .Where(bvm => bvm.StartTime <CursorPosition && bvm.EndTime> CursorPosition) .Select(bvm => bvm.GetModel()) .ToList(); using (ActionManager.CreateTransaction(false)) { foreach (var block in blocksUnderCursor) { // Generate 2 exact copies of the block. XElement serializedBlock = block.ToXML(); var newBlockLeft = Block.FromXML(model, serializedBlock); var newBlockRight = Block.FromXML(model, serializedBlock); // Adjust ramps if necessary. if (block is RampBlock ramp) { GloColor splitColor = ramp.GetColorAtTime(CursorPosition, ramp.Tracks[0]); ((RampBlock)newBlockLeft).EndColor = splitColor; ((RampBlock)newBlockRight).StartColor = splitColor; } else if (block is LoopBlock) { // TODO: Loops are unsupported when splitting. continue; } // Generically adjust times. newBlockLeft.Duration = CursorPosition - block.StartTime; newBlockRight.StartTime = CursorPosition; newBlockRight.Duration = block.Duration - (CursorPosition - block.StartTime); // Replace in collections. int index = model.Blocks.IndexOf(block); ActionManager.RecordReplace(model.Blocks, index, newBlockLeft); ActionManager.RecordInsert(model.Blocks, index + 1, newBlockRight); SelectBlock(BlockViewModel.FromModel(this, block), CompositionMode.Subtractive); SelectBlock(BlockViewModel.FromModel(this, newBlockLeft), CompositionMode.Additive); SelectBlock(BlockViewModel.FromModel(this, newBlockRight), CompositionMode.Additive); } } }
public void InsertBlock(string type) { // inherit color and tracks from previous block, if applicable GloColor prevColor = GloColor.White; Track[] prevTracks = { _selectedTrack.GetModel() }; if (EnableSmartInsert) { Func <BlockViewModel, bool> fnIsBlockApplicable = (bl => bl.StartTime < CursorPosition && (bl is ColorBlockViewModel || bl is RampBlockViewModel)); var prevBlocks = ((IEnumerable <BlockViewModel>)_selectedTrack.Blocks).Where(fnIsBlockApplicable); if (prevBlocks.Any()) { BlockViewModel prevBlock = prevBlocks.MaxBy(bl => bl.EndTimeOccupied); // inherit color if (prevBlock is ColorBlockViewModel) { prevColor = ((ColorBlockViewModel)prevBlock).GetModel().Color; } else { prevColor = ((RampBlockViewModel)prevBlock).GetModel().EndColor; } // inherit tracks, but only if the last block on the selected track is also the last block on all other tracks of the block bool lastOfAllTracks = prevBlock.GetModel().Tracks.All(t => t.Blocks .Select(bl => BlockViewModel.FromModel(this, bl)) .Where(fnIsBlockApplicable) .MaxBy(bl => bl.EndTimeOccupied) == prevBlock); if (lastOfAllTracks) { prevTracks = prevBlock.GetModel().Tracks.ToArray(); } } } Block b; switch (type) { case "color": b = new ColorBlock(model, prevTracks) { Color = prevColor }; break; case "ramp": b = new RampBlock(model, prevTracks) { StartColor = prevColor, EndColor = (prevColor == GloColor.White ? GloColor.Black : GloColor.White) }; break; default: throw new ArgumentException("unsupported block type " + type); } b.SegmentContext = ActiveMusicSegment.GetModel(); b.StartTime = CursorPosition; b.Duration = GridInterval; ActionManager.RecordAdd(model.Blocks, b); }
public SequencerViewModel(Timeline model) { this.model = model; ActionManager = new GuiLabs.Undo.ActionManager(); SelectedBlocks = new ObservableCollection <BlockViewModel>(); SelectionData = new SelectionProperties(this); Tracks = model.Tracks.Select(g => new TrackViewModel(this, g)); MusicSegments = model.MusicSegments.Select(seg => new MusicSegmentViewModel(this, seg)); AllBlocks = model.Blocks.Select(b => BlockViewModel.FromModel(this, b)); if (Tracks.Count > 0) { SelectedTrack = Tracks[0]; } ActiveMusicSegment = MusicSegments[model.DefaultMusicSegment.GetIndex()]; Playback = new PlaybackViewModel(this); Visualization = new VisualizationViewModel(this); Notes = new NotesViewModel(this); if (model.MusicFileName != null) { Playback.LoadFileAsync(model.MusicFileName).Forget(); } Action <BlockViewModel> fn_SubscribeToBlock = bvm => ForwardPropertyEvents(nameof(bvm.EndTimeOccupied), bvm, nameof(TimelineLength)); AllBlocks.ToList().ForEach(fn_SubscribeToBlock); AllBlocks.CollectionChanged += (_, e) => { if (e.NewItems != null) { e.NewItems.Cast <BlockViewModel>().ToList().ForEach(fn_SubscribeToBlock); } Notify(nameof(TimelineLength)); }; ForwardPropertyEvents(nameof(PipetteTarget), this, nameof(IsPipetteActive)); ForwardPropertyEvents(nameof(CursorPosition), this, nameof(CursorPixelPosition), nameof(CursorPixelPositionOnViewport)); ForwardPropertyEvents(nameof(TimePixelScale), this, nameof(CursorPixelPosition), nameof(CursorPixelPositionOnViewport), nameof(CurrentViewLeftPositionTime), nameof(CurrentViewRightPositionTime), nameof(TimelineWidth), nameof(GridInterval)); ForwardPropertyEvents(nameof(ActiveMusicSegment), this, nameof(GridInterval)); ForwardPropertyEvents(nameof(Playback.MusicDuration), Playback, nameof(TimelineLength)); ForwardPropertyEvents(nameof(TimelineLength), this, nameof(TimelineWidth)); ForwardCollectionEvents(SelectedBlocks, nameof(CanConvertToColor), nameof(CanConvertToRamp), nameof(CanConvertToAutoDeduced), nameof(ConvertAutoDeduceGestureText)); Tracks.CollectionChanged += (_, e) => { foreach (var b in AllBlocks) { b.OnTracksCollectionChanged(); } }; // Disable pipette whenever the selection is modified. SelectedBlocks.CollectionChanged += (_, __) => PipetteTarget = null; }
protected GroupBlockViewModel(SequencerViewModel sequencer, Model.GroupBlock model, string typeLabel) : base(sequencer, model, typeLabel) { this.model = model; _children = model.Children.Select(b => BlockViewModel.FromModel(sequencer, b)); }