/// <summary>
        /// Attempts to refresh broken thumbnails across all visualization sets in the project.
        /// </summary>
        /// <param name="project">Project reference.</param>
        public static void RefreshAllThumbnails(DisasmProject project)
        {
            ScriptSupport iapp = null;
            Dictionary <string, IPlugin> plugins = null;

            SortedList <int, VisualizationSet> visSets = project.VisualizationSets;

            foreach (KeyValuePair <int, VisualizationSet> kvp in visSets)
            {
                VisualizationSet visSet = kvp.Value;
                foreach (Visualization vis in visSet)
                {
                    if (vis.HasImage)
                    {
                        continue;
                    }
                    //Debug.WriteLine("Vis needs refresh: " + vis.Tag);

                    if (vis is VisualizationAnimation)
                    {
                        continue;
                    }

                    if (iapp == null)
                    {
                        // Prep the plugins on first need.
                        iapp = new ScriptSupport();
                        project.PrepareScripts(iapp);
                    }

                    if (plugins == null)
                    {
                        plugins = project.GetActivePlugins();
                    }
                    IPlugin_Visualizer vplug = FindPluginByVisGenIdent(plugins,
                                                                       vis.VisGenIdent, out VisDescr visDescr);
                    if (vplug == null)
                    {
                        Debug.WriteLine("Unable to refresh " + vis.Tag + ": plugin not found");
                        continue;
                    }

                    IVisualization2d vis2d;
                    try {
                        vis2d = vplug.Generate2d(visDescr,
                                                 new ReadOnlyDictionary <string, object>(vis.VisGenParams));
                        if (vis2d == null)
                        {
                            Debug.WriteLine("Vis generator returned null");
                        }
                    } catch (Exception ex) {
                        Debug.WriteLine("Vis generation failed: " + ex);
                        vis2d = null;
                    }
                    if (vis2d != null)
                    {
                        //Debug.WriteLine(" Rendered thumbnail: " + vis.Tag);
                        vis.SetThumbnail(vis2d);
                    }
                }
            }

            if (iapp != null)
            {
                project.UnprepareScripts();
            }

            // Now that we've generated images for the Visualizations, update any
            // VisualizationAnimation thumbnails that may have been affected.
            foreach (KeyValuePair <int, VisualizationSet> kvp in visSets)
            {
                VisualizationSet visSet = kvp.Value;
                foreach (Visualization vis in visSet)
                {
                    if (!(vis is VisualizationAnimation))
                    {
                        continue;
                    }
                    VisualizationAnimation visAnim = (VisualizationAnimation)vis;
                    visAnim.GenerateImage(visSets);
                }
            }
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="owner">Owner window.</param>
        /// <param name="proj">Project reference.</param>
        /// <param name="formatter">Text formatter.</param>
        /// <param name="vis">Visualization to edit, or null if this is new.</param>
        public EditVisualization(Window owner, DisasmProject proj, Formatter formatter,
                                 int setOffset, SortedList <int, VisualizationSet> editedList, Visualization vis)
        {
            InitializeComponent();
            Owner       = owner;
            DataContext = this;

            mProject    = proj;
            mFormatter  = formatter;
            mSetOffset  = setOffset;
            mEditedList = editedList;
            mOrigVis    = vis;

            mScriptSupport = new ScriptSupport(this);
            mProject.PrepareScripts(mScriptSupport);

            // this will initialize mTagLabelBrush
            if (vis != null)
            {
                TagString = vis.Tag;
            }
            else
            {
                // Could make this unique, but probably not worth the bother.
                TagString = "vis" + mSetOffset.ToString("x6");
            }

            int visSelection = -1;

            VisualizationList = new List <VisualizationItem>();
            Dictionary <string, IPlugin> plugins = proj.GetActivePlugins();

            foreach (KeyValuePair <string, IPlugin> kvp in plugins)
            {
                if (!(kvp.Value is IPlugin_Visualizer))
                {
                    continue;
                }
                IPlugin_Visualizer vplug = (IPlugin_Visualizer)kvp.Value;
                foreach (VisDescr descr in vplug.GetVisGenDescrs())
                {
                    if (vis != null && vis.VisGenIdent == descr.Ident)
                    {
                        // found matching descriptor, set selection to this
                        visSelection = VisualizationList.Count;
                    }
                    else if (visSelection < 0 && descr.Ident == sLastVisIdent)
                    {
                        // we used this one last time, use it if nothing better comes along
                        visSelection = VisualizationList.Count;
                    }
                    VisualizationList.Add(new VisualizationItem(kvp.Key, descr));
                }
            }

            // Set the selection.  This should cause the sel change event to fire.
            if (visSelection < 0)
            {
                visSelection = 0;
            }
            visComboBox.SelectedIndex = visSelection;
        }