-BasicBlockWidget::BasicBlockWidget(const QString& name)
- : x(-5), y(-20)
- , dx(270), dy(45)
- , name(name) {
- _widget.setGridStyle(Qt::NoPen);
- _widget.setMinimumHeight(_widget.rowHeight(0) + 10);
- _widget.setMaximumHeight(20);
- _widget.resizeColumnToContents(0);
- _widget.resizeColumnToContents(1);
- _widget.resizeColumnToContents(2);
- _widget.updateGeometry();
- _widget.setMaximumWidth(260);
- _widget.setColumnCount(3);
- _widget.verticalHeader()->hide();
- _widget.horizontalHeader()->hide();
- dx = _widget.rowHeight(0) + 20;
- if (dx < 270) dx = 270;
-}
-
-void BasicBlockWidget::addItem(uint8_t* bytes, size_t num_bytes,
- const QString& line) {
- size_t current_row = _widget.rowCount();
- int column_width;
-
- QString bytestring;
-
- for (size_t i(0); i < num_bytes; ++i) {
- const char * hexdigits = "0123456789ABCDEF";
- bytestring += hexdigits[(bytes[i] >> 4) & 0xF];
- bytestring += hexdigits[bytes[i] & 0xF];
- bytestring += ' ';
- }
-
- _widget.setRowCount(current_row + 1);
-
- _widget.setItem(current_row, 0, new QTableWidgetItem(bytestring));
- _widget.setItem(current_row, 1, new QTableWidgetItem(line));
- _widget.setItem(current_row, 2, new QTableWidgetItem(""));
- _widget.updateGeometry();
-
- _widget.resizeColumnToContents(0);
- _widget.resizeColumnToContents(1);
- _widget.resizeColumnToContents(2);
-
- _widget.resizeRowToContents(current_row);
-
- column_width =
- _widget.columnWidth(0) +
- _widget.columnWidth(1) +
- _widget.columnWidth(2) +
- 2;
-
- _widget.setMinimumWidth(column_width);
- _widget.setMinimumHeight(_widget.rowHeight(0) * (_widget.rowCount()) + 2);
- _widget.setMaximumHeight(_widget.rowHeight(0) * (_widget.rowCount()) + 2);
-
-
- dy = _widget.rowHeight(0) * (_widget.rowCount()) + 25;
- dx = column_width + 10;
- if (dx < 270) dx = 270;
+class CustomQGraphicsTextItem : public QObject, public QGraphicsTextItem {
+public:
+ CustomQGraphicsTextItem(const QString& text, BasicBlockWidget* parent)
+ : QGraphicsTextItem(text, parent), parent(parent) {}
+ void contextMenuEvent(QGraphicsSceneContextMenuEvent*);
+
+ void adjustSize();
+private:
+ void addComment(int row, bool global);
+
+ BasicBlockWidget* parent;
+};
+
+void CustomQGraphicsTextItem::addComment(int row, bool global) {
+ SimpleStringDialog dialog(global ? "Global comment" : "Local comment");
+ int result = dialog.exec();
+ uint64_t address = parent->instructions[row].getAddress();
+ if (QDialog::Accepted == result) {
+ Comment* comment;
+ if (global) {
+ comment = parent->block->getManager()->newGlobalComment(address);
+ } else {
+ /* TODO: 0x23 as we currently don't have the function here
+ * and setting it to null will make the comment appear
+ * global. Also means local comments are largely still
+ * broken.
+ */
+ comment = parent->block->getManager()->newLocalComment(address, (Function*)0x23);
+ }
+ comment->setText(dialog.result().toStdString());
+ parent->block->getManager()->finishComment(comment);
+ } else {
+ LOG4CXX_DEBUG(parent->logger, "addComment aborted");
+ }
+}
+
+void CustomQGraphicsTextItem::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) {
+ QTextCursor c = textCursor();
+ c.setPosition(document()->documentLayout()->hitTest(event->pos(), Qt::FuzzyHit));
+ c.select(QTextCursor::WordUnderCursor);
+
+ QMenu menu;
+ bool ok;
+ uint64_t address = c.selectedText().toLongLong(&ok, 16);
+ QTextTable* table = c.currentTable();
+ if (ok) {
+ QAction* act = menu.addAction(c.selectedText() + " is a Function");
+ QObject::connect(act, &QAction::triggered,
+ [=]() {
+ parent->mainwindow->requestNewFunctionByAddress(address);
+ if (NULL == table) return;
+ int row = table->cellAt(c).row();
+ uint64_t insAddress = parent->instructions[row].getAddress();
+ Comment* comment = parent->block->getManager()->newLocalComment(insAddress, (Function*)0x23);
+ comment->setText("#F<" + c.selectedText().toStdString() + ">");
+ parent->block->getManager()->finishComment(comment);
+ });
+ }
+
+ if (NULL != table) {
+ int row = table->cellAt(c).row();
+ QAction* globalComment = menu.addAction("Add global Comment");
+ QAction* localComment = menu.addAction("Add local Comment");
+
+ QObject::connect(globalComment, &QAction::triggered,
+ [=]() { addComment(row, true); });
+ QObject::connect(localComment, &QAction::triggered,
+ [=]() { addComment(row, false); });
+ }
+
+ menu.exec(event->screenPos());
+}
+
+/* QGraphicsTextItem has an adjustSize() function that is supposed to
+ * resize the widget to it's "ideal" size. However it totally ignores
+ * all directives to not wrap lines and "ideal" is actually just a
+ * bunch of heuristics.
+ *
+ * We are starting with a hopefully absurdly large startingwidth and
+ * reduce it untill a line is broken (detected by a change in
+ * height). As long as the width (1000 here) is sufficiently large,
+ * this should give us a widget without any line-wrapping.
+ *
+ * One needs to call this on a Pointer of tye CustomQGraphicsTextItem
+ * as the adjustSize() function is not polymorphic (vurtual).
+ */
+void CustomQGraphicsTextItem::adjustSize() {
+ int width = 1000;
+ setTextWidth(width);
+ int height = boundingRect().height();
+ while (width > 250 && height == boundingRect().height()) {
+ setTextWidth(width -= 10);
+ }
+ width += 10;
+ if (width < 250) width = 250;
+ setTextWidth(width);
+}
+
+BasicBlockWidget::BasicBlockWidget(const QString& name, BasicBlock * block,
+ Mainwindow * mainwindow)
+ : width(200), height(45), name(name)
+ , currentColor(defaultColor), _table(NULL)
+ , block(block), mainwindow(mainwindow)
+ , logger(log4cxx::Logger::getLogger("gui.BasicBlockWidget." + name.toStdString())) {
+ next[0] = NULL; next[1] = NULL;
+
+ QObject::connect(block->getManager(), &InformationManager::renameFunctionEvent,
+ [=](RenameFunctionEvent* event) {updateFunctionName(event);});
+
+ _widget.reset(new CustomQGraphicsTextItem("", this));
+ _widget->setPos(5, 20);
+ _widget->setTextInteractionFlags(Qt::TextSelectableByMouse|
+ Qt::LinksAccessibleByMouse);
+
+ if (width < 250) width = 250;
+
+ QObject::connect(_widget.get(), &QGraphicsTextItem::linkActivated,
+ [=](QString str) {
+ if (str.startsWith("function:")) {
+ QString address = str.remove("function:");
+ mainwindow->switchMainPlaneToAddress(address.toInt(NULL, 16));
+ } else if (str.startsWith("block:")) {
+ QString address = str.remove("block:");
+
+ /* next[0] is always the jumptarget. On a
+ * conditional jump, next[1] also
+ * contains the following instruction
+ *
+ * TODO: Verify we're switching to the
+ * right block -- the target
+ * address matches the next blocks
+ * start address
+ */
+ LOG4CXX_TRACE(logger, "Highlighting block at Address " << address.toStdString()
+ << " BasicBlockWidget " << std::hex << next[0]);
+ ((CFGScene*)this->scene())->highlightBlock(next[0]);
+ }
+ });
+ instructions = block->getInstructions();
+ populateWidget();
+ QObject::connect(block->getManager(), &InformationManager::changeCommentEvent,
+ [=](ChangeCommentEvent* e) {changeCommentHandler(e);});