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

More progress on visualization

Implemented Apple II hi-res bitmap conversion.  Supports B&W and
color.  Uses essentially the same algorithm as CiderPress.

Experimented with displaying non-text items in ListView.  I assumed
it would work, since it's the sort of thing WPF is designed to do,
but it's always wise to approach with caution.  Visualization Sets
now show a 64x64 button as a placeholder for the eventual thumbnail.

Some things were being flaky, which turned out to be because I
wasn't Prepare()ing the plugins before using them from Edit
Visualization.  To make this a deterministic failure I added an
Unprepare() call that tells the plugin that we're all done.

NOTE: this breaks all existing plugins.
This commit is contained in:
Andy McFadden 2019-11-30 17:52:33 -08:00
parent 9244ceda7c
commit 365864ccdf
22 changed files with 540 additions and 37 deletions

View File

@ -39,6 +39,11 @@ namespace PluginCommon {
/// <param name="fileData">65xx code and data.</param>
/// <param name="addrMap">Mapping between offsets and addresses.</param>
void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans);
/// <summary>
/// Tells the plugin that we're done talking to it for now.
/// </summary>
void Unprepare();
}
/// <summary>
@ -138,7 +143,8 @@ namespace PluginCommon {
/// </summary>
/// <param name="descr">VisGen identifier.</param>
/// <param name="parms">Parameter set.</param>
/// <returns>2D visualization object reference.</returns>
/// <returns>2D visualization object reference, or null if something went
/// wrong (unknown ident, bad parameters, etc).</returns>
IVisualization2d Generate2d(VisDescr descr, Dictionary<string, object> parms);
}

View File

@ -169,6 +169,16 @@ namespace PluginCommon {
}
}
/// <summary>
/// Invokes the Unprepare() method on all active plugins.
/// </summary>
public void UnpreparePlugins() {
foreach (KeyValuePair<string, IPlugin> kvp in mActivePlugins) {
IPlugin ipl = kvp.Value;
ipl.Unprepare();
}
}
/// <summary>
/// Returns true if any of the plugins report that the before or after label is
/// significant.

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using CommonUtil;
namespace PluginCommon {
@ -97,5 +97,23 @@ namespace PluginCommon {
public static int MakeARGB(int a, int r, int g, int b) {
return (a << 24) | (r << 16) | (g << 8) | b;
}
/// <summary>
/// Extracts a typed value from a dictionary with plain object values.
/// </summary>
/// <typeparam name="T">Type of value to retrieve.</typeparam>
/// <param name="dict">Dictionary with values.</param>
/// <param name="key">Entry to find.</param>
/// <param name="defVal">Default value.</param>
/// <returns>Value found, or the default if the key doesn't exist or the value has the
/// wrong type.</returns>
public static T GetFromObjDict<T>(Dictionary<string, object> dict, string key, T defVal) {
if (dict.TryGetValue(key, out object objVal)) {
if (objVal is T) {
return (T)objVal;
}
}
return defVal;
}
}
}

View File

@ -52,7 +52,7 @@ namespace PluginCommon {
}
public void SetPixelIndex(int x, int y, byte colorIndex) {
if (x < 0 || x >= Width || y < 0 || y >= Width) {
if (x < 0 || x >= Width || y < 0 || y >= Height) {
throw new ArgumentException("Bad x/y: " + x + "," + y + " (width=" + Width +
" height=" + Height + ")");
}
@ -82,7 +82,18 @@ namespace PluginCommon {
Debug.WriteLine("Palette is full");
return;
}
for (int i = 0; i < mNextColor; i++) {
if (mPalette[i] == color) {
Debug.WriteLine("Color " + color.ToString("x6") +
" already exists in palette (" + i + ")");
return;
}
}
mPalette[mNextColor++] = color;
}
public void AddColor(byte a, byte r, byte g, byte b) {
AddColor(Util.MakeARGB(a, r, g, b));
}
}
}

View File

