mirror of
https://github.com/markdavidlong/AppleSAWS.git
synced 2024-11-22 00:31:04 +00:00
Added viewer to show jumps in disassembly viewer
This commit is contained in:
parent
93f1b143d3
commit
e85b95b66b
@ -66,7 +66,9 @@ SOURCES += \
|
||||
src/binaryfile/AssemblerSymbolModel.cpp \
|
||||
src/ui/diskexplorer/DiskExplorer.cpp \
|
||||
src/ui/diskexplorer/DiskExplorerMapWidget.cpp \
|
||||
src/applesoftfile/ApplesoftRetokenizer.cpp
|
||||
src/applesoftfile/ApplesoftRetokenizer.cpp \
|
||||
src/internals/JumpLineManager.cpp \
|
||||
src/ui/widgets/FlowLineTextBrowser.cpp
|
||||
|
||||
|
||||
HEADERS += \
|
||||
@ -116,7 +118,9 @@ HEADERS += \
|
||||
src/ui/diskexplorer/DiskExplorer.h \
|
||||
src/ui/diskexplorer/DiskExplorerMapWidget.h \
|
||||
src/applesoftfile/ApplesoftRetokenizer.h \
|
||||
src/util/AppleColors.h
|
||||
src/util/AppleColors.h \
|
||||
src/internals/JumpLineManager.h \
|
||||
src/ui/widgets/FlowLineTextBrowser.h
|
||||
|
||||
FORMS += \
|
||||
src/ui/catalogwidget.ui \
|
||||
|
@ -15,6 +15,8 @@ Disassembler::Disassembler(QByteArray memimage)
|
||||
QList<DisassembledItem> Disassembler::disassemble(quint16 from, quint16 to,
|
||||
QList<quint16> entryPoints,
|
||||
bool processRecursively) {
|
||||
m_from = from;
|
||||
m_to = to;
|
||||
QList<DisassembledItem> retval;
|
||||
qDebug() << "\n\n*****************\n\nDisassemble: From"<<uint16ToHex(from)<<"to"<<uint16ToHex(to);
|
||||
//#define OLDDISSEM
|
||||
@ -64,12 +66,12 @@ QList<DisassembledItem> Disassembler::disassemble(quint16 from, quint16 to,
|
||||
{
|
||||
qDebug() << "Is Branch";
|
||||
m_stack.push(item.targetAddress());
|
||||
// if (!m_jumps.contains(item.targetAddress()))
|
||||
// {
|
||||
// m_jumps.append(item.targetAddress());
|
||||
// qDebug() << "Appending branch" << uint16ToHex(item.targetAddress()) << "to jump table";
|
||||
// if (!m_jumps.contains(item.targetAddress()))
|
||||
// {
|
||||
// m_jumps.append(item.targetAddress());
|
||||
// qDebug() << "Appending branch" << uint16ToHex(item.targetAddress()) << "to jump table";
|
||||
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
if (item.isJsr() && !item.canNotFollow())
|
||||
@ -148,6 +150,12 @@ QList<DisassembledItem> Disassembler::disassemble(quint16 from, quint16 to,
|
||||
// }
|
||||
// qDebug() << "Operations Args:" << hexdump;
|
||||
|
||||
if (processRecursively)
|
||||
{
|
||||
// m_jlm.dumpJumps();
|
||||
m_jumplines = m_jlm.buildJumpLines();
|
||||
qDebug() << "Num Channels: " << m_jlm.getNumJumpLineChannels();
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
@ -165,7 +173,10 @@ bool Disassembler::disassembleOp(quint16 address, DisassembledItem &retval, Memo
|
||||
retval.setInstruction(op);
|
||||
|
||||
if (opcode == 0x6C || opcode == 0x7c) // Indirect jumps
|
||||
{
|
||||
m_jlm.addJump(address,address,IsUnknownJump,m_from,m_to);
|
||||
retval.setCanNotFollow(true);
|
||||
}
|
||||
|
||||
QString disassemblyLine;
|
||||
QString hexValueString;
|
||||
@ -254,6 +265,10 @@ bool Disassembler::disassembleOp(quint16 address, DisassembledItem &retval, Memo
|
||||
quint16 offsetAddress = address+2+offset;
|
||||
|
||||
retval.setTargetAddress(offsetAddress);
|
||||
if (opcode == 0x80) // BRA
|
||||
m_jlm.addJump(address,offsetAddress,IsBRA,m_from,m_to);
|
||||
else
|
||||
m_jlm.addJump(address,offsetAddress,IsBranch,m_from,m_to);
|
||||
|
||||
disassemblyLine = QString("%1 _ARG16_ {%2%3}").arg(op.mnemonic())
|
||||
.arg((offset<0)?"-":"+")
|
||||
@ -301,16 +316,19 @@ bool Disassembler::disassembleOp(quint16 address, DisassembledItem &retval, Memo
|
||||
|
||||
if (opcode == 0x20 || opcode == 0x4c) {
|
||||
retval.setTargetAddress(makeWord(hexValues[1],hexValues[2]));
|
||||
}
|
||||
|
||||
if (opcode == 0x4c)
|
||||
if (opcode == 0x4c) // JMP
|
||||
{
|
||||
qDebug() << "JMP: Adding flow address "
|
||||
<< uint16ToHex(makeWord(hexValues[1],hexValues[2])) << "to jump table";
|
||||
|
||||
m_jlm.addJump(address,argval,IsJMP,m_from,m_to);
|
||||
m_stack.push(argval);
|
||||
retval.setCanNotFollow(true);
|
||||
|
||||
}
|
||||
else // JSR
|
||||
{
|
||||
m_jlm.addJump(address,argval,IsJSR,m_from,m_to);
|
||||
}
|
||||
}
|
||||
|
||||
retval.setAddress(address);
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "MemoryUsageMap.h"
|
||||
#include "util.h"
|
||||
#include "JumpLineManager.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QStringList>
|
||||
@ -10,6 +11,7 @@
|
||||
#include <QDebug>
|
||||
#include <QStack>
|
||||
|
||||
|
||||
enum AddressMode {
|
||||
AM_InvalidOp,
|
||||
AM_Absolute, // a
|
||||
@ -163,6 +165,9 @@ private:
|
||||
class Disassembler
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
|
||||
Disassembler(QByteArray memimage);
|
||||
|
||||
enum ProcessorType {
|
||||
@ -184,12 +189,16 @@ public:
|
||||
return m_opcodeinfo[opcode].mnemonic();
|
||||
}
|
||||
|
||||
private:
|
||||
JumpLines getJumpLines() const { return m_jumplines; }
|
||||
|
||||
private:
|
||||
bool disassembleOp(quint16 address, DisassembledItem &retval, MemoryUsageMap *memuse = Q_NULLPTR);
|
||||
void makeOpcodeTable();
|
||||
|
||||
|
||||
quint16 m_from;
|
||||
quint16 m_to;
|
||||
|
||||
QHash<quint8,AssyInstruction> m_opcodeinfo;
|
||||
QByteArray m_memimage;
|
||||
|
||||
@ -197,6 +206,10 @@ private:
|
||||
|
||||
MemoryUsageMap m_memusagemap;
|
||||
|
||||
JumpLineManager m_jlm;
|
||||
|
||||
JumpLines m_jumplines;
|
||||
|
||||
};
|
||||
|
||||
#endif // DISASSEMBLER_H
|
||||
|
173
src/internals/JumpLineManager.cpp
Normal file
173
src/internals/JumpLineManager.cpp
Normal file
@ -0,0 +1,173 @@
|
||||
#include "JumpLineManager.h"
|
||||
|
||||
JumpLineManager::JumpLineManager(quint16 from, quint16 to)
|
||||
: m_start(from),
|
||||
m_end(to)
|
||||
{
|
||||
qDebug() << "JumpLineManager(from:"<<uint16ToHex(m_start)<<", to:"<<uint16ToHex(m_end) << ")";
|
||||
|
||||
}
|
||||
|
||||
void JumpLineManager::addJump(quint16 src, quint16 dest, JumpType type, quint16 from, quint16 to)
|
||||
{
|
||||
TJump jump(src,dest);
|
||||
if (src >= from && src <= to && dest >= from && dest <= to)
|
||||
{
|
||||
if (!m_jumpmap.contains(jump))
|
||||
{
|
||||
// qDebug() << "JumpLineManager::addJump: Added Jump from" << uint16ToHex(src) << "to" << uint16ToHex(dest) << ", Type:" << type;
|
||||
m_jumpmap.insert(jump,type);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "JumpLineManager::addJump: Not adding duplicate jump:" << uint16ToHex(src) << "," << uint16ToHex(dest);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "JumpLineManager::addJump: Address range is out of bounds";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void JumpLineManager::dumpJumps() const {
|
||||
dumpJumps(m_jumpmap);
|
||||
}
|
||||
|
||||
void JumpLineManager::dumpJumps(JumpMap map) const
|
||||
{
|
||||
qDebug() << "JumpLineManager::dumpJumps()\n JumpTable:";
|
||||
QMapIterator<TJump,JumpType> it(map);
|
||||
while (it.hasNext())
|
||||
{
|
||||
it.next();
|
||||
QString jumptypelabel;
|
||||
if (it.value() == IsUnknownJump) { jumptypelabel = "Unknown Jump"; }
|
||||
if (it.value() == IsJMP) { jumptypelabel = "JMP"; }
|
||||
if (it.value() == IsBranch) { jumptypelabel = "Branch"; }
|
||||
if (it.value() == IsJSR) { jumptypelabel = "JSR"; }
|
||||
if (it.value() == IsBRA) { jumptypelabel = "BRA"; }
|
||||
qDebug() << " Jump from" << uint16ToHex(it.key().first) << "to"
|
||||
<< uint16ToHex(it.key().second) << jumptypelabel;
|
||||
}
|
||||
}
|
||||
|
||||
JumpLines JumpLineManager::buildJumpLines()
|
||||
{
|
||||
qDebug() << "A";
|
||||
|
||||
m_channelsAtAddress.clear();
|
||||
m_jumplines.m_maxChannel = 0;
|
||||
QMapIterator<TJump, JumpType> it(m_jumpmap);
|
||||
while (it.hasNext())
|
||||
{
|
||||
it.next();
|
||||
TJump range = it.key();
|
||||
|
||||
JumpLine jl;
|
||||
jl.type = it.value();
|
||||
jl.from = range.first;
|
||||
jl.to = range.second;
|
||||
int channel = findBestChannel(jl);
|
||||
setChannelForJumpLine(channel,jl);
|
||||
|
||||
m_jumplines.jumpLines.append(jl);
|
||||
m_jumplines.m_maxChannel = qMax(m_jumplines.m_maxChannel, channel);
|
||||
}
|
||||
qDebug() << "A";
|
||||
|
||||
|
||||
return m_jumplines;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
int JumpLineManager::findBestChannel(JumpLine &jl)
|
||||
{
|
||||
qDebug() << "findBestChannel()";
|
||||
if (m_jumplines.jumpLines.count() == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int potentialChannel = 0;
|
||||
bool foundChannel = false;
|
||||
while (!foundChannel)
|
||||
{
|
||||
qDebug() << "Tryning potential channel" << potentialChannel;
|
||||
bool matched = false;
|
||||
for (quint16 addr = jl.min(); addr <= jl.max(); addr++)
|
||||
{
|
||||
// If any of these addresses contain the potential channel, move on
|
||||
if (m_channelsAtAddress[addr].contains(potentialChannel))
|
||||
{
|
||||
matched = true;
|
||||
}
|
||||
}
|
||||
if (matched)
|
||||
{
|
||||
potentialChannel++;
|
||||
matched = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
foundChannel = true;
|
||||
}
|
||||
}
|
||||
return potentialChannel;
|
||||
}
|
||||
|
||||
void JumpLineManager::setChannelForJumpLine(int channel, JumpLine &jl)
|
||||
{
|
||||
jl.channel = channel;
|
||||
for (quint16 addr = jl.min(); addr <= jl.max(); addr++)
|
||||
{
|
||||
m_channelsAtAddress[addr].append(channel);
|
||||
}
|
||||
}
|
||||
|
||||
void JumpLineManager::dumpJumpLines() const
|
||||
{
|
||||
foreach (JumpLine jl, m_jumplines.jumpLines)
|
||||
{
|
||||
qDebug() << " JumpLine from:" << uint16ToHex(jl.from)
|
||||
<< " to:" << uint16ToHex(jl.to)
|
||||
<< "channel: " << jl.channel << " type:" << jl.type;
|
||||
}
|
||||
}
|
||||
|
||||
bool JumpLineManager::doJumpsIntersect(TJump &A, TJump &B) const
|
||||
{
|
||||
|
||||
if (A == B) return false;
|
||||
if (isLineWithinRange(A.first,B) || isLineWithinRange(A.second,B))
|
||||
return true;
|
||||
|
||||
if (isLineWithinRange(B.first,A) || isLineWithinRange(B.second,A))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
bool JumpLineManager::isLineWithinRange(quint16 line, TJump &jm) const
|
||||
{
|
||||
quint16 min = qMin(jm.first,jm.second);
|
||||
quint16 max = qMax(jm.first,jm.second);
|
||||
|
||||
return (line > min && line < max);
|
||||
}
|
||||
|
||||
QList<JumpLine> JumpLines::jumpLinesAtAddress(quint16 addrs)
|
||||
{
|
||||
QList<JumpLine> list;
|
||||
foreach (JumpLine jl, jumpLines)
|
||||
{
|
||||
if (addrs >= jl.min() && addrs <= jl.max())
|
||||
{
|
||||
list.append(jl);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
102
src/internals/JumpLineManager.h
Normal file
102
src/internals/JumpLineManager.h
Normal file
@ -0,0 +1,102 @@
|
||||
#ifndef JUMPLINEMANAGER_H
|
||||
#define JUMPLINEMANAGER_H
|
||||
|
||||
#include <QPair>
|
||||
#include <QMap>
|
||||
#include <QMapIterator>
|
||||
#include <QDebug>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
typedef QPair<quint16,quint16> TJump;
|
||||
typedef enum {
|
||||
IsUnknownJump,
|
||||
IsJMP,
|
||||
IsBranch,
|
||||
IsJSR,
|
||||
IsBRA
|
||||
} JumpType;
|
||||
typedef QMap<TJump,JumpType> JumpMap;
|
||||
|
||||
class JumpLine {
|
||||
public:
|
||||
JumpLine() : from(0), to(0), channel(-1), type(IsUnknownJump) { }
|
||||
quint16 from;
|
||||
quint16 to;
|
||||
int channel;
|
||||
JumpType type;
|
||||
|
||||
inline quint16 min() const { return qMin(from,to); }
|
||||
inline quint16 max() const { return qMax(from,to); }
|
||||
inline bool isUp() const { return (from > to); }
|
||||
inline bool isDown() const { return !isUp(); }
|
||||
};
|
||||
|
||||
//////////////////
|
||||
|
||||
class JumpLines
|
||||
{
|
||||
public:
|
||||
JumpLines() : m_maxChannel(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QList<int> channelsAtAddress(quint16 address)
|
||||
{
|
||||
return m_channelsAtAddress[address];
|
||||
}
|
||||
|
||||
QList<JumpLine> jumpLinesAtAddress(quint16 addrs);
|
||||
|
||||
QList<JumpLine> jumpLines;
|
||||
|
||||
int m_maxChannel;
|
||||
|
||||
QMap<quint16, QList<int> > m_channelsAtAddress;
|
||||
};
|
||||
|
||||
//////////////////
|
||||
|
||||
class JumpLineManager
|
||||
{
|
||||
public:
|
||||
JumpLineManager(quint16 from = 0x0000, quint16 to = 0xffff);
|
||||
|
||||
void addJump(quint16 src, quint16 dest, JumpType type,quint16 from = 0, quint16 to = 0xffff);
|
||||
void dumpJumps() const;
|
||||
|
||||
JumpLines buildJumpLines();
|
||||
JumpLines getJumpLines() const { return m_jumplines; }
|
||||
|
||||
|
||||
|
||||
void clear() { m_jumpmap.clear(); }
|
||||
void dumpJumpLines() const;
|
||||
|
||||
int getNumJumpLineChannels() const { return m_jumplines.m_maxChannel; }
|
||||
|
||||
protected:
|
||||
int findBestChannel(JumpLine &jl);
|
||||
|
||||
void setChannelForJumpLine(int channel, JumpLine &jl);
|
||||
|
||||
private:
|
||||
quint16 m_start;
|
||||
quint16 m_end;
|
||||
|
||||
JumpMap m_jumpmap;
|
||||
|
||||
JumpLines m_jumplines;
|
||||
|
||||
QMap<quint16, QList<int>> m_channelsAtAddress;
|
||||
|
||||
|
||||
bool doJumpsIntersect(TJump &A, TJump &B) const;
|
||||
bool isLineWithinRange(quint16 line, TJump &jm) const;
|
||||
|
||||
void dumpJumps(JumpMap map) const;
|
||||
|
||||
};
|
||||
|
||||
#endif // JUMPLINEMANAGER_H
|
@ -104,8 +104,8 @@ void DisassemblerViewer::handleDisassembleRequest(QList<quint16> addresses)
|
||||
{
|
||||
QStringList strings;
|
||||
|
||||
strings += getDisassemblyStrings(addresses);
|
||||
|
||||
disassemble(addresses);
|
||||
strings += getDisassemblyStrings();
|
||||
qSort(strings);
|
||||
strings.removeDuplicates();
|
||||
|
||||
@ -126,7 +126,11 @@ void DisassemblerViewer::handleDisassembleRequest(QList<quint16> addresses)
|
||||
}
|
||||
|
||||
|
||||
QStringList DisassemblerViewer::getDisassemblyStrings(QList<quint16> entryPoints) {
|
||||
QStringList DisassemblerViewer::getDisassemblyStrings() {
|
||||
return m_disassemblyStrings;
|
||||
}
|
||||
|
||||
void DisassemblerViewer::disassemble(QList<quint16> entryPoints) {
|
||||
|
||||
Disassembler dis(m_mem.values());
|
||||
|
||||
@ -136,6 +140,7 @@ QStringList DisassemblerViewer::getDisassemblyStrings(QList<quint16> entryPoints
|
||||
m_file->address()+length,
|
||||
entryPoints);
|
||||
dis.setUnknownToData(m_file->address(),m_file->address()+length);
|
||||
m_jumpLines = dis.getJumpLines();
|
||||
|
||||
QStringList formattedLines;
|
||||
|
||||
@ -176,7 +181,7 @@ QStringList DisassemblerViewer::getDisassemblyStrings(QList<quint16> entryPoints
|
||||
}
|
||||
qSort(formattedLines);
|
||||
|
||||
return formattedLines;
|
||||
m_disassemblyStrings = formattedLines;
|
||||
}
|
||||
|
||||
|
||||
@ -1500,12 +1505,9 @@ void DisassemblerViewer::showMetadataDialog()
|
||||
void DisassemblerViewer::setData(QByteArray data)
|
||||
{
|
||||
ui->textArea->setText(data);
|
||||
ui->textArea->setJumpLines(&m_jumpLines);
|
||||
}
|
||||
|
||||
void DisassemblerViewer::setText(QString text)
|
||||
{
|
||||
ui->textArea->setHtml(text);
|
||||
}
|
||||
|
||||
bool DisassemblerViewer::canPrint() const { return true; }
|
||||
|
||||
@ -1556,30 +1558,30 @@ QString DisassemblerViewer::makeDescriptorStringForVal(quint8 val)
|
||||
{
|
||||
QString retval;
|
||||
|
||||
// QString zone;
|
||||
// if (val <= 0x3f) zone = "Inverse";
|
||||
// else if (val <= 0x7f) zone = "Flash";
|
||||
// else if (val <= 0x9f) zone = "(Alt) Normal";
|
||||
// else zone = "Normal";
|
||||
// QString zone;
|
||||
// if (val <= 0x3f) zone = "Inverse";
|
||||
// else if (val <= 0x7f) zone = "Flash";
|
||||
// else if (val <= 0x9f) zone = "(Alt) Normal";
|
||||
// else zone = "Normal";
|
||||
|
||||
// quint8 baseascii = val;
|
||||
// if (val <= 0x1f) baseascii += 0x40;
|
||||
// else if (val <= 0x5f) baseascii += 0;
|
||||
// else if (val <= 0xbf) baseascii -= 0x40;
|
||||
// else baseascii -= 80;
|
||||
// quint8 baseascii = val;
|
||||
// if (val <= 0x1f) baseascii += 0x40;
|
||||
// else if (val <= 0x5f) baseascii += 0;
|
||||
// else if (val <= 0xbf) baseascii -= 0x40;
|
||||
// else baseascii -= 80;
|
||||
|
||||
// QString ch = QChar(baseascii);
|
||||
// if (val == 0xff) ch = "[DEL]";
|
||||
// QString ch = QChar(baseascii);
|
||||
// if (val == 0xff) ch = "[DEL]";
|
||||
|
||||
// QString appleAscii = QString("%1 %2").arg(ch).arg(zone);
|
||||
// QString appleAscii = QString("%1 %2").arg(ch).arg(zone);
|
||||
|
||||
// if (val < 0x20)
|
||||
// {
|
||||
// QString ctrl = QString(" / (^%1)").arg(QChar(val+0x40));
|
||||
// appleAscii.append(ctrl);
|
||||
// }
|
||||
// if (val < 0x20)
|
||||
// {
|
||||
// QString ctrl = QString(" / (^%1)").arg(QChar(val+0x40));
|
||||
// appleAscii.append(ctrl);
|
||||
// }
|
||||
|
||||
// retval = QString("; %1 / %2").arg(val).arg(appleAscii);
|
||||
// retval = QString("; %1 / %2").arg(val).arg(appleAscii);
|
||||
retval = QString("; %1").arg(val);
|
||||
return retval;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "relocatablefile.h"
|
||||
#include "fileviewerinterface.h"
|
||||
#include "DisassemblerMetadataDialog.h"
|
||||
#include "JumpLineManager.h"
|
||||
|
||||
namespace Ui {
|
||||
class DisassemblerViewer;
|
||||
@ -25,7 +26,6 @@ public:
|
||||
void setFile(BinaryFile *file);
|
||||
void setFile(RelocatableFile *file);
|
||||
void setData(QByteArray data);
|
||||
void setText(QString text);
|
||||
|
||||
QString getPotentialLabel(quint16 address);
|
||||
virtual bool optionsMenuItems(QMenu *);
|
||||
@ -35,7 +35,8 @@ public:
|
||||
|
||||
QString makeDescriptorStringForVal(quint8 val);
|
||||
|
||||
QStringList getDisassemblyStrings(QList<quint16> entryPoints);
|
||||
void disassemble(QList<quint16> entryPoints);
|
||||
QStringList getDisassemblyStrings();
|
||||
|
||||
public slots:
|
||||
void setFile(GenericFile *file);
|
||||
@ -60,6 +61,9 @@ private:
|
||||
Memory m_mem;
|
||||
|
||||
bool m_isRelo;
|
||||
|
||||
QStringList m_disassemblyStrings;
|
||||
JumpLines m_jumpLines;
|
||||
};
|
||||
|
||||
#endif // DISASSEMBLERVIEWER_H
|
||||
|
@ -15,7 +15,7 @@
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTextBrowser" name="textArea">
|
||||
<widget class="FlowLineTextBrowser" name="textArea">
|
||||
<property name="font">
|
||||
<font>
|
||||
<family>Courier</family>
|
||||
@ -36,6 +36,11 @@
|
||||
<header>viewerbase.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>FlowLineTextBrowser</class>
|
||||
<extends>QTextBrowser</extends>
|
||||
<header>FlowLineTextBrowser.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
299
src/ui/widgets/FlowLineTextBrowser.cpp
Normal file
299
src/ui/widgets/FlowLineTextBrowser.cpp
Normal file
@ -0,0 +1,299 @@
|
||||
#include "FlowLineTextBrowser.h"
|
||||
|
||||
#include <QPainter>
|
||||
#include <QTextBlock>
|
||||
#include <QRegularExpression>
|
||||
#include <QTextDocument>
|
||||
#include <QTextCursor>
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QScrollBar>
|
||||
|
||||
FlowLineTextBrowser::FlowLineTextBrowser(QWidget *parent) : QTextBrowser(parent)
|
||||
{
|
||||
m_lineArea = new LineArea(this);
|
||||
m_jl = Q_NULLPTR;
|
||||
|
||||
//this->verticalScrollBar()->setSliderPosition(this->verticalScrollBar()->sliderPosition());
|
||||
|
||||
// connect(this->document(), SIGNAL(blockCountChanged(int)), SLOT(updateLineAreaWidth(int)));
|
||||
connect(this, SIGNAL(updateRequest(QRect,int)), SLOT(updateLineArea(QRect,int)));
|
||||
updateLineAreaWidth();
|
||||
}
|
||||
|
||||
int FlowLineTextBrowser::getFirstVisibleBlock(QTextBlock *firstBlock) const
|
||||
{
|
||||
|
||||
QTextCursor curs = QTextCursor(this->document());
|
||||
curs.movePosition(QTextCursor::Start);
|
||||
for(int i=0; i < this->document()->blockCount(); ++i)
|
||||
{
|
||||
QTextBlock block = curs.block();
|
||||
|
||||
QRect r1 = this->viewport()->geometry();
|
||||
QRect r2 = this->document()->documentLayout()->blockBoundingRect(block).translated(
|
||||
this->viewport()->geometry().x(), this->viewport()->geometry().y() - (
|
||||
this->verticalScrollBar()->sliderPosition()
|
||||
) ).toRect();
|
||||
|
||||
if (r1.contains(r2, true) || r1.intersects(r2))
|
||||
{
|
||||
qDebug() << r2;
|
||||
if (firstBlock)
|
||||
*firstBlock = block;
|
||||
return i;
|
||||
}
|
||||
|
||||
curs.movePosition(QTextCursor::NextBlock);
|
||||
}
|
||||
|
||||
if (firstBlock)
|
||||
*firstBlock = QTextBlock();
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
void FlowLineTextBrowser::showEvent(QShowEvent *)
|
||||
{
|
||||
this->verticalScrollBar()->setSliderPosition(this->verticalScrollBar()->sliderPosition());
|
||||
}
|
||||
|
||||
void FlowLineTextBrowser::lineAreaPaintEvent(QPaintEvent *event)
|
||||
{
|
||||
|
||||
QPainter painter(m_lineArea);
|
||||
painter.fillRect(event->rect(), Qt::lightGray);
|
||||
|
||||
painter.setPen(Qt::red);
|
||||
painter.setBrush(Qt::red);
|
||||
|
||||
QTextBlock block;
|
||||
getFirstVisibleBlock(&block);
|
||||
|
||||
qDebug() << block.text();
|
||||
bool foundFirst = false;
|
||||
quint16 linenum;
|
||||
|
||||
QTextCursor curs = QTextCursor(this->document());
|
||||
curs.movePosition(QTextCursor::Start);
|
||||
bool inBlankLine = false;
|
||||
// for each visible block
|
||||
while (isBlockVisible(block) && block.next().isValid())
|
||||
{
|
||||
QString text = block.text();
|
||||
if (text.contains(QRegularExpression("[0-9A-Fa-f]{4}:")))
|
||||
{
|
||||
bool ok = true;
|
||||
quint16 addr = (quint16) text.left(4).toInt(&ok,16);
|
||||
if (ok)
|
||||
{
|
||||
// qDebug() << "Addr:" << uint16ToHex(addr);
|
||||
linenum = addr;
|
||||
foundFirst = true;
|
||||
inBlankLine = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// qDebug() << "No address!";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
inBlankLine = true;
|
||||
}
|
||||
// qDebug() << "Channels:" << m_jl->channelsAtAddress(linenum);
|
||||
|
||||
if (foundFirst)
|
||||
{
|
||||
QRect blockRect = getBlockGeometry(block);
|
||||
float topInY = blockRect.top() + ((1./6.) * blockRect.height());
|
||||
float topOutY = blockRect.top() + ((2./6.) * blockRect.height());
|
||||
float botOutY = blockRect.top() + ((3./6.) * blockRect.height());
|
||||
float botInY = blockRect.top() + ((4./6.) * blockRect.height());
|
||||
|
||||
// painter.drawLine(0,blockRect.top(),m_lineArea->width(),blockRect.bottom());
|
||||
|
||||
QList<JumpLine> jllist = m_jl->jumpLinesAtAddress(linenum);
|
||||
|
||||
foreach (JumpLine jl, jllist)
|
||||
{
|
||||
int offset = getChannelOffset(jl.channel);
|
||||
if (!inBlankLine)
|
||||
{
|
||||
if (jl.from == linenum)
|
||||
{
|
||||
if (jl.from == jl.to)
|
||||
{
|
||||
painter.drawLine(offset, botInY,
|
||||
m_lineArea->width(), botInY);
|
||||
painter.drawLine(offset, botInY, offset, topOutY);
|
||||
painter.drawLine(offset, topOutY,
|
||||
m_lineArea->width(), topOutY);
|
||||
// painter.drawEllipse(QPointF(m_lineArea->width(),topOutY),2,2);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (jl.isUp())
|
||||
{
|
||||
painter.drawLine(offset, topOutY,
|
||||
offset, blockRect.top());
|
||||
painter.drawLine(offset, topOutY,
|
||||
m_lineArea->width(), topOutY);
|
||||
painter.drawEllipse(QPointF(m_lineArea->width(),topOutY),2,2);
|
||||
}
|
||||
else
|
||||
{
|
||||
painter.drawLine(offset, botOutY,
|
||||
offset, blockRect.bottom());
|
||||
painter.drawLine(offset, botOutY,
|
||||
m_lineArea->width(), botOutY);
|
||||
// painter.drawEllipse(QPointF(m_lineArea->width(),botOutY),2,2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (jl.to == linenum)
|
||||
{
|
||||
if (jl.isUp())
|
||||
{
|
||||
painter.drawLine(offset, botInY,
|
||||
offset, blockRect.bottom());
|
||||
painter.drawLine(offset, botInY,
|
||||
m_lineArea->width(), botInY);
|
||||
|
||||
QPolygonF varrow;
|
||||
varrow.append(QPointF(offset, botInY));
|
||||
varrow.append(QPointF(offset-3, botInY+5));
|
||||
varrow.append(QPointF(offset+3, botInY+5));
|
||||
painter.drawConvexPolygon(varrow);
|
||||
|
||||
|
||||
QPolygonF harrow;
|
||||
harrow.append(QPointF(m_lineArea->width(),botInY));
|
||||
harrow.append(QPointF(m_lineArea->width()-4,botInY-3));
|
||||
harrow.append(QPointF(m_lineArea->width()-4,botInY+3));
|
||||
painter.drawConvexPolygon(harrow);
|
||||
}
|
||||
else
|
||||
{
|
||||
painter.drawLine(offset, topInY,
|
||||
offset, blockRect.top());
|
||||
painter.drawLine(offset, topInY,
|
||||
m_lineArea->width(), topInY);
|
||||
QPolygonF varrow;
|
||||
varrow.append(QPointF(offset, topInY));
|
||||
varrow.append(QPointF(offset-3, topInY-5));
|
||||
varrow.append(QPointF(offset+3, topInY-5));
|
||||
painter.drawConvexPolygon(varrow);
|
||||
|
||||
QPolygonF harrow;
|
||||
harrow.append(QPointF(m_lineArea->width(),topInY));
|
||||
harrow.append(QPointF(m_lineArea->width()-4,topInY-3));
|
||||
harrow.append(QPointF(m_lineArea->width()-4,topInY+3));
|
||||
painter.drawConvexPolygon(harrow);
|
||||
}
|
||||
}
|
||||
|
||||
if (linenum > jl.min() && linenum < jl.max())
|
||||
{
|
||||
painter.drawLine(offset, blockRect.top(),offset, blockRect.bottom());
|
||||
}
|
||||
}
|
||||
else // inBlankLine
|
||||
{
|
||||
if (jl.from == linenum)
|
||||
{
|
||||
if (jl.from != jl.to)
|
||||
{
|
||||
if (jl.isDown())
|
||||
{
|
||||
painter.drawLine(offset, blockRect.top(),offset, blockRect.bottom());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (jl.to == linenum)
|
||||
{
|
||||
if (jl.isUp())
|
||||
{
|
||||
painter.drawLine(offset, blockRect.top(),offset, blockRect.bottom());
|
||||
}
|
||||
}
|
||||
|
||||
if (linenum > jl.min() && linenum < jl.max())
|
||||
{
|
||||
painter.drawLine(offset, blockRect.top(),offset, blockRect.bottom());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
block = block.next();
|
||||
}
|
||||
}
|
||||
|
||||
QRect FlowLineTextBrowser::getBlockGeometry(QTextBlock block) const
|
||||
{
|
||||
QRect rect = this->document()->documentLayout()->blockBoundingRect(block).translated(
|
||||
this->viewport()->geometry().x(), this->viewport()->geometry().y() - (
|
||||
this->verticalScrollBar()->sliderPosition()
|
||||
) ).toRect();
|
||||
return rect;
|
||||
}
|
||||
|
||||
bool FlowLineTextBrowser::isBlockVisible(QTextBlock block) const
|
||||
{
|
||||
QRect r1 = this->viewport()->geometry();
|
||||
QRect r2 = this->document()->documentLayout()->blockBoundingRect(block).translated(
|
||||
this->viewport()->geometry().x(), this->viewport()->geometry().y() - (
|
||||
this->verticalScrollBar()->sliderPosition()
|
||||
) ).toRect();
|
||||
|
||||
return (r1.contains(r2, true) || r1.intersects(r2));
|
||||
}
|
||||
|
||||
int FlowLineTextBrowser::getChannelOffset(int channel)
|
||||
{
|
||||
int width = lineAreaWidth();
|
||||
return width - (channel * 9)- 10;
|
||||
}
|
||||
|
||||
int FlowLineTextBrowser::lineAreaWidth()
|
||||
{
|
||||
if (!m_jl) return 10;
|
||||
return m_jl->m_maxChannel* 9 + 20;
|
||||
}
|
||||
|
||||
void FlowLineTextBrowser::setJumpLines(JumpLines *jl)
|
||||
{
|
||||
m_jl = jl;
|
||||
m_lineArea->setJumpLines(jl);
|
||||
updateLineAreaWidth();
|
||||
}
|
||||
|
||||
void FlowLineTextBrowser::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
m_lineArea->update();
|
||||
QTextBrowser::paintEvent(event);
|
||||
}
|
||||
|
||||
void FlowLineTextBrowser::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
QTextBrowser::resizeEvent(event);
|
||||
|
||||
QRect cr = contentsRect();
|
||||
m_lineArea->setGeometry(QRect(cr.left(), cr.top(), lineAreaWidth(), cr.height()));
|
||||
}
|
||||
|
||||
void FlowLineTextBrowser::updateLineAreaWidth()
|
||||
{
|
||||
setViewportMargins(lineAreaWidth(),0,0,0);
|
||||
}
|
||||
|
||||
void FlowLineTextBrowser::updateLineArea(const QRect &rect, int dy)
|
||||
{
|
||||
if (dy)
|
||||
m_lineArea->scroll(0, dy);
|
||||
else
|
||||
m_lineArea->update(0, rect.y(), m_lineArea->width(), rect.height());
|
||||
|
||||
if (rect.contains(viewport()->rect()))
|
||||
updateLineAreaWidth();
|
||||
}
|
74
src/ui/widgets/FlowLineTextBrowser.h
Normal file
74
src/ui/widgets/FlowLineTextBrowser.h
Normal file
@ -0,0 +1,74 @@
|
||||
#ifndef FLOWLINETEXTBROWSER_H
|
||||
#define FLOWLINETEXTBROWSER_H
|
||||
|
||||
#include "JumpLineManager.h"
|
||||
|
||||
#include <QTextBrowser>
|
||||
#include <QObject>
|
||||
#include <QResizeEvent>
|
||||
#include <QDebug>
|
||||
#include <QPaintEvent>
|
||||
#include <QResizeEvent>
|
||||
#include <QSize>
|
||||
#include <QWidget>
|
||||
#include <QScrollEvent>
|
||||
|
||||
class LineArea;
|
||||
|
||||
class FlowLineTextBrowser : public QTextBrowser
|
||||
{
|
||||
public:
|
||||
FlowLineTextBrowser(QWidget *parent = Q_NULLPTR);
|
||||
|
||||
void lineAreaPaintEvent(QPaintEvent *event);
|
||||
int lineAreaWidth();
|
||||
|
||||
void setJumpLines(JumpLines *jl);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
|
||||
void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE;
|
||||
|
||||
int getFirstVisibleBlock(QTextBlock *firstBlock) const;
|
||||
|
||||
int getChannelOffset(int channel);
|
||||
QRect getBlockGeometry(QTextBlock block) const;
|
||||
bool isBlockVisible(QTextBlock block) const;
|
||||
void showEvent(QShowEvent *) Q_DECL_OVERRIDE;
|
||||
private slots:
|
||||
void updateLineAreaWidth();
|
||||
void updateLineArea(const QRect &, int);
|
||||
|
||||
private:
|
||||
LineArea *m_lineArea;
|
||||
|
||||
JumpLines *m_jl;
|
||||
};
|
||||
|
||||
class LineArea : public QWidget
|
||||
{
|
||||
public:
|
||||
LineArea(FlowLineTextBrowser *browser) : QWidget(browser)
|
||||
{
|
||||
m_browser = browser;
|
||||
}
|
||||
|
||||
void setJumpLines(JumpLines *jl) { m_jl = jl; }
|
||||
|
||||
QSize sizeHint() const Q_DECL_OVERRIDE
|
||||
{
|
||||
return QSize(m_browser->lineAreaWidth(),0);
|
||||
}
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE
|
||||
{
|
||||
m_browser->lineAreaPaintEvent(event);
|
||||
}
|
||||
|
||||
private:
|
||||
FlowLineTextBrowser *m_browser;
|
||||
JumpLines *m_jl;
|
||||
};
|
||||
|
||||
|
||||
#endif // FLOWLINETEXTBROWSER_H
|
Loading…
Reference in New Issue
Block a user