25 #include <QWheelEvent>
26 #include <QGraphicsSceneMouseEvent>
31 QY2Graph::QY2Graph(
const std::string& filename,
const std::string& layoutAlgorithm, QWidget* parent)
32 : QGraphicsView(parent)
36 renderGraph(filename, layoutAlgorithm);
40 QY2Graph::QY2Graph(graph_t* graph, QWidget* parent)
41 : QGraphicsView(parent)
57 setRenderHint(QPainter::Antialiasing);
58 setRenderHint(QPainter::TextAntialiasing);
59 setTransformationAnchor(AnchorUnderMouse);
60 setResizeAnchor(AnchorUnderMouse);
62 scene =
new QGraphicsScene(
this);
63 scene->setItemIndexMethod(QGraphicsScene::BspTreeIndex);
69 QY2Graph::keyPressEvent(QKeyEvent* event)
81 case Qt::Key_Asterisk:
90 QGraphicsView::keyPressEvent(event);
96 QY2Graph::wheelEvent(QWheelEvent* event)
101 scaleView(pow(2.0, event->delta() / 240.0));
107 QY2Graph::scaleView(qreal scaleFactor)
110 qreal f = sqrt(matrix().determinant());
113 if (scaleFactor * f > 8.0)
114 scaleFactor = 8.0 / f;
115 if (scaleFactor * f < 0.1)
116 scaleFactor = 0.1 / f;
118 scale(scaleFactor, scaleFactor);
123 QY2Graph::contextMenuEvent(QContextMenuEvent* event)
128 emit nodeContextMenuEvent(event, node->name);
130 emit backgroundContextMenuEvent(event);
135 QY2Graph::mouseDoubleClickEvent(QMouseEvent* event)
140 emit nodeDoubleClickEvent(event, node->name);
145 QY2Graph::gToQ(
const pointf& p,
bool upside_down)
const
147 return upside_down ? QPointF(p.x, graphRect.height() - p.y) : QPointF(p.x, -p.y);
152 QY2Graph::aggetToQString(
void* obj,
const char* name,
const QString& fallback)
const
154 const char* tmp = agget(obj,
const_cast<char*
>(name));
155 if (tmp == NULL || strlen(tmp) == 0)
157 return unescape(tmp);
162 QY2Graph::aggetToQColor(
void* obj,
const char* name,
const QColor& fallback)
const
164 const char* tmp = agget(obj,
const_cast<char*
>(name));
165 if (tmp == NULL || strlen(tmp) == 0)
172 QY2Graph::aggetToQPenStyle(
void* obj,
const char* name,
const Qt::PenStyle fallback)
const
174 const char* tmp = agget(obj,
const_cast<char*
>(name));
175 if (tmp == NULL || strlen(tmp) == 0)
177 if (strcmp(tmp,
"dashed") == 0)
179 if (strcmp(tmp,
"dotted") == 0)
186 QY2Graph::makeBezier(
const bezier& bezier)
const
189 path.moveTo(gToQ(bezier.list[0]));
190 for (
int i = 1; i < bezier.size - 1; i += 3)
191 path.cubicTo(gToQ(bezier.list[i]), gToQ(bezier.list[i+1]), gToQ(bezier.list[i+2]));
197 QY2Graph::drawArrow(
const QLineF& line,
const QColor& color, QPainter* painter)
const
199 QLineF n(line.normalVector());
200 QPointF o(n.dx() / 3.0, n.dy() / 3.0);
203 polygon.append(line.p1() + o);
204 polygon.append(line.p2());
205 polygon.append(line.p1() - o);
209 painter->setPen(pen);
212 painter->setBrush(brush);
214 painter->drawPolygon(polygon);
219 QY2Graph::renderGraph(
const std::string& filename,
const std::string& layoutAlgorithm)
221 FILE* fp = fopen(filename.c_str(),
"r");
224 GVC_t* gvc = gvContext();
228 graph_t* graph = agread(fp, NULL);
230 graph_t* graph = agread(fp);
234 if (gvLayout(gvc, graph,
const_cast<char*
>(layoutAlgorithm.c_str())) == 0)
238 gvFreeLayout(gvc, graph);
242 qCritical(
"gvLayout() failed");
249 qCritical(
"agread() failed");
256 qCritical(
"gvContext() failed");
263 qCritical(
"failed to open %s", filename.c_str());
269 QY2Graph::makeShapeHelper(node_t* node)
const
271 const polygon_t* poly = (polygon_t*) ND_shape_info(node);
273 if (poly->peripheries != 1)
275 qWarning(
"unsupported number of peripheries %d", poly->peripheries);
278 const int sides = poly->sides;
279 const pointf* vertices = poly->vertices;
282 for (
int side = 0; side < sides; side++)
283 polygon.append(gToQ(vertices[side],
false));
289 QY2Graph::makeShape(node_t* node)
const
293 const char* name = ND_shape(node)->name;
295 if ((strcmp(name,
"rectangle") == 0) ||
296 (strcmp(name,
"box") == 0) ||
297 (strcmp(name,
"hexagon") == 0) ||
298 (strcmp(name,
"polygon") == 0) ||
299 (strcmp(name,
"diamond") == 0))
301 QPolygonF polygon = makeShapeHelper(node);
302 polygon.append(polygon[0]);
303 path.addPolygon(polygon);
305 else if ((strcmp(name,
"ellipse") == 0) ||
306 (strcmp(name,
"circle") == 0))
308 QPolygonF polygon = makeShapeHelper(node);
309 path.addEllipse(QRectF(polygon[0], polygon[1]));
313 qWarning(
"unsupported shape %s", name);
321 QY2Graph::drawLabel(
const textlabel_t* textlabel, QPainter* painter)
const
323 painter->setPen(textlabel->fontcolor);
327 QFont font(textlabel->fontname, textlabel->fontsize);
328 font.setPixelSize(textlabel->fontsize);
330 if (!font.exactMatch())
332 QFontInfo fontinfo(font);
333 qWarning(
"replacing font \"%s\" by font \"%s\"", font.family().toUtf8().data(),
334 fontinfo.family().toUtf8().data());
337 painter->setFont(font);
339 QString text(unescape(textlabel->text));
340 QFontMetricsF fm(painter->fontMetrics());
341 QRectF rect(fm.boundingRect(text));
342 rect.moveCenter(gToQ(textlabel->pos,
false));
343 painter->drawText(rect.adjusted(-2, -2, +2, +2), Qt::AlignCenter, text);
348 QY2Graph::clearGraph()
350 QList<QGraphicsItem*> items(scene->items());
351 while (!items.isEmpty())
352 delete items.takeFirst();
357 QY2Graph::renderGraph(graph_t* graph)
361 if (GD_charset(graph) != 0)
363 qWarning(
"unsupported charset");
367 graphRect = QRectF(GD_bb(graph).LL.x, GD_bb(graph).LL.y, GD_bb(graph).UR.x, GD_bb(graph).UR.y);
368 scene->setSceneRect(graphRect.adjusted(-5, -5, +5, +5));
370 scene->setBackgroundBrush(aggetToQColor(graph,
"bgcolor", Qt::white));
372 for (node_t* node = agfstnode(graph); node != NULL; node = agnxtnode(graph, node))
375 QPainter painter(&picture);
378 drawLabel(ND_label(node), &painter);
382 QY2Node* item =
new QY2Node(makeShape(node), picture, agnameof(node));
384 QY2Node* item =
new QY2Node(makeShape(node), picture, node->name);
387 item->setPos(gToQ(ND_coord(node)));
389 QPen pen(aggetToQColor(node,
"color", Qt::black));
393 QBrush brush(aggetToQColor(node,
"fillcolor", Qt::gray));
394 item->setBrush(brush);
396 QString tooltip = aggetToQString(node,
"tooltip",
"");
397 if (!tooltip.isEmpty())
398 item->setToolTip(tooltip);
400 scene->addItem(item);
402 for (edge_t* edge = agfstout(graph, node); edge != NULL; edge = agnxtout(graph, edge))
404 const splines* spl = ED_spl(edge);
408 for (
int i = 0; i < spl->size; ++i)
410 const bezier& bz = spl->list[i];
412 QColor color(aggetToQColor(edge,
"color", Qt::black));
414 QPainterPath path(makeBezier(bz));
419 painter.begin(&picture);
421 drawArrow(QLineF(gToQ(bz.list[0]), gToQ(bz.sp)), color, &painter);
423 drawArrow(QLineF(gToQ(bz.list[bz.size-1]), gToQ(bz.ep)), color, &painter);
429 pen.setStyle(aggetToQPenStyle(edge,
"style", Qt::SolidLine));
433 item->setZValue(-1.0);
435 scene->addItem(item);
443 QY2Graph::unescape(
const std::string& s)
const
447 bool backslashed =
false;
449 for (
const char c : s)
484 return QString::fromUtf8(r.c_str());
488 QY2Node::QY2Node(
const QPainterPath& path,
const QPicture& picture,
const QString& name)
489 : QGraphicsPathItem(path),
497 QY2Node::paint(QPainter* painter,
const QStyleOptionGraphicsItem* option, QWidget* widget)
500 QGraphicsPathItem::paint(painter, option, widget);
503 picture.play(painter);
507 QY2Edge::QY2Edge(
const QPainterPath& path,
const QPicture& picture)
508 : QGraphicsPathItem(path),
515 QY2Edge::boundingRect()
const
517 return QGraphicsPathItem::boundingRect().united(picture.boundingRect());
522 QY2Edge::paint(QPainter* painter,
const QStyleOptionGraphicsItem* option, QWidget* widget)
525 QGraphicsPathItem::paint(painter, option, widget);
528 picture.play(painter);