def draw(cls, x, y, self: PreparedText, painter: QPainter): font = QFont(self.fontName) if self.fontSize: font.setPointSizeF(self.fontSize) if self.fontBold: font.setBold(True) if self.fontItalic: font.setItalic(True) brushStyle = self.brushStyle or Qt.BrushStyle.SolidPattern w = self.width h = self.height tx = self.left + x ty = self.top + y if self.border: w -= self.border.width h -= self.border.width * 2 tx += self.border.width ty += self.border.width rect = QRectF(tx, ty, w, h) if self.backColor: painter.setBrush(brush_style_map[brushStyle]) painter.fillRect(rect, QColor('#' + hex(self.backColor)[2:])) if self.allowTags: doc = QTextDocument() doc.setDefaultFont(font) doc.setHtml(self.text) doc.setDocumentMargin(0) painter.save() painter.translate(tx + 2, ty + 1) doc.drawContents(painter, QRectF(0, 0, self.width, self.height)) painter.restore() else: painter.save() painter.setFont(font) flags = cls.textFlags(self) rect.setX(rect.x() + 2) rect.setY(rect.y() + 1) painter.drawText(rect, flags, self.text) painter.restore() if self.border and self.border.color is not None: old_pen = painter.pen() pen = QPen( QColor(self.border.color), self.border.width, pen_style_map.get(self.border.style, Qt.PenStyle.SolidLine)) painter.setPen(pen) painter.drawLines( cls.getLines(self, self.left + x, self.top + y, self.left + self.width + x, self.top + y + self.height)) painter.setPen(old_pen)
class QBlockCode(QCachedGraphicsItem): """ Top-level code widget for a selection of text. Will construct an AST using QBlockCodeObj, mirroring the structure associated with the target object. This text is then rendered using a QTextDocument, with appropriate styles applied to it. Interaction events will be propagated to corresponding objects. """ GRAPH_ADDR_SPACING = 20 addr: int _addr_str: str obj: QBlockCodeObj _config: ConfigurationManager disasm_view: 'QDisassemblyBaseControl' workspace: 'Workspace' infodock: InfoDock parent: Any def __init__(self, addr: int, obj: QBlockCodeObj, config: ConfigurationManager, disasm_view: 'QDisassemblyBaseControl', workspace: 'Workspace', infodock: InfoDock, parent: Any = None): super().__init__(parent=parent) self.addr = addr self._addr_str = "%08x" % self.addr self._addr_item: QGraphicsSimpleTextItem = None self.obj = obj self._width = 0 self._height = 0 self._config = config self.parent = parent self.workspace = workspace self.infodock = infodock self._disasm_view = disasm_view self._qtextdoc = QTextDocument() self._qtextdoc.setDefaultFont(self._config.disasm_font) self._qtextdoc.setDocumentMargin(0) self._addr_item = QGraphicsSimpleTextItem(self._addr_str, self) self._addr_item.setBrush(Conf.disasm_view_node_address_color) self._addr_item.setFont(Conf.disasm_font) self.update_document() self.setToolTip("Address: " + self._addr_str) self.refresh() def refresh(self): self._addr_item.setVisible(self._disasm_view.show_address) self._layout_items_and_update_size() def update_document(self): self._qtextdoc.clear() cur = QTextCursor(self._qtextdoc) self.obj.render_to_doc(cur) def paint(self, painter, option, widget): #pylint: disable=unused-argument self.update_document() painter.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform | QPainter.HighQualityAntialiasing) painter.setFont(self._config.disasm_font) if self.infodock.is_instruction_selected( self.addr) or self.obj.should_highlight_line: highlight_color = Conf.disasm_view_node_instruction_selected_background_color painter.setBrush(highlight_color) painter.setPen(highlight_color) painter.drawRect(0, 0, self.width, self.height) x = 0 if self._disasm_view.show_address: x += self._addr_item.boundingRect().width( ) + self.GRAPH_ADDR_SPACING painter.translate(QPointF(x, 0)) self._qtextdoc.drawContents(painter) # # Event handlers # def get_obj_for_mouse_event(self, event: QMouseEvent) -> QBlockCodeObj: p = event.pos() if self._disasm_view.show_address: offset = self._addr_item.boundingRect().width( ) + self.GRAPH_ADDR_SPACING p.setX(p.x() - offset) if p.x() >= 0: hitpos = self._qtextdoc.documentLayout().hitTest( p, Qt.HitTestAccuracy.ExactHit) if hitpos >= 0: return self.obj.get_hit_obj(hitpos) return None def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.infodock.select_instruction(self.addr) obj = self.get_obj_for_mouse_event(event) if obj is not None: obj.mousePressEvent(event) def mouseDoubleClickEvent(self, event): obj = self.get_obj_for_mouse_event(event) if obj is not None: obj.mouseDoubleClickEvent(event) # # Private methods # def _layout_items_and_update_size(self): self.update_document() x, y = 0, 0 if self._disasm_view.show_address: self._addr_item.setPos(x, y) x += self._addr_item.boundingRect().width( ) + self.GRAPH_ADDR_SPACING x += self._qtextdoc.size().width() y += self._qtextdoc.size().height() self._width = x self._height = y self.recalculate_size() def _boundingRect(self): return QRectF(0, 0, self._width, self._height)