/// <summary> /// This method iterates the child nodes of the given <see cref="SlideshowTreeNode"/> to /// find the <see cref="SlideshowTreeNode"/> with the given name using the /// <see cref="TreeNode.Name"/> or <see cref="TreeNode.Text"/> property /// of the <see cref="TreeNode"/> as indicated in the third paramter. /// </summary> /// <param name="name">The <see cref="String"/> with the name or text to search for</param> /// <param name="rootNode">The base <see cref="SlideshowTreeNode"/> to start the search from.</param> /// <param name="useNameProperty"><strong>True</strong>, if the <see cref="TreeNode.Name"/> /// property should be used for checking, <strong>false</strong> if the <see cref="TreeNode.Text"/> /// property should be used.</param> /// <returns>The <see cref="SlideshowTreeNode"/> that matches the search criteria or null if /// no node was found.</returns> public static SlideshowTreeNode IterateTreeNodes(string name, SlideshowTreeNode rootNode, bool useNameProperty) { SlideshowTreeNode returnTreeNode; foreach (SlideshowTreeNode childNode in rootNode.Nodes) { if (CheckNameOrText(childNode, name, useNameProperty)) { return(childNode); } else { if (childNode.Nodes.Count > 0) { returnTreeNode = IterateTreeNodes(name, childNode, useNameProperty); if (returnTreeNode != null) { return(returnTreeNode); } } } } return(null); }
/// <summary> /// This method parses the xml file for a slide layout of Ogama V1 /// and updates them to the current format. /// </summary> /// <param name="reader">The <see cref="XmlReader"/> with the file to parse.</param> /// <param name="slideSerializer">The <see cref="XmlSerializer"/> for deserializing slide nodes.</param> protected void ParseOgamaV1Slideshow(XmlReader reader, XmlSerializer slideSerializer) { string message = "You are opening an OGAMA 1.X experiment." + Environment.NewLine + Environment.NewLine + "The experiment file along with the database need to be converted to the new Ogama 2 format." + Environment.NewLine + "PLEASE NOTE: Be sure to have a backup copy of the whole experiment along with the database before doing this conversion !" + " (Zip or copy the whole experiment folder.)" + Environment.NewLine + "The possibility is not ruled out, that the conversion fails and your experiment data " + "is lost." + Environment.NewLine + Environment.NewLine + "Can we start with the conversion now ?"; DialogResult result = InformationDialog.Show("Upgrade experiment to new version ?", message, true, MessageBoxIcon.Question); switch (result) { case DialogResult.Cancel: case DialogResult.No: throw new WarningException("Upgrading cancelled due to user request"); case DialogResult.Yes: break; } // Go through all slides in the serialized List<Slide> while (reader.Name == "Slide") { Slide actualSlide = (Slide)slideSerializer.Deserialize(reader); // Remove fill flag for images, because in V1 it was used // to indicate the drawing of the image, but in V2 // It indicates a transparent fill over the image. foreach (VGElement element in actualSlide.VGStimuli) { if (element is VGImage) { element.ShapeDrawAction |= ~ShapeDrawAction.Fill; } if (element is VGText) { element.ShapeDrawAction |= ~ShapeDrawAction.Edge; } if ((int)element.ShapeDrawAction < 0) { element.ShapeDrawAction = ShapeDrawAction.None; } } var slideshow = this as Slideshow; // Add node var slideNode = new SlideshowTreeNode(actualSlide.Name); slideNode.Name = slideshow.GetUnusedNodeID(); slideNode.Slide = actualSlide; slideshow.Nodes.Add(slideNode); } }
/// <summary> /// This method is a custom deserialization with an <see cref="XmlSerializer"/> /// to read the contents of the <see cref="SlideshowTreeNode"/> that /// are exposed in this override. /// It deserializes recursively. /// </summary> /// <param name="reader">The <see cref="XmlReader"/> to use.</param> /// <param name="node">The <see cref="SlideshowTreeNode"/> to deserialize.</param> public override void DeserializeNode(XmlReader reader, SlideshowTreeNode node) { var slideSerializer = new XmlSerializer(typeof(Slide)); var dictionarySerializer = new XmlSerializer(typeof(XMLSerializableDictionary <string, int>)); var browserTreeNode = node as BrowserTreeNode; reader.ReadStartElement("BrowserTreeNode"); reader.ReadStartElement("Name"); browserTreeNode.Name = reader.ReadString(); reader.ReadEndElement(); if (reader.Name == "XMLSerializableDictionaryOfStringInt32") { browserTreeNode.UrlToID = (XMLSerializableDictionary <string, int>)dictionarySerializer.Deserialize(reader); } reader.ReadStartElement("OriginURL"); browserTreeNode.OriginURL = reader.ReadString(); reader.ReadEndElement(); reader.ReadStartElement("Category"); browserTreeNode.Category = reader.ReadString(); reader.ReadEndElement(); reader.ReadStartElement("BrowseDepth"); browserTreeNode.BrowseDepth = reader.ReadContentAsInt(); reader.ReadEndElement(); reader.ReadStartElement("Randomize"); browserTreeNode.Randomize = reader.ReadContentAsBoolean(); reader.ReadEndElement(); reader.ReadStartElement("NumberOfItemsToUse"); browserTreeNode.NumberOfItemsToUse = reader.ReadContentAsInt(); reader.ReadEndElement(); if (reader.Name == "Slide") { browserTreeNode.Slide = (Slide)slideSerializer.Deserialize(reader); } reader.ReadStartElement("Tag"); if (reader.Value != string.Empty) { browserTreeNode.Tag = reader.ReadContentAsString(); reader.ReadEndElement(); } reader.ReadStartElement("Text"); node.Text = reader.ReadContentAsString(); reader.ReadEndElement(); while (reader.Name == "SlideshowTreeNode" && reader.NodeType == XmlNodeType.Element) { SlideshowTreeNode newNode = new SlideshowTreeNode(); base.DeserializeNode(reader, newNode); this.SetTreeNodeImageKey(newNode); node.Nodes.Add(newNode); } reader.ReadEndElement(); reader.MoveToContent(); }
/// <summary> /// This method is a custom serialization with an <see cref="XmlSerializer"/> /// to write the contents of the <see cref="BrowserTreeNode"/> that /// are exposed in this override into an XML file. /// It serializes recursively. /// </summary> /// <param name="writer">The <see cref="XmlWriter"/> to use.</param> /// <param name="node">The <see cref="SlideshowTreeNode"/> to serialize.</param> public override void SerializeNode(XmlWriter writer, SlideshowTreeNode node) { var slideSerializer = new XmlSerializer(typeof(Slide)); var dictionarySerializer = new XmlSerializer(typeof(XMLSerializableDictionary <string, int>)); var browserTreeNode = node as BrowserTreeNode; writer.WriteStartElement("BrowserTreeNode"); writer.WriteStartElement("Name"); writer.WriteValue(browserTreeNode.Name); writer.WriteEndElement(); dictionarySerializer.Serialize(writer, browserTreeNode.UrlToID); writer.WriteStartElement("OriginURL"); writer.WriteValue(browserTreeNode.OriginURL); writer.WriteEndElement(); writer.WriteStartElement("Category"); writer.WriteValue(browserTreeNode.Category); writer.WriteEndElement(); writer.WriteStartElement("BrowseDepth"); writer.WriteValue(browserTreeNode.BrowseDepth); writer.WriteEndElement(); writer.WriteStartElement("Randomize"); writer.WriteValue(browserTreeNode.Randomize); writer.WriteEndElement(); writer.WriteStartElement("NumberOfItemsToUse"); writer.WriteValue(browserTreeNode.NumberOfItemsToUse); writer.WriteEndElement(); if (browserTreeNode.Slide != null) { slideSerializer.Serialize(writer, browserTreeNode.Slide); } writer.WriteStartElement("Tag"); if (browserTreeNode.Tag != null) { writer.WriteValue(browserTreeNode.Tag); } writer.WriteEndElement(); writer.WriteStartElement("Text"); writer.WriteValue(browserTreeNode.Text); writer.WriteEndElement(); if (browserTreeNode.Nodes.Count > 0) { foreach (SlideshowTreeNode subNode in browserTreeNode.Nodes) { // Use base serializer for subnodes, because // they are basically scroll image slides base.SerializeNode(writer, subNode); } } writer.WriteEndElement(); }
/// <summary> /// Initializes a new instance of the SlideshowTreeNode class by cloning /// the given <see cref="SlideshowTreeNode"/> /// </summary> /// <param name="newSlideshowTreeNode">The <see cref="SlideshowTreeNode"/> to clone.</param> public SlideshowTreeNode(SlideshowTreeNode newSlideshowTreeNode) : base(newSlideshowTreeNode.Text) { this.Name = newSlideshowTreeNode.Name; this.Tag = newSlideshowTreeNode.Tag; this.randomize = newSlideshowTreeNode.Randomize; this.numberOfItemsToUse = newSlideshowTreeNode.NumberOfItemsToUse; this.ImageKey = newSlideshowTreeNode.ImageKey; this.Slide = newSlideshowTreeNode.Slide; foreach (SlideshowTreeNode node in newSlideshowTreeNode.Nodes) { this.Nodes.Add((SlideshowTreeNode)node.Clone()); } }
/////////////////////////////////////////////////////////////////////////////// // Methods for doing main class job // /////////////////////////////////////////////////////////////////////////////// #region METHODS /// <summary> /// This method is a custom serialization with an <see cref="XmlSerializer"/> /// to write the contents of the <see cref="SlideshowTreeNode"/> that /// are exposed in this override into an XML file. /// It serializes recursively. /// </summary> /// <param name="writer">The <see cref="XmlWriter"/> to use.</param> /// <param name="node">The <see cref="SlideshowTreeNode"/> to serialize.</param> public virtual void SerializeNode(XmlWriter writer, SlideshowTreeNode node) { var slideSerializer = new XmlSerializer(typeof(Slide)); writer.WriteStartElement("SlideshowTreeNode"); writer.WriteStartElement("Name"); writer.WriteValue(node.Name); writer.WriteEndElement(); writer.WriteStartElement("Randomize"); writer.WriteValue(node.Randomize); writer.WriteEndElement(); writer.WriteStartElement("NumberOfItemsToUse"); writer.WriteValue(node.NumberOfItemsToUse); writer.WriteEndElement(); if (node.Slide != null) { slideSerializer.Serialize(writer, node.Slide); } writer.WriteStartElement("Tag"); if (node.Tag != null) { writer.WriteValue(node.Tag); } writer.WriteEndElement(); writer.WriteStartElement("Text"); writer.WriteValue(node.Text); writer.WriteEndElement(); if (node.Nodes.Count > 0) { foreach (SlideshowTreeNode subNode in node.Nodes) { subNode.SerializeNode(writer, subNode); } } writer.WriteEndElement(); }
/// <summary> /// This method iterates recursively through the tree to return only the trials /// contained in the tree. /// </summary> /// <param name="trials">Ref. The output <see cref="TrialCollection"/> of trials.</param> /// <param name="node">The <see cref="TreeNode"/> to parse.</param> private void ParseNodeForTrials(ref TrialCollection trials, TreeNode node) { Trial trial = null; // Add trial if this node is marked to be a trial if (node.Tag != null && node.Tag.ToString() == "Trial") { trial = new Trial(node.Text, GetIdOfNode(node)); trials.Add(trial); } foreach (TreeNode subNode in node.Nodes) { SlideshowTreeNode subSlideNode = subNode as SlideshowTreeNode; Slide slide = subSlideNode.Slide; if (slide != null) { // By default use a new trial for each slide trial = new Trial(subNode.Text, GetIdOfNode(subNode)); // If parent is already marked as trial use this // instead. if (subNode.Parent != null && subNode.Parent.Tag != null && subNode.Parent.Tag.ToString() == "Trial") { trial = trials[trials.Count - 1]; } // Add slide to correct trial trial.Add(slide); } // Iterate through the tree. this.ParseNodeForTrials(ref trials, subNode); // If a trial was found, add it to the list. if (trial != null && !trials.Contains(trial)) { trials.Add(trial); } } }
/// <summary> /// This method is a custom serialization with an <see cref="XmlSerializer"/> /// to write the contents of the <see cref="Slideshow"/> that /// are exposed in this override into an XML file. /// It serializes recursively. /// </summary> /// <param name="writer">The <see cref="XmlWriter"/> to use.</param> /// <param name="node">The <see cref="SlideshowTreeNode"/> to serialize.</param> public override void SerializeNode(XmlWriter writer, SlideshowTreeNode node) { writer.WriteStartElement("IsModified"); writer.WriteValue(this.IsModified); writer.WriteEndElement(); var shuffleSerializer = new XmlSerializer(typeof(CustomShuffling)); if (this.Shuffling != null) { shuffleSerializer.Serialize(writer, this.Shuffling); } if (node.Nodes.Count > 0) { foreach (SlideshowTreeNode subNode in node.Nodes) { subNode.SerializeNode(writer, subNode); } } }
/// <summary> /// This method is a custom deserialization with an <see cref="XmlSerializer"/> /// to read the contents of the <see cref="Slideshow"/> that /// are exposed in this override. It deserializes recursively. /// </summary> /// <param name="reader">The <see cref="XmlReader"/> to use.</param> /// <param name="node">The <see cref="SlideshowTreeNode"/> to deserialize.</param> public override void DeserializeNode(XmlReader reader, SlideshowTreeNode node) { var shuffleSerializer = new XmlSerializer(typeof(CustomShuffling)); // Check for Versions < Ogama 4.3 if (reader.Name == "SlideshowTreeNode") { base.DeserializeNode(reader, node); return; } reader.ReadStartElement("IsModified"); this.IsModified = reader.ReadContentAsBoolean(); reader.ReadEndElement(); if (reader.Name == "CustomShuffling") { this.shuffling = (CustomShuffling)shuffleSerializer.Deserialize(reader); } while ((reader.Name == "SlideshowTreeNode" && reader.NodeType == XmlNodeType.Element) || (reader.Name == "BrowserTreeNode" && reader.NodeType == XmlNodeType.Element)) { if (reader.Name == "SlideshowTreeNode") { var newNode = new SlideshowTreeNode(); newNode.DeserializeNode(reader, newNode); this.SetTreeNodeImageKey(newNode); node.Nodes.Add(newNode); } else if (reader.Name == "BrowserTreeNode") { var newNode = new BrowserTreeNode(); newNode.DeserializeNode(reader, newNode); this.SetTreeNodeImageKey(newNode); node.Nodes.Add(newNode); } } }
/// <summary> /// This method returns a list of slides from the trial node that is selected /// to be shown before the slide. /// </summary> /// <param name="preSlideNode">The pre slide node.</param> /// <returns>The slide list.</returns> private List <Slide> GetPreSlideTrialSlides(SlideshowTreeNode preSlideNode) { var slides = new List <Slide>(); if (preSlideNode.Tag != null && preSlideNode.Tag.ToString() == "Trial") { foreach (SlideshowTreeNode subNode in preSlideNode.Nodes) { var slide = subNode.Slide; if (slide != null) { slides.Add(slide); } } } else { slides.Add(preSlideNode.Slide); } return(slides); }
/// <summary> /// This method iterates the slideshow recursively to find the /// trial with the given ID. /// </summary> /// <param name="trialID">An <see cref="Int32"/> with the trial id to search for.</param> /// <returns>The <see cref="Trial"/> that matches the trial id.</returns> public Trial GetTrialByID(int trialID) { SlideshowTreeNode trialNode = IterateTreeNodes(trialID.ToString(), this, true); if (trialNode != null) { Trial trial = new Trial(trialNode.Text, trialID); if (trialNode.Slide != null) { if (trialNode.Parent.Tag != null && trialNode.Parent.Tag.ToString() == "Trial") { trial = new Trial(trialNode.Parent.Text, Convert.ToInt32(trialNode.Parent.Name)); foreach (SlideshowTreeNode slideNode in trialNode.Parent.Nodes) { trial.Add((Slide)slideNode.Slide.Clone()); } } else { trial.Add((Slide)trialNode.Slide.Clone()); } } else if (trialNode.Nodes.Count > 0 && (trialNode.Tag != null && trialNode.Tag.ToString() == "Trial")) { foreach (SlideshowTreeNode slideNode in trialNode.Nodes) { trial.Add((Slide)slideNode.Slide.Clone()); } } return(trial); } return(null); }
/// <summary> /// This method deletes the given node from the listview, /// and updates the views. /// </summary> /// <param name="treeNode">The <see cref="SlideshowTreeNode"/> to delete.</param> private void DeleteNode(SlideshowTreeNode treeNode) { if (treeNode == null) { return; } Slide deleteSlide = treeNode.Slide; if (deleteSlide != null) { deleteSlide.Dispose(); } foreach (SlideshowTreeNode subNode in treeNode.Nodes) { this.DeleteNode(subNode); } // Update UrlToID dictionary of BrowserTreeNodes if (treeNode.Parent is BrowserTreeNode) { BrowserTreeNode parent = (BrowserTreeNode)treeNode.Parent; string keyToRemove = string.Empty; foreach (KeyValuePair<string, int> urlToID in parent.UrlToID) { if (urlToID.Value == Convert.ToInt32(treeNode.Name)) { keyToRemove = urlToID.Key; } } if (keyToRemove != string.Empty) { var fileName = Path.Combine(Document.ActiveDocument.ExperimentSettings.SlideResourcesPath, keyToRemove); if (File.Exists(fileName)) { File.Delete(fileName); } parent.UrlToID.Remove(keyToRemove); } } // Update TreeView. treeNode.Remove(); this.UpdateListView(this.trvSlideshow.SelectedNodes); }
/// <summary> /// The <see cref="Control.Click"/> event handler for the /// <see cref="Button"/> <see cref="btnAddFolder"/>. /// Inserts an empty folder for slides in the treeview. /// </summary> /// <param name="sender">Source of the event.</param> /// <param name="e">An empty <see cref="EventArgs"/></param> private void btnAddFolder_Click(object sender, EventArgs e) { SlideshowTreeNode folderNode = new SlideshowTreeNode("SlideFolder"); folderNode.ImageKey = "Folder"; folderNode.Name = this.slideshow.GetUnusedNodeID(); // Get root node for insertion SlideshowTreeNode firstNode = this.trvSlideshow.Nodes[0] as SlideshowTreeNode; // If there is a selected node use this instead NodesCollection selectedNodes = this.trvSlideshow.SelectedNodes; if (selectedNodes.Count > 0) { firstNode = selectedNodes[0] as SlideshowTreeNode; this.trvSlideshow.SelectedNodes.Clear(); } // Add to node, if it is a slide node add to parent instead if (firstNode.Slide != null) { firstNode.Parent.Nodes.Add(folderNode); } else { firstNode.Nodes.Add(folderNode); } // Select added node. this.trvSlideshow.SelectedNodes.Add(folderNode); this.UpdateListView(this.trvSlideshow.SelectedNodes); }
/// <summary> /// This method is a custom deserialization with an <see cref="XmlSerializer"/> /// to read the contents of the <see cref="SlideshowTreeNode"/> that /// are exposed in this override. /// It deserializes recursively. /// </summary> /// <param name="reader">The <see cref="XmlReader"/> to use.</param> /// <param name="node">The <see cref="SlideshowTreeNode"/> to deserialize.</param> public override void DeserializeNode(XmlReader reader, SlideshowTreeNode node) { var slideSerializer = new XmlSerializer(typeof(Slide)); var dictionarySerializer = new XmlSerializer(typeof(XMLSerializableDictionary<string, int>)); var browserTreeNode = node as BrowserTreeNode; reader.ReadStartElement("BrowserTreeNode"); reader.ReadStartElement("Name"); browserTreeNode.Name = reader.ReadString(); reader.ReadEndElement(); if (reader.Name == "XMLSerializableDictionaryOfStringInt32") { browserTreeNode.UrlToID = (XMLSerializableDictionary<string, int>)dictionarySerializer.Deserialize(reader); } reader.ReadStartElement("OriginURL"); browserTreeNode.OriginURL = reader.ReadString(); reader.ReadEndElement(); reader.ReadStartElement("Category"); browserTreeNode.Category = reader.ReadString(); reader.ReadEndElement(); reader.ReadStartElement("BrowseDepth"); browserTreeNode.BrowseDepth = reader.ReadContentAsInt(); reader.ReadEndElement(); reader.ReadStartElement("Randomize"); browserTreeNode.Randomize = reader.ReadContentAsBoolean(); reader.ReadEndElement(); reader.ReadStartElement("NumberOfItemsToUse"); browserTreeNode.NumberOfItemsToUse = reader.ReadContentAsInt(); reader.ReadEndElement(); if (reader.Name == "Slide") { browserTreeNode.Slide = (Slide)slideSerializer.Deserialize(reader); } reader.ReadStartElement("Tag"); if (reader.Value != string.Empty) { browserTreeNode.Tag = reader.ReadContentAsString(); reader.ReadEndElement(); } reader.ReadStartElement("Text"); node.Text = reader.ReadContentAsString(); reader.ReadEndElement(); while (reader.Name == "SlideshowTreeNode" && reader.NodeType == XmlNodeType.Element) { SlideshowTreeNode newNode = new SlideshowTreeNode(); base.DeserializeNode(reader, newNode); this.SetTreeNodeImageKey(newNode); node.Nodes.Add(newNode); } reader.ReadEndElement(); reader.MoveToContent(); }
/// <summary> /// This method is a custom deserialization with an <see cref="XmlSerializer"/> /// to read the contents of the <see cref="SlideshowTreeNode"/> that /// are exposed in this override. /// It deserializes recursively. /// </summary> /// <param name="reader">The <see cref="XmlReader"/> to use.</param> /// <param name="node">The <see cref="SlideshowTreeNode"/> to deserialize.</param> public virtual void DeserializeNode(XmlReader reader, SlideshowTreeNode node) { var slideSerializer = new XmlSerializer(typeof(Slide)); // Check for older versions of Ogama 1.X if (reader.Name == "Slides") { // A List<Slide> instead of SlideshowTreeNodes // in the slideshow node reader.ReadStartElement("Slides"); this.ParseOgamaV1Slideshow(reader, slideSerializer); reader.ReadEndElement(); } else if (reader.Name == "Slide") { // Directly the slides in the slideshow node this.ParseOgamaV1Slideshow(reader, slideSerializer); } else { // Ogama V2 format reader.ReadStartElement("SlideshowTreeNode"); reader.ReadStartElement("Name"); node.Name = reader.ReadString(); reader.ReadEndElement(); reader.ReadStartElement("Randomize"); node.Randomize = reader.ReadContentAsBoolean(); reader.ReadEndElement(); reader.ReadStartElement("NumberOfItemsToUse"); node.NumberOfItemsToUse = reader.ReadContentAsInt(); reader.ReadEndElement(); if (reader.Name == "Slide") { node.Slide = (Slide)slideSerializer.Deserialize(reader); } reader.ReadStartElement("Tag"); if (reader.Value != string.Empty) { node.Tag = reader.ReadContentAsString(); reader.ReadEndElement(); } reader.ReadStartElement("Text"); node.Text = reader.ReadContentAsString(); reader.ReadEndElement(); while ((reader.Name == "SlideshowTreeNode" && reader.NodeType == XmlNodeType.Element) || (reader.Name == "BrowserTreeNode" && reader.NodeType == XmlNodeType.Element)) { if (reader.Name == "SlideshowTreeNode") { var newNode = new SlideshowTreeNode(); newNode.DeserializeNode(reader, newNode); this.SetTreeNodeImageKey(newNode); node.Nodes.Add(newNode); } else if (reader.Name == "BrowserTreeNode") { var newNode = new BrowserTreeNode(); newNode.DeserializeNode(reader, newNode); this.SetTreeNodeImageKey(newNode); node.Nodes.Add(newNode); } } reader.ReadEndElement(); reader.MoveToContent(); } }
/// <summary> /// This method iterates the child nodes of the given <see cref="SlideshowTreeNode"/> to /// find the <see cref="SlideshowTreeNode"/> with the given name using the /// <see cref="TreeNode.Name"/> or <see cref="TreeNode.Text"/> property /// of the <see cref="TreeNode"/> as indicated in the third paramter. /// </summary> /// <param name="name">The <see cref="String"/> with the name or text to search for</param> /// <param name="rootNode">The base <see cref="SlideshowTreeNode"/> to start the search from.</param> /// <param name="useNameProperty"><strong>True</strong>, if the <see cref="TreeNode.Name"/> /// property should be used for checking, <strong>false</strong> if the <see cref="TreeNode.Text"/> /// property should be used.</param> /// <returns>The <see cref="SlideshowTreeNode"/> that matches the search criteria or null if /// no node was found.</returns> public static SlideshowTreeNode IterateTreeNodes(string name, SlideshowTreeNode rootNode, bool useNameProperty) { SlideshowTreeNode returnTreeNode; foreach (SlideshowTreeNode childNode in rootNode.Nodes) { if (CheckNameOrText(childNode, name, useNameProperty)) { return childNode; } else { if (childNode.Nodes.Count > 0) { returnTreeNode = IterateTreeNodes(name, childNode, useNameProperty); if (returnTreeNode != null) { return returnTreeNode; } } } } return null; }
/// <summary> /// This method returns a list of slides from the trial node that is selected /// to be shown before the slide. /// </summary> /// <param name="preSlideNode">The pre slide node.</param> /// <returns>The slide list.</returns> private List<Slide> GetPreSlideTrialSlides(SlideshowTreeNode preSlideNode) { var slides = new List<Slide>(); if (preSlideNode.Tag != null && preSlideNode.Tag.ToString() == "Trial") { foreach (SlideshowTreeNode subNode in preSlideNode.Nodes) { var slide = subNode.Slide; if (slide != null) { slides.Add(slide); } } } else { slides.Add(preSlideNode.Slide); } return slides; }
/// <summary> /// This static method creates a slide with a sized image /// for each trial and adds it to the slideshow. /// </summary> /// <param name="detectonSettings"> /// The <see cref="DetectionSettings"/> /// used in this import. /// </param> /// <param name="mainWindow"> /// The <see cref="MainForm"/> to get access to the status label. /// </param> public static void GenerateOgamaSlideshowTrials(DetectionSettings detectonSettings, MainForm mainWindow) { // Stores found stimuli files List<string> trialNames = Document.ActiveDocument.ExperimentSettings.SlideShow.GetTrialNames(); foreach (KeyValuePair<int, int> kvp in detectonSettings.TrialSequenceToTrialIDAssignments) { int trialID = kvp.Value; string file = string.Empty; if (detectonSettings.TrialIDToImageAssignments.ContainsKey(trialID)) { file = detectonSettings.TrialIDToImageAssignments[trialID]; } string filename = Path.GetFileNameWithoutExtension(file); // Create slide var stopConditions = new StopConditionCollection { new MouseStopCondition( MouseButtons.Left, true, string.Empty, null, Point.Empty) }; VGImage stimulusImage = null; if (file != string.Empty) { stimulusImage = new VGImage( ShapeDrawAction.None, Pens.Black, Brushes.Black, SystemFonts.MenuFont, Color.White, Path.GetFileName(file), Document.ActiveDocument.ExperimentSettings.SlideResourcesPath, ImageLayout.Zoom, 1f, Document.ActiveDocument.PresentationSize, VGStyleGroup.None, filename, string.Empty, true) { Size = Document.ActiveDocument.PresentationSize }; } var newSlide = new Slide( filename, Color.White, null, stopConditions, null, string.Empty, Document.ActiveDocument.PresentationSize) { Modified = true, MouseCursorVisible = true }; // Only add stimulus if an image exists if (file != string.Empty) { newSlide.VGStimuli.Add(stimulusImage); } else { newSlide.Name = "No stimulus detected"; } // Create trial if (Document.ActiveDocument.ExperimentSettings.SlideShow.GetNodeByID(trialID) != null) { // trialID = int.Parse(Document.ActiveDocument.ExperimentSettings.SlideShow.GetUnusedNodeID()); // var message = string.Format("The trial with the ID:{0} exists already in the slideshow so it will not be created." // + Environment.NewLine + "Delete the trial with this ID in the slideshow design module if you want it to be newly created by the importer, or assign a new ID to the imported data.", trialID); // ExceptionMethods.ProcessMessage("This trial exists already", message); continue; } var newTrial = new Trial(filename, trialID) { Name = filename }; newTrial.Add(newSlide); if (trialNames.Contains(filename) || (filename == string.Empty && trialNames.Contains("No stimulus detected"))) { // Trial already exists continue; } trialNames.Add(filename); // Create slide node var slideNode = new SlideshowTreeNode(newSlide.Name) { Name = trialID.ToString(CultureInfo.InvariantCulture), Slide = newSlide }; // Add slide node to slideshow Document.ActiveDocument.ExperimentSettings.SlideShow.Nodes.Add(slideNode); Document.ActiveDocument.Modified = true; } mainWindow.StatusLabel.Text = "Saving slideshow to file ..."; if (!Document.ActiveDocument.SaveSettingsToFile(Document.ActiveDocument.ExperimentSettings.DocumentFilename)) { ExceptionMethods.ProcessErrorMessage("Couldn't save slideshow to experiment settings."); } mainWindow.StatusLabel.Text = "Refreshing context panel ..."; mainWindow.RefreshContextPanelImageTabs(); mainWindow.StatusLabel.Text = "Ready ..."; mainWindow.StatusProgressbar.Value = 0; }
/// <summary> /// This static method draws a chain icon at the bottom right corner of the list view item /// to the given graphics object with the given bounding rect. /// </summary> /// <param name="g">The <see cref="Graphics"/> to draw to.</param> /// <param name="boundsWithoutPadding">The bounding <see cref="Rectangle"/> of the list view item.</param> /// <param name="node">The <see cref="SlideshowTreeNode"/> that is the object of the list view item.</param> protected static void DrawChains(Graphics g, Rectangle boundsWithoutPadding, SlideshowTreeNode node) { if (node.Parent != null) { // Draw Chains int nodesAtLevel = node.Parent.Nodes.Count; if (nodesAtLevel > 1) { bool randomize = ((SlideshowTreeNode)node.Parent).Randomize; if (node.Index == 0) { DrawLeftConnectionPart(g, boundsWithoutPadding, randomize); } else if (node.Index > 0 && node.Index < nodesAtLevel - 1) { DrawLeftConnectionPart(g, boundsWithoutPadding, randomize); DrawRightConnectionPart(g, boundsWithoutPadding, randomize); } else { DrawRightConnectionPart(g, boundsWithoutPadding, randomize); } } } }
/// <summary> /// The web browser_ navigating lock. /// </summary> /// <param name="sender"> /// The sender. /// </param> /// <param name="e"> /// The e. /// </param> private void WebBrowserNavigatingLock(object sender, WebBrowserNavigatingEventArgs e) { try { // Get WebBrowser object var browser = sender as WebBrowser; // Check if website has frames which leads to multiple // calls to navigated for each frame if (this.isWebbrowserClicked) { // Reset flag to avoid multiple trial notifications on frame loading this.isWebbrowserClicked = false; // Disable navigation, if max browse depth is reached this.numberOfTimesNavigated++; if (this.numberOfTimesNavigated >= this.maxBrowseDepth) { if (browser != null) { browser.AllowNavigation = false; } } // Get slideshow var documentsSlideshow = Document.ActiveDocument.ExperimentSettings.SlideShow; // Make screenshot of newly navigated web page in // separate thread, if it is not already there. var screenshotFilename = BrowserDialog.GetFilenameFromUrl(e.Url); // WebsiteScreenshot.Instance.ScreenshotFilename = screenshotFilename; if (!this.currentBrowserTreeNode.UrlToID.ContainsKey(screenshotFilename)) { // This webpage has never been visited before, so get a new id // and create slide and slideshownode for the slideshow var newTrialId = Convert.ToInt32(documentsSlideshow.GetUnusedNodeID()); this.currentBrowserTreeNode.UrlToID.Add(screenshotFilename, newTrialId); // Create new trial var newName = Path.GetFileNameWithoutExtension(screenshotFilename); var newWebpageTrial = new Trial(newName, newTrialId); // Create VGScrollImageSlide var newWebpageSlide = (Slide)this.shownSlideContainer.Slide.Clone(); newWebpageSlide.Modified = true; if (!newWebpageSlide.IsThumbNull) { newWebpageSlide.Thumb.Dispose(); newWebpageSlide.Thumb = null; } newWebpageSlide.Name = newName; newWebpageSlide.ActiveXStimuli.Clear(); newWebpageSlide.VGStimuli.Clear(); var baseUrlScreenshot = new VGScrollImage( ShapeDrawAction.None, Pens.Transparent, Brushes.Black, SystemFonts.DefaultFont, Color.Black, Path.GetFileName(screenshotFilename), Document.ActiveDocument.ExperimentSettings.SlideResourcesPath, ImageLayout.None, 1f, Document.ActiveDocument.PresentationSize, VGStyleGroup.None, newWebpageSlide.Name, string.Empty); newWebpageSlide.VGStimuli.Add(baseUrlScreenshot); // Finish creating new trial newWebpageTrial.Add(newWebpageSlide); // Update trial lists with new trial TrialsIncludingNavigatedWebpages.Add(newWebpageTrial); // Now update slideshow with new trial // Create node var slideNode = new SlideshowTreeNode(newName); slideNode.Name = newTrialId.ToString(CultureInfo.InvariantCulture); slideNode.Slide = newWebpageSlide; // Add node to slideshow at browser tree node subgroup this.currentBrowserTreeNode.Nodes.Add(slideNode); documentsSlideshow.IsModified = true; Document.ActiveDocument.Modified = true; } if (this.getTimeMethod != null) { this.getTimeMethod(); } // Get the corrent trial ID // use the existing, if we have already a screenshot of this url var trialID = this.currentBrowserTreeNode.UrlToID[screenshotFilename]; var trialIndex = TrialsIncludingNavigatedWebpages.GetIndexOfTrialByID(trialID); var trial = TrialsIncludingNavigatedWebpages[trialIndex]; // Reset slide counter // but do not increase trial counter, otherwise // we would have wrong counting in CheckForSlideChange // instead note the trials added this.slideCounter = 0; this.trialsAdded++; // Send counter changed event to increase trial sequence // and update recorders control view this.OnCounterChanged(new CounterChangedEventArgs(trialID, this.slideCounter)); var webcamTime = this.userCamera != null ? this.userCamera.GetCurrentTime() : -1; var navigatedCondition = new NavigatedStopCondition(e.Url); this.OnTrialChanged( new TrialChangedEventArgs( this.shownSlideContainer.Trial, trial, navigatedCondition, "Webpage", this.trialCounter + this.trialsAdded, webcamTime)); // Update shown slide container this.shownSlideContainer.Trial = trial; this.shownSlideContainer.Slide = trial[0]; var fullFile = Path.Combine(Document.ActiveDocument.ExperimentSettings.SlideResourcesPath, screenshotFilename); if (!File.Exists(fullFile)) { // Create screenshot on Document completed event using // the filename stated above var navigateThread = new Thread(this.NavigateMirror); navigateThread.SetApartmentState(ApartmentState.STA); var parameters = new object[2]; parameters[0] = e; parameters[1] = fullFile; navigateThread.Start(parameters); } } } catch (Exception ex) { ExceptionMethods.HandleException(ex); } }
/// <summary> /// This method is a custom serialization with an <see cref="XmlSerializer"/> /// to write the contents of the <see cref="BrowserTreeNode"/> that /// are exposed in this override into an XML file. /// It serializes recursively. /// </summary> /// <param name="writer">The <see cref="XmlWriter"/> to use.</param> /// <param name="node">The <see cref="SlideshowTreeNode"/> to serialize.</param> public override void SerializeNode(XmlWriter writer, SlideshowTreeNode node) { var slideSerializer = new XmlSerializer(typeof(Slide)); var dictionarySerializer = new XmlSerializer(typeof(XMLSerializableDictionary<string, int>)); var browserTreeNode = node as BrowserTreeNode; writer.WriteStartElement("BrowserTreeNode"); writer.WriteStartElement("Name"); writer.WriteValue(browserTreeNode.Name); writer.WriteEndElement(); dictionarySerializer.Serialize(writer, browserTreeNode.UrlToID); writer.WriteStartElement("OriginURL"); writer.WriteValue(browserTreeNode.OriginURL); writer.WriteEndElement(); writer.WriteStartElement("Category"); writer.WriteValue(browserTreeNode.Category); writer.WriteEndElement(); writer.WriteStartElement("BrowseDepth"); writer.WriteValue(browserTreeNode.BrowseDepth); writer.WriteEndElement(); writer.WriteStartElement("Randomize"); writer.WriteValue(browserTreeNode.Randomize); writer.WriteEndElement(); writer.WriteStartElement("NumberOfItemsToUse"); writer.WriteValue(browserTreeNode.NumberOfItemsToUse); writer.WriteEndElement(); if (browserTreeNode.Slide != null) { slideSerializer.Serialize(writer, browserTreeNode.Slide); } writer.WriteStartElement("Tag"); if (browserTreeNode.Tag != null) { writer.WriteValue(browserTreeNode.Tag); } writer.WriteEndElement(); writer.WriteStartElement("Text"); writer.WriteValue(browserTreeNode.Text); writer.WriteEndElement(); if (browserTreeNode.Nodes.Count > 0) { foreach (SlideshowTreeNode subNode in browserTreeNode.Nodes) { // Use base serializer for subnodes, because // they are basically scroll image slides base.SerializeNode(writer, subNode); } } writer.WriteEndElement(); }
/// <summary> /// This method interates through the slide contents of the given /// <see cref="SlideshowTreeNode"/> to set the appropriate slide /// icon in the treeview depending on the slide contents. /// </summary> /// <param name="newSlideshowTreeNode">The <see cref="SlideshowTreeNode"/> /// to set the <see cref="TreeNode.ImageKey"/> value for.</param> public void SetTreeNodeImageKey(SlideshowTreeNode newSlideshowTreeNode) { if (newSlideshowTreeNode is BrowserTreeNode) { newSlideshowTreeNode.ImageKey = "Browser"; return; } if (newSlideshowTreeNode.Slide != null) { newSlideshowTreeNode.slide = (Slide)newSlideshowTreeNode.Slide.Clone(); SlideType currentSlideType = SlideType.None; if (newSlideshowTreeNode.Slide.IsDesktopSlide) { newSlideshowTreeNode.ImageKey = "Desktop"; return; } foreach (VGElement element in newSlideshowTreeNode.slide.VGStimuli) { if (element is VGEllipse || element is VGRectangle || element is VGLine || element is VGCursor || element is VGPolyline || element is VGSharp) { newSlideshowTreeNode.ImageKey = "Shapes"; currentSlideType |= SlideType.Shapes; } else if (element is VGText || element is VGRichText) { newSlideshowTreeNode.ImageKey = "Instructions"; currentSlideType |= SlideType.Instructions; } else if (element is VGImage) { newSlideshowTreeNode.ImageKey = "Images"; currentSlideType |= SlideType.Images; } else if (element is VGSound) { newSlideshowTreeNode.ImageKey = "Sound"; currentSlideType |= SlideType.Sound; } } foreach (VGElement element in newSlideshowTreeNode.slide.ActiveXStimuli) { if (element is VGBrowser) { newSlideshowTreeNode.ImageKey = "Browser"; currentSlideType |= SlideType.Browser; } } if (currentSlideType != SlideType.None) { if (currentSlideType != SlideType.Shapes && currentSlideType != SlideType.Instructions && currentSlideType != SlideType.Flash && currentSlideType != SlideType.Browser && currentSlideType != SlideType.Images && currentSlideType != SlideType.Sound) { newSlideshowTreeNode.ImageKey = "MixedMedia"; } } else { newSlideshowTreeNode.ImageKey = "Blank"; } } else if (newSlideshowTreeNode.Tag != null && newSlideshowTreeNode.Tag.ToString() == "Trial") { newSlideshowTreeNode.ImageKey = "Trial"; } else { newSlideshowTreeNode.ImageKey = "Folder"; } }
/// <summary> /// This method moves the given nodes on level up. /// They can be marked as a trial using the second parameter. /// </summary> /// <param name="nodes">A <see cref="NodesCollection"/> with the <see cref="TreeView"/> nodes.</param> /// <param name="markAsTrial"><strong>True</strong> if the nodes in the collection /// are referred to be one trial in the presentation.</param> private void MoveNodesLevelUp(NodesCollection nodes, bool markAsTrial) { nodes.Sort(); if (nodes.Count > 0) { TreeNode firstNode = nodes[0]; // Skip if the node is the root node if (firstNode.Parent == null) { return; } int insertionIndex = firstNode.Index; string parentNodeName = this.slideshow.GetUnusedNodeID(); SlideshowTreeNode newParent = new SlideshowTreeNode(parentNodeName); newParent.Name = parentNodeName; if (markAsTrial) { newParent.Tag = "Trial"; } newParent.SetTreeNodeImageKey(newParent); firstNode.Parent.Nodes.Insert(insertionIndex, newParent); foreach (TreeNode collectionNode in nodes) { collectionNode.Remove(); newParent.Nodes.Add(collectionNode); } } this.UpdateListView(this.trvSlideshow.SelectedNodes); this.SlideShowModified(); }
/// <summary> /// This method opens the given slide in a new <see cref="SlideDesignModule"/> form /// for modification. /// </summary> /// <param name="treeNode">The <see cref="SlideshowTreeNode"/> that indicates the slide.</param> /// <param name="currentSlide">The <see cref="Slide"/> to be edited.</param> private void OpenSlideDesignForm(SlideshowTreeNode treeNode, Slide currentSlide) { SlideDesignModule newStimulusDesignForm = new SlideDesignModule(StimuliTypes.None); newStimulusDesignForm.Slide = (Slide)currentSlide.Clone(); this.OpenStimulusDesignerForm(newStimulusDesignForm, treeNode.Name); }
/// <summary> /// This method opens the given slide in a new <see cref="SlideDesignModule"/> form /// for modification. /// </summary> /// <param name="treeNode">The <see cref="SlideshowTreeNode"/> that indicates the slide.</param> /// <param name="currentSlide">The <see cref="Slide"/> to be edited.</param> private void OpenDesktopDesignForm(SlideshowTreeNode treeNode, Slide currentSlide) { DesktopDialog newDesktopDesignForm = new DesktopDialog(); newDesktopDesignForm.Slide = (Slide)currentSlide.Clone(); this.OpenDesktopDesignerForm(newDesktopDesignForm, treeNode.Name); }
/// <summary> /// This method interates through the slide contents of the given /// <see cref="SlideshowTreeNode"/> to set the appropriate slide /// icon in the treeview depending on the slide contents. /// </summary> /// <param name="newSlideshowTreeNode">The <see cref="SlideshowTreeNode"/> /// to set the <see cref="TreeNode.ImageKey"/> value for.</param> public void SetTreeNodeImageKey(SlideshowTreeNode newSlideshowTreeNode) { if (newSlideshowTreeNode is BrowserTreeNode) { newSlideshowTreeNode.ImageKey = "Browser"; return; } if (newSlideshowTreeNode.Slide != null) { newSlideshowTreeNode.slide = (Slide)newSlideshowTreeNode.Slide.Clone(); SlideType currentSlideType = SlideType.None; if (newSlideshowTreeNode.Slide.IsDesktopSlide) { newSlideshowTreeNode.ImageKey = "Desktop"; return; } foreach (VGElement element in newSlideshowTreeNode.slide.VGStimuli) { if (element is VGEllipse || element is VGRectangle || element is VGLine || element is VGCursor || element is VGPolyline || element is VGSharp) { newSlideshowTreeNode.ImageKey = "Shapes"; currentSlideType |= SlideType.Shapes; } else if (element is VGText || element is VGRichText) { newSlideshowTreeNode.ImageKey = "Instructions"; currentSlideType |= SlideType.Instructions; } else if (element is VGImage) { newSlideshowTreeNode.ImageKey = "Images"; currentSlideType |= SlideType.Images; } else if (element is VGSound) { newSlideshowTreeNode.ImageKey = "Sound"; currentSlideType |= SlideType.Sound; } } foreach (VGElement element in newSlideshowTreeNode.slide.ActiveXStimuli) { if (element is VGFlash) { newSlideshowTreeNode.ImageKey = "Flash"; currentSlideType |= SlideType.Flash; } else if (element is VGBrowser) { newSlideshowTreeNode.ImageKey = "Browser"; currentSlideType |= SlideType.Browser; } } if (currentSlideType != SlideType.None) { if (currentSlideType != SlideType.Shapes && currentSlideType != SlideType.Instructions && currentSlideType != SlideType.Flash && currentSlideType != SlideType.Browser && currentSlideType != SlideType.Images && currentSlideType != SlideType.Sound) { newSlideshowTreeNode.ImageKey = "MixedMedia"; } } else { newSlideshowTreeNode.ImageKey = "Blank"; } } else if (newSlideshowTreeNode.Tag != null && newSlideshowTreeNode.Tag.ToString() == "Trial") { newSlideshowTreeNode.ImageKey = "Trial"; } else { newSlideshowTreeNode.ImageKey = "Folder"; } }
/// <summary> /// This method adds the given <see cref="Slide"/> at the /// current treeview position. /// </summary> /// <param name="newSlide">The new <see cref="Slide"/> to be added to the slideshow.</param> private void AddSlide(Slide newSlide) { // Add node SlideshowTreeNode slideNode = new SlideshowTreeNode(newSlide.Name); slideNode.Name = this.slideshow.GetUnusedNodeID(); slideNode.Slide = newSlide; ((SlideshowTreeNode)slideNode).SetTreeNodeImageKey((SlideshowTreeNode)slideNode); // Get root node for insertion SlideshowTreeNode firstNode = this.trvSlideshow.Nodes[0] as SlideshowTreeNode; // If there is a selected node use this instead NodesCollection selectedNodes = this.trvSlideshow.SelectedNodes; if (selectedNodes.Count > 0) { firstNode = selectedNodes[0] as SlideshowTreeNode; this.trvSlideshow.SelectedNodes.Clear(); } // Add to node, if it is a slide node add to parent instead if (firstNode.Slide != null) { firstNode.Parent.Nodes.Add(slideNode); } else { firstNode.Nodes.Add(slideNode); } // Select added node. this.trvSlideshow.SelectedNodes.Add(slideNode); this.UpdateListView(this.trvSlideshow.SelectedNodes); }
/////////////////////////////////////////////////////////////////////////////// // Eventhandler // /////////////////////////////////////////////////////////////////////////////// #region EVENTS /////////////////////////////////////////////////////////////////////////////// // Eventhandler for UI, Menu, Buttons, Toolbars etc. // /////////////////////////////////////////////////////////////////////////////// #region WINDOWSEVENTHANDLER #endregion //WINDOWSEVENTHANDLER /////////////////////////////////////////////////////////////////////////////// // Eventhandler for Custom Defined Events // /////////////////////////////////////////////////////////////////////////////// #region CUSTOMEVENTHANDLER #endregion //CUSTOMEVENTHANDLER #endregion //EVENTS /////////////////////////////////////////////////////////////////////////////// // Methods and Eventhandling for Background tasks // /////////////////////////////////////////////////////////////////////////////// #region BACKGROUNDWORKER #endregion //BACKGROUNDWORKER /////////////////////////////////////////////////////////////////////////////// // Methods for doing main class job // /////////////////////////////////////////////////////////////////////////////// #region PRIVATEMETHODS /// <summary> /// This method does the main job of drawing the given <see cref="SlideshowTreeNode"/> /// in the given <see cref="Rectangle"/> using the given <see cref="Graphics"/> object. /// It calls the various drawing methods from the base class <see cref="CustomRenderer"/>. /// </summary> /// <param name="node">The <see cref="SlideshowTreeNode"/> to be rendered.</param> /// <param name="thumbRect">A <see cref="Rectangle"/> with the bounds for the node.</param> /// <param name="g">The <see cref="Graphics"/> to be used.</param> private void DrawNodeInRect(SlideshowTreeNode node, Rectangle thumbRect, Graphics g) { if (thumbRect.Width < 8 || thumbRect.Height < 5) { return; } int nodeCount = node.Nodes.Count; Rectangle newBounds = thumbRect; newBounds.Inflate(-2, -2); int newWidth = newBounds.Width; int newHeight = newBounds.Height; if (nodeCount == 0) { if (node.Slide != null) { if (node.Slide.PresentationSize == Size.Empty) { node.Slide.PresentationSize = Document.ActiveDocument.PresentationSize; } g.DrawImage(node.Slide.Thumb, newBounds); } else { StringFormat sf = new StringFormat(); sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Center; g.DrawString("No slides defined ...", SystemFonts.MenuFont, Brushes.Black, thumbRect, sf); } } else if (nodeCount == 1) { this.DrawNodeInRect((SlideshowTreeNode)node.Nodes[0], newBounds, g); } else if (nodeCount <= 4) { g.DrawRectangle(Pens.DarkGray, newBounds); newWidth = (int)((newBounds.Width - 1) / 2f); newHeight = (int)((newBounds.Height - 1) / 2f); Rectangle subItemRect = new Rectangle(newBounds.Location, new Size(newWidth, newHeight)); for (int i = 0; i < nodeCount; i++) { this.DrawNodeInRect((SlideshowTreeNode)node.Nodes[i], subItemRect, g); if (i % 2 == 0) { subItemRect.Offset(newWidth + 1, 0); } else { subItemRect.Offset(-newWidth - 1, newHeight + 1); } } } else { g.DrawRectangle(Pens.DarkGray, newBounds); newWidth = (int)((newBounds.Width - 2) / 3f); newHeight = (int)((newBounds.Height - 2) / 3f); Rectangle subItemRect = new Rectangle(newBounds.Location, new Size(newWidth, newHeight)); for (int i = 0; i <= Math.Min(9, nodeCount - 1); i++) { if (i == 8 && nodeCount > 9) { int itemsLeft = nodeCount - i; g.DrawString(itemsLeft.ToString() + " more", SystemFonts.MenuFont, Brushes.Red, subItemRect); return; } this.DrawNodeInRect((SlideshowTreeNode)node.Nodes[i], subItemRect, g); if (i == 2 || i == 5) { subItemRect.Offset(-newWidth * 2 - 2, newHeight + 1); } else { subItemRect.Offset(newWidth + 1, 0); } } } }