@ -292,6 +292,9 @@ namespace SourceGen {
searchStart = FindFirstUnvisitedInstruction(searchStart);
}
if (mScriptManager != null) {
mScriptManager.UnprepareScripts();
}
mScriptSupport.Shutdown();
MarkUnexecutedEmbeddedCode();

View File

@ -2443,9 +2443,16 @@ namespace SourceGen {
return bestSym;
}
// Punch-through functions; trying to avoid exposing ScriptManager for now.
public List<PluginCommon.IPlugin> GetActivePlugins() {
return mScriptManager.GetActivePlugins();
}
public void PrepareScripts(PluginCommon.IApplication appRef) {
mScriptManager.PrepareScripts(appRef);
}
public void UnprepareScripts() {
mScriptManager.UnprepareScripts();
}
/// <summary>
/// For debugging purposes, get some information about the currently loaded

View File

@ -354,6 +354,7 @@ namespace SourceGen {
public string Operand { get; private set; }
public string Comment { get; private set; }
public bool IsLongComment { get; private set; }
public bool IsVisualizationSet { get; private set; }
public bool HasBackgroundColor { get; private set; }
public Brush BackgroundBrush { get; private set; }
@ -448,6 +449,13 @@ namespace SourceGen {
return parts;
}
public static FormattedParts CreateVisualizationSet(string summary) {
FormattedParts parts = new FormattedParts();
parts.Comment = summary;
parts.IsVisualizationSet = true;
return parts;
}
public static FormattedParts AddSelectionHighlight(FormattedParts orig) {
FormattedParts newParts = Clone(orig);
newParts.HasAddrLabelHighlight = true;

View File

@ -509,7 +509,7 @@ namespace SourceGen {
// TODO(xyzzy)
mProject.VisualizationSets.TryGetValue(line.FileOffset,
out VisualizationSet visSet);
parts = FormattedParts.CreateLongComment("!VISUALIZATION SET! " +
parts = FormattedParts.CreateVisualizationSet("!VISUALIZATION SET! " +
(visSet != null ? "VS:" + visSet.Count : "???"));
break;
case Line.Type.Blank:

View File

@ -54,7 +54,11 @@ namespace RuntimeData.Apple {
mAppRef.DebugLog("GSOS(id=" + AppDomain.CurrentDomain.Id + "): prepare()");
//System.Diagnostics.Debugger.Break();
}
public void Unprepare() {
mAppRef = null;
mFileData = null;
}
public void UpdateSymbolList(List<PlSymbol> plSyms) {

View File

@ -47,6 +47,11 @@ namespace RuntimeData.Apple {
mAppRef.DebugLog("IIgsToolbox(id=" + AppDomain.CurrentDomain.Id + "): prepare()");
}
public void Unprepare() {
mAppRef = null;
mFileData = null;
}
public void UpdateSymbolList(List<PlSymbol> plSyms) {
// Extract the list of function name constants from the platform symbol file.
mFunctionList = PlSymbol.GeneratePlatformValueList(plSyms, TOOLBOX_FUNC_TAG, mAppRef);

View File

@ -166,8 +166,14 @@ namespace RuntimeData.Apple {
mAppRef.DebugLog("ProDOS(id=" + AppDomain.CurrentDomain.Id + "): prepare()");
//System.Diagnostics.Debugger.Break();
}
public void Unprepare() {
mAppRef = null;
mFileData = null;
mAddrTrans = null;
}
public void UpdateSymbolList(List<PlSymbol> plSyms) {
// Extract the list of function name constants from the platform symbol file.
mFunctionList = PlSymbol.GeneratePlatformValueList(plSyms, P8_MLI_TAG, mAppRef);

View File

@ -51,6 +51,11 @@ namespace RuntimeData.Apple {
//System.Diagnostics.Debugger.Break();
}
public void Unprepare() {
mAppRef = null;
mFileData = null;
}
public void UpdateSymbolList(List<PlSymbol> plSyms) {
// Extract the list of function name constants from the platform symbol file.
mFunctionList = PlSymbol.GeneratePlatformValueList(plSyms, SOS_MLI_TAG, mAppRef);

View File

@ -21,6 +21,7 @@ using PluginCommon;
namespace RuntimeData.Apple {
public class VisHiRes : MarshalByRefObject, IPlugin, IPlugin_Visualizer {
// IPlugin
public string Identifier {
get { return "Apple II Hi-Res Graphic Visualizer"; }
}
@ -32,49 +33,71 @@ namespace RuntimeData.Apple {
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 P_OFFSET = "offset";
private const string P_BYTE_WIDTH = "byteWidth";
private const string P_HEIGHT = "height";
private const string P_COL_STRIDE = "colStride";
private const string P_ROW_STRIDE = "rowStride";
private const string P_IS_COLOR = "isColor";
private const string P_IS_FIRST_ODD = "isFirstOdd";
private const string P_ITEM_BYTE_WIDTH = "itemByteWidth";
private const string P_ITEM_HEIGHT = "itemHeight";
private const string P_COUNT = "count";
private const int MAX_DIM = 4096;
// Visualization descriptors.
private VisDescr[] mDescriptors = new VisDescr[] {
new VisDescr(VIS_GEN_BITMAP, "Apple II Hi-Res Bitmap", VisDescr.VisType.Bitmap,
new VisParamDescr[] {
new VisParamDescr("File offset (hex)",
"offset", typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset,
P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset,
0x2000),
new VisParamDescr("Width (in bytes)",
"byteWidth", typeof(int), 1, 40, 0, 1),
P_BYTE_WIDTH, typeof(int), 1, 40, 0, 1),
new VisParamDescr("Height",
"height", typeof(int), 1, 192, 0, 1),
P_HEIGHT, typeof(int), 1, 192, 0, 1),
new VisParamDescr("Column stride (bytes)",
"colStride", typeof(int), 0, 256, 0, 0),
P_COL_STRIDE, typeof(int), 0, 256, 0, 0),
new VisParamDescr("Row stride (bytes)",
"rowStride", typeof(int), 0, 256, 0, 0),
P_ROW_STRIDE, typeof(int), 0, 256, 0, 0),
new VisParamDescr("Color",
"color", typeof(bool), 0, 0, 0, true),
new VisParamDescr("First byte odd",
"firstOdd", typeof(bool), 0, 0, 0, false),
P_IS_COLOR, typeof(bool), 0, 0, 0, true),
new VisParamDescr("First col odd",
P_IS_FIRST_ODD, typeof(bool), 0, 0, 0, false),
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 VisParamDescr[] {
new VisParamDescr("File offset (hex)",
"offset", typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset,
P_OFFSET, typeof(int), 0, 0x00ffffff, VisParamDescr.SpecialMode.Offset,
0x1000),
new VisParamDescr("Item width (in bytes)",
"itemByteWidth", typeof(int), 1, 40, 0, 1),
P_ITEM_BYTE_WIDTH, typeof(int), 1, 40, 0, 1),
new VisParamDescr("Item height",
"itemHeight", typeof(int), 1, 192, 0, 8),
P_ITEM_HEIGHT, typeof(int), 1, 192, 0, 8),
new VisParamDescr("Number of items",
"count", typeof(int), 1, 256, 0, 1),
P_COUNT, typeof(int), 1, 256, 0, 1),
}),
};
// IPlugin
public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) {
mAppRef = appRef;
mFileData = fileData;
mAddrTrans = addrTrans;
}
// IPlugin
public void Unprepare() {
mAppRef = null;
mFileData = null;
mAddrTrans = null;
}
// IPlugin_Visualizer
public VisDescr[] GetVisGenDescrs() {
return mDescriptors;
@ -83,17 +106,241 @@ namespace RuntimeData.Apple {
// IPlugin_Visualizer
public IVisualization2d Generate2d(VisDescr descr,
Dictionary<string, object> parms) {
// TODO: replace with actual
VisBitmap8 vb = new VisBitmap8(16, 16);
vb.AddColor(Util.MakeARGB(0xff, 0x40, 0x40, 0x40));
vb.AddColor(Util.MakeARGB(0xff, 0xff, 0x00, 0x00));
vb.AddColor(Util.MakeARGB(0xff, 0x00, 0xff, 0x80));
switch (descr.Ident) {
case VIS_GEN_BITMAP:
return GenerateBitmap(parms);
case VIS_GEN_MULTI_MAP:
// TODO
return null;
default:
mAppRef.DebugLog("Unknown ident " + descr.Ident);
return null;
}
}
for (int i = 0; i < 16; i++) {
vb.SetPixelIndex(i, i, 1);
vb.SetPixelIndex(15 - i, i, 2);
private IVisualization2d GenerateBitmap(Dictionary<string, object> parms) {
int offset, byteWidth, height, colStride, rowStride;
bool isColor, isFirstOdd;
offset = Util.GetFromObjDict(parms, P_OFFSET, 0);
byteWidth = Util.GetFromObjDict(parms, P_BYTE_WIDTH, 0); // width ignoring colStride
height = Util.GetFromObjDict(parms, P_HEIGHT, 0);
colStride = Util.GetFromObjDict(parms, P_COL_STRIDE, 0);
rowStride = Util.GetFromObjDict(parms, P_ROW_STRIDE, 0);
isColor = Util.GetFromObjDict(parms, P_IS_COLOR, true);
isFirstOdd = Util.GetFromObjDict(parms, P_IS_FIRST_ODD, false);
// We allow the stride entries to be zero to indicate a "dense" bitmap.
if (colStride == 0) {
colStride = 1;
}
if (rowStride == 0) {
rowStride = byteWidth * colStride;
}
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) {
mAppRef.DebugLog("Invalid parameter");
return null;
}
int lastOffset = offset + rowStride * height - (colStride - 1) - 1;
if (lastOffset >= mFileData.Length) {
mAppRef.DebugLog("Bitmap runs off end of file (lastOffset=" + lastOffset + ")");
return null;
}
VisBitmap8 vb = new VisBitmap8(byteWidth * 7, height);
SetHiResPalette(vb);
if (!isColor) {
// B&W mode. Since we're not displaying this we don't need to worry about
// half-pixel shifts, and can just convert 7 bits to pixels.
int bx = 0;
int by = 0;
for (int row = 0; row < height; row++) {
int colIdx = 0;
for (int col = 0; col < byteWidth; col++) {
byte val = mFileData[offset + colIdx];
for (int bit = 0; bit < 7; bit++) {
vb.SetPixelIndex(bx, by, (byte)((val & 0x01) + 1)); // black or white
val >>= 1;
bx++;
}
colIdx += colStride;
}
bx = 0;
by++;
offset += rowStride;
}
} else {
int bx = 0;
int by = 0;
#if false
// Color mode. We treat the data as a strictly 140-mode bitmap, which doesn't
// quite match up with how the pixels will be displayed, but does allow a
// straightforward conversion between file formats. Color fringing is severe.
for (int row = 0; row < height; row++) {
int lastBit;
if (isFirstOdd) {
lastBit = 0; // pretend we already have one bit
} else {
lastBit = -1;
}
for (int colByte = 0; colByte < byteWidth; colByte += colStride) {
byte val = mFileData[offset + colByte];
bool hiBitSet = (val & 0x80) != 0;
// Grab 3 or 4 pairs of bits.
int pairCount = (lastBit < 0) ? 3 : 4;
while (pairCount-- > 0) {
int twoBits;
if (lastBit >= 0) {
// merge with bit from previous byte
twoBits = (lastBit << 1) | (val & 0x01);
val >>= 1;
lastBit = -1;
} else {
// grab two bits
twoBits = (val & 0x03);
val >>= 2;
}
if (hiBitSet) {
twoBits += 4;
}
// We're in 140 mode, so set two adjacent pixels.
vb.SetPixelIndex(bx++, by, sHiResColorMap[twoBits]);
vb.SetPixelIndex(bx++, by, sHiResColorMap[twoBits]);
}
bool thisEven = ((colByte & 0x01) == 0) ^ isFirstOdd;
if (thisEven) {
// started in even column we have one bit left over
lastBit = val & 0x01;
} else {
// started in odd column, all bits consumed
lastBit = -1;
}
}
bx = 0;
by++;
offset += rowStride;
}
#else
// Color conversion similar to what CiderPress does, but without the half-pixel
// shift (we're trying to create a 1:1 bitmap).
bool[] lineBits = new bool[byteWidth * 7];
bool[] hiFlags = new bool[byteWidth * 7]; // overkill, but simplifies things
int[] colorBuf = new int[byteWidth * 7];
for (int row = 0; row < height; row++) {
// Unravel the bits.
int idx = 0;
int colIdx = 0;
for (int col = 0; col < byteWidth; col++) {
byte val = mFileData[offset + colIdx];
bool hiBitSet = (val & 0x80) != 0;
for (int bit = 0; bit < 7; bit++) {
hiFlags[idx] = hiBitSet;
lineBits[idx] = (val & 0x01) != 0;
idx++;
val >>= 1;
}
colIdx += colStride;
}
// Convert to color.
int lastBit = byteWidth * 7;
for (idx = 0; idx < lastBit; idx++) {
int colorShift = hiFlags[idx] ? 4 : 0;
if (!lineBits[idx]) {
// Bit not set, set pixel to black.
colorBuf[idx] = (int)HiResColors.Black0 + colorShift;
} else {
// Bit set, set pixel to white or color.
if (idx > 0 && colorBuf[idx - 1] != (int)HiResColors.Black0 &&
colorBuf[idx - 1] != (int)HiResColors.Black1) {
// previous bit was also set, this is white
colorBuf[idx] = (int)HiResColors.White0 + colorShift;
// the previous pixel is part of a run of white
colorBuf[idx - 1] = (int)HiResColors.White0 + colorShift;
} else {
// previous bit not set *or* was first pixel in line;
// set color based on whether this is even or odd pixel col
bool isOdd = ((idx & 0x01) != 0) ^ isFirstOdd;
if (isOdd) {
colorBuf[idx] = (int)HiResColors.Green + colorShift;
} else {
colorBuf[idx] = (int)HiResColors.Purple + colorShift;
}
}
// Do we have a run of the same color? If so, smooth the color out.
// Note that white blends smoothly with everything.
if (idx > 1 && (colorBuf[idx - 2] == colorBuf[idx] ||
colorBuf[idx - 2] == (int)HiResColors.White0 ||
colorBuf[idx - 2] == (int)HiResColors.White1)) {
//if (colorBuf[idx - 1] != (int)HiResColors.Black0 &&
// colorBuf[idx - 1] != (int) HiResColors.Black1) {
// mAppRef.DebugLog("Unexpected color at row=" + by +
// " idx=" + idx + ": " + colorBuf[idx - 1]);
//}
colorBuf[idx - 1] = colorBuf[idx];
}
}
}
// Write to bitmap.
for (idx = 0; idx < lastBit; idx++) {
vb.SetPixelIndex(bx++, by, sHiResColorMap[colorBuf[idx]]);
}
// move to next row
bx = 0;
by++;
offset += rowStride;
}
#endif
}
return vb;
}
private enum HiResColors {
Black0 = 0,
Green = 1,
Purple = 2,
White0 = 3,
Black1 = 4,
Orange = 5,
Blue = 6,
White1 = 7
}
// HiRes: black0, green, purple, white0, black1, orange, blue, white1
private static readonly byte[] sHiResColorMap = new byte[8] {
1, 3, 4, 2, 1, 5, 6, 2
};
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(0xff, 0x00, 0x00, 0x00); // 1=black0/black1
vb.AddColor(0xff, 0xff, 0xff, 0xff); // 2=white0/white1
vb.AddColor(0xff, 0x11, 0xdd, 0x00); // 3=green
vb.AddColor(0xff, 0xdd, 0x22, 0xdd); // 4=purple
vb.AddColor(0xff, 0xff, 0x66, 0x00); // 5=orange
vb.AddColor(0xff, 0x22, 0x22, 0xff); // 6=blue
}
}
}

View File

@ -24,6 +24,11 @@ namespace RuntimeData.Test2011 {
mAppRef.DebugLog("Test2011(id=" + AppDomain.CurrentDomain.Id + "): prepare()");
}
public void Unprepare() {
mAppRef = null;
mFileData = null;
}
public void CheckJsr(int offset, int operand, out bool noContinue) {
int ADDR = 0x2456;

View File

@ -31,6 +31,12 @@ namespace RuntimeData.Test2022 {
mAppRef.DebugLog("Test2022-A(id=" + AppDomain.CurrentDomain.Id + "): prepare()");
}
public void Unprepare() {
mAppRef = null;
mFileData = null;
}
public void UpdateSymbolList(List<PlSymbol> plSyms) {
foreach (PlSymbol sym in plSyms) {
switch (sym.Label) {

View File

@ -26,6 +26,12 @@ namespace RuntimeData.Test2022 {
mAppRef.DebugLog("Test2022-B(id=" + AppDomain.CurrentDomain.Id + "): prepare()");
}
public void Unprepare() {
mAppRef = null;
mFileData = null;
mAddrTrans = null;
}
public void CheckBrk(int offset, bool twoByteBrk, out bool noContinue) {
noContinue = true;

View File

@ -173,6 +173,22 @@ namespace SourceGen.Sandbox {
}
}
/// <summary>
/// Puts scripts back to sleep.
/// </summary>
public void UnprepareScripts() {
if (DomainMgr == null) {
foreach (KeyValuePair<string, IPlugin> kvp in mActivePlugins) {
IPlugin ipl = kvp.Value;
ipl.Unprepare();
}
} else {
List<AddressMap.AddressMapEntry> addrEnts = mProject.AddrMap.GetEntryList();
DomainMgr.PluginMgr.UnpreparePlugins();
}
}
/// <summary>
/// Returns true if any of the plugins report that the before or after label is
/// significant.

View File

@ -38,15 +38,15 @@ namespace SourceGen {
/// </summary>
public Dictionary<string, object> VisGenParams { get; private set; }
public double Thumbnail { get; } // TODO - 64x64(?) bitmap
private BitmapSource Thumbnail { get; set; } // TODO - 64x64(?) bitmap
/// <summary>
/// Constructor.
/// </summary>
/// <param name="tag"></param>
/// <param name="visGenIdent"></param>
/// <param name="visGenParams"></param>
/// <param name="tag">Unique identifier.</param>
/// <param name="visGenIdent">Visualization generator identifier.</param>
/// <param name="visGenParams">Parameters for visualization generator.</param>
public Visualization(string tag, string visGenIdent,
Dictionary<string, object> visGenParams) {
Tag = tag;
@ -54,7 +54,12 @@ namespace SourceGen {
VisGenParams = visGenParams;
}
public static BitmapSource CreateBitmapSource(IVisualization2d vis2d) {
/// <summary>
/// Converts an IVisualization2d to a BitmapSource for display.
/// </summary>
/// <param name="vis2d"></param>
/// <returns></returns>
public static BitmapSource ConvertToBitmapSource(IVisualization2d vis2d) {
// Create indexed color palette.
int[] intPal = vis2d.GetPalette();
List<Color> colors = new List<Color>(intPal.Length);

View File

@ -76,6 +76,38 @@ See also https://github.com/fadden/DisasmUiTest
Width="{Binding LongCommentWidth}"/>
</GridViewColumnCollection>
<GridViewColumnCollection x:Key="gvcc_vs">
<GridViewColumn DisplayMemberBinding="{Binding Offset}" Width="{Binding
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}
}, Path=View.Columns[0].ActualWidth}"/>
<GridViewColumn DisplayMemberBinding="{Binding Addr}" Width="{Binding
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}
}, Path=View.Columns[1].ActualWidth}"/>
<GridViewColumn DisplayMemberBinding="{Binding Bytes}" Width="{Binding
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}
}, Path=View.Columns[2].ActualWidth}"/>
<GridViewColumn DisplayMemberBinding="{Binding Flags}" Width="{Binding
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}
}, Path=View.Columns[3].ActualWidth}"/>
<GridViewColumn DisplayMemberBinding="{Binding Attr}" Width="{Binding
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}
}, Path=View.Columns[4].ActualWidth}"/>
<!-- This column holds the long comment. There's no easy way to set its width, so we
have to let the main window take care of that. (It's tempting to just make its width
very large, but that causes the GridView contents to horizontally scroll independently
of the GridView header when you reach the edge of the "normal" column set.) -->
<GridViewColumn Header="(visualization set)" Width="{Binding LongCommentWidth}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Comment}" VerticalAlignment="Center"/>
<Button Width="64" Height="64" Content="Whee"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridViewColumnCollection>
<!-- Base template for ListView items, derived from the system default. We have to define
this fully so things don't turn into a big mess on long-comment lines. -->
<ControlTemplate x:Key="baseListItemTemplate" TargetType="{x:Type ListViewItem}">
@ -199,6 +231,68 @@ See also https://github.com/fadden/DisasmUiTest
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- yet another copy of the template, this one for VisualizationSet lines -->
<ControlTemplate x:Key="visualizationSetTemplate" TargetType="{x:Type ListViewItem}">
<StackPanel>
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
CornerRadius="2"
SnapsToDevicePixels="true">
<Border x:Name="InnerBorder" BorderThickness="1" CornerRadius="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition MaxHeight="11"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle x:Name="UpperHighlight" Fill="#75ffffff" Visibility="Collapsed"/>
<GridViewRowPresenter Content="{TemplateBinding Content}"
Columns="{StaticResource gvcc_vs}"
Grid.RowSpan="2"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
</Border>
</Border>
</StackPanel>
<!-- triggers for hover, selection, and activation effects -->
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="{StaticResource ListItemHoverFill}"/>
<Setter Property="BorderBrush" Value="{DynamicResource Brush_MouseOverBorder}"/>
<Setter Property="Visibility" TargetName="UpperHighlight" Value="Visible"/>
</Trigger>
<Trigger Property="IsSelected" Value="true">
<Setter Property="Background" Value="{StaticResource ListItemSelectedFill}"/>
<Setter Property="BorderBrush" Value="{DynamicResource Brush_SelectedBorder}"/>
<Setter Property="BorderBrush" TargetName="InnerBorder" Value="#80FFFFFF"/>
<Setter Property="Visibility" TargetName="UpperHighlight" Value="Visible"/>
<Setter Property="Fill" TargetName="UpperHighlight" Value="#40FFFFFF"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="Selector.IsSelectionActive" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="{StaticResource ListItemSelectedInactiveFill}"/>
<Setter Property="BorderBrush" Value="{DynamicResource Brush_SelectedActiveBorder}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true"/>
<Condition Property="IsMouseOver" Value="true"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="{StaticResource ListItemSelectedHoverFill}"/>
<Setter Property="BorderBrush" Value="{DynamicResource Brush_SelectedMouseOverBorder}"/>
</MultiTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- ListView style. We have to override the general foreground/background colors to
avoid making the whole thing look freakish. -->
<Style x:Key="codeListStyle" TargetType="{x:Type ListView}">
@ -225,6 +319,9 @@ See also https://github.com/fadden/DisasmUiTest
<DataTrigger Binding="{Binding Path=IsLongComment}" Value="True">
<Setter Property="Template" Value="{StaticResource longCommentTemplate}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsVisualizationSet}" Value="True">
<Setter Property="Template" Value="{StaticResource visualizationSetTemplate}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=HasBackgroundColor}" Value="True">
<Setter Property="Background" Value="{Binding Path=BackgroundBrush}"/>
</DataTrigger>

