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 { /// /// A single track in the track view. /// 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 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; } } /// /// True if the track is always on top and does not scroll. /// 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 /// /// Allows to automatically resize child bounds when painting /// public virtual void ComputeChildBounds(Graphics graphics) { } /// /// Called after all track size have been computed, allows to bake final position in track items. /// 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 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(selectedItem)); } } #region Sorting private class SelectedItemComparer : IComparer { #region Variables private readonly T m_selectedItem; #endregion #region Constructor public SelectedItemComparer(T selectedItem) { m_selectedItem = selectedItem; } #endregion #region Implementation of IComparer 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 { public TrackItemCollection(IObservableList originalCollection) : base(originalCollection) { } } #endregion } }