using System; using System.Drawing; using System.Windows.Forms; using JD.Kernel.Structs; using JD.Maths.Utils; using JD.ScreenWrappers; using JD.Sequencers.Controls; using JD.Sequencers.Windowlesses; namespace JD.Sequencers.Times { using RangeF = Range; /// /// Provides base behaviors for any sequencer control (TrackView, Timeline, Zoom, Optional scroll bars, etc) /// public abstract class SequencerControlBase : WindowlessRootControl { #region Variables private Timeline m_timeline; private HScrollBar m_horizontalScrollbar; private VScrollBar m_verticalScrollbar; private TrackView m_trackView; private Cursor m_previousCursor; private Point m_lastMouseMoveLocation; private bool m_suspendScrollbarEvents; private bool m_isTrackViewVisible = true; #endregion #region Constructor protected SequencerControlBase() { SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer, true); m_previousCursor = DefaultCursor; } protected void Init() { RootContainer.BoundsChanged += RootContainer_BoundsChanged; } #endregion #region Properties public bool IsTrackViewVisible { get { return m_isTrackViewVisible; } set { m_isTrackViewVisible = value; } } public TrackView TrackView { get { if (m_trackView == null) { m_trackView = new TrackView(this, Timeline); m_trackView.ScrollMaximumChanged += m_trackView_ScrollMaximumChanged; m_trackView.ScrollValueChanged += m_trackView_ScrollValueChanged; RootContainer.Controls.Add(m_trackView); } m_trackView.IsVisible = m_isTrackViewVisible; return m_trackView; } } private Timeline Timeline { get { if (m_timeline == null) { m_timeline = CreateTimeline(); m_timeline.CurrentTimeChanged += m_timeline_CurrentTimeChanged; m_timeline.TimeRangeChanged += m_timeline_TimeRangeChanged; m_timeline.PixelsPerTimeChanged += m_timeline_PixelsPerTimeChanged; m_timeline.TimeOffsetChanged += m_timeline_TimeOffsetChanged; m_timeline.MouseWheel += m_timeline_MouseWheel; } return m_timeline; } } private HScrollBar HorizontalScrollbar { get { if (m_horizontalScrollbar == null) { m_horizontalScrollbar = CreateHorizontalScrollbar(); if (m_horizontalScrollbar != null) { m_horizontalScrollbar.ValueChanged += m_horizontalScrollbar_ValueChanged; } } return m_horizontalScrollbar; } } private VScrollBar VerticalScrollbar { get { if (m_verticalScrollbar == null) { m_verticalScrollbar = CreateVerticalScrollbar(); if (m_verticalScrollbar != null) { m_verticalScrollbar.ValueChanged += m_verticalScrollbar_ValueChanged; } } return m_verticalScrollbar; } } #endregion #region Methods protected abstract Timeline CreateTimeline(); protected abstract HScrollBar CreateHorizontalScrollbar(); protected abstract VScrollBar CreateVerticalScrollbar(); public void Zoom(float factor) { float zoomFactor = factor > 0.0f ? -0.1f : 0.1f; RangeF currentRange = Timeline.VisibleTimeRange; currentRange = currentRange.Inflate(currentRange.Width() * zoomFactor); if (currentRange.Width() > 0.1f) { Timeline.VisibleTimeRange = currentRange; } } public void SelectTrack(Track track) { int index = TrackView.GetTrackIndex(track); if (index >= 0) { TrackView.ScrollValue = index <= TrackView.ScrollMaximum ? index : TrackView.ScrollMaximum; TrackView.ActiveTrack = track; UpdateScrollbars(); } } protected virtual void UpdateBoundsRootContainer() { Rectangle bounds = RootContainer.Bounds; Timeline.Bounds = new Rectangle(bounds.Left, bounds.Top, bounds.Width, Timeline.Height); if (TrackView.IsVisible) { TrackView.Bounds = new Rectangle(bounds.Left, Timeline.Bounds.Bottom, bounds.Width, bounds.Height - Timeline.Height); } UpdateScrollbars(); } protected virtual void UpdateHorizontalScrollbar() { double rangeWidth = Timeline.TimeRange.Width() * Timeline.PixelsPerTime; double displayWidth = Timeline.Width - 2 * Timeline.Margin; if (System.Math.Abs(displayWidth - rangeWidth) > 1e-3) { m_suspendScrollbarEvents = true; HorizontalScrollbar.Minimum = (int)System.Math.Round(Timeline.TimeRange.Min * Timeline.PixelsPerTime) - Timeline.Margin; HorizontalScrollbar.Maximum = (int)System.Math.Round((Timeline.TimeRange.Max + Timeline.MajorInterval) * Timeline.PixelsPerTime - Timeline.Width + Timeline.Margin); m_suspendScrollbarEvents = false; HorizontalScrollbar.LargeChange = (int)System.Math.Round(Timeline.MajorInterval * Timeline.PixelsPerTime); HorizontalScrollbar.SmallChange = (int)System.Math.Round(Timeline.MinorInterval * Timeline.PixelsPerTime); if (Timeline.TimeOffset > HorizontalScrollbar.Maximum / Timeline.PixelsPerTime) { HorizontalScrollbar.Value = HorizontalScrollbar.Maximum; } else if (Timeline.TimeOffset < HorizontalScrollbar.Minimum / Timeline.PixelsPerTime) { HorizontalScrollbar.Value = HorizontalScrollbar.Minimum; } else { HorizontalScrollbar.Value = (int)System.Math.Round(Timeline.TimeOffset * Timeline.PixelsPerTime); } } else { m_suspendScrollbarEvents = true; HorizontalScrollbar.Maximum = HorizontalScrollbar.Minimum = 0; m_suspendScrollbarEvents = false; } } protected virtual void UpdateVerticalScrollbar() { if (TrackView.ScrollMaximum <= 0) { VerticalScrollbar.Value = 0; VerticalScrollbar.Visible = false; } else { VerticalScrollbar.Visible = true; m_suspendScrollbarEvents = true; VerticalScrollbar.Minimum = 0; VerticalScrollbar.Maximum = System.Math.Max(TrackView.ScrollMaximum + VerticalScrollbar.LargeChange - 1, TrackView.ScrollValue); m_suspendScrollbarEvents = false; VerticalScrollbar.Value = TrackView.ScrollValue; } } private void UpdateScrollbars() { if (m_suspendScrollbarEvents) { return; } if (HorizontalScrollbar != null) { int valueBefore = HorizontalScrollbar.Value; UpdateHorizontalScrollbar(); HorizontalScrollbar.Invalidate(); if (HorizontalScrollbar.Value != valueBefore) { Timeline.Invalidate(); TrackView.Invalidate(); } } if (VerticalScrollbar != null) { int valueBefore = VerticalScrollbar.Value; UpdateVerticalScrollbar(); VerticalScrollbar.Invalidate(); if (VerticalScrollbar.Value != valueBefore) { Timeline.Invalidate(); TrackView.Invalidate(); } } } #endregion #region Event Handlers protected override void OnMouseClick(MouseEventArgs e) { base.OnMouseClick(e); Focus(); } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); if (e.Button == MouseButtons.Middle) { m_previousCursor = Cursor; } } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); if (e.Button == MouseButtons.Middle) { Cursor = m_previousCursor; } } protected override void OnMouseWheel(MouseEventArgs e) { base.OnMouseWheel(e); if (Bounds.Contains(e.Location)) HandleZoomOnMouseWheel(e); } private void m_timeline_MouseWheel(object sender, MouseEventArgs e) { if (Bounds.Contains(e.Location)) HandleZoomOnMouseWheel(e); } protected virtual void HandleZoomOnMouseWheel(MouseEventArgs e) { if ((ModifierKeys & Keys.Control) == Keys.Control) { Zoom(e.Delta); } else { if (m_suspendScrollbarEvents || e.Delta == 0) { return; } if (e.Delta > 0 && TrackView.ScrollValue > 0) { TrackView.ScrollValue--; } if (e.Delta < 1 && TrackView.ScrollValue < TrackView.ScrollMaximum) { TrackView.ScrollValue++; } UpdateScrollbars(); } } protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); Point delta = new Point( e.Location.X - m_lastMouseMoveLocation.X, e.Location.Y - m_lastMouseMoveLocation.Y ); m_lastMouseMoveLocation = e.Location; if (e.Button == MouseButtons.Middle) { Cursor = PanCursor(true); PanHorizontal(delta); PanVertical(delta); int lastLocationX = m_lastMouseMoveLocation.X; int lastLocationY = m_lastMouseMoveLocation.Y; GuiUtil.WrapAround(this, ref lastLocationX, ref lastLocationY); m_lastMouseMoveLocation = new Point(lastLocationX, lastLocationY); } } private void PanVertical(Point delta) { // Scroll according to an average track size, ... //int trackSize = TrackView.Tracks.Where(track => !track.Fixed).Select(track => track.Height).FirstOrDefault(); int trackSize = 16; if (trackSize > 0 && VerticalScrollbar != null && VerticalScrollbar.Visible) { double scroll = delta.Y/(double) trackSize; double appliedScroll = System.Math.Round(delta.Y/(double) trackSize); int initialValue = VerticalScrollbar.Value; int newValue = (initialValue - (int) appliedScroll).Clamp(VerticalScrollbar.Minimum, VerticalScrollbar.Maximum - VerticalScrollbar.LargeChange + 1); VerticalScrollbar.Value = newValue; // Set last point according to what was already consumed by the event. int unconsumedDelta = (int) ((scroll - appliedScroll)*trackSize); m_lastMouseMoveLocation.Y -= unconsumedDelta; } } private void PanHorizontal(Point delta) { float deltaInTime = delta.X / Timeline.PixelsPerTime; float oldOffset = Timeline.TimeOffset; float newOffset = oldOffset - deltaInTime; if (oldOffset != newOffset) { Cursor = PanCursor(oldOffset < newOffset); Timeline.TimeOffset = newOffset; } } protected virtual Cursor PanCursor(bool west) { return west ? Cursors.PanWest : Cursors.PanEast; } private void m_timeline_CurrentTimeChanged(object sender, EventArgs e) { UpdateScrollbars(); } private void m_timeline_TimeRangeChanged(object sender, EventArgs e) { UpdateScrollbars(); } private void m_timeline_PixelsPerTimeChanged(object sender, EventArgs e) { UpdateScrollbars(); } private void m_timeline_TimeOffsetChanged(object sender, EventArgs e) { UpdateScrollbars(); } private void m_trackView_ScrollMaximumChanged(object sender, EventArgs e) { UpdateScrollbars(); } void m_trackView_ScrollValueChanged(object sender, EventArgs e) { UpdateScrollbars(); } private void m_horizontalScrollbar_ValueChanged(object sender, EventArgs e) { if (m_suspendScrollbarEvents) { return; } var currentOffset = Timeline.TimeOffset; var offset = HorizontalScrollbar.Value / Timeline.PixelsPerTime; if (currentOffset != offset) { Timeline.TimeOffset = offset; Invalidate(); } OnScrolled(EventArgs.Empty); } private void m_verticalScrollbar_ValueChanged(object sender, EventArgs e) { if (m_suspendScrollbarEvents) { return; } TrackView.ScrollValue = VerticalScrollbar.Value; OnScrolled(EventArgs.Empty); } private void RootContainer_BoundsChanged(object sender, EventArgs e) { UpdateBoundsRootContainer(); } #endregion #region Events public event EventHandler Scrolled; protected virtual void OnScrolled(EventArgs e) { Scrolled?.Invoke(this, e); } #endregion } }