private void BringChainIntoView(TimeLineItemControl first, TimeLineItemControl last, int direction) { Double l1 = 0; Double l2 = 0; Double w = 0; Double w2 = 0; Double end = 0; first.GetPlacementInfo(ref l1, ref w, ref end); last.GetPlacementInfo(ref l2, ref w2, ref end); Double chainW = end - l1; Double leadBuffer = 4 * UnitSize; chainW += leadBuffer; if (direction > 0) { first.BringIntoView(new Rect(new Point(0, 0), new Point(chainW, Height))); } else { first.BringIntoView(new Rect(new Point(-leadBuffer, 0), new Point(chainW, Height))); } }
/// <summary> /// Returns a list of all timeline controls starting with the current one and moving forward /// so long as they are contiguous. /// </summary> /// <param name="current"></param> /// <returns></returns> private List<TimeLineItemControl> GetTimeLineForwardChain(TimeLineItemControl current, int afterIndex, ref Double chainGap) { List<TimeLineItemControl> returner = new List<TimeLineItemControl>() { current }; Double left = 0, width = 0, end = 0; current.GetPlacementInfo(ref left, ref width, ref end); if (afterIndex < 0) { //we are on the end of the list so there is no limit. chainGap = Double.MaxValue; return returner; } Double bumpThreshold = _bumpThreshold; Double lastAddedEnd = end; while (afterIndex < Items.Count) { left = width = end = 0; var checker = GetTimeLineItemControlAt(afterIndex++); if (checker != null) { checker.GetPlacementInfo(ref left, ref width, ref end); Double gap = left - lastAddedEnd; if (gap > bumpThreshold) { chainGap = gap; return returner; } returner.Add(checker); lastAddedEnd = end; } } //we have chained off to the end and thus have no need to worry about our gap chainGap = Double.MaxValue; return returner; }
internal void HandleItemManipulation(TimeLineItemControl ctrl, TimeLineItemChangedEventArgs e) { Boolean doStretch = false; TimeSpan deltaT = e.DeltaTime; TimeSpan zeroT = new TimeSpan(); int direction = deltaT.CompareTo(zeroT); if (direction == 0) return;//shouldn't happen TimeLineItemControl previous = null; TimeLineItemControl after = null; int afterIndex = -1; int previousIndex = -1; after = GetTimeLineItemControlStartingAfter(ctrl.StartTime, ref afterIndex); previous = GetTimeLineItemControlStartingBefore(ctrl.StartTime, ref previousIndex); if (after != null) after.ReadyToDraw = false; if (ctrl != null) ctrl.ReadyToDraw = false; Double useDeltaX = e.DeltaX; Double cLeft = 0; Double cWidth = 0; Double cEnd = 0; ctrl.GetPlacementInfo(ref cLeft, ref cWidth, ref cEnd); switch (e.Action) { case TimeLineAction.Move: #region move Double chainGap = Double.MaxValue; if (direction > 0) { //find chain connecteds that are after this one //delta each one in that chain that we are pushing List<TimeLineItemControl> afterChain = GetTimeLineForwardChain(ctrl, afterIndex, ref chainGap); if (chainGap < useDeltaX) useDeltaX = chainGap; foreach (var ti in afterChain) { ti.MoveMe(useDeltaX); } //find the size of our chain so we bring it into view var first = afterChain[0]; var last = afterChain[afterChain.Count - 1]; BringChainIntoView(first, last, direction); } if (direction < 0) { Boolean previousBackToStart = false; List<TimeLineItemControl> previousChain = GetTimeLineBackwardsChain(ctrl, previousIndex, ref previousBackToStart, ref chainGap); if (-chainGap > useDeltaX) { useDeltaX = chainGap; } if (!previousBackToStart) { foreach (var ti in previousChain) { ti.MoveMe(useDeltaX); } } var first = previousChain[0];//previousChain[previousChain.Count - 1]; var last = previousChain[previousChain.Count - 1]; BringChainIntoView(last, first, direction); } #endregion break; case TimeLineAction.StretchStart: switch (e.Mode) { #region stretchstart case TimeLineManipulationMode.Linked: #region linked Double gap = Double.MaxValue; if (previous != null) { Double pLeft = 0; Double pWidth = 0; Double pEnd = 0; previous.GetPlacementInfo(ref pLeft, ref pWidth, ref pEnd); gap = cLeft - pEnd; } if (direction < 0 && Math.Abs(gap) < Math.Abs(useDeltaX) && Math.Abs(gap) > _bumpThreshold)//if we are negative and not linked, but about to bump useDeltaX = -gap; if (Math.Abs(gap) < _bumpThreshold) {//we are linked if (ctrl.CanDelta(0, useDeltaX) && previous.CanDelta(1, useDeltaX)) { ctrl.MoveStartTime(useDeltaX); previous.MoveEndTime(useDeltaX); } } else if (ctrl.CanDelta(0, useDeltaX)) { ctrl.MoveStartTime(useDeltaX); } break; #endregion case TimeLineManipulationMode.Free: #region free gap = Double.MaxValue; doStretch = direction > 0; if (direction < 0) { //disallow us from free stretching into another item if (previous != null) { Double pLeft = 0; Double pWidth = 0; Double pEnd = 0; previous.GetPlacementInfo(ref pLeft, ref pWidth, ref pEnd); gap = cLeft - pEnd; } else { //don't allow us to stretch further than the gap between current and start time DateTime s = (DateTime)GetValue(StartDateProperty); gap = cLeft; } doStretch = gap > _bumpThreshold; if (gap < useDeltaX) { useDeltaX = gap; } } doStretch &= ctrl.CanDelta(0, useDeltaX); if (doStretch) { ctrl.MoveStartTime(useDeltaX); } #endregion break; default: break; #endregion } break; case TimeLineAction.StretchEnd: switch (e.Mode) { #region stretchend case TimeLineManipulationMode.Linked: #region linked Double gap = Double.MaxValue; if (after != null) { Double aLeft = 0; Double aWidth = 0; Double aEnd = 0; after.GetPlacementInfo(ref aLeft, ref aWidth, ref aEnd); gap = aLeft - cEnd; } if (direction > 0 && gap > _bumpThreshold && gap < useDeltaX)//if we are positive, not linked but about to bump useDeltaX = -gap; if (gap < _bumpThreshold) {//we are linked if (ctrl.CanDelta(1, useDeltaX) && after.CanDelta(0, useDeltaX)) { ctrl.MoveEndTime(useDeltaX); after.MoveStartTime(useDeltaX); } } else if (ctrl.CanDelta(0, useDeltaX)) { ctrl.MoveEndTime(useDeltaX); } break; #endregion case TimeLineManipulationMode.Free: #region free Double nextGap = Double.MaxValue; doStretch = true; if (direction > 0 && after != null) { //disallow us from free stretching into another item Double nLeft = 0; Double nWidth = 0; Double nEnd = 0; after.GetPlacementInfo(ref nLeft, ref nWidth, ref nEnd); nextGap = nLeft - cEnd; doStretch = nextGap > _bumpThreshold; if (nextGap < useDeltaX) useDeltaX = nextGap; } doStretch &= ctrl.CanDelta(1, useDeltaX); if (doStretch) { ctrl.MoveEndTime(useDeltaX); } break; #endregion default: break; #endregion } break; default: break; } }
/// <summary> /// Returns a list of all timeline controls starting with the current one and moving backwoards /// so long as they are contiguous. If the chain reaches back to the start time of the timeline then the /// ChainsBackToStart boolean is modified to reflect that. /// </summary> /// <param name="current"></param> /// <returns></returns> private List<TimeLineItemControl> GetTimeLineBackwardsChain(TimeLineItemControl current, int prevIndex, ref Boolean ChainsBackToStart, ref Double chainGap) { List<TimeLineItemControl> returner = new List<TimeLineItemControl>() { current }; Double left = 0, width = 0, end = 0; current.GetPlacementInfo(ref left, ref width, ref end); if (prevIndex < 0) { chainGap = Double.MaxValue; ChainsBackToStart = left == 0; return returner; } Double lastAddedLeft = left; while (prevIndex >= 0) { left = width = end = 0; var checker = GetTimeLineItemControlAt(prevIndex--); if (checker != null) { checker.GetPlacementInfo(ref left, ref width, ref end); if (lastAddedLeft - end > _bumpThreshold) { //our chain just broke; chainGap = lastAddedLeft - end; ChainsBackToStart = lastAddedLeft == 0; return returner; } returner.Add(checker); lastAddedLeft = left; } } ChainsBackToStart = lastAddedLeft == 0; chainGap = lastAddedLeft;//gap between us and zero; return returner; }