1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-05-31 22:41:37 +00:00

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.
This commit is contained in:
Andy McFadden 2019-12-02 16:38:32 -08:00
parent 365864ccdf
commit 125080dbda
16 changed files with 480 additions and 132 deletions

BIN
ImageSrc/RedX.xcf Normal file

Binary file not shown.

View File

@ -449,9 +449,21 @@ namespace SourceGen {
return parts; return parts;
} }
public static FormattedParts CreateVisualizationSet(string summary) { public static FormattedParts CreateVisualizationSet(VisualizationSet visSet) {
FormattedParts parts = new FormattedParts(); 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; parts.IsVisualizationSet = true;
return parts; return parts;
} }

View File

@ -506,11 +506,9 @@ namespace SourceGen {
parts = GenerateLvTableLine(line.FileOffset, line.SubLineIndex); parts = GenerateLvTableLine(line.FileOffset, line.SubLineIndex);
break; break;
case Line.Type.VisualizationSet: case Line.Type.VisualizationSet:
// TODO(xyzzy)
mProject.VisualizationSets.TryGetValue(line.FileOffset, mProject.VisualizationSets.TryGetValue(line.FileOffset,
out VisualizationSet visSet); out VisualizationSet visSet);
parts = FormattedParts.CreateVisualizationSet("!VISUALIZATION SET! " + parts = FormattedParts.CreateVisualizationSet(visSet);
(visSet != null ? "VS:" + visSet.Count : "???"));
break; break;
case Line.Type.Blank: case Line.Type.Blank:
// Nothing to do. // Nothing to do.

View File

@ -2161,7 +2161,7 @@ namespace SourceGen {
mProject.VisualizationSets.TryGetValue(offset, out VisualizationSet curVisSet); mProject.VisualizationSets.TryGetValue(offset, out VisualizationSet curVisSet);
EditVisualizationSet dlg = new EditVisualizationSet(mMainWin, mProject, EditVisualizationSet dlg = new EditVisualizationSet(mMainWin, mProject,
mOutputFormatter, curVisSet); mOutputFormatter, curVisSet, offset);
if (dlg.ShowDialog() != true) { if (dlg.ShowDialog() != true) {
return; return;
} }

View File

@ -359,6 +359,29 @@ namespace SourceGen {
ClearPrevious = varTab.ClearPrevious; ClearPrevious = varTab.ClearPrevious;
} }
} }
public class SerVisualization {
public string Tag { get; set; }
public string VisGenIdent { get; set; }
public Dictionary<string, object> VisGenParams { get; set; }
public SerVisualization() { }
public SerVisualization(Visualization vis) {
Tag = vis.Tag;
VisGenIdent = vis.VisGenIdent;
VisGenParams = vis.VisGenParams; // Dictionary
}
}
public class SerVisualizationSet {
public List<SerVisualization> Items { get; set; }
public SerVisualizationSet() { }
public SerVisualizationSet(VisualizationSet visSet) {
Items = new List<SerVisualization>(visSet.Count);
foreach (Visualization vis in visSet) {
Items.Add(new SerVisualization(vis));
}
}
}
// Fields are serialized to/from JSON. Do not change the field names. // Fields are serialized to/from JSON. Do not change the field names.
public int _ContentVersion { get; set; } public int _ContentVersion { get; set; }
@ -374,6 +397,7 @@ namespace SourceGen {
public Dictionary<string, SerSymbol> UserLabels { get; set; } public Dictionary<string, SerSymbol> UserLabels { get; set; }
public Dictionary<string, SerFormatDescriptor> OperandFormats { get; set; } public Dictionary<string, SerFormatDescriptor> OperandFormats { get; set; }
public Dictionary<string, SerLocalVariableTable> LvTables { get; set; } public Dictionary<string, SerLocalVariableTable> LvTables { get; set; }
public Dictionary<string, SerVisualizationSet> VisualizationSets { get; set; }
/// <summary> /// <summary>
/// Serializes a DisasmProject into an augmented JSON string. /// 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.LvTables.Add(kvp.Key.ToString(), new SerLocalVariableTable(kvp.Value));
} }
spf.VisualizationSets = new Dictionary<string, SerVisualizationSet>();
foreach (KeyValuePair<int, VisualizationSet> kvp in proj.VisualizationSets) {
spf.VisualizationSets.Add(kvp.Key.ToString(), new SerVisualizationSet(kvp.Value));
}
spf.ProjectProps = new SerProjectProperties(proj.ProjectProps); spf.ProjectProps = new SerProjectProperties(proj.ProjectProps);
JavaScriptSerializer ser = new JavaScriptSerializer(); 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<string, SerVisualizationSet> 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; return true;
} }
@ -924,6 +972,42 @@ namespace SourceGen {
return true; 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<string, object> parms =
new Dictionary<string, object>(serVis.VisGenParams.Count);
foreach (KeyValuePair<string, object> 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;
}
/// <summary> /// <summary>
/// Parses an integer key that was stored as a string, and checks to see if the /// Parses an integer key that was stored as a string, and checks to see if the
/// value falls within an acceptable range. /// value falls within an acceptable range.

BIN
SourceGen/Res/RedX.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 B

View File

@ -54,6 +54,8 @@ limitations under the License.
<system:String x:Key="str_ErrBadSymbolSt">Unknown Source or Type in symbol</system:String> <system:String x:Key="str_ErrBadSymbolSt">Unknown Source or Type in symbol</system:String>
<system:String x:Key="str_ErrBadSymrefPart">Bad symbol reference part</system:String> <system:String x:Key="str_ErrBadSymrefPart">Bad symbol reference part</system:String>
<system:String x:Key="str_ErrBadTypeHint">Type hint not recognized</system:String> <system:String x:Key="str_ErrBadTypeHint">Type hint not recognized</system:String>
<system:String x:Key="str_ErrBadVisualizationFmt">Invalid visualization item: {0}</system:String>
<system:String x:Key="str_ErrBadVisualizationSetFmt">Invalid visualization set at +{0:x6}</system:String>
<system:String x:Key="str_ErrDuplicateLabelFmt">Removed duplicate label '{0}' (offset +{1:x6})</system:String> <system:String x:Key="str_ErrDuplicateLabelFmt">Removed duplicate label '{0}' (offset +{1:x6})</system:String>
<system:String x:Key="str_ErrFileCopyFailedFmt">Failed copying {0} to {1}: {2}.</system:String> <system:String x:Key="str_ErrFileCopyFailedFmt">Failed copying {0} to {1}: {2}.</system:String>
<system:String x:Key="str_ErrFileExistsNotDirFmt">The file {0} exists, but is not a directory.</system:String> <system:String x:Key="str_ErrFileExistsNotDirFmt">The file {0} exists, but is not a directory.</system:String>
@ -173,4 +175,6 @@ limitations under the License.
<system:String x:Key="str_TitleNewProject">[new project]</system:String> <system:String x:Key="str_TitleNewProject">[new project]</system:String>
<system:String x:Key="str_TitleReadOnly">*READ-ONLY*</system:String> <system:String x:Key="str_TitleReadOnly">*READ-ONLY*</system:String>
<system:String x:Key="str_Unset">[unset]</system:String> <system:String x:Key="str_Unset">[unset]</system:String>
<system:String x:Key="str_VisSetMultipleFmt">Vis: {0} (+{1} more)</system:String>
<system:String x:Key="str_VisSetSingleFmt">Vis: {0}</system:String>
</ResourceDictionary> </ResourceDictionary>

View File

@ -89,6 +89,10 @@ namespace SourceGen.Res {
(string)Application.Current.FindResource("str_ErrBadSymrefPart"); (string)Application.Current.FindResource("str_ErrBadSymrefPart");
public static string ERR_BAD_TYPE_HINT = public static string ERR_BAD_TYPE_HINT =
(string)Application.Current.FindResource("str_ErrBadTypeHint"); (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 = public static string ERR_DUPLICATE_LABEL_FMT =
(string)Application.Current.FindResource("str_ErrDuplicateLabelFmt"); (string)Application.Current.FindResource("str_ErrDuplicateLabelFmt");
public static string ERR_FILE_COPY_FAILED_FMT = public static string ERR_FILE_COPY_FAILED_FMT =
@ -327,5 +331,9 @@ namespace SourceGen.Res {
(string)Application.Current.FindResource("str_TitleReadOnly"); (string)Application.Current.FindResource("str_TitleReadOnly");
public static string UNSET = public static string UNSET =
(string)Application.Current.FindResource("str_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");
} }
} }