View File

@ -25,7 +25,8 @@ limitations under the License.
Title="Edit Visualization"
Width="460" SizeToContent="Height" ResizeMode="NoResize"
ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
Loaded="Window_Loaded">
Loaded="Window_Loaded"
Closed="Window_Closed">
<Window.Resources>
<!-- big thanks: http://drwpf.com/blog/2008/01/03/itemscontrol-d-is-for-datatemplate/ -->
@ -125,7 +126,8 @@ limitations under the License.
</Grid>
<Border Grid.Row="1" BorderThickness="1"
BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}">
BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}"
Background="LightGray">
<Image Name="previewImage" Width="400" Height="400" Source="/Res/AboutImage.png"
RenderOptions.BitmapScalingMode="NearestNeighbor"/>
</Border>

View File

@ -100,6 +100,21 @@ namespace SourceGen.WpfGui {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private class ScriptSupport : MarshalByRefObject, PluginCommon.IApplication {
public ScriptSupport() { }
public void DebugLog(string msg) {
Debug.WriteLine("Vis plugin: " + msg);
}
public bool SetOperandFormat(int offset, DataSubType subType, string label) {
throw new InvalidOperationException();
}
public bool SetInlineDataFormat(int offset, int length, DataType type,
DataSubType subType, string label) {
throw new InvalidOperationException();
}
}
private ScriptSupport mScriptSupport = new ScriptSupport();
/// <summary>
/// Constructor.
@ -140,6 +155,7 @@ namespace SourceGen.WpfGui {
// Set the selection. This should cause the sel change event to fire.
visComboBox.SelectedIndex = visSelection;
mProject.PrepareScripts(mScriptSupport);
}
/// <summary>
@ -188,6 +204,10 @@ namespace SourceGen.WpfGui {
private void Window_Loaded(object sender, RoutedEventArgs e) {
}
private void Window_Closed(object sender, EventArgs e) {
mProject.UnprepareScripts();
}
private void OkButton_Click(object sender, RoutedEventArgs e) {
VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem;
Debug.Assert(item != null);
@ -305,12 +325,27 @@ namespace SourceGen.WpfGui {
}
if (!IsValid) {
// TODO(xyzzy): default to a meaningful image
previewImage.Source = new BitmapImage(new Uri("pack://application:,,,/Res/Logo.png"));
} else {
VisualizationItem item = (VisualizationItem)visComboBox.SelectedItem;
IVisualization2d vis2d = item.Plugin.Generate2d(item.VisDescriptor,
CreateVisGenParams());
previewImage.Source = Visualization.CreateBitmapSource(vis2d);
IVisualization2d vis2d;
try {
vis2d = item.Plugin.Generate2d(item.VisDescriptor,
CreateVisGenParams());
if (vis2d == null) {
Debug.WriteLine("Vis generator returned null");
}
} catch (Exception ex) {
// TODO(xyzzy): use different image for failure
Debug.WriteLine("Vis generation failed: " + ex.Message);
vis2d = null;
}
if (vis2d == null) {
previewImage.Source = new BitmapImage(new Uri("pack://application:,,,/Res/Logo.png"));
} else {
previewImage.Source = Visualization.ConvertToBitmapSource(vis2d);
}
}
}

View File

@ -98,8 +98,9 @@ namespace SourceGen.WpfGui {
private void EditButton_Click(object sender, RoutedEventArgs e) {
Dictionary<string, object> testDict = new Dictionary<string, object>();
testDict.Add("offset", 0x1234);
testDict.Add("height", 57);
testDict.Add("offset", 0);
testDict.Add("byteWidth", 2);
testDict.Add("height", 7);
EditVisualization dlg = new EditVisualization(this, mProject, mFormatter,
new Visualization("arbitrary tag", "apple2-hi-res-bitmap", testDict));
if (dlg.ShowDialog() == true) {