275 lines
No EOL
8.1 KiB
C#
275 lines
No EOL
8.1 KiB
C#
using JD.Core.Collections;
|
|
using JD.Timeline.View.ClipView;
|
|
using JD.Timeline.View.ClipView.Widgets;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.Linq;
|
|
using JD.Application.Utils;
|
|
using JD.Collections.Extensions;
|
|
using JD.Kernel.Classes;
|
|
using JD.Sequencers.Times;
|
|
using TeaBox;
|
|
using JD.Serialization;
|
|
|
|
namespace JD.Timeline.View.DataBinding
|
|
{
|
|
/// <summary>
|
|
/// Responsible of updating the view when the tape content changes.
|
|
/// </summary>
|
|
public class TapeViewReplicator : IDisposable
|
|
{
|
|
#region Variables
|
|
|
|
private readonly Tape _tape;
|
|
private readonly TrackView _trackView;
|
|
private readonly Dictionary<Clip, ClipWidget> _clipToWidgetDictionary = new Dictionary<Clip, ClipWidget>();
|
|
private readonly Dictionary<TapeTrack, TapeTrackWidget> _trackToWidgetDictionary = new Dictionary<TapeTrack, TapeTrackWidget>();
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
public TapeViewReplicator(Tape tape, TrackView trackView)
|
|
{
|
|
_tape = tape;
|
|
_trackView = trackView;
|
|
_tape.UpdateOperation().EndScope += TapeViewReplicator_EndScope;
|
|
_tape.Clips.CollectionChanged += clips_CollectionChanged;
|
|
_tape.Clips.ItemChanged += clips_ItemChanged;
|
|
_tape.Tracks.CollectionChanged += tracks_CollectionChanged;
|
|
_tape.EditionContextChanged += tape_EditionContextChanged;
|
|
Refresh();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_tape.UpdateOperation().EndScope -= TapeViewReplicator_EndScope;
|
|
_tape.Clips.CollectionChanged -= clips_CollectionChanged;
|
|
_tape.Clips.ItemChanged -= clips_ItemChanged;
|
|
_tape.Tracks.CollectionChanged -= tracks_CollectionChanged;
|
|
_tape.EditionContextChanged -= tape_EditionContextChanged;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
public ClipWidget this[Clip clip]
|
|
{
|
|
get
|
|
{
|
|
ClipWidget result;
|
|
_clipToWidgetDictionary.TryGetValue(clip, out result);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
public TapeTrackWidget this[TapeTrack track]
|
|
{
|
|
get
|
|
{
|
|
if (track != null)
|
|
{
|
|
TapeTrackWidget result;
|
|
_trackToWidgetDictionary.TryGetValue(track, out result);
|
|
return result;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
/// <summary>
|
|
/// A full refresh of the view.
|
|
/// </summary>
|
|
public void Refresh()
|
|
{
|
|
using (KeepSelection())
|
|
{
|
|
_trackToWidgetDictionary.Keys.ToArray().ForEach(UnregisterTrack);
|
|
_trackToWidgetDictionary.Clear();
|
|
_clipToWidgetDictionary.Clear();
|
|
|
|
_tape.Tracks.ForEach(RegisterTrack);
|
|
_tape.Clips.ForEach(Register);
|
|
}
|
|
}
|
|
|
|
private IDisposable KeepSelection()
|
|
{
|
|
var selection = _clipToWidgetDictionary.Values.Where(widget => widget.Selected).Select(widget => widget.Model.Id).ToSet();
|
|
return new DisposableHelper(() =>
|
|
{
|
|
var newSelection = _clipToWidgetDictionary.Values.Where(widget => selection.Contains(widget.Model.Id));
|
|
_trackView.Selection.Select(newSelection);
|
|
});
|
|
}
|
|
|
|
private void Register(Clip clip)
|
|
{
|
|
var view = clip.CreateClipWidget();
|
|
_clipToWidgetDictionary.Add(clip, view);
|
|
view.Update();
|
|
var track = GetTrack(clip);
|
|
track?.Children.Add(view);
|
|
}
|
|
|
|
private void Unregister(Clip clip)
|
|
{
|
|
var view = _clipToWidgetDictionary[clip];
|
|
_clipToWidgetDictionary.Remove(clip);
|
|
view.Dispose();
|
|
var track = GetTrack(clip);
|
|
track?.Children.Remove(view);
|
|
}
|
|
|
|
private void RegisterTrack(TapeTrack track)
|
|
{
|
|
var view = track.CreateTapeTrackWidget();
|
|
_trackToWidgetDictionary.Add(track, view);
|
|
_trackView.AddTrack(view);
|
|
_trackView.Invalidate();
|
|
}
|
|
|
|
private void UnregisterTrack(TapeTrack track)
|
|
{
|
|
var view = _trackToWidgetDictionary[track] ;
|
|
_trackView.RemoveTrack(view);
|
|
_trackToWidgetDictionary.Remove(track);
|
|
view.Dispose();
|
|
_trackView.Invalidate();
|
|
}
|
|
|
|
private TapeTrackWidget GetTrack(Clip clip)
|
|
{
|
|
var track = _tape.Tracks.FirstOrDefault(x => x.Id == clip.TrackId);
|
|
return track == null ? null : _trackToWidgetDictionary[track];
|
|
}
|
|
|
|
private void UpdateClip(Clip clip)
|
|
{
|
|
if (!_clipToWidgetDictionary.ContainsKey(clip))
|
|
return;
|
|
|
|
var trackItem = _clipToWidgetDictionary[clip];
|
|
if (trackItem == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var expectedTrack = GetTrack(clip);
|
|
if (expectedTrack == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (trackItem.Track == null)
|
|
{
|
|
Console.WriteLine(trackItem + " On a null track");
|
|
return;
|
|
}
|
|
|
|
if (trackItem.Track != expectedTrack)
|
|
{
|
|
// Removing the item from its container removes the selection.
|
|
// here we keep the selection since we are changing track.
|
|
bool wasSelected = trackItem.Selected;
|
|
|
|
trackItem.Invalidate();
|
|
trackItem.Track.Children.Remove(trackItem);
|
|
|
|
expectedTrack.Children.Add(trackItem);
|
|
|
|
if (wasSelected)
|
|
{
|
|
trackItem.Track.TrackView.Selection.Add(trackItem);
|
|
}
|
|
}
|
|
|
|
trackItem.Update();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Handlers
|
|
|
|
void tape_EditionContextChanged(object sender, EventArgs e)
|
|
{
|
|
Refresh();
|
|
}
|
|
|
|
private void clips_ItemChanged(object sender, ItemChangedEventArgs<Clip> args)
|
|
{
|
|
if (_tape.UpdateOperation().InScope)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
UpdateClip(args.Item);
|
|
}
|
|
catch (Exception)
|
|
{
|
|
MessageService.MessageService.ShowError("Error while trynig to update clip " + args.Item);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Each time the source collection is modified, the corresponding items are added and removed.
|
|
/// </summary>
|
|
private void clips_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
{
|
|
if (_tape.UpdateOperation().InScope)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (e.Action == NotifyCollectionChangedAction.Reset)
|
|
{
|
|
Refresh();
|
|
}
|
|
else
|
|
{
|
|
e.OldItems<Clip>().ForEach(Unregister);
|
|
e.NewItems<Clip>().ForEach(Register);
|
|
}
|
|
}
|
|
|
|
private void tracks_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
{
|
|
if (_tape.UpdateOperation().InScope)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//Reorder
|
|
if (e.Action == NotifyCollectionChangedAction.Reset || e.Action == NotifyCollectionChangedAction.Move || e.Action == NotifyCollectionChangedAction.Add)
|
|
{
|
|
Refresh();
|
|
}
|
|
else
|
|
{
|
|
foreach (TapeTrack track in e.OldItems<TapeTrack>())
|
|
{
|
|
UnregisterTrack(track);
|
|
}
|
|
foreach (TapeTrack track in e.NewItems<TapeTrack>())
|
|
{
|
|
RegisterTrack(track);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TapeViewReplicator_EndScope(object sender, EventArgs e)
|
|
{
|
|
Refresh();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |