private void timelineControl_ContextSelected(object sender, ContextSelectedEventArgs e) { _contextMenuStrip.Items.Clear(); Element element = e.ElementsUnderCursor.FirstOrDefault(); TimedSequenceElement tse = element as TimedSequenceElement; #region Add Effect ToolStripMenuItem contextMenuItemAddEffect = new ToolStripMenuItem("Add Effect(s)") { Image = Resources.effects }; IEnumerable <IEffectModuleDescriptor> effectDesriptors = ApplicationServices.GetModuleDescriptors <IEffectModuleInstance>() .Cast <IEffectModuleDescriptor>() .OrderBy(x => x.EffectGroup) .ThenBy(n => n.EffectName); EffectGroups group = effectDesriptors.First().EffectGroup; foreach (IEffectModuleDescriptor effectDesriptor in effectDesriptors) { if (effectDesriptor.EffectName == "Nutcracker") { continue; //Remove this when the Nutcracker module is removed } if (effectDesriptor.EffectGroup != group) { ToolStripSeparator seperator = new ToolStripSeparator(); contextMenuItemAddEffect.DropDownItems.Add(seperator); group = effectDesriptor.EffectGroup; } // Add an entry to the menu ToolStripMenuItem contextMenuItemEffect = new ToolStripMenuItem(effectDesriptor.EffectName); contextMenuItemEffect.Image = effectDesriptor.GetRepresentativeImage(); contextMenuItemEffect.Tag = effectDesriptor.TypeId; contextMenuItemEffect.ToolTipText = @"Use Shift key to add multiple effects of the same type."; contextMenuItemEffect.Click += (mySender, myE) => { if (e.Row != null) { //add multiple if (ModifierKeys == Keys.Shift || ModifierKeys == (Keys.Shift | Keys.Control)) { AddMultipleEffects(e.GridTime, effectDesriptor.EffectName, (Guid)contextMenuItemEffect.Tag, e.Row); } else //add single { AddNewEffectById((Guid)contextMenuItemEffect.Tag, e.Row, e.GridTime, TimeSpan.FromSeconds(2), true); } } }; contextMenuItemAddEffect.DropDownItems.Add(contextMenuItemEffect); } _contextMenuStrip.Items.Add(contextMenuItemAddEffect); #endregion #region Layer Section ConfigureLayerMenu(e); #endregion #region Effect Alignment Section ToolStripMenuItem contextMenuItemAlignment = new ToolStripMenuItem("Alignment") { Enabled = TimelineControl.grid.OkToUseAlignmentHelper(TimelineControl.SelectedElements), Image = Resources.alignment }; //Disables the Alignment menu if too many effects are selected in a row. if (!contextMenuItemAlignment.Enabled) { contextMenuItemAlignment.ToolTipText = @"Disabled, maximum selected effects per row is 32."; } ToolStripMenuItem contextMenuItemAlignStart = new ToolStripMenuItem("Align Start Times") { ToolTipText = @"Holding shift will align the start times, while holding duration.", Image = Resources.alignStart }; contextMenuItemAlignStart.Click += (mySender, myE) => TimelineControl.grid.AlignElementStartTimes(TimelineControl.SelectedElements, element, ModifierKeys == Keys.Shift); contextMenuItemAlignStart.ShortcutKeyDisplayString = @"(Shift)+S"; ToolStripMenuItem contextMenuItemAlignEnd = new ToolStripMenuItem("Align End Times") { ToolTipText = @"Holding shift will align the end times, while holding duration.", Image = Resources.alignEnd }; contextMenuItemAlignEnd.Click += (mySender, myE) => TimelineControl.grid.AlignElementEndTimes(TimelineControl.SelectedElements, element, ModifierKeys == Keys.Shift); contextMenuItemAlignEnd.ShortcutKeyDisplayString = @"(Shift)+E"; ToolStripMenuItem contextMenuItemAlignBoth = new ToolStripMenuItem("Align Both Times") { Image = Resources.alignBoth }; contextMenuItemAlignBoth.Click += (mySender, myE) => TimelineControl.grid.AlignElementStartEndTimes(TimelineControl.SelectedElements, element); contextMenuItemAlignBoth.ShortcutKeyDisplayString = @"B"; ToolStripMenuItem contextMenuItemMatchDuration = new ToolStripMenuItem("Match Duration") { ToolTipText = @"Holding shift will hold the effects end time and adjust the start time, by default the end time is adjusted.", Image = Resources.matchDuration }; contextMenuItemMatchDuration.Click += (mySender, myE) => TimelineControl.grid.AlignElementDurations(TimelineControl.SelectedElements, element, ModifierKeys == Keys.Shift); contextMenuItemMatchDuration.ShortcutKeyDisplayString = @"(Shift)"; ToolStripMenuItem contextMenuItemAlignStartToEnd = new ToolStripMenuItem("Align Start to End") { ToolTipText = @"Holding shift will hold the effects end time and only adjust the start time, by default the entire effect is moved.", Image = Resources.alignStartEnd }; contextMenuItemAlignStartToEnd.Click += (mySender, myE) => TimelineControl.grid.AlignElementStartToEndTimes(TimelineControl.SelectedElements, element, ModifierKeys == Keys.Shift); contextMenuItemAlignStartToEnd.ShortcutKeyDisplayString = @"(Shift)"; ToolStripMenuItem contextMenuItemAlignEndToStart = new ToolStripMenuItem("Align End to Start") { ToolTipText = @"Holding shift will hold the effects start time and only adjust the end time, by default the entire effect is moved.", Image = Resources.alignStartEnd }; contextMenuItemAlignEndToStart.Click += (mySender, myE) => TimelineControl.grid.AlignElementEndToStartTime(TimelineControl.SelectedElements, element, ModifierKeys == Keys.Shift); contextMenuItemAlignEndToStart.ShortcutKeyDisplayString = @"(Shift)"; ToolStripMenuItem contextMenuItemDistDialog = new ToolStripMenuItem("Distribute Effects") { Image = Resources.distribute }; contextMenuItemDistDialog.Click += (mySender, myE) => DistributeSelectedEffects(); ToolStripMenuItem contextMenuItemAlignCenter = new ToolStripMenuItem("Align Centerpoints") { Image = Resources.alignCenter }; contextMenuItemAlignCenter.Click += (mySender, myE) => TimelineControl.grid.AlignElementCenters(TimelineControl.SelectedElements, element); ToolStripMenuItem contextMenuItemDistributeEqually = new ToolStripMenuItem("Distribute Equally") { ToolTipText = @"This will stair step the selected elements, starting with the element that has the earlier start mouseLocation on the time line.", Image = Resources.distribute }; contextMenuItemDistributeEqually.Click += (mySender, myE) => DistributeSelectedEffectsEqually(); ToolStripMenuItem contextMenuItemAlignStartToMark = new ToolStripMenuItem("Align Start to nearest mark") { Image = Resources.alignStartMark }; contextMenuItemAlignStartToMark.Click += (mySender, myE) => AlignEffectsToNearestMarks("Start"); contextMenuItemAlignStartToMark.ShortcutKeyDisplayString = @"Ctrl+Shift+S"; ToolStripMenuItem contextMenuItemAlignEndToMark = new ToolStripMenuItem("Align End to nearest mark") { Image = Resources.alignEndMark }; contextMenuItemAlignEndToMark.Click += (mySender, myE) => AlignEffectsToNearestMarks("End"); contextMenuItemAlignEndToMark.ShortcutKeyDisplayString = @"Ctrl+Shift+E"; ToolStripMenuItem contextMenuItemAlignBothToMark = new ToolStripMenuItem("Align Both to nearest mark") { Image = Resources.alignBothMark }; contextMenuItemAlignBothToMark.Click += (mySender, myE) => AlignEffectsToNearestMarks("Both"); contextMenuItemAlignBothToMark.ShortcutKeyDisplayString = @"Ctrl+Shift+B"; _contextMenuStrip.Items.Add(contextMenuItemAlignment); contextMenuItemAlignment.DropDown.Items.Add(contextMenuItemAlignStart); contextMenuItemAlignment.DropDown.Items.Add(contextMenuItemAlignEnd); contextMenuItemAlignment.DropDown.Items.Add(contextMenuItemAlignBoth); contextMenuItemAlignment.DropDown.Items.Add(contextMenuItemAlignCenter); contextMenuItemAlignment.DropDown.Items.Add(contextMenuItemMatchDuration); contextMenuItemAlignment.DropDown.Items.Add(contextMenuItemAlignStartToEnd); contextMenuItemAlignment.DropDown.Items.Add(contextMenuItemAlignEndToStart); contextMenuItemAlignment.DropDown.Items.Add(contextMenuItemDistributeEqually); contextMenuItemAlignment.DropDown.Items.Add(contextMenuItemDistDialog); contextMenuItemAlignment.DropDown.Items.Add(contextMenuItemAlignStartToMark); contextMenuItemAlignment.DropDown.Items.Add(contextMenuItemAlignEndToMark); contextMenuItemAlignment.DropDown.Items.Add(contextMenuItemAlignBothToMark); if (TimelineControl.SelectedElements.Count() > 1 || (TimelineControl.SelectedElements.Any() && !element.Selected)) { contextMenuItemDistributeEqually.Enabled = true; contextMenuItemDistDialog.Enabled = true; contextMenuItemAlignStart.Enabled = true; contextMenuItemAlignEnd.Enabled = true; contextMenuItemAlignBoth.Enabled = true; contextMenuItemAlignCenter.Enabled = true; contextMenuItemMatchDuration.Enabled = true; contextMenuItemAlignEndToStart.Enabled = true; contextMenuItemAlignStartToEnd.Enabled = true; contextMenuItemAlignment.Enabled = true; contextMenuItemAlignment.ToolTipText = string.Empty; } else { contextMenuItemDistributeEqually.Enabled = false; contextMenuItemDistDialog.Enabled = false; contextMenuItemAlignStart.Enabled = false; contextMenuItemAlignEnd.Enabled = false; contextMenuItemAlignBoth.Enabled = false; contextMenuItemAlignCenter.Enabled = false; contextMenuItemMatchDuration.Enabled = false; contextMenuItemAlignEndToStart.Enabled = false; contextMenuItemAlignStartToEnd.Enabled = false; contextMenuItemAlignment.Enabled = false; if (TimelineControl.SelectedElements.Count() == 1) { contextMenuItemAlignment.ToolTipText = @"Select more then one effect or ensure you have Marks added to enable the Alignment feature."; } else { contextMenuItemAlignment.ToolTipText = @"Select more then one effect to enable the Alignment feature."; } } contextMenuItemAlignStartToMark.Enabled = false; contextMenuItemAlignEndToMark.Enabled = false; contextMenuItemAlignBothToMark.Enabled = false; foreach (MarkCollection mc in _sequence.LabeledMarkCollections) { if (mc.Marks.Any()) { contextMenuItemAlignStartToMark.Enabled = true; contextMenuItemAlignEndToMark.Enabled = true; contextMenuItemAlignBothToMark.Enabled = true; contextMenuItemAlignment.Enabled = true; contextMenuItemAlignment.ToolTipText = string.Empty; break; } } #endregion #region Effect Manipulation Section if (tse != null) { ToolStripMenuItem contextMenuItemManipulation = new ToolStripMenuItem("Manipulation"); ToolStripMenuItem contextMenuItemManipulateDivide = new ToolStripMenuItem("Divide at cursor") { Image = Resources.divide }; contextMenuItemManipulateDivide.Click += (mySender, myE) => { if (TimelineControl.SelectedElements.Any()) { TimelineControl.grid.SplitElementsAtTime( TimelineControl.SelectedElements.Where(elem => elem.StartTime <e.GridTime && elem.EndTime> e.GridTime) .ToList(), e.GridTime); } else { TimelineControl.grid.SplitElementsAtTime(new List <Element> { element }, e.GridTime); } }; ToolStripMenuItem contextMenuItemManipulationClone = new ToolStripMenuItem("Clone") { Image = Resources.page_copy }; contextMenuItemManipulationClone.Click += (mySender, myE) => { if (TimelineControl.SelectedElements.Any()) { CloneElements(TimelineControl.SelectedElements ?? new List <Element> { element }); } else { CloneElements(new List <Element> { element }); } }; ToolStripMenuItem contextMenuItemManipulationCloneToOther = new ToolStripMenuItem("Clone to selected effects") { Image = Resources.copySelect }; contextMenuItemManipulationCloneToOther.Click += (mySender, myE) => { if (TimelineControl.SelectedElements.Any(elem => elem.EffectNode.Effect.TypeId != element.EffectNode.Effect.TypeId)) { //messageBox Arguments are (Text, Title, No Button Visible, Cancel Button Visible) MessageBoxForm.msgIcon = SystemIcons.Warning; //this is used if you want to add a system icon to the message form. var messageBox = new MessageBoxForm(string.Format( "Some of the selected effects are not of the same type, only effects of {0} type will be modified.", element.EffectNode.Effect.EffectName), @"Multiple type effect selected", false, true); messageBox.ShowDialog(); if (messageBox.DialogResult == DialogResult.Cancel) { return; } } foreach ( Element elem in TimelineControl.SelectedElements.Where(elem => elem != element) .Where(elem => elem.EffectNode.Effect.TypeId == element.EffectNode.Effect.TypeId)) { elem.EffectNode.Effect.ParameterValues = element.EffectNode.Effect.ParameterValues; elem.RenderElement(); } }; contextMenuItemManipulationCloneToOther.Enabled = (TimelineControl.SelectedElements.Count() > 2); _contextMenuStrip.Items.Add(contextMenuItemManipulation); contextMenuItemManipulation.DropDown.Items.Add(contextMenuItemManipulateDivide); contextMenuItemManipulation.DropDown.Items.Add(contextMenuItemManipulationClone); contextMenuItemManipulation.DropDown.Items.Add(contextMenuItemManipulationCloneToOther); ToolStripMenuItem contextMenuItemEditTime = new ToolStripMenuItem("Edit Time") { Image = Resources.clock_edit }; contextMenuItemEditTime.Click += (mySender, myE) => { EffectTimeEditor editor = new EffectTimeEditor(tse.EffectNode.StartTime, tse.EffectNode.TimeSpan, SequenceLength); if (editor.ShowDialog(this) != DialogResult.OK) { return; } if (TimelineControl.SelectedElements.Any()) { var elementsToMove = TimelineControl.SelectedElements.ToDictionary(elem => elem, elem => new Tuple <TimeSpan, TimeSpan>(editor.Start, editor.Start + editor.Duration)); TimelineControl.grid.MoveResizeElements(elementsToMove); } else { TimelineControl.grid.MoveResizeElement(element, editor.Start, editor.Duration); } }; //Why do we set .Tag ? contextMenuItemEditTime.Tag = tse; contextMenuItemEditTime.Enabled = TimelineControl.grid.OkToUseAlignmentHelper(TimelineControl.SelectedElements); if (!contextMenuItemEditTime.Enabled) { contextMenuItemEditTime.ToolTipText = @"Disabled, maximum selected effects per row is 32."; } _contextMenuStrip.Items.Add(contextMenuItemEditTime); } #endregion #region Cut Copy Paste Section _contextMenuStrip.Items.Add("-"); ToolStripMenuItem contextMenuItemCopy = new ToolStripMenuItem("Copy", null, toolStripMenuItem_Copy_Click) { ShortcutKeyDisplayString = @"Ctrl+C", Image = Resources.page_copy }; ToolStripMenuItem contextMenuItemCut = new ToolStripMenuItem("Cut", null, toolStripMenuItem_Cut_Click) { ShortcutKeyDisplayString = @"Ctrl+X", Image = Resources.cut }; contextMenuItemCopy.Enabled = contextMenuItemCut.Enabled = TimelineControl.SelectedElements.Any(); ToolStripMenuItem contextMenuItemPaste = new ToolStripMenuItem("Paste", null, toolStripMenuItem_Paste_Click) { ShortcutKeyDisplayString = @"Ctrl+V", Image = Resources.page_white_paste, Enabled = ClipboardHasData() }; _contextMenuStrip.Items.AddRange(new ToolStripItem[] { contextMenuItemCut, contextMenuItemCopy, contextMenuItemPaste }); if (TimelineControl.SelectedElements.Any()) { //Add Delete/Collections ToolStripMenuItem contextMenuItemDelete = new ToolStripMenuItem("Delete Effect(s)", null, toolStripMenuItem_deleteElements_Click) { ShortcutKeyDisplayString = @"Del", Image = Resources.delete }; _contextMenuStrip.Items.Add(contextMenuItemDelete); AddContextCollectionsMenu(); } #endregion #region Mark Section ToolStripMenuItem contextMenuItemAddMark = new ToolStripMenuItem("Add Marks to Effects") { Image = Resources.marks }; contextMenuItemAddMark.Click += (mySender, myE) => AddMarksToSelectedEffects(); _contextMenuStrip.Items.Add(contextMenuItemAddMark); #endregion e.AutomaticallyHandleSelection = false; _contextMenuStrip.Show(MousePosition); }
public static void Rasterize(TimedSequenceElement tsElement, Graphics g, TimeSpan visibleStartOffset, TimeSpan visibleEndOffset, int overallWidth) { //var sw = new System.Diagnostics.Stopwatch(); sw.Start(); IEffectModuleInstance effect = tsElement.EffectNode.Effect; if (effect.ForceGenerateVisualRepresentation || Vixen.Common.Graphics.DisableEffectsEditorRendering) { var startX = (int)((visibleStartOffset.Ticks / (float)tsElement.Duration.Ticks) * overallWidth); effect.GenerateVisualRepresentation(g, new Rectangle(-startX, 0, overallWidth, (int)g.VisibleClipBounds.Height)); } else { double width = g.VisibleClipBounds.Width; double height = g.VisibleClipBounds.Height; // As recommended by R# if (Math.Abs(width - 0) < double.Epsilon || Math.Abs(height - 0) < double.Epsilon) { return; } // limit the number of 'rows' rasterized int tmpsiz = (int)(height / 2) + 1; EffectIntents effectIntents = effect.Render(); int count = effectIntents.Count; int skipCount = count > tmpsiz ? count / tmpsiz: 1; double heightPerElement = height / (count / skipCount); double y = 0; int ctr = 0; var elements = effect.TargetNodes.GetElements(); foreach (var element in elements) { if (ctr++ % skipCount != 0) { continue; } IntentNodeCollection elementIntents = effectIntents.GetIntentNodesForElement(element.Id); //effectIntents.GetIntentNodesForElement(element.Id); if (elementIntents != null && elementIntents.Count > 0) { //Determine if we have parallel intents used on this element for this effect. var stack = new List <List <IIntentNode> > { new List <IIntentNode> { elementIntents[0] } }; for (int i = 1; i < elementIntents.Count; i++) { bool add = true; foreach (List <IIntentNode> t in stack) { if (elementIntents[i].StartTime >= t.Last().EndTime) { t.Add(elementIntents[i]); add = false; break; } } if (add) { stack.Add(new List <IIntentNode> { elementIntents[i] }); } } int skip = 0; //Check for base or minimum level intent. if (stack.Count > 1 && stack[0].Count == 1 && stack[0][0].TimeSpan.Equals(effect.TimeSpan) && stack[1][0].TimeSpan != effect.TimeSpan) { //this is most likely a underlying base intent like chase, spin and twinkle use to provide a minimum value //so render it full size as it is usually a lower intensity and the pulses can be drawn over the top and look nice. intentRasterizer.Rasterize(stack[0][0].Intent, new RectangleF(0, (float)y, (float)width, (float)heightPerElement), g, visibleStartOffset, stack[0][0].TimeSpan); skip = 1; } float h = (float)heightPerElement / (stack.Count - skip); int stackCount = 0; //Now we have a good idea what our element should look like, lets draw it up foreach (List <IIntentNode> intentNodes in stack.Skip(skip)) { foreach (IntentNode elementIntentNode in intentNodes) { if (elementIntentNode == null) { Logging.Error("Error: elementIntentNode was null when Rasterizing an effect (ID: " + effect.InstanceId + ")"); continue; } if (elementIntentNode.EndTime < visibleStartOffset || elementIntentNode.StartTime > visibleEndOffset) { continue; } TimeSpan visibleIntentStart = elementIntentNode.StartTime < visibleStartOffset ? visibleStartOffset - elementIntentNode.StartTime : TimeSpan.Zero; TimeSpan visibleIntentEnd = elementIntentNode.EndTime > visibleEndOffset ? visibleEndOffset - elementIntentNode.StartTime : elementIntentNode.TimeSpan; double startPixelX = overallWidth * _GetPercentage(elementIntentNode.StartTime, effect.TimeSpan); double widthPixelX = overallWidth * _GetPercentage(elementIntentNode.TimeSpan, effect.TimeSpan); widthPixelX = widthPixelX * ((visibleIntentEnd.TotalMilliseconds - visibleIntentStart.TotalMilliseconds) / elementIntentNode.TimeSpan.TotalMilliseconds); if (visibleIntentStart == TimeSpan.Zero) { startPixelX -= overallWidth * _GetPercentage(visibleStartOffset, effect.TimeSpan); } else { startPixelX = 0; } intentRasterizer.Rasterize(elementIntentNode.Intent, new RectangleF((float)startPixelX, (float)y + h * stackCount, (float)widthPixelX, h), g, visibleIntentStart, visibleIntentEnd); } stackCount++; } } y += heightPerElement; } //long tRast = sw.ElapsedMilliseconds - tRend; //if( tRast > 10) // Logging.Debug(" oh: {0}, rend: {1}, rast: {2}, eff: {3}, node:{4}", tOh, tRend, tRast, effect.EffectName, effect.TargetNodes[0].Name); } }
private void EditElement(TimedSequenceElement element) { EditElements(new[] { element }); }
private TimedSequenceElement setupNewElementFromNode(EffectNode node) { TimedSequenceElement element = new TimedSequenceElement(node); element.ContentChanged += ElementContentChangedHandler; element.TimeChanged += ElementTimeChangedHandler; return element; }
/// <summary> /// Populates the TimelineControl grid with a new TimedSequenceElement for the given EffectNode. /// Will add a single TimedSequenceElement to in each row that each targeted channel of /// the EffectNode references. It will also add callbacks to event handlers for the element. /// </summary> /// <param name="node">The EffectNode to make element(s) in the grid for.</param> private TimedSequenceElement addElementForEffectNode(EffectNode node) { TimedSequenceElement element = new TimedSequenceElement(node); element.ContentChanged += ElementContentChangedHandler; element.TimeChanged += ElementTimeChangedHandler; // for the effect, make a single element and add it to every row that represents its target channels foreach(ChannelNode target in node.Effect.TargetNodes) { if(_channelNodeToRows.ContainsKey(target)) { // Add the element to each row that represents the channel this command is in. foreach(Row row in _channelNodeToRows[target]) { if(!_effectNodeToElement.ContainsKey(node)) _effectNodeToElement[node] = element; //else // VixenSystem.Logging.Debug("TimedSequenceEditor: Making a new element, but the map already has one!"); row.AddElement(element); } } else { // we don't have a row for the channel this effect is referencing; most likely, the row has // been deleted, or we're opening someone else's sequence, etc. Big fat TODO: here for that, then. // dunno what we want to do: prompt to add new channels for them? map them to others? etc. string message = "No Timeline.Row is associated with a target ChannelNode for this EffectNode. It now exists in the sequence, but not in the GUI."; MessageBox.Show(message); VixenSystem.Logging.Error(message); } } return element; }
// copy ctor public TimedSequenceElement(TimedSequenceElement other) : base(other) { //TODO: This needs to be a deep-copy of the effect node. EffectNode = other.EffectNode; }
public static void Rasterize(TimedSequenceElement tsElement, Graphics g, TimeSpan visibleStartOffset, TimeSpan visibleEndOffset, int overallWidth) { //var sw = new System.Diagnostics.Stopwatch(); sw.Start(); IEffectModuleInstance effect = tsElement.EffectNode.Effect; if (effect.ForceGenerateVisualRepresentation || Vixen.Common.Graphics.DisableEffectsEditorRendering) { effect.GenerateVisualRepresentation(g, new Rectangle(0, 0, (int)g.VisibleClipBounds.Width, (int)g.VisibleClipBounds.Height)); } else { double width = g.VisibleClipBounds.Width; double height = g.VisibleClipBounds.Height; // As recommended by R# if (Math.Abs(width - 0) < double.Epsilon || Math.Abs(height - 0) < double.Epsilon) return; IEnumerable<Element> elements = effect.TargetNodes.GetElements(); // limit the number of 'rows' rasterized int tmpsiz = (int)(height / 2) + 1; if (elements.Count() > tmpsiz) { int skip = elements.Count() / tmpsiz; elements = elements.Where((element, index) => (index + 1) % skip == 0); } double heightPerElement = height / elements.Count(); //long tOh = sw.ElapsedMilliseconds; EffectIntents effectIntents = effect.Render(); //long tRend = sw.ElapsedMilliseconds - tOh; double y = 0; foreach (Element element in elements) { //Getting exception on null elements here... A simple check to look for these null values and ignore them if (element != null) { IntentNodeCollection elementIntents = effectIntents.GetIntentNodesForElement(element.Id); if (elementIntents != null && elementIntents.Count > 0) { //Determine if we have parallel intents used on this element for this effect. var stack = new List<List<IIntentNode>> {new List<IIntentNode> {elementIntents[0]}}; for (int i = 1; i < elementIntents.Count; i++) { bool add = true; foreach (List<IIntentNode> t in stack) { if (elementIntents[i].StartTime >= t.Last().EndTime) { t.Add(elementIntents[i]); add = false; break; } } if (add) stack.Add(new List<IIntentNode> { elementIntents[i] }); } int skip = 0; //Check for base or minimum level intent. if (stack.Count > 1 && stack[0].Count == 1 && stack[0][0].TimeSpan.Equals(effect.TimeSpan) && stack[1][0].TimeSpan != effect.TimeSpan) { //this is most likely a underlying base intent like chase, spin and twinkle use to provide a minimum value //so render it full size as it is usually a lower intensity and the pulses can be drawn over the top and look nice. intentRasterizer.Rasterize(stack[0][0].Intent, new RectangleF(0, (float)y, (float)width, (float)heightPerElement), g, visibleStartOffset,stack[0][0].TimeSpan); skip=1; } float h = (float)heightPerElement / (stack.Count-skip); int stackCount = 0; //Now we have a good idea what our element should look like, lets draw it up foreach (List<IIntentNode> intentNodes in stack.Skip(skip)) { foreach (IntentNode elementIntentNode in intentNodes) { if (elementIntentNode == null) { Logging.Error("Error: elementIntentNode was null when Rasterizing an effect (ID: " + effect.InstanceId + ")"); continue; } if(elementIntentNode.EndTime<visibleStartOffset || elementIntentNode.StartTime>visibleEndOffset) continue; TimeSpan visibleIntentStart = elementIntentNode.StartTime < visibleStartOffset ? visibleStartOffset - elementIntentNode.StartTime : TimeSpan.Zero; TimeSpan visibleIntentEnd = elementIntentNode.EndTime > visibleEndOffset ? visibleEndOffset - elementIntentNode.StartTime : elementIntentNode.TimeSpan; double startPixelX = overallWidth * _GetPercentage(elementIntentNode.StartTime, effect.TimeSpan); double widthPixelX = overallWidth * _GetPercentage(elementIntentNode.TimeSpan, effect.TimeSpan); widthPixelX = widthPixelX * ((visibleIntentEnd.TotalMilliseconds - visibleIntentStart.TotalMilliseconds) / elementIntentNode.TimeSpan.TotalMilliseconds); if (visibleIntentStart == TimeSpan.Zero) { startPixelX -= overallWidth*_GetPercentage(visibleStartOffset, effect.TimeSpan); } else { startPixelX = 0; } intentRasterizer.Rasterize(elementIntentNode.Intent, new RectangleF((float)startPixelX, (float)y+h*stackCount , (float)widthPixelX, h), g, visibleIntentStart , visibleIntentEnd); } stackCount++; } } } y += heightPerElement; } //long tRast = sw.ElapsedMilliseconds - tRend; //if( tRast > 10) // Logging.Debug(" oh: {0}, rend: {1}, rast: {2}, eff: {3}, node:{4}", tOh, tRend, tRast, effect.EffectName, effect.TargetNodes[0].Name); } }
private void EditElement(TimedSequenceElement element) { if (element == null) return; using (TimedSequenceEditorEffectEditor editor = new TimedSequenceEditorEffectEditor(element.EffectNode)) { DialogResult result = editor.ShowDialog(); if (result == System.Windows.Forms.DialogResult.OK) sequenceModified(); } }