View File

@ -29,9 +29,9 @@ namespace RuntimeData.Apple {
private byte[] mFileData; private byte[] mFileData;
private AddressTranslate mAddrTrans; 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_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_OFFSET = "offset";
private const string P_BYTE_WIDTH = "byteWidth"; 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 VisDescr(VIS_GEN_BITMAP, "Apple II Hi-Res Bitmap", VisDescr.VisType.Bitmap,
new VisParamDescr[] { new VisParamDescr[] {
new VisParamDescr("File offset (hex)", new VisParamDescr("File offset (hex)",
P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, 0),
0x2000),
new VisParamDescr("Width (in bytes)", new VisParamDescr("Width (in bytes)",
P_BYTE_WIDTH, typeof(int), 1, 40, 0, 1), P_BYTE_WIDTH, typeof(int), 1, 40, 0, 1),
new VisParamDescr("Height", new VisParamDescr("Height",
@ -69,11 +68,10 @@ namespace RuntimeData.Apple {
new VisParamDescr("Test Float", new VisParamDescr("Test Float",
"floaty", typeof(float), -5.0f, 5.0f, 0, 0.1f), "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[] {
new VisParamDescr("File offset (hex)", new VisParamDescr("File offset (hex)",
P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset, 0),
0x1000),
new VisParamDescr("Item width (in bytes)", new VisParamDescr("Item width (in bytes)",
P_ITEM_BYTE_WIDTH, typeof(int), 1, 40, 0, 1), P_ITEM_BYTE_WIDTH, typeof(int), 1, 40, 0, 1),
new VisParamDescr("Item height", new VisParamDescr("Item height",
@ -109,8 +107,8 @@ namespace RuntimeData.Apple {
switch (descr.Ident) { switch (descr.Ident) {
case VIS_GEN_BITMAP: case VIS_GEN_BITMAP:
return GenerateBitmap(parms); return GenerateBitmap(parms);
case VIS_GEN_MULTI_MAP: case VIS_GEN_BITMAP_FONT:
// TODO // TODO (xyzzy)
return null; return null;
default: default:
mAppRef.DebugLog("Unknown ident " + descr.Ident); mAppRef.DebugLog("Unknown ident " + descr.Ident);
@ -140,16 +138,24 @@ namespace RuntimeData.Apple {
if (offset < 0 || offset >= mFileData.Length || if (offset < 0 || offset >= mFileData.Length ||
byteWidth <= 0 || byteWidth > MAX_DIM || byteWidth <= 0 || byteWidth > MAX_DIM ||
height <= 0 || height > MAX_DIM || height <= 0 || height > MAX_DIM) {
colStride <= 0 || colStride > MAX_DIM || // the UI should flag these based on range (and ideally wouldn't have called us)
rowStride < byteWidth * colStride - (colStride-1) || rowStride > MAX_DIM) {
mAppRef.DebugLog("Invalid parameter"); mAppRef.DebugLog("Invalid parameter");
return null; 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; int lastOffset = offset + rowStride * height - (colStride - 1) - 1;
if (lastOffset >= mFileData.Length) { 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; return null;
} }
@ -235,7 +241,7 @@ namespace RuntimeData.Apple {
} }
#else #else
// Color conversion similar to what CiderPress does, but without the half-pixel // 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[] lineBits = new bool[byteWidth * 7];
bool[] hiFlags = new bool[byteWidth * 7]; // overkill, but simplifies things bool[] hiFlags = new bool[byteWidth * 7]; // overkill, but simplifies things
int[] colorBuf = new int[byteWidth * 7]; int[] colorBuf = new int[byteWidth * 7];
@ -326,7 +332,7 @@ namespace RuntimeData.Apple {
White1 = 7 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] { private static readonly byte[] sHiResColorMap = new byte[8] {
1, 3, 4, 2, 1, 5, 6, 2 1, 3, 4, 2, 1, 5, 6, 2
}; };
@ -334,7 +340,7 @@ namespace RuntimeData.Apple {
private void SetHiResPalette(VisBitmap8 vb) { private void SetHiResPalette(VisBitmap8 vb) {
// These don't match directly to hi-res color numbers because we want to // These don't match directly to hi-res color numbers because we want to
// avoid adding black/white twice. // 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, 0x00, 0x00, 0x00); // 1=black0/black1
vb.AddColor(0xff, 0xff, 0xff, 0xff); // 2=white0/white1 vb.AddColor(0xff, 0xff, 0xff, 0xff); // 2=white0/white1
vb.AddColor(0xff, 0x11, 0xdd, 0x00); // 3=green vb.AddColor(0xff, 0x11, 0xdd, 0x00); // 3=green

View File

@ -419,5 +419,8 @@
<ItemGroup> <ItemGroup>
<Resource Include="Res\AboutImage.png" /> <Resource Include="Res\AboutImage.png" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Resource Include="Res\RedX.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View File

@ -38,6 +38,13 @@ namespace SourceGen {
/// </summary> /// </summary>
public Dictionary<string, object> VisGenParams { get; private set; } public Dictionary<string, object> VisGenParams { get; private set; }
/// <summary>
/// Cached reference to thumbnail.
/// </summary>
/// <remarks>
/// Because the underlying data never changes, we only need to regenerate the
/// thumbnail if the set of active plugins changes.
/// </remarks>
private BitmapSource Thumbnail { get; set; } // TODO - 64x64(?) bitmap private BitmapSource Thumbnail { get; set; } // TODO - 64x64(?) bitmap
@ -54,11 +61,30 @@ namespace SourceGen {
VisGenParams = visGenParams; VisGenParams = visGenParams;
} }
/// <summary>
/// Trims a tag, removing leading/trailing whitespace, and checks it for validity.
/// </summary>
/// <param name="tag">Tag to trim and validate.</param>
/// <param name="isValid">Set to true if the tag is valid.</param>
/// <returns>Trimmed tag string. Returns an empty string if tag is null.</returns>
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;
}
/// <summary> /// <summary>
/// Converts an IVisualization2d to a BitmapSource for display. /// Converts an IVisualization2d to a BitmapSource for display.
/// </summary> /// </summary>
/// <param name="vis2d"></param>
/// <returns></returns>
public static BitmapSource ConvertToBitmapSource(IVisualization2d vis2d) { public static BitmapSource ConvertToBitmapSource(IVisualization2d vis2d) {
// Create indexed color palette. // Create indexed color palette.
int[] intPal = vis2d.GetPalette(); int[] intPal = vis2d.GetPalette();

View File

@ -16,12 +16,14 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text;
namespace SourceGen { namespace SourceGen {
/// <summary> /// <summary>
/// Ordered list of visualization objects. /// Ordered list of visualization objects.
/// </summary> /// </summary>
/// <remarks>
/// Right now the only thing separating this from a plain List<> is the operator== stuff.
/// </remarks>
public class VisualizationSet : IEnumerable<Visualization> { public class VisualizationSet : IEnumerable<Visualization> {
/// <summary> /// <summary>
/// Object list. /// Object list.
@ -32,9 +34,9 @@ namespace SourceGen {
/// <summary> /// <summary>
/// Constructor. /// Constructor.
/// </summary> /// </summary>
/// <param name="cap">Initial capacity.</param> /// <param name="initialCap">Initial capacity.</param>
public VisualizationSet(int cap = 1) { public VisualizationSet(int initialCap = 1) {
mList = new List<Visualization>(cap); mList = new List<Visualization>(initialCap);
} }
// IEnumerable // IEnumerable
@ -54,10 +56,24 @@ namespace SourceGen {
get { return mList.Count; } get { return mList.Count; }
} }
/// <summary>
/// Accesses the Nth element.
/// </summary>
/// <param name="key">Element number.</param>
public Visualization this[int key] {
get {
return mList[key];
}
}
public void Add(Visualization vis) { public void Add(Visualization vis) {
mList.Add(vis); mList.Add(vis);
} }
public void Remove(Visualization vis) {
mList.Remove(vis);
}
public override string ToString() { public override string ToString() {
return "[VS: " + mList.Count + " items]"; return "[VS: " + mList.Count + " items]";

View File

@ -25,7 +25,6 @@ limitations under the License.
Title="Edit Visualization" Title="Edit Visualization"
Width="460" SizeToContent="Height" ResizeMode="NoResize" Width="460" SizeToContent="Height" ResizeMode="NoResize"
ShowInTaskbar="False" WindowStartupLocation="CenterOwner" ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
Loaded="Window_Loaded"
Closed="Window_Closed"> Closed="Window_Closed">
<Window.Resources> <Window.Resources>
@ -41,7 +40,7 @@ limitations under the License.
Text="{Binding UiString}" Foreground="{Binding ForegroundBrush}"/> Text="{Binding UiString}" Foreground="{Binding ForegroundBrush}"/>
<CheckBox Grid.Column="1" Margin="0,0,0,4" IsChecked="{Binding Value}" <CheckBox Grid.Column="1" Margin="0,0,0,4" IsChecked="{Binding Value}"
Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/> Checked="CheckBox_Changed" Unchecked="CheckBox_Changed"/>
<TextBlock Grid.Column="2" Text="{Binding RangeText}" Margin="0,1,0,0" <TextBlock Grid.Column="2" Text="{Binding RangeText}" Margin="4,1,0,0"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
</Grid> </Grid>
</DataTemplate> </DataTemplate>
@ -58,7 +57,7 @@ limitations under the License.
Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"
FontFamily="{StaticResource GeneralMonoFont}" FontFamily="{StaticResource GeneralMonoFont}"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<TextBlock Grid.Column="2" Text="{Binding RangeText}" Margin="0,1,0,0" <TextBlock Grid.Column="2" Text="{Binding RangeText}" Margin="4,1,0,0"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
</Grid> </Grid>
</DataTemplate> </DataTemplate>
@ -75,7 +74,7 @@ limitations under the License.
Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"
FontFamily="{StaticResource GeneralMonoFont}" FontFamily="{StaticResource GeneralMonoFont}"
TextChanged="TextBox_TextChanged"/> TextChanged="TextBox_TextChanged"/>
<TextBlock Grid.Column="2" Text="{Binding RangeText}" Margin="0,1,0,0" <TextBlock Grid.Column="2" Text="{Binding RangeText}" Margin="4,1,0,0"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
</Grid> </Grid>
</DataTemplate> </DataTemplate>
@ -88,6 +87,7 @@ limitations under the License.
FloatTemplate="{StaticResource FloatTemplate}"/> FloatTemplate="{StaticResource FloatTemplate}"/>
</Window.Resources> </Window.Resources>
<Grid Margin="8"> <Grid Margin="8">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
@ -108,7 +108,7 @@ limitations under the License.
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right" Margin="0,0,4,0" <TextBlock Grid.Column="0" Grid.Row="0" HorizontalAlignment="Right" Margin="0,3,4,0"
Text="Visualizer:"/> Text="Visualizer:"/>
<ComboBox Name="visComboBox" Grid.Column="1" Grid.Row="0" Margin="0,0,0,4" <ComboBox Name="visComboBox" Grid.Column="1" Grid.Row="0" Margin="0,0,0,4"
ItemsSource="{Binding VisualizationList}" DisplayMemberPath="VisDescriptor.UiName" ItemsSource="{Binding VisualizationList}" DisplayMemberPath="VisDescriptor.UiName"
@ -116,7 +116,7 @@ limitations under the License.
<TextBlock Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right" Margin="0,0,4,0" <TextBlock Grid.Column="0" Grid.Row="1" HorizontalAlignment="Right" Margin="0,0,4,0"
Text="Tag:" Foreground="{Binding TagLabelBrush}"/> Text="Tag:" Foreground="{Binding TagLabelBrush}"/>
<TextBox Grid.Column="1" Grid.Row="1" <TextBox Grid.Column="1" Grid.Row="1" Margin="0,1,0,0"
Text="{Binding TagString, UpdateSourceTrigger=PropertyChanged}" Text="{Binding TagString, UpdateSourceTrigger=PropertyChanged}"
FontFamily="{StaticResource GeneralMonoFont}"/> FontFamily="{StaticResource GeneralMonoFont}"/>
@ -125,23 +125,30 @@ limitations under the License.
<TextBlock Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="2" Margin="0,20,0,4" Text="Preview:"/> <TextBlock Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="2" Margin="0,20,0,4" Text="Preview:"/>
</Grid> </Grid>
<Border Grid.Row="1" BorderThickness="1" <Border Grid.Row="1" BorderThickness="1" HorizontalAlignment="Center"
BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}"
Background="LightGray"> Background="LightGray">
<Image Name="previewImage" Width="400" Height="400" Source="/Res/AboutImage.png" <Image Name="previewImage" Width="320" Height="320" Source="/Res/RedX.png"
RenderOptions.BitmapScalingMode="NearestNeighbor"/> RenderOptions.BitmapScalingMode="NearestNeighbor"/>
<!--<Button Width="400" Height="400" Content="Test"/>-->
</Border> </Border>
<Grid Grid.Row="2"> <Grid Grid.Row="2">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Parameters:" Margin="0,0,0,4"/> <StackPanel Grid.Row="0" Orientation="Horizontal" Margin="0,0,0,4">
<!--<TextBlock Text="Message:" Margin="0,0,8,0"/>-->
<TextBlock Text="{Binding PluginErrMessage}" Foreground="Red"/>
</StackPanel>
<TextBlock Grid.Row="1" Text="Parameters:" Margin="0,0,0,4"/>
<!-- generated controls are placed here --> <!-- generated controls are placed here -->
<ItemsControl Grid.Row="1" <ItemsControl Grid.Row="2"
ItemsSource="{Binding ParameterList}" ItemsSource="{Binding ParameterList}"
ItemTemplateSelector="{StaticResource ParameterTemplateSelector}"> ItemTemplateSelector="{StaticResource ParameterTemplateSelector}">
</ItemsControl> </ItemsControl>

View File

@ -14,16 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using Asm65; using Asm65;
@ -34,8 +31,6 @@ namespace SourceGen.WpfGui {
/// Visualization editor. /// Visualization editor.
/// </summary> /// </summary>
public partial class EditVisualization : Window, INotifyPropertyChanged { public partial class EditVisualization : Window, INotifyPropertyChanged {
private const int MIN_TRIMMED_TAG_LEN = 2;
/// <summary> /// <summary>
/// Dialog result. /// Dialog result.
/// </summary> /// </summary>
@ -43,6 +38,7 @@ namespace SourceGen.WpfGui {
private DisasmProject mProject; private DisasmProject mProject;
private Formatter mFormatter; private Formatter mFormatter;
private int mSetOffset;
private Visualization mOrigVis; private Visualization mOrigVis;
private Brush mDefaultLabelColor = SystemColors.WindowTextBrush; private Brush mDefaultLabelColor = SystemColors.WindowTextBrush;
@ -64,7 +60,7 @@ namespace SourceGen.WpfGui {
/// </summary> /// </summary>
public string TagString { public string TagString {
get { return mTagString; } get { return mTagString; }
set { mTagString = value; OnPropertyChanged(); } set { mTagString = value; OnPropertyChanged(); UpdateControls(); }
} }
private string mTagString; private string mTagString;
@ -74,6 +70,13 @@ namespace SourceGen.WpfGui {
} }
private Brush mTagLabelBrush; private Brush mTagLabelBrush;
/// <summary>
/// Item for combo box.
/// </summary>
/// <remarks>
/// Strictly speaking we could just create an ItemsSource from VisDescr objects, but
/// the plugin reference saves a lookup later.
/// </remarks>
public class VisualizationItem { public class VisualizationItem {
public IPlugin_Visualizer Plugin { get; private set; } public IPlugin_Visualizer Plugin { get; private set; }
public VisDescr VisDescriptor { get; private set; } public VisDescr VisDescriptor { get; private set; }
@ -88,6 +91,21 @@ namespace SourceGen.WpfGui {
/// </summary> /// </summary>
public List<VisualizationItem> VisualizationList { get; private set; } public List<VisualizationItem> VisualizationList { get; private set; }
/// <summary>
/// Error message, shown in red.
/// </summary>
public string PluginErrMessage {
get { return mPluginErrMessage; }
set { mPluginErrMessage = value; OnPropertyChanged(); }
}
private string mPluginErrMessage = string.Empty;
/// <summary>
/// Set by the plugin callback. WPF doesn't like it when we try to fire off a
/// property changed event from here.
/// </summary>
public string LastPluginMessage { get; set; }
/// <summary> /// <summary>
/// ItemsSource for the ItemsControl with the generated parameter controls. /// ItemsSource for the ItemsControl with the generated parameter controls.
/// </summary> /// </summary>
@ -101,10 +119,17 @@ namespace SourceGen.WpfGui {
} }
private class ScriptSupport : MarshalByRefObject, PluginCommon.IApplication { private class ScriptSupport : MarshalByRefObject, PluginCommon.IApplication {
public ScriptSupport() { } private EditVisualization mOuter;
public ScriptSupport(EditVisualization outer) {
mOuter = outer;
}
public void DebugLog(string msg) { public void DebugLog(string msg) {
Debug.WriteLine("Vis plugin: " + msg); Debug.WriteLine("Vis plugin: " + msg);
mOuter.LastPluginMessage = msg;
} }
public bool SetOperandFormat(int offset, DataSubType subType, string label) { public bool SetOperandFormat(int offset, DataSubType subType, string label) {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
@ -113,7 +138,13 @@ namespace SourceGen.WpfGui {
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
} }
private ScriptSupport mScriptSupport = new ScriptSupport(); private ScriptSupport mScriptSupport;
/// <summary>
/// URI for the image we show when we fail to generate a visualization.
/// </summary>
private static BitmapImage sBadParamsImage =
new BitmapImage(new Uri("pack://application:,,,/Res/RedX.png"));
/// <summary> /// <summary>
@ -124,17 +155,23 @@ namespace SourceGen.WpfGui {
/// <param name="formatter">Text formatter.</param> /// <param name="formatter">Text formatter.</param>
/// <param name="vis">Visualization to edit, or null if this is new.</param> /// <param name="vis">Visualization to edit, or null if this is new.</param>
public EditVisualization(Window owner, DisasmProject proj, Formatter formatter, public EditVisualization(Window owner, DisasmProject proj, Formatter formatter,
Visualization vis) { int setOffset, Visualization vis) {
InitializeComponent(); InitializeComponent();
Owner = owner; Owner = owner;
DataContext = this; DataContext = this;
mProject = proj; mProject = proj;
mFormatter = formatter; mFormatter = formatter;
mSetOffset = setOffset;
mOrigVis = vis; mOrigVis = vis;
mScriptSupport = new ScriptSupport(this);
if (vis != null) { if (vis != null) {
TagString = vis.Tag; TagString = vis.Tag;
} else {
// Could make this unique, but probably not worth the bother.
TagString = "vis" + mSetOffset.ToString("x6");
} }
int visSelection = 0; int visSelection = 0;
@ -174,9 +211,19 @@ namespace SourceGen.WpfGui {
foreach (VisParamDescr vpd in paramDescrs) { foreach (VisParamDescr vpd in paramDescrs) {
string rangeStr = string.Empty; string rangeStr = string.Empty;
object defaultVal = vpd.DefaultValue; object defaultVal = vpd.DefaultValue;
if (mOrigVis.VisGenParams.TryGetValue(vpd.Name, out object val)) {
// Do we need to confirm that val has the correct type? // If we're editing a visualization, use the values from that as default.
defaultVal = val; 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. // 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) { private void Window_Closed(object sender, EventArgs e) {
mProject.UnprepareScripts(); mProject.UnprepareScripts();
} }
@ -212,7 +256,9 @@ namespace SourceGen.WpfGui {
VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem; VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem;
Debug.Assert(item != null); Debug.Assert(item != null);
Dictionary<string, object> valueDict = CreateVisGenParams(); Dictionary<string, object> 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; DialogResult = true;
} }
@ -226,22 +272,12 @@ namespace SourceGen.WpfGui {
Debug.Assert(pv.Value is bool); Debug.Assert(pv.Value is bool);
valueDict.Add(pv.Descr.Name, (bool)pv.Value); valueDict.Add(pv.Descr.Name, (bool)pv.Value);
} else if (pv.Descr.CsType == typeof(int)) { } else if (pv.Descr.CsType == typeof(int)) {
int intVal; bool ok = ParseIntObj(pv.Value, pv.Descr.Special, out int intVal);
if (pv.Value is int) { Debug.Assert(ok);
intVal = (int)pv.Value;
} else {
bool ok = ParseInt((string)pv.Value, pv.Descr.Special, out intVal);
Debug.Assert(ok);
}
valueDict.Add(pv.Descr.Name, intVal); valueDict.Add(pv.Descr.Name, intVal);
} else if (pv.Descr.CsType == typeof(float)) { } else if (pv.Descr.CsType == typeof(float)) {
float floatVal; bool ok = ParseFloatObj(pv.Value, out float floatVal);
if (pv.Value is float || pv.Value is double) { Debug.Assert(ok);
floatVal = (float)pv.Value;
} else {
bool ok = float.TryParse((string)pv.Value, out floatVal);
Debug.Assert(ok);
}
valueDict.Add(pv.Descr.Name, floatVal); valueDict.Add(pv.Descr.Name, floatVal);
} else { } else {
// skip it // skip it
@ -252,7 +288,12 @@ namespace SourceGen.WpfGui {
return valueDict; 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; int numBase = (special == VisParamDescr.SpecialMode.Offset) ? 16 : 10;
string trimStr = str.Trim(); 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() { private void UpdateControls() {
IsValid = true; 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) { foreach (ParameterValue pv in ParameterList) {
pv.ForegroundBrush = mDefaultLabelColor; pv.ForegroundBrush = mDefaultLabelColor;
if (pv.Descr.CsType == typeof(bool)) { if (pv.Descr.CsType == typeof(bool)) {
@ -300,14 +352,7 @@ namespace SourceGen.WpfGui {
continue; continue;
} else if (pv.Descr.CsType == typeof(int)) { } else if (pv.Descr.CsType == typeof(int)) {
// integer, possibly Offset special // integer, possibly Offset special
bool ok = true; bool ok = ParseIntObj(pv.Value, pv.Descr.Special, out int intVal);
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;
}
if (ok && (intVal < (int)pv.Descr.Min || intVal > (int)pv.Descr.Max)) { if (ok && (intVal < (int)pv.Descr.Min || intVal > (int)pv.Descr.Max)) {
// TODO(someday): make the range text red instead of the label // TODO(someday): make the range text red instead of the label
ok = false; ok = false;
@ -318,17 +363,28 @@ namespace SourceGen.WpfGui {
} }
} else if (pv.Descr.CsType == typeof(float)) { } else if (pv.Descr.CsType == typeof(float)) {
// 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 { } else {
// unexpected // unexpected
Debug.Assert(false); Debug.Assert(false);
} }
} }
if (!IsValid) { VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem;
// TODO(xyzzy): default to a meaningful image
previewImage.Source = new BitmapImage(new Uri("pack://application:,,,/Res/Logo.png")); if (!IsValid || item == null) {
previewImage.Source = sBadParamsImage;
} else { } else {
VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem; // Invoke the plugin.
PluginErrMessage = string.Empty;
IVisualization2d vis2d; IVisualization2d vis2d;
try { try {
vis2d = item.Plugin.Generate2d(item.VisDescriptor, vis2d = item.Plugin.Generate2d(item.VisDescriptor,
@ -337,16 +393,48 @@ namespace SourceGen.WpfGui {
Debug.WriteLine("Vis generator returned null"); Debug.WriteLine("Vis generator returned null");
} }
} catch (Exception ex) { } catch (Exception ex) {
// TODO(xyzzy): use different image for failure Debug.WriteLine("Vis generation failed: " + ex);
Debug.WriteLine("Vis generation failed: " + ex.Message);
vis2d = null; vis2d = null;
} }
if (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 { } else {
previewImage.Source = Visualization.ConvertToBitmapSource(vis2d); 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;
}
}
/// <summary>
/// Finds a Visualization with a matching tag, searching across all sets.
/// </summary>
/// <param name="tag">Tag to search for.</param>
/// <returns>Matching Visualization, or null if not found.</returns>
private Visualization FindVisualizationByTag(string tag) {
foreach (KeyValuePair<int, VisualizationSet> 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) { private void VisComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {

View File

@ -19,12 +19,18 @@ limitations under the License.
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:SourceGen.WpfGui" xmlns:local="clr-namespace:SourceGen.WpfGui"
mc:Ignorable="d" mc:Ignorable="d"
Title="Edit Visualization Set" Title="Edit Visualization Set"
Width="460" Height="400" ResizeMode="NoResize" Width="500" Height="400" ResizeMode="NoResize"
ShowInTaskbar="False" WindowStartupLocation="CenterOwner" ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
Loaded="Window_Loaded"> Closing="Window_Closing">
<Window.Resources>
<system:String x:Key="str_ConfirmDiscardChanges">Are you sure you wish to discard your changes?</system:String>
<system:String x:Key="str_ConfirmDiscardChangesCaption">Discard Changes?</system:String>
</Window.Resources>
<Grid Margin="8"> <Grid Margin="8">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@ -36,7 +42,7 @@ limitations under the License.
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<DataGrid Name="visualizationList" Grid.Column="0" Grid.Row="0" Margin="4,4,4,0" <DataGrid Name="visualizationGrid" Grid.Column="0" Grid.Row="0" Margin="4,4,4,0"
ItemsSource="{Binding VisualizationList}" ItemsSource="{Binding VisualizationList}"
IsReadOnly="True" IsReadOnly="True"
FontFamily="{StaticResource GeneralMonoFont}" FontFamily="{StaticResource GeneralMonoFont}"
@ -60,22 +66,22 @@ limitations under the License.
</DataGrid.Resources> </DataGrid.Resources>
<DataGrid.Columns> <DataGrid.Columns>
<DataGridTextColumn Header="Tag" Width="155" Binding="{Binding Tag}"/> <DataGridTextColumn Header="Tag" Width="155" Binding="{Binding Tag}"/>
<DataGridTextColumn Header="Visualization Generator" Width="180" Binding="{Binding VisGenName}"/> <DataGridTextColumn Header="Visualization Generator" Width="220" Binding="{Binding VisGenIdent}"/>
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
<StackPanel Grid.Column="1" Grid.Row="0"> <StackPanel Grid.Column="1" Grid.Row="0">
<Button Width="75" Margin="4" Content="_New..." <Button Width="75" Margin="4" Content="_New..."
Click="NewButton_Click"/> IsEnabled="{Binding HasVisPlugins}" Click="NewButton_Click"/>
<Button Width="75" Margin="4" Content="_Edit" <Button Width="75" Margin="4" Content="_Edit"
Click="EditButton_Click"/> IsEnabled="{Binding IsEditEnabled}" Click="EditButton_Click"/>
<Button Width="75" Margin="4" Content="_Remove" <Button Width="75" Margin="4" Content="_Remove"
Click="RemoveButton_Click"/> IsEnabled="{Binding IsRemoveEnabled}" Click="RemoveButton_Click"/>
<Button Width="75" Margin="4,20,4,4" Content="_Up" <Button Width="75" Margin="4,20,4,4" Content="_Up"
Click="UpButton_Click"/> IsEnabled="{Binding IsUpEnabled}" Click="UpButton_Click"/>
<Button Width="75" Margin="4,4" Content="_Down" <Button Width="75" Margin="4,4" Content="_Down"
Click="DownButton_Click"/> IsEnabled="{Binding IsDownEnabled}" Click="DownButton_Click"/>
</StackPanel> </StackPanel>
<DockPanel Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Margin="0,8,0,0" LastChildFill="False"> <DockPanel Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Margin="0,8,0,0" LastChildFill="False">

View File

@ -38,10 +38,45 @@ namespace SourceGen.WpfGui {
private DisasmProject mProject; private DisasmProject mProject;
private Formatter mFormatter; private Formatter mFormatter;
private VisualizationSet mOrigSet;
private int mOffset;
public ObservableCollection<Visualization> VisualizationList { get; private set; } = public ObservableCollection<Visualization> VisualizationList { get; private set; } =
new ObservableCollection<Visualization>(); new ObservableCollection<Visualization>();
/// <summary>
/// True if there are plugins that implement the visualization generation interface.
/// </summary>
public bool HasVisPlugins {
get { return mHasVisPlugins; }
set { mHasVisPlugins = value; OnPropertyChanged(); }
}
private bool mHasVisPlugins;
public bool IsEditEnabled {
get { return mIsEditEnabled; }
set { mIsEditEnabled = value; OnPropertyChanged(); }
}
private bool mIsEditEnabled;
public bool IsRemoveEnabled {
get { return mIsRemoveEnabled; }
set { mIsRemoveEnabled = value; OnPropertyChanged(); }
}
private bool mIsRemoveEnabled;
public bool IsUpEnabled {
get { return mIsUpEnabled; }
set { mIsUpEnabled = value; OnPropertyChanged(); }
}
private bool mIsUpEnabled;
public bool IsDownEnabled {
get { return mIsDownEnabled; }
set { mIsDownEnabled = value; OnPropertyChanged(); }
}
private bool mIsDownEnabled;
// INotifyPropertyChanged implementation // INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged; public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "") { private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
@ -49,78 +84,133 @@ namespace SourceGen.WpfGui {
} }
public EditVisualizationSet(Window owner, DisasmProject project, Formatter formatter, public EditVisualizationSet(Window owner, DisasmProject project, Formatter formatter,
VisualizationSet curSet) { VisualizationSet curSet, int offset) {
InitializeComponent(); InitializeComponent();
Owner = owner; Owner = owner;
DataContext = this; DataContext = this;
mProject = project; mProject = project;
mFormatter = formatter; mFormatter = formatter;
mOrigSet = curSet;
mOffset = offset;
if (curSet != null) { if (curSet != null) {
// Populate the ItemsSource.
foreach (Visualization vis in curSet) { foreach (Visualization vis in curSet) {
VisualizationList.Add(vis); VisualizationList.Add(vis);
} }
} }
}
private void Window_Loaded(object sender, RoutedEventArgs e) { // Check to see if we have any relevant plugins. If not, disable New/Edit.
List<IPlugin> plugins = project.GetActivePlugins();
foreach (IPlugin chkPlug in plugins) {
if (chkPlug is IPlugin_Visualizer) {
HasVisPlugins = true;
break;
}
}
} }
private void OkButton_Click(object sender, RoutedEventArgs e) { private void OkButton_Click(object sender, RoutedEventArgs e) {
if (VisualizationList.Count == 0) { NewVisSet = MakeVisSet();
NewVisSet = null; DialogResult = true;
} else { }
NewVisSet = new VisualizationSet(VisualizationList.Count);
foreach (Visualization vis in VisualizationList) { private void Window_Closing(object sender, CancelEventArgs e) {
NewVisSet.Add(vis); if (DialogResult == true) {
return;
}
// Check to see if changes have been made.
VisualizationSet newSet = MakeVisSet();
if (newSet != mOrigSet) {
string msg = (string)FindResource("str_ConfirmDiscardChanges");
string caption = (string)FindResource("str_ConfirmDiscardChangesCaption");
MessageBoxResult result = MessageBox.Show(msg, caption, MessageBoxButton.OKCancel,
MessageBoxImage.Question);
if (result == MessageBoxResult.Cancel) {
e.Cancel = true;
} }
} }
DialogResult = true; }
private VisualizationSet MakeVisSet() {
if (VisualizationList.Count == 0) {
return null;
}
VisualizationSet newSet = new VisualizationSet(VisualizationList.Count);
foreach (Visualization vis in VisualizationList) {
newSet.Add(vis);
}
return newSet;
} }
private void VisualizationList_SelectionChanged(object sender, private void VisualizationList_SelectionChanged(object sender,
SelectionChangedEventArgs e) { SelectionChangedEventArgs e) {
Debug.WriteLine("SEL CHANGE"); bool isItemSelected = (visualizationGrid.SelectedItem != null);
IsEditEnabled = HasVisPlugins && isItemSelected;
IsRemoveEnabled = isItemSelected;
IsUpEnabled = isItemSelected && visualizationGrid.SelectedIndex != 0;
IsDownEnabled = isItemSelected &&
visualizationGrid.SelectedIndex != VisualizationList.Count - 1;
} }
private void VisualizationList_MouseDoubleClick(object sender, MouseButtonEventArgs e) { private void VisualizationList_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
Debug.WriteLine("DBL CLICK"); EditSelectedItem();
} }
private void NewButton_Click(object sender, RoutedEventArgs e) { private void NewButton_Click(object sender, RoutedEventArgs e) {
VisualizationList.Add(new Visualization("VIS #" + VisualizationList.Count, EditVisualization dlg = new EditVisualization(this, mProject, mFormatter, mOffset,
"apple2-hi-res-bitmap", new Dictionary<string, object>())); null);
if (dlg.ShowDialog() != true) {
// TODO: disable New button if no appropriate vis plugins can be found (or maybe return;
// MessageBox here) }
VisualizationList.Add(dlg.NewVis);
} }
private void EditButton_Click(object sender, RoutedEventArgs e) { private void EditButton_Click(object sender, RoutedEventArgs e) {
Dictionary<string, object> testDict = new Dictionary<string, object>(); EditSelectedItem();
testDict.Add("offset", 0); }
testDict.Add("byteWidth", 2);
testDict.Add("height", 7); private void EditSelectedItem() {
EditVisualization dlg = new EditVisualization(this, mProject, mFormatter, if (!IsEditEnabled) {
new Visualization("arbitrary tag", "apple2-hi-res-bitmap", testDict)); // can happen on a double-click
if (dlg.ShowDialog() == true) { return;
Visualization newVis = dlg.NewVis; }
Debug.WriteLine("New vis: " + newVis); Visualization item = (Visualization)visualizationGrid.SelectedItem;
EditVisualization dlg = new EditVisualization(this, mProject, mFormatter, mOffset,
item);
if (dlg.ShowDialog() != true) {
return;
} }
// TODO: disable edit button if matching vis can't be found (or maybe MessageBox) int index = VisualizationList.IndexOf(item);
VisualizationList.Remove(item);
VisualizationList.Insert(index, dlg.NewVis);
} }
private void RemoveButton_Click(object sender, RoutedEventArgs e) { private void RemoveButton_Click(object sender, RoutedEventArgs e) {
Visualization item = (Visualization)visualizationGrid.SelectedItem;
VisualizationList.Remove(item);
} }
private void UpButton_Click(object sender, RoutedEventArgs e) { private void UpButton_Click(object sender, RoutedEventArgs e) {
Visualization item = (Visualization)visualizationGrid.SelectedItem;
int index = VisualizationList.IndexOf(item);
Debug.Assert(index > 0);
VisualizationList.Remove(item);
VisualizationList.Insert(index - 1, item);
visualizationGrid.SelectedIndex = index - 1;
} }
private void DownButton_Click(object sender, RoutedEventArgs e) { private void DownButton_Click(object sender, RoutedEventArgs e) {
Visualization item = (Visualization)visualizationGrid.SelectedItem;
int index = VisualizationList.IndexOf(item);
Debug.Assert(index < VisualizationList.Count - 1);
VisualizationList.Remove(item);
VisualizationList.Insert(index + 1, item);
visualizationGrid.SelectedIndex = index + 1;
} }
} }
} }