JD2022-TU1/main/tools/framework/JD.Sequencers/Controls/Track.cs

295 lines
7.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using JD.Collections;
using JD.Collections.Interfaces;
using JD.Kernel.StaticHelpers;
using JD.Sequencers.Times;
using JD.Sequencers.Windowlesses;
namespace JD.Sequencers.Controls
{
/// <summary>
/// A single track in the track view.
/// </summary>
public class Track : WindowlessControl
{
#region Variables
private readonly TrackItemCollection m_children;
private TrackView m_trackView;
private System.Drawing.Color m_color = SystemColors.AppWorkspace;
#endregion
#region Constructor
public Track()
{
m_children = new TrackItemCollection(Controls);
m_children.CollectionChanged += c_children_CollectionChanged;
}
#endregion
#region Properties
public bool IsActiveTrack
{
get { return TrackView != null && TrackView.ActiveTrack == this; }
}
public System.Drawing.Color Color
{
get { return m_color; }
set
{
if (m_color != value)
{
m_color = value;
Invalidate();
}
}
}
public IObservableList<TrackItem> Children
{
get { return m_children; }
}
public TrackView TrackView
{
get { return m_trackView; }
internal set
{
Throw.InvalidOperationIf(m_trackView != null && value != null, "Track is already owned by another track view");
m_trackView = value;
}
}
public Timeline Timeline
{
get { return TrackView != null ? TrackView.Timeline : null; }
}
/// <summary>
/// True if the track is always on top and does not scroll.
/// </summary>
public bool Fixed
{
get;
set;
}
#endregion
#region Methods
public virtual void Initialize()
{
}
#region Painting
protected override void OnPaintSelf(System.Windows.Forms.PaintEventArgs e)
{
base.OnPaintSelf(e);
OnPaintBackground(e);
OnPaintBorder(e);
}
protected virtual void OnPaintBackground(System.Windows.Forms.PaintEventArgs e)
{
using (Brush backgroundBrush = new SolidBrush(IsActiveTrack ? System.Drawing.Color.FromArgb(50, SystemColors.Highlight) : Color))
{
e.Graphics.FillRectangle(backgroundBrush, Bounds);
}
}
protected virtual void OnPaintBorder(System.Windows.Forms.PaintEventArgs e)
{
using (var pen = new Pen(System.Drawing.Color.FromArgb(50, 0, 0, 0)))
{
e.Graphics.DrawLine(pen, Bounds.Left, Bounds.Bottom - 1, Bounds.Right, Bounds.Bottom - 1);
}
}
#endregion
#region Layout
/// <summary>
/// Allows to automatically resize child bounds when painting
/// </summary>
public virtual void ComputeChildBounds(Graphics graphics)
{
}
/// <summary>
/// Called after all track size have been computed, allows to bake final position in track items.
/// </summary>
public virtual void FinalizeChildBounds(Graphics graphics)
{
}
private void RefreshChildrenBounds()
{
foreach (TrackItem item in Children)
{
RefreshTrackItemBounds(item);
}
}
private void RefreshTrackItemBounds(TrackItem item)
{
if (TrackView != null)
{
AlignTrackItemBounds(item);
}
else
{
item.Height = 0;
item.Location = new Point(item.Location.X, Bounds.Top);
}
}
protected virtual void AlignTrackItemBounds(TrackItem item)
{
switch (TrackView.TrackItemAlignment)
{
case TrackView.VerticalAlignment.Bottom:
item.Height = Height - TrackView.InnerTrackSpacing;
item.Location = new Point(item.Location.X, Bounds.Top + TrackView.InnerTrackSpacing);
break;
case TrackView.VerticalAlignment.Middle:
item.Height = Height - TrackView.InnerTrackSpacing - 1;
item.Location = new Point(item.Location.X, Bounds.Top + TrackView.InnerTrackSpacing / 2);
break;
case TrackView.VerticalAlignment.Top:
item.Height = Height - TrackView.InnerTrackSpacing;
item.Location = new Point(item.Location.X, Bounds.Top);
break;
default:
throw new NotImplementedException();
}
}
protected override void OnBoundsChanged(EventArgs e)
{
base.OnBoundsChanged(e);
RefreshChildrenBounds();
}
public void OnTimeScaleChanged()
{
OnVisibleRangeChanged();
}
public void OnTimeOffsetChanged()
{
OnVisibleRangeChanged();
}
protected virtual void OnVisibleRangeChanged()
{
foreach (TrackItem item in Children)
{
item.OnVisibleRangeChanged();
}
}
#endregion
#endregion
#region Event Handlers
private void c_children_CollectionChanged(object sender, CollectionChangedEventArgs<TrackItem> e)
{
m_children.Synchronize(e, RegisterTrackItem, UnregisterTrackItem);
}
private void RegisterTrackItem(TrackItem item)
{
item.Track = this;
item.Initialize();
RefreshTrackItemBounds(item);
item.SelectedChanged += item_SelectedChanged;
}
private void UnregisterTrackItem(TrackItem item)
{
item.SelectedChanged -= item_SelectedChanged;
item.Track = null;
}
private void item_SelectedChanged(object sender, EventArgs e)
{
// We sort items so that the selected item is always on top (easier to see)
TrackItem selectedItem = (TrackItem)sender;
if (selectedItem.Selected)
{
Controls.Sort(new SelectedItemComparer<WindowlessControl>(selectedItem));
}
}
#region Sorting
private class SelectedItemComparer<T> : IComparer<T>
{
#region Variables
private readonly T m_selectedItem;
#endregion
#region Constructor
public SelectedItemComparer(T selectedItem)
{
m_selectedItem = selectedItem;
}
#endregion
#region Implementation of IComparer<TrackItem>
public int Compare(T x, T y)
{
bool xIsSelected = Equals(x, m_selectedItem);
bool yIsSelected = Equals(y, m_selectedItem);
if (xIsSelected)
{
return yIsSelected ? 0 : 1;
}
return yIsSelected ? -1 : 0;
}
#endregion
}
#endregion
#endregion
#region Inner Types
private class TrackItemCollection : AttachedControlCollection<TrackItem>
{
public TrackItemCollection(IObservableList<WindowlessControl> originalCollection)
: base(originalCollection)
{
}
}
#endregion
}
}