From 125080dbda9a850a86d5d6ebc1c0ba4c1182c8c8 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Mon, 2 Dec 2019 16:38:32 -0800 Subject: [PATCH] Finish basic visualization editor implementation The Visualization and Visualization Set editors are now fully functional. You can create, edit, and rearrange sets, and they're now stored in the project file. --- ImageSrc/RedX.xcf | Bin 0 -> 2081 bytes SourceGen/DisplayList.cs | 16 +- SourceGen/LineListGen.cs | 4 +- SourceGen/MainController.cs | 2 +- SourceGen/ProjectFile.cs | 84 ++++++++ SourceGen/Res/RedX.png | Bin 0 -> 470 bytes SourceGen/Res/Strings.xaml | 4 + SourceGen/Res/Strings.xaml.cs | 8 + SourceGen/RuntimeData/Apple/VisHiRes.cs | 38 ++-- SourceGen/SourceGen.csproj | 3 + SourceGen/Visualization.cs | 30 ++- SourceGen/VisualizationSet.cs | 24 ++- SourceGen/WpfGui/EditVisualization.xaml | 27 ++- SourceGen/WpfGui/EditVisualization.xaml.cs | 198 +++++++++++++----- SourceGen/WpfGui/EditVisualizationSet.xaml | 24 ++- SourceGen/WpfGui/EditVisualizationSet.xaml.cs | 150 ++++++++++--- 16 files changed, 480 insertions(+), 132 deletions(-) create mode 100644 ImageSrc/RedX.xcf create mode 100644 SourceGen/Res/RedX.png diff --git a/ImageSrc/RedX.xcf b/ImageSrc/RedX.xcf new file mode 100644 index 0000000000000000000000000000000000000000..95a66568d524e03650f0bf91c78609241233d735 GIT binary patch literal 2081 zcmeH{%Wl(95QZHmZtiXF1p&gPR3t({iG+~4BBG0m1ro22I*w~KiL1ngb_GwtG%J6eC}st&!1o0x6ai5G#CyIVw6bu8>*vBhDQ?>&Pa)f7Rn(L zbrP0#ZQ7sXRgaJkuY#FB>6N-u&k1J1((cCAUwRi~d-!S)hdoGPlS!dR*NcC3O&e|pOEc2- zc6B{={Do(~d7Vr_^6qcRB-g)L_@V7DoJ)5l{e$Ur<%XLDO7A~Y>&$LfIR_P)@9HJR z5g?M&Ex#K(SKczXS;R*uHzTo!eU(Pi_-QUb+?&#*Al*A1f{?wiTBZR}@zj zAHXtS8hJqGmhn###*F8}5rH2o*40Kj4f97z_$ZN#OPD{`9zRDsMTGfl?NYj@k(J0a z%;?mxHj?)~GRc`nHhPKHoQrxaQ8AreQJ(pgCUYqPGU9;Yc-lN{T zJ`;Ub`V95i>ND4Oq3=lFoxW3j*SZEw)|}SHZq00KXNdi JbNt_`{0;l6)m;Dp literal 0 HcmV?d00001 diff --git a/SourceGen/DisplayList.cs b/SourceGen/DisplayList.cs index f219d51..d56c96e 100644 --- a/SourceGen/DisplayList.cs +++ b/SourceGen/DisplayList.cs @@ -449,9 +449,21 @@ namespace SourceGen { return parts; } - public static FormattedParts CreateVisualizationSet(string summary) { + public static FormattedParts CreateVisualizationSet(VisualizationSet visSet) { FormattedParts parts = new FormattedParts(); - parts.Comment = summary; + if (visSet.Count == 0) { + // not expected + parts.Comment = "!EMPTY!"; + } else { + string fmt; + if (visSet.Count == 1) { + fmt = Res.Strings.VIS_SET_SINGLE_FMT; + } else { + fmt = Res.Strings.VIS_SET_MULTIPLE_FMT; + } + parts.Comment = string.Format(fmt, visSet[0].Tag, visSet.Count - 1); + } + // TODO(xyzzy): show image thumbnails parts.IsVisualizationSet = true; return parts; } diff --git a/SourceGen/LineListGen.cs b/SourceGen/LineListGen.cs index 18c4a58..0bb9088 100644 --- a/SourceGen/LineListGen.cs +++ b/SourceGen/LineListGen.cs @@ -506,11 +506,9 @@ namespace SourceGen { parts = GenerateLvTableLine(line.FileOffset, line.SubLineIndex); break; case Line.Type.VisualizationSet: - // TODO(xyzzy) mProject.VisualizationSets.TryGetValue(line.FileOffset, out VisualizationSet visSet); - parts = FormattedParts.CreateVisualizationSet("!VISUALIZATION SET! " + - (visSet != null ? "VS:" + visSet.Count : "???")); + parts = FormattedParts.CreateVisualizationSet(visSet); break; case Line.Type.Blank: // Nothing to do. diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs index 25c960a..ca43677 100644 --- a/SourceGen/MainController.cs +++ b/SourceGen/MainController.cs @@ -2161,7 +2161,7 @@ namespace SourceGen { mProject.VisualizationSets.TryGetValue(offset, out VisualizationSet curVisSet); EditVisualizationSet dlg = new EditVisualizationSet(mMainWin, mProject, - mOutputFormatter, curVisSet); + mOutputFormatter, curVisSet, offset); if (dlg.ShowDialog() != true) { return; } diff --git a/SourceGen/ProjectFile.cs b/SourceGen/ProjectFile.cs index 5a81b47..3d39cd8 100644 --- a/SourceGen/ProjectFile.cs +++ b/SourceGen/ProjectFile.cs @@ -359,6 +359,29 @@ namespace SourceGen { ClearPrevious = varTab.ClearPrevious; } } + public class SerVisualization { + public string Tag { get; set; } + public string VisGenIdent { get; set; } + public Dictionary VisGenParams { get; set; } + + public SerVisualization() { } + public SerVisualization(Visualization vis) { + Tag = vis.Tag; + VisGenIdent = vis.VisGenIdent; + VisGenParams = vis.VisGenParams; // Dictionary + } + } + public class SerVisualizationSet { + public List Items { get; set; } + + public SerVisualizationSet() { } + public SerVisualizationSet(VisualizationSet visSet) { + Items = new List(visSet.Count); + foreach (Visualization vis in visSet) { + Items.Add(new SerVisualization(vis)); + } + } + } // Fields are serialized to/from JSON. Do not change the field names. public int _ContentVersion { get; set; } @@ -374,6 +397,7 @@ namespace SourceGen { public Dictionary UserLabels { get; set; } public Dictionary OperandFormats { get; set; } public Dictionary LvTables { get; set; } + public Dictionary VisualizationSets { get; set; } /// /// Serializes a DisasmProject into an augmented JSON string. @@ -463,6 +487,11 @@ namespace SourceGen { spf.LvTables.Add(kvp.Key.ToString(), new SerLocalVariableTable(kvp.Value)); } + spf.VisualizationSets = new Dictionary(); + foreach (KeyValuePair kvp in proj.VisualizationSets) { + spf.VisualizationSets.Add(kvp.Key.ToString(), new SerVisualizationSet(kvp.Value)); + } + spf.ProjectProps = new SerProjectProperties(proj.ProjectProps); JavaScriptSerializer ser = new JavaScriptSerializer(); @@ -714,6 +743,25 @@ namespace SourceGen { } } + // Deserialize visualization sets. These were added in v1.5. + if (spf.VisualizationSets != null) { + foreach (KeyValuePair kvp in spf.VisualizationSets) { + if (!ParseValidateKey(kvp.Key, spf.FileDataLength, + Res.Strings.PROJECT_FIELD_LV_TABLE, report, out int intKey)) { + continue; + } + + if (!CreateVisualizationSet(kvp.Value, report, out VisualizationSet visSet)) { + report.Add(FileLoadItem.Type.Warning, + string.Format(Res.Strings.ERR_BAD_VISUALIZATION_SET_FMT, intKey)); + continue; + } + + proj.VisualizationSets[intKey] = visSet; + + } + } + return true; } @@ -924,6 +972,42 @@ namespace SourceGen { return true; } + private static bool CreateVisualizationSet(SerVisualizationSet serVisSet, + FileLoadReport report, out VisualizationSet outVisSet) { + outVisSet = new VisualizationSet(); + foreach (SerVisualization serVis in serVisSet.Items) { + string trimTag = Visualization.TrimAndValidateTag(serVis.Tag, out bool isTagValid); + if (!isTagValid) { + Debug.WriteLine("Visualization with invalid tag: " + serVis.Tag); + string str = string.Format(Res.Strings.ERR_BAD_VISUALIZATION_FMT, trimTag); + report.Add(FileLoadItem.Type.Warning, str); + continue; + } + if (string.IsNullOrEmpty(serVis.VisGenIdent) || serVis.VisGenParams == null) { + string str = string.Format(Res.Strings.ERR_BAD_VISUALIZATION_FMT, + "ident/params"); + report.Add(FileLoadItem.Type.Warning, str); + continue; + } + + // The JavaScript deserialization turns floats into Decimal. Change it back + // so we don't have to deal with it later. + Dictionary parms = + new Dictionary(serVis.VisGenParams.Count); + foreach (KeyValuePair kvp in serVis.VisGenParams) { + object val = kvp.Value; + if (val is decimal) { + val = (double)((decimal)val); + } + parms.Add(kvp.Key, val); + } + + Visualization vis = new Visualization(serVis.Tag, serVis.VisGenIdent, parms); + outVisSet.Add(vis); + } + return true; + } + /// /// Parses an integer key that was stored as a string, and checks to see if the /// value falls within an acceptable range. diff --git a/SourceGen/Res/RedX.png b/SourceGen/Res/RedX.png new file mode 100644 index 0000000000000000000000000000000000000000..c5d1c95358dca8b57715249efbeafffd950f3681 GIT binary patch literal 470 zcmV;{0V)28P)@h{qaA)6575WUe!MhxKE~#E2%HXZNiLJBLqpPl**B^fZ!!cbADn) z!75($ED(iY8LxgAVk20`n>-D&5Io||9*4aVyy8vQ1H2GCgVC81XIu z76`0(HvkO+Gu{Unknown Source or Type in symbol Bad symbol reference part Type hint not recognized + Invalid visualization item: {0} + Invalid visualization set at +{0:x6} Removed duplicate label '{0}' (offset +{1:x6}) Failed copying {0} to {1}: {2}. The file {0} exists, but is not a directory. @@ -173,4 +175,6 @@ limitations under the License. [new project] *READ-ONLY* [unset] + Vis: {0} (+{1} more) + Vis: {0} \ No newline at end of file diff --git a/SourceGen/Res/Strings.xaml.cs b/SourceGen/Res/Strings.xaml.cs index 6fbbd16..acaa626 100644 --- a/SourceGen/Res/Strings.xaml.cs +++ b/SourceGen/Res/Strings.xaml.cs @@ -89,6 +89,10 @@ namespace SourceGen.Res { (string)Application.Current.FindResource("str_ErrBadSymrefPart"); public static string ERR_BAD_TYPE_HINT = (string)Application.Current.FindResource("str_ErrBadTypeHint"); + public static string ERR_BAD_VISUALIZATION_FMT = + (string)Application.Current.FindResource("str_ErrBadVisualizationFmt"); + public static string ERR_BAD_VISUALIZATION_SET_FMT = + (string)Application.Current.FindResource("str_ErrBadVisualizationSetFmt"); public static string ERR_DUPLICATE_LABEL_FMT = (string)Application.Current.FindResource("str_ErrDuplicateLabelFmt"); public static string ERR_FILE_COPY_FAILED_FMT = @@ -327,5 +331,9 @@ namespace SourceGen.Res { (string)Application.Current.FindResource("str_TitleReadOnly"); public static string UNSET = (string)Application.Current.FindResource("str_Unset"); + public static string VIS_SET_MULTIPLE_FMT = + (string)Application.Current.FindResource("str_VisSetMultipleFmt"); + public static string VIS_SET_SINGLE_FMT = + (string)Application.Current.FindResource("str_VisSetSingleFmt"); } } diff --git a/SourceGen/RuntimeData/Apple/VisHiRes.cs b/SourceGen/RuntimeData/Apple/VisHiRes.cs index 29e5fc3..6b01b49 100644 --- a/SourceGen/RuntimeData/Apple/VisHiRes.cs +++ b/SourceGen/RuntimeData/Apple/VisHiRes.cs @@ -29,9 +29,9 @@ namespace RuntimeData.Apple { private byte[] mFileData; private AddressTranslate mAddrTrans; - // Visualization identifiers; DO NOT change or projects will break. + // Visualization identifiers; DO NOT change or projects that use them will break. private const string VIS_GEN_BITMAP = "apple2-hi-res-bitmap"; - private const string VIS_GEN_MULTI_MAP = "apple2-hi-res-multi-map"; + private const string VIS_GEN_BITMAP_FONT = "apple2-hi-res-bitmap-font"; private const string P_OFFSET = "offset"; private const string P_BYTE_WIDTH = "byteWidth"; @@ -52,8 +52,7 @@ namespace RuntimeData.Apple { new VisDescr(VIS_GEN_BITMAP, "Apple II Hi-Res Bitmap", VisDescr.VisType.Bitmap, new VisParamDescr[] { new VisParamDescr("File offset (hex)", - P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, - 0x2000), + P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, 0), new VisParamDescr("Width (in bytes)", P_BYTE_WIDTH, typeof(int), 1, 40, 0, 1), new VisParamDescr("Height", @@ -69,11 +68,10 @@ namespace RuntimeData.Apple { new VisParamDescr("Test Float", "floaty", typeof(float), -5.0f, 5.0f, 0, 0.1f), }), - new VisDescr(VIS_GEN_MULTI_MAP, "Apple II Hi-Res Multi-Map", VisDescr.VisType.Bitmap, + new VisDescr(VIS_GEN_BITMAP_FONT, "Apple II Hi-Res Bitmap Font", VisDescr.VisType.Bitmap, new VisParamDescr[] { new VisParamDescr("File offset (hex)", - P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, - 0x1000), + P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, 0), new VisParamDescr("Item width (in bytes)", P_ITEM_BYTE_WIDTH, typeof(int), 1, 40, 0, 1), new VisParamDescr("Item height", @@ -109,8 +107,8 @@ namespace RuntimeData.Apple { switch (descr.Ident) { case VIS_GEN_BITMAP: return GenerateBitmap(parms); - case VIS_GEN_MULTI_MAP: - // TODO + case VIS_GEN_BITMAP_FONT: + // TODO (xyzzy) return null; default: mAppRef.DebugLog("Unknown ident " + descr.Ident); @@ -140,16 +138,24 @@ namespace RuntimeData.Apple { if (offset < 0 || offset >= mFileData.Length || byteWidth <= 0 || byteWidth > MAX_DIM || - height <= 0 || height > MAX_DIM || - colStride <= 0 || colStride > MAX_DIM || - rowStride < byteWidth * colStride - (colStride-1) || rowStride > MAX_DIM) { + height <= 0 || height > MAX_DIM) { + // the UI should flag these based on range (and ideally wouldn't have called us) mAppRef.DebugLog("Invalid parameter"); return null; } + if (colStride <= 0 || colStride > MAX_DIM) { + mAppRef.DebugLog("Invalid column stride"); + return null; + } + if (rowStride < byteWidth * colStride - (colStride-1) || rowStride > MAX_DIM) { + mAppRef.DebugLog("Invalid row stride"); + return null; + } int lastOffset = offset + rowStride * height - (colStride - 1) - 1; if (lastOffset >= mFileData.Length) { - mAppRef.DebugLog("Bitmap runs off end of file (lastOffset=" + lastOffset + ")"); + mAppRef.DebugLog("Bitmap runs off end of file (last offset +" + + lastOffset.ToString("x6") + ")"); return null; } @@ -235,7 +241,7 @@ namespace RuntimeData.Apple { } #else // Color conversion similar to what CiderPress does, but without the half-pixel - // shift (we're trying to create a 1:1 bitmap). + // shift (we're trying to create a 1:1 bitmap, not 1:2). bool[] lineBits = new bool[byteWidth * 7]; bool[] hiFlags = new bool[byteWidth * 7]; // overkill, but simplifies things int[] colorBuf = new int[byteWidth * 7]; @@ -326,7 +332,7 @@ namespace RuntimeData.Apple { White1 = 7 } - // HiRes: black0, green, purple, white0, black1, orange, blue, white1 + // Maps HiResColors to the palette entries. private static readonly byte[] sHiResColorMap = new byte[8] { 1, 3, 4, 2, 1, 5, 6, 2 }; @@ -334,7 +340,7 @@ namespace RuntimeData.Apple { private void SetHiResPalette(VisBitmap8 vb) { // These don't match directly to hi-res color numbers because we want to // avoid adding black/white twice. - vb.AddColor(0xff, 0xff, 0, 0); // 0=transparent (xyzzy: all 0) + vb.AddColor(0, 0, 0, 0); // 0=transparent vb.AddColor(0xff, 0x00, 0x00, 0x00); // 1=black0/black1 vb.AddColor(0xff, 0xff, 0xff, 0xff); // 2=white0/white1 vb.AddColor(0xff, 0x11, 0xdd, 0x00); // 3=green diff --git a/SourceGen/SourceGen.csproj b/SourceGen/SourceGen.csproj index 2dc19cc..d15603c 100644 --- a/SourceGen/SourceGen.csproj +++ b/SourceGen/SourceGen.csproj @@ -419,5 +419,8 @@ + + + \ No newline at end of file diff --git a/SourceGen/Visualization.cs b/SourceGen/Visualization.cs index 021ef63..2ba1693 100644 --- a/SourceGen/Visualization.cs +++ b/SourceGen/Visualization.cs @@ -38,6 +38,13 @@ namespace SourceGen { /// public Dictionary VisGenParams { get; private set; } + /// + /// Cached reference to thumbnail. + /// + /// + /// Because the underlying data never changes, we only need to regenerate the + /// thumbnail if the set of active plugins changes. + /// private BitmapSource Thumbnail { get; set; } // TODO - 64x64(?) bitmap @@ -54,11 +61,30 @@ namespace SourceGen { VisGenParams = visGenParams; } + /// + /// Trims a tag, removing leading/trailing whitespace, and checks it for validity. + /// + /// Tag to trim and validate. + /// Set to true if the tag is valid. + /// Trimmed tag string. Returns an empty string if tag is null. + public static string TrimAndValidateTag(string tag, out bool isValid) { + if (tag == null) { + isValid = false; + return string.Empty; + } + + string trimTag = tag.Trim(); + if (trimTag.Length < 2) { + isValid = false; + } else { + isValid = true; + } + return trimTag; + } + /// /// Converts an IVisualization2d to a BitmapSource for display. /// - /// - /// public static BitmapSource ConvertToBitmapSource(IVisualization2d vis2d) { // Create indexed color palette. int[] intPal = vis2d.GetPalette(); diff --git a/SourceGen/VisualizationSet.cs b/SourceGen/VisualizationSet.cs index 78f82e7..34068bc 100644 --- a/SourceGen/VisualizationSet.cs +++ b/SourceGen/VisualizationSet.cs @@ -16,12 +16,14 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Text; namespace SourceGen { /// /// Ordered list of visualization objects. /// + /// + /// Right now the only thing separating this from a plain List<> is the operator== stuff. + /// public class VisualizationSet : IEnumerable { /// /// Object list. @@ -32,9 +34,9 @@ namespace SourceGen { /// /// Constructor. /// - /// Initial capacity. - public VisualizationSet(int cap = 1) { - mList = new List(cap); + /// Initial capacity. + public VisualizationSet(int initialCap = 1) { + mList = new List(initialCap); } // IEnumerable @@ -54,10 +56,24 @@ namespace SourceGen { get { return mList.Count; } } + /// + /// Accesses the Nth element. + /// + /// Element number. + public Visualization this[int key] { + get { + return mList[key]; + } + } + public void Add(Visualization vis) { mList.Add(vis); } + public void Remove(Visualization vis) { + mList.Remove(vis); + } + public override string ToString() { return "[VS: " + mList.Count + " items]"; diff --git a/SourceGen/WpfGui/EditVisualization.xaml b/SourceGen/WpfGui/EditVisualization.xaml index 0ca2d0e..56c448f 100644 --- a/SourceGen/WpfGui/EditVisualization.xaml +++ b/SourceGen/WpfGui/EditVisualization.xaml @@ -25,7 +25,6 @@ limitations under the License. Title="Edit Visualization" Width="460" SizeToContent="Height" ResizeMode="NoResize" ShowInTaskbar="False" WindowStartupLocation="CenterOwner" - Loaded="Window_Loaded" Closed="Window_Closed"> @@ -41,7 +40,7 @@ limitations under the License. Text="{Binding UiString}" Foreground="{Binding ForegroundBrush}"/> - @@ -58,7 +57,7 @@ limitations under the License. Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}" FontFamily="{StaticResource GeneralMonoFont}" TextChanged="TextBox_TextChanged"/> - @@ -75,7 +74,7 @@ limitations under the License. Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}" FontFamily="{StaticResource GeneralMonoFont}" TextChanged="TextBox_TextChanged"/> - @@ -88,6 +87,7 @@ limitations under the License. FloatTemplate="{StaticResource FloatTemplate}"/> + @@ -108,7 +108,7 @@ limitations under the License. - - @@ -125,23 +125,30 @@ limitations under the License. - - + + - + + + + + + - diff --git a/SourceGen/WpfGui/EditVisualization.xaml.cs b/SourceGen/WpfGui/EditVisualization.xaml.cs index ec2f74b..270b66f 100644 --- a/SourceGen/WpfGui/EditVisualization.xaml.cs +++ b/SourceGen/WpfGui/EditVisualization.xaml.cs @@ -14,16 +14,13 @@ * limitations under the License. */ using System; -using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; -using System.Text; using System.Windows; using System.Windows.Controls; -using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using Asm65; @@ -34,8 +31,6 @@ namespace SourceGen.WpfGui { /// Visualization editor. /// public partial class EditVisualization : Window, INotifyPropertyChanged { - private const int MIN_TRIMMED_TAG_LEN = 2; - /// /// Dialog result. /// @@ -43,6 +38,7 @@ namespace SourceGen.WpfGui { private DisasmProject mProject; private Formatter mFormatter; + private int mSetOffset; private Visualization mOrigVis; private Brush mDefaultLabelColor = SystemColors.WindowTextBrush; @@ -64,7 +60,7 @@ namespace SourceGen.WpfGui { /// public string TagString { get { return mTagString; } - set { mTagString = value; OnPropertyChanged(); } + set { mTagString = value; OnPropertyChanged(); UpdateControls(); } } private string mTagString; @@ -74,6 +70,13 @@ namespace SourceGen.WpfGui { } private Brush mTagLabelBrush; + /// + /// Item for combo box. + /// + /// + /// Strictly speaking we could just create an ItemsSource from VisDescr objects, but + /// the plugin reference saves a lookup later. + /// public class VisualizationItem { public IPlugin_Visualizer Plugin { get; private set; } public VisDescr VisDescriptor { get; private set; } @@ -88,6 +91,21 @@ namespace SourceGen.WpfGui { /// public List VisualizationList { get; private set; } + /// + /// Error message, shown in red. + /// + public string PluginErrMessage { + get { return mPluginErrMessage; } + set { mPluginErrMessage = value; OnPropertyChanged(); } + } + private string mPluginErrMessage = string.Empty; + + /// + /// Set by the plugin callback. WPF doesn't like it when we try to fire off a + /// property changed event from here. + /// + public string LastPluginMessage { get; set; } + /// /// ItemsSource for the ItemsControl with the generated parameter controls. /// @@ -101,10 +119,17 @@ namespace SourceGen.WpfGui { } private class ScriptSupport : MarshalByRefObject, PluginCommon.IApplication { - public ScriptSupport() { } + private EditVisualization mOuter; + + public ScriptSupport(EditVisualization outer) { + mOuter = outer; + } + public void DebugLog(string msg) { Debug.WriteLine("Vis plugin: " + msg); + mOuter.LastPluginMessage = msg; } + public bool SetOperandFormat(int offset, DataSubType subType, string label) { throw new InvalidOperationException(); } @@ -113,7 +138,13 @@ namespace SourceGen.WpfGui { throw new InvalidOperationException(); } } - private ScriptSupport mScriptSupport = new ScriptSupport(); + private ScriptSupport mScriptSupport; + + /// + /// URI for the image we show when we fail to generate a visualization. + /// + private static BitmapImage sBadParamsImage = + new BitmapImage(new Uri("pack://application:,,,/Res/RedX.png")); /// @@ -124,17 +155,23 @@ namespace SourceGen.WpfGui { /// Text formatter. /// Visualization to edit, or null if this is new. public EditVisualization(Window owner, DisasmProject proj, Formatter formatter, - Visualization vis) { + int setOffset, Visualization vis) { InitializeComponent(); Owner = owner; DataContext = this; mProject = proj; mFormatter = formatter; + mSetOffset = setOffset; mOrigVis = vis; + mScriptSupport = new ScriptSupport(this); + if (vis != null) { TagString = vis.Tag; + } else { + // Could make this unique, but probably not worth the bother. + TagString = "vis" + mSetOffset.ToString("x6"); } int visSelection = 0; @@ -174,9 +211,19 @@ namespace SourceGen.WpfGui { foreach (VisParamDescr vpd in paramDescrs) { string rangeStr = string.Empty; object defaultVal = vpd.DefaultValue; - if (mOrigVis.VisGenParams.TryGetValue(vpd.Name, out object val)) { - // Do we need to confirm that val has the correct type? - defaultVal = val; + + // If we're editing a visualization, use the values from that as default. + if (mOrigVis != null) { + if (mOrigVis.VisGenParams.TryGetValue(vpd.Name, out object val)) { + // Do we need to confirm that val has the correct type? + defaultVal = val; + } + } else { + // New visualization. Use the set's offset as the default value for + // any parameter called "offset". + if (vpd.Name.ToLowerInvariant() == "offset") { + defaultVal = mSetOffset; + } } // Set up rangeStr, if appropriate. @@ -201,9 +248,6 @@ namespace SourceGen.WpfGui { } } - private void Window_Loaded(object sender, RoutedEventArgs e) { - } - private void Window_Closed(object sender, EventArgs e) { mProject.UnprepareScripts(); } @@ -212,7 +256,9 @@ namespace SourceGen.WpfGui { VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem; Debug.Assert(item != null); Dictionary valueDict = CreateVisGenParams(); - NewVis = new Visualization(TagString, item.VisDescriptor.Ident, valueDict); + string trimTag = Visualization.TrimAndValidateTag(TagString, out bool isTagValid); + Debug.Assert(isTagValid); + NewVis = new Visualization(trimTag, item.VisDescriptor.Ident, valueDict); DialogResult = true; } @@ -226,22 +272,12 @@ namespace SourceGen.WpfGui { Debug.Assert(pv.Value is bool); valueDict.Add(pv.Descr.Name, (bool)pv.Value); } else if (pv.Descr.CsType == typeof(int)) { - int intVal; - if (pv.Value is int) { - intVal = (int)pv.Value; - } else { - bool ok = ParseInt((string)pv.Value, pv.Descr.Special, out intVal); - Debug.Assert(ok); - } + bool ok = ParseIntObj(pv.Value, pv.Descr.Special, out int intVal); + Debug.Assert(ok); valueDict.Add(pv.Descr.Name, intVal); } else if (pv.Descr.CsType == typeof(float)) { - float floatVal; - if (pv.Value is float || pv.Value is double) { - floatVal = (float)pv.Value; - } else { - bool ok = float.TryParse((string)pv.Value, out floatVal); - Debug.Assert(ok); - } + bool ok = ParseFloatObj(pv.Value, out float floatVal); + Debug.Assert(ok); valueDict.Add(pv.Descr.Name, floatVal); } else { // skip it @@ -252,7 +288,12 @@ namespace SourceGen.WpfGui { return valueDict; } - private bool ParseInt(string str, VisParamDescr.SpecialMode special, out int intVal) { + private bool ParseIntObj(object val, VisParamDescr.SpecialMode special, out int intVal) { + if (val is int) { + intVal = (int)val; + return true; + } + string str = (string)val; int numBase = (special == VisParamDescr.SpecialMode.Offset) ? 16 : 10; string trimStr = str.Trim(); @@ -281,18 +322,29 @@ namespace SourceGen.WpfGui { } } + private bool ParseFloatObj(object val, out float floatVal) { + if (val is float) { + floatVal = (float)val; + return true; + } else if (val is double) { + floatVal = (float)(double)val; + return true; + } else if (val is int) { + floatVal = (int)val; + return true; + } + + string str = (string)val; + if (!float.TryParse(str, out floatVal)) { + floatVal = 0.0f; + return false; + } + return true; + } + private void UpdateControls() { IsValid = true; - string trimTag = TagString.Trim(); - if (trimTag.Length < MIN_TRIMMED_TAG_LEN) { - IsValid = false; - TagLabelBrush = mErrorLabelColor; - } else { - TagLabelBrush = mDefaultLabelColor; - } - // TODO: verify tag is unique - foreach (ParameterValue pv in ParameterList) { pv.ForegroundBrush = mDefaultLabelColor; if (pv.Descr.CsType == typeof(bool)) { @@ -300,14 +352,7 @@ namespace SourceGen.WpfGui { continue; } else if (pv.Descr.CsType == typeof(int)) { // integer, possibly Offset special - bool ok = true; - int intVal; - if (pv.Value is int) { - // happens initially, before the TextBox can futz with it - intVal = (int)pv.Value; - } else if (!ParseInt((string)pv.Value, pv.Descr.Special, out intVal)) { - ok = false; - } + bool ok = ParseIntObj(pv.Value, pv.Descr.Special, out int intVal); if (ok && (intVal < (int)pv.Descr.Min || intVal > (int)pv.Descr.Max)) { // TODO(someday): make the range text red instead of the label ok = false; @@ -318,17 +363,28 @@ namespace SourceGen.WpfGui { } } else if (pv.Descr.CsType == typeof(float)) { // float + bool ok = ParseFloatObj(pv.Value, out float floatVal); + if (ok && (floatVal < (float)pv.Descr.Min || floatVal > (float)pv.Descr.Max)) { + ok = false; + } + if (!ok) { + pv.ForegroundBrush = mErrorLabelColor; + IsValid = false; + } } else { // unexpected Debug.Assert(false); } } - if (!IsValid) { - // TODO(xyzzy): default to a meaningful image - previewImage.Source = new BitmapImage(new Uri("pack://application:,,,/Res/Logo.png")); + VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem; + + if (!IsValid || item == null) { + previewImage.Source = sBadParamsImage; } else { - VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem; + // Invoke the plugin. + PluginErrMessage = string.Empty; + IVisualization2d vis2d; try { vis2d = item.Plugin.Generate2d(item.VisDescriptor, @@ -337,16 +393,48 @@ namespace SourceGen.WpfGui { Debug.WriteLine("Vis generator returned null"); } } catch (Exception ex) { - // TODO(xyzzy): use different image for failure - Debug.WriteLine("Vis generation failed: " + ex.Message); + Debug.WriteLine("Vis generation failed: " + ex); vis2d = null; } if (vis2d == null) { - previewImage.Source = new BitmapImage(new Uri("pack://application:,,,/Res/Logo.png")); + previewImage.Source = sBadParamsImage; + if (!string.IsNullOrEmpty(LastPluginMessage)) { + // Report the last message we got as an error. + PluginErrMessage = LastPluginMessage; + } } else { previewImage.Source = Visualization.ConvertToBitmapSource(vis2d); } } + + string trimTag = Visualization.TrimAndValidateTag(TagString, out bool tagOk); + Visualization match = FindVisualizationByTag(trimTag); + if (match != null && match != mOrigVis) { + // Another vis already has this tag. + tagOk = false; + } + if (!tagOk) { + TagLabelBrush = mErrorLabelColor; + IsValid = false; + } else { + TagLabelBrush = mDefaultLabelColor; + } + } + + /// + /// Finds a Visualization with a matching tag, searching across all sets. + /// + /// Tag to search for. + /// Matching Visualization, or null if not found. + private Visualization FindVisualizationByTag(string tag) { + foreach (KeyValuePair kvp in mProject.VisualizationSets) { + foreach (Visualization vis in kvp.Value) { + if (vis.Tag == tag) { + return vis; + } + } + } + return null; } private void VisComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { diff --git a/SourceGen/WpfGui/EditVisualizationSet.xaml b/SourceGen/WpfGui/EditVisualizationSet.xaml index c130ff4..f412b5e 100644 --- a/SourceGen/WpfGui/EditVisualizationSet.xaml +++ b/SourceGen/WpfGui/EditVisualizationSet.xaml @@ -19,12 +19,18 @@ limitations under the License. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:local="clr-namespace:SourceGen.WpfGui" mc:Ignorable="d" Title="Edit Visualization Set" - Width="460" Height="400" ResizeMode="NoResize" + Width="500" Height="400" ResizeMode="NoResize" ShowInTaskbar="False" WindowStartupLocation="CenterOwner" - Loaded="Window_Loaded"> + Closing="Window_Closing"> + + + Are you sure you wish to discard your changes? + Discard Changes? + @@ -36,7 +42,7 @@ limitations under the License. - - +