JD2022-TU1/main/tools/framework/JD.Timeline/View/TrackTreeView/TrackTreeView.cs

428 lines
14 KiB
C#

using Divelements.SandGrid;
using JD.Timeline.Document;
using JD.Timeline.Model;
using JD.Timeline.View.ClipView;
using JD.Timeline.View.ScoreView;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using JD.Collections.Extensions;
using JD.Collections.Interfaces;
using JD.Controls;
using JD.Kernel.Classes;
using JD.Kernel.StaticHelpers;
using JD.Kernel.Structs;
using JD.Sequencers.Controls;
using JD.Sequencers.Times;
using TeaBox;
using TeaBox.Actions;
using TeaBox.Gui;
using JD.Serialization;
namespace JD.Timeline.View.TrackTreeView
{
public class TrackTreeView : UIHierarchy
{
#region Variables
private readonly TD.SandBar.ToolBar _toolbar;
private readonly Tape _tape;
private readonly FixedTracksTreeView _fixedTracksTreeView;
private readonly TrackView _trackView;
private readonly RefreshTask _updateTracksTask;
private readonly Scope _isUpdatingSelection = new Scope();
#endregion
#region Constructors
public TrackTreeView(Tape tape, TrackView trackView)
{
Throw.IfNull(tape, "tape");
Throw.IfNull(trackView, "trackView");
_tape = tape;
_trackView = trackView;
_updateTracksTask = new RefreshTask(UpdateTracks);
MainColumn.AutoSize = ColumnAutoSizeMode.Spring;
Grid.ShowTreeLines = false;
Grid.ShowTreeButtons = true;
Grid.BackColor = StyleSheet.BackgroundColor;
Grid.HideVerticalScrollbar = true;
Grid.HideScrollbars = true;
Grid.RowHighlightType = RowHighlightType.Full;
Grid.Paint += Grid_Paint;
Grid.VerticalScrollOverflow = ScrollOverflowBehavior.AlwaysVisible;
AllowSorting = false;
_fixedTracksTreeView = new FixedTracksTreeView {Dock = DockStyle.Top};
Controls.Add(_fixedTracksTreeView);
_toolbar = TeaBox.ObjectModel.Application.Main.Actions.CreateToolBar("TrackTreeView Toolbar", "Application/TreeViewToolbar/TrackTreeView", this);
Controls.Add(_toolbar);
TeaBox.ObjectModel.Application.Main.Actions.CreateContextMenu("Application/TrackTreeView/ContextMenu", this).AssignOnControl(this);
_trackView.TrackSizeChanged += _trackView_TrackSizeChanged;
_trackView.ScrollValueChanged += _trackView_ScrollValueChanged;
_trackView.CollectionChanged += _updateTracksTask.ModelChanged;
_trackView.MouseDown += trackView_MouseDown;
UpdateTracks();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_updateTracksTask.Dispose();
_trackView.TrackSizeChanged -= _trackView_TrackSizeChanged;
_trackView.ScrollValueChanged -= _trackView_ScrollValueChanged;
_trackView.CollectionChanged -= _updateTracksTask.ModelChanged;
_trackView.MouseDown -= trackView_MouseDown;
_fixedTracksTreeView.Items.OfType<TrackTreeItem>().ForEach(x => x.Dispose());
Items.OfType<TrackTreeItem>().ForEach(x => x.Dispose());
}
base.Dispose(disposing);
}
#endregion
#region Properties
public override bool UseVirtualRowTechnique => true;
private IEnumerable<Track> FixedTracks
{
get { return _trackView.GetTracks().Where(track => track.Fixed); }
}
public ClipTrackTreeItem this[TapeTrackWidget track]
{
get
{
if (track != null)
return (ClipTrackTreeItem)(Items.Where(item =>
((ClipTrackTreeItem)item).Track == track).First());
else
return null;
}
}
#endregion
#region Methods
protected override void OnSelectedItemsChanged(EventArgs e)
{
if (!IsUpdating && !_isUpdatingSelection.InScope)
{
using (_trackView.Selection.BeginScopedUpdate())
{
var tracks = SelectedItems.OfType<TrackTreeItem>();
if (tracks.Any())
{
var trackSelected = tracks.First();
if (trackSelected.SelectItemsOnClick)
{
_trackView.Selection.Clear();
tracks.ForEach(track => _trackView.Selection.AddRange(track.Track.Children));
}
_trackView.ActiveTrack = tracks.First().Track;
}
}
}
base.OnSelectedItemsChanged(e);
}
private void UpdateTracks()
{
UpdateFixedPanel();
using (_isUpdatingSelection.Begin())
using (KeepTreeState())
using (BeginUpdate())
{
_fixedTracksTreeView.Items.OfType<TrackTreeItem>().ForEach(x => x.Dispose());
_fixedTracksTreeView.Items.Clear();
Items.OfType<TrackTreeItem>().ForEach(x => x.Dispose());
Items.Clear();
// Maps the row to its track so that we can build the track hierarchy
var newRowsByTrack = new Dictionary<object, TrackTreeItem>();
foreach (var track in _trackView.GetTracks())
{
var item = TrackTreeItem.FromTrack(track);
newRowsByTrack.Add(item.Key, item);
}
foreach (var treeViewItem in newRowsByTrack.Values)
{
var parentCollection = !treeViewItem.Track.Fixed ? Items : _fixedTracksTreeView.Items;
TrackTreeItem parentItem;
if (treeViewItem.ParentGroupKey != null && newRowsByTrack.TryGetValue(treeViewItem.ParentGroupKey, out parentItem))
{
parentCollection = parentItem.Items;
}
parentCollection.Add(treeViewItem);
}
}
// Refresh the height after the creation of the grid, because the height
// is not considered until the grid is fully built.
RefreshRowHeights();
UpdateScoreTreeItems();
}
private void UpdateScoreTreeItems()
{
Items.OfType<ScoreTrackTreeItem>().ForEach(scoreTrackTreeItem =>
{
var view = TeaBox.ObjectModel.Application.Main.Views.SelectedItem as DanceTapeDocumentView;
if (view == null) return;
ScoreTrack scoreTrack = scoreTrackTreeItem.TrackView.Track as ScoreTrack;
if (scoreTrack == null)
return;
scoreTrackTreeItem.UnregisterItem();
if(view.ScoreViewManager.CurrentScoreViewState != ScoreViewState.AverageTrackOnly /*|| (scoreTrack.Name == scoreTrack.ReferenceTrack.Name + " - Average Score")*/)
{
var referencedTapeTrackWidget = view.ClipView[scoreTrack.ReferenceTrack];
if (this[referencedTapeTrackWidget] != null)
this[referencedTapeTrackWidget].Items.Add(scoreTrackTreeItem);
}
});
}
private IDisposable KeepTreeState()
{
var memento = new Memento(this);
return new DisposableHelper(() => memento.Apply(this));
}
private void UpdateFixedPanel()
{
int fixedHeaderHeight = FixedTracks.Sum(track => track.Bounds.Height);
fixedHeaderHeight += _trackView.Bounds.Top - _toolbar.Height - 1;
if (_fixedTracksTreeView.Height != fixedHeaderHeight)
{
_fixedTracksTreeView.Height = fixedHeaderHeight;
_fixedTracksTreeView.FixedHeaderHeight = _trackView.Bounds.Top - _toolbar.Height - 1;
}
}
#endregion
#region Event Handlers
void Grid_Paint(object sender, PaintEventArgs e)
{
if (Grid.VScrollOffset != GetScrollValueInPixels())
{
RefreshScrollbars();
}
}
private int GetScrollValueInPixels()
{
return _trackView.GetTracks()
.Where(track => !track.Fixed)
.Take(_trackView.ScrollValue).Sum(track => track.Height);
}
/// <summary>
/// Synchronizes the scroll bar of the treeview with the scrollbar of the clip view.
/// </summary>
private void RefreshScrollbars()
{
RefreshRowHeights();
// Find the best scroll value for the clipview using the current treeview scroll value.
int y = 0;
int index = 0;
foreach (var item in AllVisibleItems)
{
if (new Range<int>(y, y + item.Height - 1).Contains(Grid.VScrollOffset))
{
_trackView.ScrollValue = index;
Grid.VScrollOffset = GetScrollValueInPixels();
break;
}
index++;
y += item.Height;
}
}
/// <summary>
/// This forces a resynchronization between the grid and the tree view.
/// </summary>
private void RefreshRowHeights()
{
foreach (var item in AllVisibleItems.OfType<ClipTrackTreeItem>())
{
item.Height = item.TrackView.Height;
}
}
private void _trackView_ScrollValueChanged(object sender, EventArgs e)
{
Grid.VScrollOffset = _trackView.ScrollValueInPixel;
}
void _trackView_TrackSizeChanged(object sender, EventArgs e)
{
Grid.Invalidate();
UpdateFixedPanel();
}
void trackView_MouseDown(object sender, MouseEventArgs e)
{
using (_isUpdatingSelection.Begin())
{
ClearSelection();
Invalidate();
}
}
private static string ToString<T>(T value)
{
return value.ToString();
}
#endregion
#region Action Handlers
[ActionUpdateHandler(EditActions.Delete)]
private void UpdateDelete(ActionInfo info)
{
info.Enabled = SelectedItems.OfType<ClipTrackTreeItem>().Any(item => item.TrackView.CanDelete);
}
[ActionHandler(EditActions.Delete)]
private void HandleDelete()
{
var selectedEventTracks = SelectedItems.OfType<ClipTrackTreeItem>().Where(item => item.TrackView.CanDelete).Select(item => item.TrackView.Track);
foreach (var track in selectedEventTracks)
{
_tape.RemoveTrackWithClips(track);
}
}
[ActionHandler(TimelineActions.AddTrack)]
[ActionHandler(TimelineActions.InsertTrack)]
private void HandleInsertTrack()
{
if (SelectedItems == null || SelectedItems.IsEmpty())
{
_tape.CreateTrack();
return;
}
int index = SelectedItems.Select(item => item.AbsoluteRowIndex).Min();
_tape.InsertTrack(index > 0 ? index : 0);
}
#endregion
#region Nested
private class Memento : UIHierarchyMemento
{
public Memento(UIHierarchy hierarchy)
: base(hierarchy)
{
}
protected override string GetItemKey(UIHierarchyItem item)
{
var row = item as ClipTrackTreeItem;
return row == null ? item.Text : row.TrackView.Track.Id.ToString();
}
}
internal class TrackTreeDataObject
{
private readonly uint[] _trackIds;
public TrackTreeDataObject(IEnumerable<uint> trackIds)
{
_trackIds = trackIds.ToArray();
}
public uint[] TrackIds
{
get { return _trackIds; }
}
}
private class FixedTracksTreeView : UIHierarchy
{
private readonly Panel _fixedHeader;
public FixedTracksTreeView()
{
MainColumn.AutoSize = ColumnAutoSizeMode.Spring;
Grid.ShowTreeLines = false;
Grid.ShowTreeButtons = true;
Grid.BackColor = StyleSheet.BackgroundColor;
Grid.HideVerticalScrollbar = true;
Grid.HideScrollbars = true;
Grid.RowHighlightType = RowHighlightType.Full;
Grid.VerticalScrollOverflow = ScrollOverflowBehavior.AlwaysVisible;
AllowSorting = false;
_fixedHeader = new UserDrawnPanel();
_fixedHeader.Dock = DockStyle.Top;
_fixedHeader.Paint += _fixedHeader_Paint;
Controls.Add(_fixedHeader);
}
public override bool UseVirtualRowTechnique
{
get { return true; }
}
void _fixedHeader_Paint(object sender, PaintEventArgs e)
{
var rect = new Rectangle(Point.Empty, _fixedHeader.Size);
using (var brush = StyleSheet.GetTrackTreeBrush(rect, isAlternate: false, isSelected: false))
using (var textBrush = new SolidBrush(StyleSheet.TrackForeColor))
using (var stringFormat = new StringFormat { Trimming = StringTrimming.EllipsisPath, Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center })
{
e.Graphics.FillRectangle(brush, rect);
}
}
public int FixedHeaderHeight { get { return _fixedHeader.Height; } set { _fixedHeader.Height = value; } }
private class UserDrawnPanel : Panel
{
public UserDrawnPanel()
{
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
}
}
}
#endregion
}
}