1 #include "BasicBlockWidget.hxx"
2 #include "CFGScene.hxx"
3 #include "gui/Mainwindow.hxx"
4 #include "gui/dialogs/SimpleStringDialog.hxx"
5 #include "core/BasicBlock.hxx"
6 #include "core/Function.hxx"
7 #include "core/Comment.hxx"
8 #include "disassembler/Instruction.hxx"
9 #include "core/InformationManager.hxx"
10 #include "core/events/RenameFunctionEvent.hxx"
11 #include "core/events/ChangeCommentEvent.hxx"
14 class CustomQGraphicsTextItem
: public QObject
, public QGraphicsTextItem
{
16 CustomQGraphicsTextItem(const QString
& text
, BasicBlockWidget
* parent
)
17 : QGraphicsTextItem(text
, parent
), parent(parent
) {}
18 void contextMenuEvent(QGraphicsSceneContextMenuEvent
*);
22 void addComment(int row
, bool global
);
24 BasicBlockWidget
* parent
;
27 void CustomQGraphicsTextItem::addComment(int row
, bool global
) {
28 SimpleStringDialog
dialog(global
? "Global comment" : "Local comment");
29 int result
= dialog
.exec();
30 uint64_t address
= parent
->instructions
[row
].getAddress();
31 if (QDialog::Accepted
== result
) {
34 comment
= parent
->block
->getManager()->newGlobalComment(address
);
36 /* TODO: 0x23 as we currently don't have the function here
37 * and setting it to null will make the comment appear
38 * global. Also means local comments are largely still
41 comment
= parent
->block
->getManager()->newLocalComment(address
, (Function
*)0x23);
43 comment
->setText(dialog
.result().toStdString());
44 parent
->block
->getManager()->finishComment(comment
);
46 LOG4CXX_DEBUG(parent
->logger
, "addComment aborted");
50 void CustomQGraphicsTextItem::contextMenuEvent(QGraphicsSceneContextMenuEvent
* event
) {
51 QTextCursor c
= textCursor();
52 c
.setPosition(document()->documentLayout()->hitTest(event
->pos(), Qt::FuzzyHit
));
53 c
.select(QTextCursor::WordUnderCursor
);
57 uint64_t address
= c
.selectedText().toLongLong(&ok
, 16);
58 QTextTable
* table
= c
.currentTable();
60 QAction
* act
= menu
.addAction(c
.selectedText() + " is a Function");
61 QObject::connect(act
, &QAction::triggered
,
63 parent
->mainwindow
->requestNewFunctionByAddress(address
);
64 if (NULL
== table
) return;
65 int row
= table
->cellAt(c
).row();
66 uint64_t insAddress
= parent
->instructions
[row
].getAddress();
67 Comment
* comment
= parent
->block
->getManager()->newLocalComment(insAddress
, (Function
*)0x23);
68 comment
->setText("#F<" + c
.selectedText().toStdString() + ">");
69 parent
->block
->getManager()->finishComment(comment
);
74 int row
= table
->cellAt(c
).row();
75 QAction
* globalComment
= menu
.addAction("Add global Comment");
76 QAction
* localComment
= menu
.addAction("Add local Comment");
78 QObject::connect(globalComment
, &QAction::triggered
,
79 [=]() { addComment(row
, true); });
80 QObject::connect(localComment
, &QAction::triggered
,
81 [=]() { addComment(row
, false); });
84 menu
.exec(event
->screenPos());
87 /* QGraphicsTextItem has an adjustSize() function that is supposed to
88 * resize the widget to it's "ideal" size. However it totally ignores
89 * all directives to not wrap lines and "ideal" is actually just a
90 * bunch of heuristics.
92 * We are starting with a hopefully absurdly large startingwidth and
93 * reduce it untill a line is broken (detected by a change in
94 * height). As long as the width (1000 here) is sufficiently large,
95 * this should give us a widget without any line-wrapping.
97 * One needs to call this on a Pointer of tye CustomQGraphicsTextItem
98 * as the adjustSize() function is not polymorphic (vurtual).
100 void CustomQGraphicsTextItem::adjustSize() {
103 int height
= boundingRect().height();
104 while (width
> 250 && height
== boundingRect().height()) {
105 setTextWidth(width
-= 10);
108 if (width
< 250) width
= 250;
112 BasicBlockWidget::BasicBlockWidget(const QString
& name
, BasicBlock
* block
,
113 Mainwindow
* mainwindow
)
114 : width(200), height(45), name(name
)
115 , currentColor(defaultColor
), _table(NULL
)
116 , block(block
), mainwindow(mainwindow
)
117 , logger(log4cxx::Logger::getLogger("gui.BasicBlockWidget." + name
.toStdString())) {
118 next
[0] = NULL
; next
[1] = NULL
;
120 QObject::connect(block
->getManager(), &InformationManager::renameFunctionEvent
,
121 [=](RenameFunctionEvent
* event
) {updateFunctionName(event
);});
123 _widget
.reset(new CustomQGraphicsTextItem("", this));
124 _widget
->setPos(5, 20);
125 _widget
->setTextInteractionFlags(Qt::TextSelectableByMouse
|
126 Qt::LinksAccessibleByMouse
);
128 if (width
< 250) width
= 250;
130 QObject::connect(_widget
.get(), &QGraphicsTextItem::linkActivated
,
132 if (str
.startsWith("function:")) {
133 QString address
= str
.remove("function:");
134 mainwindow
->switchMainPlaneToAddress(address
.toInt(NULL
, 16));
135 } else if (str
.startsWith("block:")) {
136 QString address
= str
.remove("block:");
138 /* next[0] is always the jumptarget. On a
139 * conditional jump, next[1] also
140 * contains the following instruction
142 * TODO: Verify we're switching to the
143 * right block -- the target
144 * address matches the next blocks
147 LOG4CXX_TRACE(logger
, "Highlighting block at Address " << address
.toStdString()
148 << " BasicBlockWidget " << std::hex
<< next
[0]);
149 ((CFGScene
*)this->scene())->highlightBlock(next
[0]);
152 instructions
= block
->getInstructions();
154 QObject::connect(block
->getManager(), &InformationManager::changeCommentEvent
,
155 [=](ChangeCommentEvent
* e
) {changeCommentHandler(e
);});
158 void BasicBlockWidget::updateFunctionName(RenameFunctionEvent
*event
) {
159 QString search
= QString("function:") + QString::number(event
->address
, 16);
160 QTextDocument
*document
= _widget
->document();
161 QTextBlock b
= document
->begin();
162 while (b
.isValid()) {
163 for (QTextBlock::iterator i
= b
.begin(); !i
.atEnd(); ++i
) {
164 QTextCharFormat format
= i
.fragment().charFormat();
165 bool isLink
= format
.isAnchor();
168 if (search
== format
.anchorHref()) {
169 LOG4CXX_DEBUG(logger
, i
.fragment().text().toStdString() << " ---> "
170 << format
.anchorHref().toStdString());
172 /* This should select the function name. It stars
173 * by selecting the whole link fragment from back
174 * to front and then moves one word to the back
175 * again deselecting whatever mnemonic is used for
176 * the call instruction.
179 c
.setPosition(i
.fragment().position());
180 c
.movePosition(QTextCursor::Right
, QTextCursor::MoveAnchor
, i
.fragment().length());
181 c
.movePosition(QTextCursor::Left
, QTextCursor::KeepAnchor
, i
.fragment().length());
182 c
.movePosition(QTextCursor::WordRight
, QTextCursor::KeepAnchor
);
183 c
.insertText(event
->new_name
.c_str());
185 QGraphicsTextItem
* item
= _widget
.get();
186 ((CustomQGraphicsTextItem
*)item
)->adjustSize();
194 void BasicBlockWidget::changeCommentHandler(ChangeCommentEvent
* event
) {
195 auto inst_it
= std::find_if(instructions
.begin(), instructions
.end(),
196 [=](Instruction
& inst
) {
197 return inst
.getAddress() == event
->address
;
199 if (inst_it
!= instructions
.end()) {
200 if (std::find(inst_it
->comments().begin(),
201 inst_it
->comments().begin(),
202 event
->comment
) == inst_it
->comments().end()) {
203 LOG4CXX_DEBUG(logger
, "Change Comment Event -- New Comment!");
204 inst_it
->comments().push_back(event
->comment
);
206 int row
= inst_it
- instructions
.begin();
207 LOG4CXX_DEBUG(logger
, "Inserting comment for instruction at row " << std::hex
<< row
);
208 QTextCursor cursor
= _table
->cellAt(row
, 2).lastCursorPosition();
209 while (cursor
!= _table
->cellAt(row
, 2).firstCursorPosition()) {
210 cursor
.movePosition(QTextCursor::Left
, QTextCursor::KeepAnchor
, 1);
212 cursor
.removeSelectedText();
213 cursor
.insertHtml(formatComments(&*inst_it
));
214 QGraphicsTextItem
* item
= _widget
.get();
215 ((CustomQGraphicsTextItem
*)item
)->adjustSize();
219 void BasicBlockWidget::populateWidget() {
221 QTextTableFormat format
;
222 format
.setBorderStyle(QTextFrameFormat::BorderStyle_None
);
225 for (Instruction
& inst
: instructions
) {
227 row
= _table
->rows();
228 _table
->appendRows(1);
231 _table
= _widget
->textCursor().insertTable(1, 3, format
);
234 for (uint8_t byte
: inst
.getBytes()) {
235 const char * hexdigits
= "0123456789ABCDEF";
236 bytestring
+= hexdigits
[(byte
>> 4) & 0xF];
237 bytestring
+= hexdigits
[byte
& 0xF];
240 _table
->cellAt(row
, 0).firstCursorPosition().insertHtml("<nobr>" + bytestring
+ "</nobr>");
242 QString line
= inst
.getText().c_str();
243 line
= line
.replace('\t', ' ').toHtmlEscaped();
244 if (inst
.getReference() != "") {
245 QString href
= inst
.getReference().c_str();
246 QStringList list
= href
.split(":");
247 if (list
[0] == "function") {
248 uint64_t address
= href
.split(":")[1].toLongLong(NULL
, 16);
249 Function
* fun
= block
->getManager()->getFunction(address
);
252 line
= line
.split(" ")[0] + " " + QString(fun
->getName().c_str()).toHtmlEscaped();
253 LOG4CXX_DEBUG(logger
, "Naming function at " << address
<< " " << fun
->getName());
256 line
= "<a href=\"" + href
+ "\">" + line
+ "</a>";
258 _table
->cellAt(row
, 1).firstCursorPosition().insertHtml("<nobr>" + line
+ "</nobr>");
259 _table
->cellAt(row
, 2).firstCursorPosition().insertHtml(formatComments(&inst
));
261 QGraphicsTextItem
* item
= _widget
.get();
262 ((CustomQGraphicsTextItem
*)item
)->adjustSize();
265 QString
BasicBlockWidget::formatComments(Instruction
* inst
) {
266 QStringList comments
;
267 for (Comment
* c
: inst
->comments()) {
268 comments
<< QString(c
->getText().c_str()).toHtmlEscaped();
270 return (comments
.empty() ? "" : ";; ") + comments
.join("<br />").trimmed();
273 void BasicBlockWidget::paint(QPainter
*painter
, const QStyleOptionGraphicsItem
*,
275 width
= 10 + _widget
->boundingRect().width();
276 height
= 25 + _widget
->boundingRect().height();
277 if (width
< 250) width
= 250;
279 painter
->fillRect(0, 0, width
, height
, currentColor
);
280 painter
->setPen(QColor(0x00, 0x00, 0xff, 0xff));
281 painter
->drawRect(0, 0, width
, height
);
282 painter
->drawText(5, 15, name
);
285 QRectF
BasicBlockWidget::boundingRect() const {
287 QRectF
result(- penWidth
/ 2, - penWidth
/ 2,
288 width
+ penWidth
, height
+ penWidth
);
292 std::array
<QPointF
, 3> BasicBlockWidget::getExits() const {
293 return { { mapToScene(QPointF( width
/3, height
)),
294 mapToScene(QPointF( width
/2, height
)),
295 mapToScene(QPointF(2*width
/3, height
)) } };