Ejemplo n.º 1
0
        /// <summary>
        /// Deletes a part.
        /// </summary>
        /// <param name="part">The part to delete.</param>
        public static void Delete(Part part)
        {
            if (part == null)
            {
                throw new ArgumentNullException("part");
            }

            if (part.children != null && part.children.Count > 0)
            {
                throw new ArgumentException("Specified part has children and may not be deleted.", "part");
            }

            // First, get the parent part and delete the child part.
            Part parent = part.parent;

            parent.removeChild(part);

            // Second, do the creepy stalker way of forcing EditorLogic to change the selected part.
            EditorLogic.fetch.OnSubassemblyDialogDismiss(part);

            // Third, ask the editor to destroy the part, which requires the part to have been selected previously.
            EditorLogic.DeletePart(part);

            // Finally, poke the staging logic to sort out any changes due to deleting this part.
            PartWizard.UpdateStaging();
        }
Ejemplo n.º 2
0
 private void OnPartRemoved(GameEvents.HostTargetAction <Part, Part> e)
 {
     if (this.symmetryEditorWindow.Visible)
     {
         if (PartWizard.IsSibling(e.target, this.symmetryEditorWindow.Part))
         {
             this.symmetryEditorWindow.Part = null;
         }
     }
 }
Ejemplo n.º 3
0
        /// <summary>
        /// Determines if a part is deleteable.
        /// </summary>
        /// <param name="part">The part to test for deletability.</param>
        /// <returns>True if the part can be deleted, false if not.</returns>
        /// <remarks>
        /// This method attempts to check for all the reasons a part should not be deleted.
        /// <list type="bullet">
        ///     <listheader>
        ///         <rule>Rule</rule>
        ///         <explanation>Explanation</explanation>
        ///     </listheader>
        ///     <item>
        ///         <rule>The part must not be the root part.</rule>
        ///         <explanation>The root part is given at least some special consideration by the editor that is not handled by the Delete method.</explanation>
        ///     </item>
        ///     <item>
        ///         <rule>The part must not have any child parts attached to it.</rule>
        ///         <explanation>
        ///         Deleting this part would require "stitching" the part's parent part and all of its children, which would be nearly impossible to do
        ///         automatically.
        ///         </explanation>
        ///     </item>
        ///     <item>
        ///         <rule>If the part is symmetrical, it must be eligible for having its symmetry broken.</rule>
        ///         <explanation>See <see cref="HasBreakableSymmetry"/> for details.</explanation>
        ///     </item>
        /// </list>
        /// </remarks>
        public static bool IsDeleteable(Part part)
        {
            if (part == null)
            {
                throw new ArgumentNullException("part");
            }

            string report = default(string);

            return(part.parent != null && (part.children.Count == 0) && (!PartWizard.HasSymmetry(part) || PartWizard.HasBreakableSymmetry(part, out report)));
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Determines if a part is the ancestor of another part.
        /// </summary>
        /// <param name="part">The child part of a lineage to search for the possibleAncestor part.</param>
        /// <param name="possibleAncestor">The part to search for within a lineage.</param>
        /// <returns>True if the possibleAncestor is an ancestor of part, false if not.</returns>
        private static bool IsAncestor(Part part, Part possibleAncestor)
        {
            bool result = false;

            if (part.parent != null)
            {
                if (!(result = (part == possibleAncestor)))
                {
                    result = PartWizard.IsAncestor(part.parent, possibleAncestor);
                }
            }

            return(result);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Determines if any of a part's symmetrical counterparts are also ancestors.
        /// </summary>
        /// <param name="part">The part to examine.</param>
        /// <returns>True if any of the part's symmetrical counterparts are an ancestor.</returns>
        private static bool HasAncestralCounterpart(Part part)
        {
            bool result = false;

            foreach (Part counterpart in part.symmetryCounterparts)
            {
                if (result = (PartWizard.IsAncestor(part, counterpart)))
                {
                    break;
                }
            }

            return(result);
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Determines if any of a part's symmetrical counterparts are also descendants.
        /// </summary>
        /// <param name="part">The part to examine.</param>
        /// <returns>True if any of the part's symmetrical counterparts are descended from part.</returns>
        private static bool HasChildCounterparts(Part part)
        {
            bool result = false;

            foreach (Part counterpart in part.symmetryCounterparts)
            {
                if (PartWizard.IsDescendant(part, counterpart))
                {
                    result = true;
                    break;
                }
            }

            return(result);
        }
Ejemplo n.º 7
0
        public static void Buy(Part part, bool saveGame = false)
        {
            if (part == null)
            {
                throw new ArgumentNullException("part");
            }

            ResearchAndDevelopment.Instance.GetTechState(part.partInfo.TechRequired).partsPurchased.Add(part.partInfo);

            GameEvents.OnPartPurchased.Fire(part.partInfo);

            EditorPartList.Instance.Refresh();

            if (saveGame)
            {
                PartWizard.SaveGame();
            }
        }
Ejemplo n.º 8
0
        private void OnPartRemove(GameEvents.HostTargetAction <Part, Part> e)
        {
            foreach (PartResource partResource in e.target.Resources)
            {
                this.availableResources[partResource.resourceName].PartCount--;

                if (this.availableResources[partResource.resourceName].PartCount == 0)
                {
                    this.availableResources.Remove(partResource.resourceName);
                }
            }

            if (this.symmetryEditorWindow.Visible)
            {
                if (PartWizard.IsSibling(e.target, this.symmetryEditorWindow.Part))
                {
                    this.symmetryEditorWindow.Part = null;
                }
            }
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Determines if a part is descended from another part.
        /// </summary>
        /// <param name="part">The parent part of a lineage to search for the possibleDescendant part.</param>
        /// <param name="possibleDescendant">The part to search for within a lineage.</param>
        /// <returns>True if the possibleDescendant is a descendant of part, false if not.</returns>
        private static bool IsDescendant(Part part, Part possibleDescendant)
        {
            bool result = false;

            foreach (Part childPart in part.children)
            {
                if (childPart == possibleDescendant)
                {
                    result = true;
                    break;
                }
                else if (PartWizard.IsDescendant(childPart, possibleDescendant))
                {
                    result = true;
                    break;
                }
            }

            return(result);
        }
Ejemplo n.º 10
0
        public static void WriteSymmetryReport(Part part)
        {
            Part r = PartWizard.FindSymmetryRoot(part);

            LOG.trace("SYMMETRY REPORT FOR {0}", r.name);
            LOG.trace("Root:");
            LOG.trace("\tname = {0}", r.name);
            LOG.trace("\tsymMethod = {0}", r.symMethod);
            LOG.trace("\tstackSymmetry = {0}", r.stackSymmetry);
            LOG.trace("Counterparts:");
            for (int index = 0; index < r.symmetryCounterparts.Count; index++)
            {
                Part c = r.symmetryCounterparts[index];

                LOG.trace("\t{0} name = {1}", index, c.name);
                LOG.trace("\t{0} symMethod = {1}", index, c.symMethod);
                LOG.trace("\t{0} stackSymmetry = {1}", index, c.stackSymmetry);
                LOG.trace("\t{0} children = {1}", index, c.children.Count);
            }
            LOG.trace("END OF SYMMETRY REPORT");
        }
Ejemplo n.º 11
0
        public override void OnRender()
        {
            try
            {
                List <Part> parts = EditorLogic.fetch.ship != null ? EditorLogic.fetch.ship.Parts : new List <Part>();

                this.highlight.BeginTracking();

                GUILayout.BeginVertical();

                // If Blizzy's toolbar is available, give the user the option to pick the stock toolbar.
                if (ToolbarManager.ToolbarAvailable)
                {
                    bool stockToolbar = PartWizardPlugin.ToolbarIsStock;
                    stockToolbar = GUILayout.Toggle(stockToolbar, "Use KSP's Stock Toolbar", GUILayout.Width(200));
                    if (stockToolbar != PartWizardPlugin.ToolbarIsStock)
                    {
                        PartWizardPlugin.ToolbarTypeToggleActive = true;
                    }
                }

                #region Display Mode Control

                GUILayout.BeginHorizontal();

                this.viewType = (ViewType)GUIControls.HorizontalToggleSet((int)this.viewType, this.viewTypeContents, this.selectedViewTypeStyle, this.unselectedViewTypeStyle);

                GUILayout.EndHorizontal();

                List <Part> buyableParts = null;

                if (this.viewType == ViewType.Hidden)
                {
                    parts = parts.FindAll((p) => { return(p.partInfo.category == PartCategories.none); });
                }
                else if (this.viewType == ViewType.Unavailable)
                {
                    parts = parts.FindAll((p) => {
                        bool result = false;

                        // Get the R&D technology state for the current part.
                        ProtoTechNode techState = ResearchAndDevelopment.Instance.GetTechState(p.partInfo.TechRequired);

                        // If there is a state or the technology is locked or the part hasn't been purchased...
                        if (techState == null || techState.state != RDTech.State.Available || !techState.partsPurchased.Contains(p.partInfo))
                        {
                            // ...add it to the list.
                            result = true;
                        }

                        return(result);
                    });

                    // Stash the filtered list in to the buyable list.
                    buyableParts = parts;
                    // Create a new collection with a copy of all the buyable parts.
                    parts = new List <Part>(buyableParts);
                    // Create a hash set to act as a filter for duplicate parts.
                    HashSet <string> duplicatePartFilter = new HashSet <string>();
                    // Remove each part that has already been added to the hash filter.
                    parts.RemoveAll((p) => !duplicatePartFilter.Add(p.name));

                    // Here parts is a list of unique buyable parts and buyableParts is all of the buyable parts, including duplicates.
                }

                #endregion

                #region Part List

                GUILayout.BeginVertical(GUIControls.PanelStyle);

                this.scrollPosition = GUILayout.BeginScrollView(this.scrollPosition, false, false);

                int totalEntryCost = 0;

                foreach (Part part in parts)
                {
                    totalEntryCost += part.partInfo.entryCost;

                    GUIControls.BeginMouseOverHorizontal();

                    bool actionEditorPartButtonMouseOver = false;

                    #region Part Label

                    if (EditorLogic.fetch.editorScreen != EditorScreen.Actions)
                    {
                        GUILayout.Label(new GUIContent(part.partInfo.title, part.partInfo.name));
                    }
                    else
                    {
                        if (GUIControls.MouseOverButton(new GUIContent(part.partInfo.title, part.partInfo.name), out actionEditorPartButtonMouseOver, this.actionEditorModePartButtonStyle))
                        {
                            // Each part gets the EditorActionPartSelector added to it when the editor switches to the Actions screen. (And it
                            // gets taken away when leaving that screen.)
                            EditorActionPartSelector selector = part.GetComponent <EditorActionPartSelector>();

                            // Make sure we have it...
                            if (selector != null)
                            {
                                // ...and select it.
                                selector.Select();

                                Log.Write("Action editor selecting part {0}.", part.name);
                            }
                        }
                    }

                    #endregion

                    GUILayout.FlexibleSpace();

                    // Only enable the following buttons if there is no actively selected part, but we want to have them drawn.
                    GUI.enabled = EditorLogic.SelectedPart == null;

                    bool deleted = false;                   // Will be set to true if the delete button was pressed.
                    bool bought  = false;                   // Will be set to true if the buy button was pressed.

                    bool breakSymmetryMouseOver = false;    // Will be set to true if the mouse is over the part's break symmetry button.
                    bool deleteButtonMouseOver  = false;    // Will be set to true if the mouse is over the part's delete button.
                    bool buyButtonMouseOver     = false;    // Will be set to true if the mouse is over the part's buy button.

                    string deleteTooltip = default(string);
                    string buyTooltip    = default(string);

                    if (this.viewType == ViewType.All || this.viewType == ViewType.Hidden)
                    {
                        #region Break Symmetry Button

                        string breakabilityReport = default(string);
                        GUI.enabled = EditorLogic.SelectedPart == null && EditorLogic.fetch.editorScreen == EditorScreen.Parts && PartWizard.HasBreakableSymmetry(part, out breakabilityReport);

                        string breakSymmetryTooltip = GUI.enabled ? Localized.BreakSymmetryDescription : default(string);

                        breakSymmetryMouseOver = false;
                        if (GUIControls.MouseOverButton(new GUIContent(Localized.BreakSymmetryButtonText, breakSymmetryTooltip), out breakSymmetryMouseOver, Configuration.PartActionButtonWidth))
                        {
                            this.symmetryEditorWindow.Part = part;

                            if (!this.symmetryEditorWindow.Visible)
                            {
                                this.symmetryEditorWindow.Show(this);

                                // Short circuit the mouse over for breaking symmetry when showing the Symmetry Editor in case it appears over top of this
                                // button and immediately begins highlighting parts. This would cause *this* window's highlighting to be stuck on the part.
                                breakSymmetryMouseOver = false;
                            }
                        }

                        breakSymmetryMouseOver &= GUI.enabled;  // Clear mouse over flag if the symmetry button was disabled.

                        #endregion

                        #region Delete Button

                        GUI.enabled = EditorLogic.SelectedPart == null && EditorLogic.fetch.editorScreen == EditorScreen.Parts && PartWizard.IsDeleteable(part);

                        deleteTooltip = GUI.enabled
                            ? ((part.symmetryCounterparts.Count == 0) ? Localized.DeletePartSingularDescription : Localized.DeletePartPluralDescription)
                            : default(string);

                        if (GUIControls.MouseOverButton(new GUIContent(Localized.DeletePartButtonText, deleteTooltip), out deleteButtonMouseOver, Configuration.PartActionButtonWidth))
                        {
                            PartWizard.Delete(part);

                            // Set a flag so additional GUI logic can decide what to do in the case where a part is deleted.
                            deleted = true;
                        }

                        deleteButtonMouseOver &= GUI.enabled;   // Clear mouse over flag if the delete button was disabled.

                        #endregion
                    }
                    else // this.viewType == ViewType.Unavailable
                    {
                        #region Buy Button

                        GUI.enabled = EditorLogic.SelectedPart == null && (double)part.partInfo.entryCost <= Funding.Instance.Funds && PartWizard.IsBuyable(part);

                        buyTooltip = GUI.enabled ? string.Format(Localized.BuyPartDescriptionTextFormat, part.partInfo.entryCost) : default(string);

                        if (GUIControls.MouseOverButton(new GUIContent(Localized.BuyPartButtonText, buyTooltip), out buyButtonMouseOver, Configuration.PartActionButtonWidth))
                        {
                            Log.Write("Buying part {0}.", part.name);

                            PartWizard.Buy(part, true);

                            // Set a flag so additional GUI logic can decide what to do in the case where a part is bought.
                            bought = true;
                        }

                        buyButtonMouseOver &= GUI.enabled;  // Clear mouse over flag if the buy button was disabled.

                        #endregion
                    }

                    GUI.enabled = true;

                    bool groupMouseOver = false;
                    GUIControls.EndMouseOverHorizontal(out groupMouseOver);     // End of row for this part.

                    // If we deleted a part, then just jump out of the loop since the parts list has been modified.
                    if (deleted || bought)
                    {
                        break;
                    }

                    #region Part Highlighting Control

                    if (breakSymmetryMouseOver)
                    {
                        this.highlight.Add(part, Configuration.HighlightColorEditableSymmetryRoot, Configuration.HighlightColorEditableSymmetryCounterparts, true);
                    }
                    else if (deleteButtonMouseOver)
                    {
                        this.highlight.Add(part, Configuration.HighlightColorDeletablePart, Configuration.HighlightColorDeletableCounterparts, true);
                    }
                    else if (buyButtonMouseOver)
                    {
                        // TODO: Duplicate code!
                        buyableParts.ForEach((p) => {
                            if (part.name == p.name)
                            {
                                this.highlight.Add(p, Configuration.HighlightColorBuyablePart);
                            }
                        });
                    }
                    else if (groupMouseOver)
                    {
                        if (viewType != ViewType.Unavailable)
                        {
                            Color highlightColor            = (part == EditorLogic.RootPart) ? Configuration.HighlightColorRootPart : Configuration.HighlightColorSinglePart;
                            Color counterpartHighlightColor = Configuration.HighlightColorCounterparts;

                            if (EditorLogic.fetch.editorScreen == EditorScreen.Actions)
                            {
                                highlightColor            = Configuration.HighlightColorActionEditorTarget;
                                counterpartHighlightColor = Configuration.HighlightColorActionEditorTarget;
                            }

                            this.highlight.Add(part, highlightColor, counterpartHighlightColor, false);
                        }
                        else
                        {
                            // TODO: Duplicate code!
                            buyableParts.ForEach((p) => {
                                if (part.name == p.name)
                                {
                                    this.highlight.Add(p, Configuration.HighlightColorBuyablePart, false);
                                }
                            });
                        }
                    }
                    else if (actionEditorPartButtonMouseOver)
                    {
                        this.highlight.Add(part, Configuration.HighlightColorActionEditorTarget, Configuration.HighlightColorActionEditorTarget);
                    }

                    #endregion
                }

                GUILayout.EndScrollView();

                GUILayout.EndVertical();

                if (viewType == ViewType.Unavailable)
                {
                    int  buyableEntryCost = 0;
                    bool enableBulkBuy    = false;

                    foreach (Part p in parts)
                    {
                        buyableEntryCost += p.partInfo.entryCost;

                        enableBulkBuy |= PartWizard.IsBuyable(p);
                    }

                    GUI.enabled = parts.Count > 0 && (double)buyableEntryCost <= Funding.Instance.Funds && enableBulkBuy;

                    bool buyAllMouseOver = false;
                    if (GUIControls.MouseOverButton(new GUIContent(string.Format(Localized.BuyAllButtonTextFormat, buyableEntryCost)), out buyAllMouseOver))
                    {
                        foreach (Part part in parts)
                        {
                            Log.Write("Buying part {0}.", part.name);

                            PartWizard.Buy(part);
                        }

                        PartWizard.SaveGame();
                    }

                    // TODO: Highlight all parts that will be bought by clicking Buy All.
                    if (buyAllMouseOver)
                    {
                        buyableParts.ForEach((p) => {
                            this.highlight.Add(p, Configuration.HighlightColorBuyablePart);
                        });
                    }

                    GUI.enabled = true;
                }

                #endregion

                #region Status Area

                GUILayout.Space(3);

                string status = default(string);

                if (!string.IsNullOrEmpty(GUI.tooltip))
                {
                    if (parts.Count != 1)
                    {
                        status = string.Format(CultureInfo.CurrentCulture, Localized.StatusLabelPluralTooltipTextFormat, parts.Count, GUI.tooltip);
                    }
                    else
                    {
                        status = string.Format(CultureInfo.CurrentCulture, Localized.StatusLabelSingularTooltipTextFormat, parts.Count, GUI.tooltip);
                    }
                }
                else
                {
                    if (parts.Count != 1)
                    {
                        status = string.Format(CultureInfo.CurrentCulture, Localized.StatusLabelPluralTextFormat, parts.Count);
                    }
                    else
                    {
                        status = string.Format(CultureInfo.CurrentCulture, Localized.StatusLabelSingularTextFormat, parts.Count);
                    }
                }

                GUILayout.Label(status, this.tooltipLabelStyle);

                #endregion

                GUILayout.EndVertical();

                if (this.Visible && this.mouseOver)
                {
                    this.highlight.EndTracking();
                }
                else
                {
                    this.highlight.CancelTracking();
                }
            }
            catch (Exception e)
            {
                Log.Write("PartWizardWindow.OnRender() unexpected exception caught.");

                Log.Write(e.Message);
                Log.Write(e.StackTrace);

                this.highlight.CancelTracking();

                throw;
            }
            finally
            {
                GUI.DragWindow();
            }
        }
Ejemplo n.º 12
0
        public override void OnRender()
        {
            try
            {
                List <Part> parts = EditorLogic.fetch.ship != null ? EditorLogic.fetch.ship.Parts : new List <Part>();

                this.highlight.BeginTracking();

                GUILayout.BeginVertical();

#if false
                // If Blizzy's toolbar is available, give the user the option to pick the stock toolbar.
                if (ToolbarManager.ToolbarAvailable)
#endif
                {
                    //bool stockToolbar = PartWizardPlugin.ToolbarIsStock;
                    bool stockToolbar = GUILayout.Toggle(PartWizardPlugin.ToolbarIsStock, Localized.UseStockToolbar, GUILayout.Width(200));
                    if (stockToolbar != PartWizardPlugin.ToolbarIsStock)
                    {
#if false
                        PartWizardPlugin.ToolbarTypeToggleActive = true;
#endif
                        PartWizardPlugin.ToolbarIsStock = stockToolbar;
                        PartWizardPlugin.Instance.SaveToolbarConfiguration();
                    }
                }

                #region Display Mode Control

                GUILayout.BeginHorizontal();
                this.viewType = (ViewType)GUIControls.HorizontalToggleSet((int)this.viewType, this.viewTypeContents, this.selectedViewTypeStyle, this.unselectedViewTypeStyle);
                GUILayout.EndHorizontal();

                GUILayout.BeginHorizontal();
                GUILayout.Label("Sort by:");
                this.sortBy = (SortBy)GUIControls.HorizontalToggleSet((int)this.sortBy, this.sortTypeContents, this.selectedViewTypeStyle, this.unselectedViewTypeStyle);
                GUILayout.EndHorizontal();

                List <Part> buyableParts = null;

                if (this.viewType == ViewType.Hidden)
                {
                    parts = parts.FindAll((p) => { return(p.partInfo.category == PartCategories.none); });
                }
                else if (this.viewType == ViewType.Unavailable)
                {
                    parts = parts.FindAll((p) => {
                        bool result = false;

                        // Get the R&D technology state for the current part.
                        ProtoTechNode techState = ResearchAndDevelopment.Instance.GetTechState(p.partInfo.TechRequired);

                        // If there is a state or the technology is locked or the part hasn't been purchased...
                        if (techState == null || techState.state != RDTech.State.Available || !techState.partsPurchased.Contains(p.partInfo))
                        {
                            // ...add it to the list.
                            result = true;
                        }

                        return(result);
                    });
                    Debug.Log("total # buyable part: " + parts.Count.ToString());
                    // Stash the filtered list in to the buyable list.
                    buyableParts = parts;
                    // Create a new collection with a copy of all the buyable parts.
                    parts = new List <Part>(buyableParts);
                    // Create a hash set to act as a filter for duplicate parts.
                    HashSet <string> duplicatePartFilter = new HashSet <string>();
                    // Remove each part that has already been added to the hash filter.
                    parts.RemoveAll((p) => !duplicatePartFilter.Add(p.name));

                    // Here parts is a list of unique buyable parts and buyableParts is all of the buyable parts, including duplicates.
                    Debug.Log("total # buyable part after dup filter: " + parts.Count.ToString());
                }

                #endregion
                if (parts != null && parts.Count > 0)
                {
                    switch (sortBy)
                    {
                    case SortBy.Name:
                        parts.Sort((p, q) => p.partInfo.title.CompareTo(q.partInfo.title));
                        break;

                    case SortBy.StageAsc:
                        if (this.viewType != ViewType.Unavailable)
                        {
                            parts.Sort((p, q) => p.inverseStage.CompareTo(q.inverseStage));
                        }
                        else
                        {
                            parts.Sort((p, q) => p.partInfo.title.CompareTo(q.partInfo.title));
                        }
                        break;

                    case SortBy.StageDesc:
                        if (this.viewType != ViewType.Unavailable)
                        {
                            parts.Sort((q, p) => p.inverseStage.CompareTo(q.inverseStage));
                        }
                        else
                        {
                            parts.Sort((p, q) => p.partInfo.title.CompareTo(q.partInfo.title));
                        }
                        break;
                    }
                }
                #region Part List

                GUILayout.BeginVertical(GUIControls.PanelStyle);

                this.scrollPosition = GUILayout.BeginScrollView(this.scrollPosition, false, false);

                int totalEntryCost   = 0;
                int visiblePartCount = 0;
                int lastStage        = 0;
                if (parts != null && parts.Count > 0)
                {
                    lastStage = parts[0].inverseStage;
                }

                if (this.viewType == ViewType.Category)
                {
                    if (GUILayout.Button(Localized.ViewAll))
                    {
                        for (int i = 0; i < visibleCategories.Length; i++)
                        {
                            visibleCategories[i] = true;
                        }
                    }
                    if (GUILayout.Button(Localized.Clear))
                    {
                        for (int i = 0; i < visibleCategories.Length; i++)
                        {
                            visibleCategories[i] = false;
                        }
                    }

                    for (PartCategories partCategories = PartCategories.Propulsion; partCategories < PartCategories.Coupling; partCategories++)
                    {
                        // Need to add one to the PartCategories because "none" is a -1, and some parts have a category = none
                        visibleCategories[(int)partCategories + 1] = GUILayout.Toggle(visibleCategories[(int)partCategories + 1], partCategories.ToString(), toggleStyle);
                    }
                }
                else if (this.viewType == ViewType.Resources)
                {
                    if (GUILayout.Button(Localized.ViewAll))
                    {
                        foreach (ResourceInfo resourceInfo in this.availableResources.Values)
                        {
                            resourceInfo.Visible = true;
                        }
                    }

                    if (GUILayout.Button(Localized.Clear))
                    {
                        foreach (ResourceInfo resourceInfo in this.availableResources.Values)
                        {
                            resourceInfo.Visible = false;
                        }
                    }

                    foreach (string availableResource in this.availableResources.Keys)
                    {
                        bool resourceVisible = GUILayout.Toggle(this.availableResources[availableResource].Visible, availableResource, toggleStyle);

                        this.availableResources[availableResource].Visible = resourceVisible;
                    }
                }
                else
                {
                    foreach (Part part in parts)
                    {
                        // Reset part name label color to default; some conditions may change the color to indicate various things.
                        labelStyle.normal.textColor = PartWizardWindow.DefaultPartNameColor;
                        // Check if this part's category is currently visible.
                        // Need to add one to the PartCategories because "none" is a -1, and some parts have a category = none
                        if (visibleCategories[(int)part.partInfo.category + 1])
                        {
                            // The part's category is visible, now check resource conditions to determine final visibility.
                            bool partVisible = false;

                            if (this.availableResources[Localized.ShowPartsWithoutResources].Visible && part.Resources.Count == 0)
                            {
                                partVisible = true;
                            }
                            else
                            {
                                foreach (PartResource partResource in part.Resources)
                                {
                                    if (this.availableResources[partResource.resourceName].Visible)
                                    {
                                        partVisible = true;
                                        break;
                                    }
                                }
                            }
                            if (partVisible)
                            {
                                totalEntryCost += part.partInfo.entryCost;
                                visiblePartCount++;

                                GUIControls.BeginMouseOverHorizontal();

                                bool actionEditorPartButtonMouseOver = false;

                                #region Part Label

                                if (sortBy == SortBy.StageAsc || sortBy == SortBy.StageDesc)
                                {
                                    if (lastStage != part.inverseStage)
                                    {
                                        lastStage = part.inverseStage;
                                    }
                                    GUILayout.Label(lastStage.ToString() + ": ");
                                }
                                if (EditorLogic.fetch.editorScreen != EditorScreen.Actions)
                                {
                                    // Check compound parts for integrity.
                                    if (part is CompoundPart)
                                    {
                                        CompoundPart compoundPart = (CompoundPart)part;
                                        if (compoundPart.attachState == CompoundPart.AttachState.Detached || compoundPart.attachState == CompoundPart.AttachState.Attaching || compoundPart.target == compoundPart.parent)
                                        {
                                            labelStyle.normal.textColor = Color.red;
                                        }
                                    }
                                    labelStyle.fixedWidth = 250;
                                    GUILayout.Label(new GUIContent(part.partInfo.title, part.partInfo.name), labelStyle);
                                }
                                else
                                {
                                    Log.Write("EditorScreen.Actions, part: " + part.partInfo.title);
                                    if (GUIControls.MouseOverButton(new GUIContent(part.partInfo.title, part.partInfo.name), out actionEditorPartButtonMouseOver, this.actionEditorModePartButtonStyle))
                                    {
                                        // Each part gets the EditorActionPartSelector added to it when the editor switches to the Actions screen. (And it
                                        // gets taken away when leaving that screen.)
                                        EditorActionPartSelector selector = part.GetComponent <EditorActionPartSelector>();

                                        // Make sure we have it...
                                        if (selector != null)
                                        {
                                            // ...and select it.
                                            selector.Select();

                                            Log.Write("Action editor selecting part {0}.", part.name);
                                        }
                                    }
                                }

                                #endregion

                                // Adds space between the part name and the buttons (if any) associated with the part.
                                GUILayout.FlexibleSpace();

                                // Only enable the following buttons if there is no actively selected part, but we want to have them drawn.
                                GUI.enabled = EditorLogic.SelectedPart == null;

                                bool deleted = false;                   // Will be set to true if the delete button was pressed.
                                bool bought  = false;                   // Will be set to true if the buy button was pressed.

                                bool breakSymmetryMouseOver = false;    // Will be set to true if the mouse is over the part's break symmetry button.
                                bool deleteButtonMouseOver  = false;    // Will be set to true if the mouse is over the part's delete button.
                                bool buyButtonMouseOver     = false;    // Will be set to true if the mouse is over the part's buy button.

                                string deleteTooltip = default(string);
                                string buyTooltip    = default(string);

                                if (this.viewType == ViewType.All || this.viewType == ViewType.Hidden)
                                {
                                    #region Break Symmetry Button

                                    string breakabilityReport = default(string);
                                    GUI.enabled = EditorLogic.SelectedPart == null && EditorLogic.fetch.editorScreen == EditorScreen.Parts && PartWizard.HasBreakableSymmetry(part, out breakabilityReport);

                                    string breakSymmetryTooltip = GUI.enabled ? Localized.BreakSymmetryDescription : default(string);

                                    breakSymmetryMouseOver = false;
                                    if (GUIControls.MouseOverButton(new GUIContent(Localized.BreakSymmetryButtonText, breakSymmetryTooltip), out breakSymmetryMouseOver, Configuration.PartActionButtonWidth))
                                    {
                                        this.symmetryEditorWindow.Part = part;

                                        if (!this.symmetryEditorWindow.Visible)
                                        {
                                            this.symmetryEditorWindow.Show(this);

                                            // Short circuit the mouse over for breaking symmetry when showing the Symmetry Editor in case it appears over top of this
                                            // button and immediately begins highlighting parts. This would cause *this* window's highlighting to be stuck on the part.
                                            breakSymmetryMouseOver = false;
                                        }
                                    }

                                    breakSymmetryMouseOver &= GUI.enabled;  // Clear mouse over flag if the symmetry button was disabled.

                                    #endregion

                                    #region Delete Button

                                    GUI.enabled = EditorLogic.SelectedPart == null && EditorLogic.fetch.editorScreen == EditorScreen.Parts && PartWizard.IsDeleteable(part);

                                    deleteTooltip = GUI.enabled
                                        ? ((part.symmetryCounterparts.Count == 0) ? Localized.DeletePartSingularDescription : Localized.DeletePartPluralDescription)
                                        : default(string);

                                    if (GUIControls.MouseOverButton(new GUIContent(Localized.DeletePartButtonText, deleteTooltip), out deleteButtonMouseOver, Configuration.PartActionButtonWidth))
                                    {
                                        PartWizard.Delete(part);

                                        // Set a flag so additional GUI logic can decide what to do in the case where a part is deleted.
                                        deleted = true;
                                    }

                                    deleteButtonMouseOver &= GUI.enabled;   // Clear mouse over flag if the delete button was disabled.

                                    #endregion
                                }
                                else // this.viewType == ViewType.Unavailable
                                {
                                    #region Buy Button

                                    GUI.enabled = EditorLogic.SelectedPart == null && (double)part.partInfo.entryCost <= Funding.Instance.Funds && PartWizard.IsBuyable(part);

                                    buyTooltip = GUI.enabled ? string.Format(Localized.BuyPartDescriptionTextFormat, part.partInfo.entryCost) : default(string);

                                    if (GUIControls.MouseOverButton(new GUIContent(Localized.BuyPartButtonText, buyTooltip), out buyButtonMouseOver, Configuration.PartActionButtonWidth))
                                    {
                                        Log.Write("Buying part {0}.", part.name);

                                        PartWizard.Buy(part, true);

                                        // Set a flag so additional GUI logic can decide what to do in the case where a part is bought.
                                        bought = true;
                                    }

                                    buyButtonMouseOver &= GUI.enabled;  // Clear mouse over flag if the buy button was disabled.

                                    #endregion
                                }

                                GUI.enabled = true;

                                bool groupMouseOver = false;
                                GUIControls.EndMouseOverHorizontal(out groupMouseOver);     // End of row for this part.

                                // If we deleted a part, then just jump out of the loop since the parts list has been modified.
                                if (deleted || bought)
                                {
                                    break;
                                }

                                #region Part Highlighting Control

                                if (breakSymmetryMouseOver)
                                {
                                    this.highlight.Add(part, Configuration.HighlightColorEditableSymmetryRoot, Configuration.HighlightColorEditableSymmetryCounterparts, true);
                                }
                                else if (deleteButtonMouseOver)
                                {
                                    this.highlight.Add(part, Configuration.HighlightColorDeletablePart, Configuration.HighlightColorDeletableCounterparts, true);
                                }
                                else if (buyButtonMouseOver)
                                {
                                    // TODO: Duplicate code!
                                    buyableParts.ForEach((p) => {
                                        if (part.name == p.name)
                                        {
                                            this.highlight.Add(p, Configuration.HighlightColorBuyablePart);
                                        }
                                    });
                                }
                                else if (groupMouseOver)
                                {
                                    if (viewType != ViewType.Unavailable)
                                    {
                                        Color highlightColor            = (part == EditorLogic.RootPart) ? Configuration.HighlightColorRootPart : Configuration.HighlightColorSinglePart;
                                        Color counterpartHighlightColor = Configuration.HighlightColorCounterparts;

                                        if (EditorLogic.fetch.editorScreen == EditorScreen.Actions)
                                        {
                                            highlightColor            = Configuration.HighlightColorActionEditorTarget;
                                            counterpartHighlightColor = Configuration.HighlightColorActionEditorTarget;
                                        }

                                        this.highlight.Add(part, highlightColor, counterpartHighlightColor, false);
                                    }
                                    else
                                    {
                                        // TODO: Duplicate code!
                                        buyableParts.ForEach((p) => {
                                            if (part.name == p.name)
                                            {
                                                Log.Write("Highlighting 2 part: " + part.partInfo.title);
                                                this.highlight.Add(p, Configuration.HighlightColorBuyablePart, false);
                                            }
                                        });
                                    }
                                }
                                else if (actionEditorPartButtonMouseOver)
                                {
                                    Log.Write("Highlighting part: " + part.partInfo.title);
                                    this.highlight.Add(part, Configuration.HighlightColorActionEditorTarget, Configuration.HighlightColorActionEditorTarget);
                                }

                                #endregion
                            }
                        }
                    }
                }

                GUILayout.EndScrollView();

                GUILayout.EndVertical();

                if (viewType == ViewType.Unavailable)
                {
                    int  buyableEntryCost = 0;
                    bool enableBulkBuy    = false;

                    foreach (Part p in parts)
                    {
                        buyableEntryCost += p.partInfo.entryCost;

                        enableBulkBuy |= PartWizard.IsBuyable(p);
                    }

                    GUI.enabled = parts.Count > 0 && (double)buyableEntryCost <= Funding.Instance.Funds && enableBulkBuy;

                    bool buyAllMouseOver = false;
                    if (GUIControls.MouseOverButton(new GUIContent(string.Format(Localized.BuyAllButtonTextFormat, buyableEntryCost)), out buyAllMouseOver))
                    {
                        foreach (Part part in parts)
                        {
                            if (PartWizard.IsBuyable(part))
                            {
                                Log.Write("Buying part {0}.", part.name);

                                PartWizard.Buy(part, false);
                            }
                        }

                        PartWizard.SaveGame();
                    }

                    // TODO: Highlight all parts that will be bought by clicking Buy All.
                    if (buyAllMouseOver)
                    {
                        buyableParts.ForEach((p) => {
                            this.highlight.Add(p, Configuration.HighlightColorBuyablePart);
                        });
                    }

                    GUI.enabled = true;
                }

                #endregion

                #region Status Area

                // Push everything above this up, otherwise it will be centered vertically.
                GUILayout.FlexibleSpace();

                if (viewType == ViewType.All || viewType == ViewType.Hidden || viewType == ViewType.Unavailable)
                {
                    string status = default(string);

                    if (!string.IsNullOrEmpty(GUI.tooltip))
                    {
                        if (parts.Count != 1)
                        {
                            status = string.Format(CultureInfo.CurrentCulture, Localized.StatusLabelPluralTooltipTextFormat, visiblePartCount, GUI.tooltip, parts.Count - visiblePartCount);
                        }
                        else
                        {
                            status = string.Format(CultureInfo.CurrentCulture, Localized.StatusLabelSingularTooltipTextFormat, visiblePartCount, GUI.tooltip, parts.Count - visiblePartCount);
                        }
                    }
                    else
                    {
                        if (parts.Count != 1)
                        {
                            status = string.Format(CultureInfo.CurrentCulture, Localized.StatusLabelPluralTextFormat, visiblePartCount, parts.Count - visiblePartCount);
                        }
                        else
                        {
                            status = string.Format(CultureInfo.CurrentCulture, Localized.StatusLabelSingularTextFormat, visiblePartCount, parts.Count - visiblePartCount);
                        }
                    }

                    GUILayout.Label(status, this.tooltipLabelStyle);
                }

                #endregion

                GUILayout.EndVertical();

                if (this.Visible && this.mouseOver)
                {
                    this.highlight.EndTracking();
                }
                else
                {
                    this.highlight.CancelTracking();
                }
            }
            catch (Exception e)
            {
                Log.Write("PartWizardWindow.OnRender() unexpected exception caught.");

                Log.Write(e.Message);
                Log.Write(e.StackTrace);

                this.highlight.CancelTracking();

                throw;
            }
            finally
            {
                GUI.DragWindow();
            }
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Determines if a part has breakable symmetry.
        /// </summary>
        /// <param name="part">The part to test for breakable symmetry.</param>
        /// <param name="report">A string containing a short rationale for the breakability result.</param>
        /// <returns>True if the part has symmetry that can be broken, false if not.</returns>
        /// <remarks>
        /// This method attempts to check for all the reasons a symmetrical part should not have its symmetry broken.
        /// <list type="bullet">
        ///     <listheader>
        ///         <rule>Rule</rule>
        ///         <explanation>Explanation</explanation>
        ///     </listheader>
        ///     <item>
        ///         <rule>The part must have symmetry.</rule>
        ///         <explanation>A part cannot have symmetry broken if it is not symmetrical with at least one other part.</explanation>
        ///     </item>
        ///     <item>
        ///         <rule>The symmetrical counterparts must not be descendants of the part.</rule>
        ///         <explanation>
        ///         This would create a circular reference during the breaking process. This can happen if one of the symmetrical parts or one of its descendants
        ///         has been made the root part.
        ///         </explanation>
        ///     </item>
        ///     <item>
        ///         <rule>The symmetrical counterparts must not be ancestors of the part.</rule>
        ///         <explanation>
        ///         This would create a logical error in symmetrical breakability where one counterpart may be determined breakable while another may not, based on
        ///         the rule set. This can happen if one of the symmetrical parts or one of its descendants has been made the root part.
        ///         </explanation>
        ///     </item>
        ///     <item>
        ///         <rule>The part, or one of its counterparts, must not also be the root part.</rule>
        ///         <explanation>
        ///         The symmetry breaking process requires that all child parts also have their symmetry broken. The root part's child parts include all
        ///         of the parts of a vessel, including parts that are not descended from the symmetrical root. Symmetry can not be reliable determined
        ///         due to the root part not having a parent, and as a rule, symmetrical parts share a common parent - either the same part ("symmetrical root")
        ///         or each is the child of an identical counterpart.
        ///         </explanation>
        ///     </item>
        ///     <item>
        ///         <rule>A child part must either not have symmetry or have breakable symmetry.</rule>
        ///         <explanation>
        ///         In the case where a non-symmetrical part was added as a child, symmetry is breakable. However, if a symmetrical part is added as a child, it must
        ///         be breakable due to the requirement that all symmetrical descendants of a part have their symmetry broken.
        ///         </explanation>
        ///     </item>
        /// </list>
        /// </remarks>
        public static bool HasBreakableSymmetry(Part part, out string report)
        {
            if (part == null)
            {
                throw new ArgumentNullException("part");
            }

            bool result = false;

            report = default(string);

            if (result = (PartWizard.HasSymmetry(part)))
            {
                if (result = !PartWizard.HasChildCounterparts(part))
                {
                    if (result = !PartWizard.HasAncestralCounterpart(part))
                    {
                        if (result = (part.parent != null))
                        {
                            foreach (Part childPart in part.children)
                            {
                                string internalBreakabilityReport = default(string);
                                if (!(result = (!PartWizard.HasSymmetry(childPart) || PartWizard.HasBreakableSymmetry(childPart, out internalBreakabilityReport))))
                                {
                                    report = string.Format(CultureInfo.CurrentCulture, Localized.NotBreakableChildNotBreakable, part.name, childPart.name);

                                    break;
                                }
                            }

                            if (result)
                            {
                                foreach (Part counterpart in part.symmetryCounterparts)
                                {
                                    if (!(result = (counterpart.parent != null)))
                                    {
                                        report = string.Format(CultureInfo.CurrentCulture, Localized.NotBreakableCounterpartHasNoParent, part.name, counterpart.name);

                                        break;
                                    }
                                }
                            }
                        }
                        else
                        {
                            report = string.Format(CultureInfo.CurrentCulture, Localized.NotBreakableNoParent, part.name);
                        }
                    }
                    else
                    {
                        report = string.Format(CultureInfo.CurrentCulture, Localized.NotBreakableHasAncestralCounterpart, part.name);
                    }
                }
                else
                {
                    report = string.Format(CultureInfo.CurrentCulture, Localized.NotBreakableHasDescendantCounterpart, part.name);
                }
            }
            else
            {
                report = string.Format(CultureInfo.CurrentCulture, Localized.NotBreakableNotSymmetrical, part.name);
            }

            if (result)
            {
                report = string.Format(CultureInfo.CurrentCulture, Localized.Breakable, part.name);
            }

            return(result);
        }
Ejemplo n.º 14
0
        public static void CreateSymmetry(Part symmetricRoot, List <Part> counterparts)
        {
            if (symmetricRoot == null)
            {
                throw new ArgumentNullException("symmetricRoot");
            }

            if (counterparts == null)
            {
                throw new ArgumentNullException("counterparts");
            }

            // The symmetric root's method is always radial.
            symmetricRoot.symMethod = SymmetryMethod.Radial;

            // Clear out the current list of counterparts, we'll recreate this as we go along.
            symmetricRoot.symmetryCounterparts.Clear();

            // Update all the counterparts...
            for (int index = 0; index < counterparts.Count; index++)
            {
                Part counterpart = counterparts[index];

                // Each counterpart must be added to the symmetric root's list.
                symmetricRoot.symmetryCounterparts.Add(counterpart);
                // Clear the counterpart's list.
                counterpart.symmetryCounterparts.Clear();
                // Set the appropriate symmetry method.
                counterpart.symMethod = SymmetryMethod.Radial;
                // Add the symmetrical root part as a counterpart.
                counterpart.symmetryCounterparts.Add(symmetricRoot);

                // Add all the *other* counterparts.
                for (int siblingIndex = 0; siblingIndex < counterparts.Count; siblingIndex++)
                {
                    if (index != siblingIndex)
                    {
                        counterpart.symmetryCounterparts.Add(counterparts[siblingIndex]);
                    }
                }
            }

            // Now we must deal with the children...if they have symmetry.
            foreach (Part child in symmetricRoot.children)
            {
                if (PartWizard.HasSymmetry(child))
                {
                    // A list to hold the relevant counterparts. We only care about the counterparts that descend from the counterparts we've been given.
                    List <Part> childSymmetricCounterparts = new List <Part>();

                    // Look through all the child's counterparts to find the ones relevant to our counterparts...
                    foreach (Part childSymmetricCounterpart in child.symmetryCounterparts)
                    {
                        if (counterparts.Contains(childSymmetricCounterpart.parent))
                        {
                            // ...and add those to the list.
                            childSymmetricCounterparts.Add(childSymmetricCounterpart);
                        }
                    }

                    // We now have a part and a list of parts to make symmetrical, which we already know how to do!
                    PartWizard.CreateSymmetry(child, childSymmetricCounterparts);
                }
            }

            // Poke the staging logic to sort out any changes due to modifying the symmetry of this part.
            Staging.SortIcons();
        }
Ejemplo n.º 15
0
        public override void OnRender()
        {
            try
            {
                GUILayout.BeginVertical();

                this.scrollPosition = GUILayout.BeginScrollView(scrollPosition, false, false);

                this.highlight.BeginTracking();

                if (this.mouseOver)
                {
                    this.highlight.Add(this.part, Configuration.HighlightColorSymmetryEditor, Configuration.HighlightColorSymmetryEditor);
                }

                for (int index = 0; index < this.symmetryGroups.Count; index++)
                {
                    PartGroup group = this.symmetryGroups[index];

                    GUIControls.BeginMouseOverVertical(GUIControls.PanelStyle);

                    GUILayout.BeginHorizontal();

                    GUILayout.Label(new GUIContent(string.Format(CultureInfo.CurrentCulture, Localized.GroupLabelText, index + 1)));

                    // Don't allow group removal if there is only one group.
                    GUI.enabled = (this.symmetryGroups.Count > 1);

                    if (GUILayout.Button(SymmetryEditorWindow.RemoveGroupButtonText))
                    {
                        // If there's a group above, use it. If not, then use the one below.
                        PartGroup destinationGroup = (index > 0) ? this.symmetryGroups[index - 1] : this.symmetryGroups[index + 1];

                        destinationGroup.MergeFrom(group);

                        this.symmetryGroups.Remove(group);

                        break;
                    }

                    GUILayout.EndHorizontal();

                    GUI.enabled = true;

                    bool mouseOverPart = false;

                    foreach (Part groupPart in group.Parts)
                    {
                        GUIControls.BeginMouseOverHorizontal();

                        GUILayout.Label(new GUIContent(groupPart.partInfo.title));

                        GUI.enabled = index < this.symmetryGroups.Count - 1;

                        if (GUILayout.Button(SymmetryEditorWindow.MoveDownButtonText, Configuration.PartActionButtonWidth))
                        {
                            PartGroup nextGroup = this.symmetryGroups[index + 1];

                            group.MoveTo(groupPart, nextGroup);

                            break;
                        }

                        GUI.enabled = index > 0;

                        if (GUILayout.Button(SymmetryEditorWindow.MoveUpButtonText, Configuration.PartActionButtonWidth))
                        {
                            PartGroup previousGroup = this.symmetryGroups[index - 1];

                            group.MoveTo(groupPart, previousGroup);

                            break;
                        }

                        GUI.enabled = true;

                        bool mouseOverPartArea = false;
                        GUIControls.EndMouseOverVertical(out mouseOverPartArea);

                        if (mouseOverPartArea)
                        {
                            // First add the group with the child part color, recursively.
                            this.highlight.Add(group, Configuration.HighlightColorEditableSymmetryChildParts, true);
                            // Next add the group with the counterparts highlighted, non-recursively.
                            this.highlight.Add(group, Configuration.HighlightColorCounterparts, false);
                            // Last add the specific part, non-recursively.
                            this.highlight.Add(groupPart, Configuration.HighlightColorSinglePart, false);

                            mouseOverPart = true;
                        }
                    }

                    bool groupMouseOver = false;
                    GUIControls.EndMouseOverVertical(out groupMouseOver);

                    if (!mouseOverPart && groupMouseOver)
                    {
                        this.highlight.Add(group, Configuration.HighlightColorEditableSymmetryCounterparts, true);
                    }
                }

                // Enable the Add Group button only if there is enough symmetrical parts to fill it.
                GUI.enabled = (this.symmetryGroups.Count < (this.part.symmetryCounterparts.Count + 1));

                if (GUILayout.Button(SymmetryEditorWindow.AddGroupButtonText))
                {
                    this.symmetryGroups.Add(new PartGroup());
                }

                GUI.enabled = true;

                GUILayout.EndScrollView();

                GUILayout.Space(4);

                GUILayout.BeginHorizontal();

                #region OK Button

                if (GUILayout.Button(Localized.OK))
                {
                    int symmetricGroupsCreated = 0;
                    int partsProcessed         = 0;

                    foreach (PartGroup group in this.symmetryGroups)
                    {
                        if (group.Parts.Count > 0)
                        {
                            partsProcessed += group.Count;

                            Part symmetricRoot = group.Extract(0);

                            PartWizard.CreateSymmetry(symmetricRoot, group.Parts);

                            symmetricGroupsCreated++;

#if DEBUG
                            Log.WriteSymmetryReport(symmetricRoot);
#endif
                        }
                    }

                    Log.Write("Modified symmetry for {0}, creating {1} symmetric group(s) from {2} parts.", part.name, symmetricGroupsCreated, partsProcessed);

                    this.Hide();
                }

                #endregion

                #region Cancel Button

                if (GUILayout.Button(Localized.Cancel))
                {
                    this.Hide();
                }

                #endregion

                GUILayout.EndHorizontal();

                GUILayout.EndVertical();
            }
            catch (Exception)
            {
                highlight.CancelTracking();

                throw;
            }
            finally
            {
                GUI.DragWindow();

                if (this.Visible && this.mouseOver)
                {
                    this.highlight.EndTracking();
                }
                else
                {
                    this.highlight.CancelTracking();
                }
            }
        }