libyui-qt-graph  2.42.7
 All Classes Functions
QY2Graph.cc
1 /*
2  * Copyright (c) [2009-2012] Novell, Inc.
3  *
4  * All Rights Reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of version 2 of the GNU General Public License as published
8  * by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, contact Novell, Inc.
17  *
18  * To contact Novell about this file by physical or electronic mail, you may
19  * find current contact information at www.novell.com.
20  */
21 
22 /*
23  * File: QY2Graph.cc
24  * Author: Arvin Schnell <aschnell@suse.de>
25  */
26 
27 
28 #include <math.h>
29 
30 #include <QKeyEvent>
31 #include <QWheelEvent>
32 #include <QGraphicsSceneMouseEvent>
33 
34 #include "QY2Graph.h"
35 
36 
37 QY2Graph::QY2Graph(const std::string& filename, const std::string& layoutAlgorithm, QWidget* parent)
38  : QGraphicsView(parent)
39 {
40  init();
41 
42  renderGraph(filename, layoutAlgorithm);
43 }
44 
45 
46 QY2Graph::QY2Graph(graph_t* graph, QWidget* parent)
47  : QGraphicsView(parent)
48 {
49  init();
50 
51  renderGraph(graph);
52 }
53 
54 
55 QY2Graph::~QY2Graph()
56 {
57 }
58 
59 
60 void
61 QY2Graph::init()
62 {
63  setRenderHint(QPainter::Antialiasing);
64  setRenderHint(QPainter::TextAntialiasing);
65  setTransformationAnchor(AnchorUnderMouse);
66  setResizeAnchor(AnchorUnderMouse);
67 
68  scene = new QGraphicsScene(this);
69  scene->setItemIndexMethod(QGraphicsScene::BspTreeIndex);
70  setScene(scene);
71 }
72 
73 
74 void
75 QY2Graph::keyPressEvent(QKeyEvent* event)
76 {
77  switch (event->key())
78  {
79  case Qt::Key_Plus:
80  scaleView(1.2);
81  break;
82  case Qt::Key_Minus:
83  scaleView(1.0 / 1.2);
84  break;
85 
86 #if 0
87  case Qt::Key_Asterisk:
88  rotate(10.0);
89  break;
90  case Qt::Key_Slash:
91  rotate(-10.0);
92  break;
93 #endif
94 
95  default:
96  QGraphicsView::keyPressEvent(event);
97  }
98 }
99 
100 
101 void
102 QY2Graph::wheelEvent(QWheelEvent* event)
103 {
104  scaleView(pow(2.0, -event->delta() / 240.0));
105 }
106 
107 
108 void
109 QY2Graph::scaleView(qreal scaleFactor)
110 {
111  qreal f = sqrt(matrix().det());
112 
113  if (scaleFactor * f > 8.0)
114  scaleFactor = 8.0 / f;
115  if (scaleFactor * f < 0.1)
116  scaleFactor = 0.1 / f;
117 
118  scale(scaleFactor, scaleFactor);
119 }
120 
121 
122 void
123 QY2Graph::contextMenuEvent(QContextMenuEvent* event)
124 {
125  QY2Node* node = dynamic_cast<QY2Node*>(itemAt(event->pos()));
126 
127  if (node)
128  emit nodeContextMenuEvent(event, node->name);
129  else
130  emit backgroundContextMenuEvent(event);
131 }
132 
133 
134 void
135 QY2Graph::mouseDoubleClickEvent(QMouseEvent* event)
136 {
137  QY2Node* node = dynamic_cast<QY2Node*>(itemAt(event->pos()));
138 
139  if (node)
140  emit nodeDoubleClickEvent(event, node->name);
141 }
142 
143 
144 QPointF
145 QY2Graph::gToQ(const pointf& p, bool upside_down) const
146 {
147  return upside_down ? QPointF(p.x, graphRect.height() - p.y) : QPointF(p.x, -p.y);
148 }
149 
150 
151 QString
152 QY2Graph::aggetToQString(void* obj, const char* name, const QString& fallback) const
153 {
154  const char* tmp = agget(obj, const_cast<char*>(name));
155  if (tmp == NULL || strlen(tmp) == 0)
156  return fallback;
157  return QString::fromUtf8(tmp);
158 }
159 
160 
161 QColor
162 QY2Graph::aggetToQColor(void* obj, const char* name, const QColor& fallback) const
163 {
164  const char* tmp = agget(obj, const_cast<char*>(name));
165  if (tmp == NULL || strlen(tmp) == 0)
166  return fallback;
167  return QColor(tmp);
168 }
169 
170 
171 Qt::PenStyle
172 QY2Graph::aggetToQPenStyle(void* obj, const char* name, const Qt::PenStyle fallback) const
173 {
174  const char* tmp = agget(obj, const_cast<char*>(name));
175  if (tmp == NULL || strlen(tmp) == 0)
176  return fallback;
177  if (strcmp(tmp, "dashed") == 0)
178  return Qt::DashLine;
179  if (strcmp(tmp, "dotted") == 0)
180  return Qt::DotLine;
181  return fallback;
182 }
183 
184 
185 QPainterPath
186 QY2Graph::makeBezier(const bezier& bezier) const
187 {
188  QPainterPath path;
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]));
192  return path;
193 }
194 
195 
196 void
197 QY2Graph::drawArrow(const QLineF& line, const QColor& color, QPainter* painter) const
198 {
199  QLineF n(line.normalVector());
200  QPointF o(n.dx() / 3.0, n.dy() / 3.0);
201 
202  QPolygonF polygon;
203  polygon.append(line.p1() + o);
204  polygon.append(line.p2());
205  polygon.append(line.p1() - o);
206 
207  QPen pen(color);
208  pen.setWidthF(1.0);
209  painter->setPen(pen);
210 
211  QBrush brush(color);
212  painter->setBrush(brush);
213 
214  painter->drawPolygon(polygon);
215 }
216 
217 
218 void
219 QY2Graph::renderGraph(const std::string& filename, const std::string& layoutAlgorithm)
220 {
221  FILE* fp = fopen(filename.c_str(), "r");
222  if (fp)
223  {
224  GVC_t* gvc = gvContext();
225  if (gvc != NULL)
226  {
227  graph_t* graph = agread(fp);
228  if (graph != NULL)
229  {
230  if (gvLayout(gvc, graph, const_cast<char*>(layoutAlgorithm.c_str())) == 0)
231  {
232  renderGraph(graph);
233 
234  gvFreeLayout(gvc, graph);
235  }
236  else
237  {
238  qCritical("gvLayout() failed");
239  }
240 
241  agclose(graph);
242  }
243  else
244  {
245  qCritical("agread() failed");
246  }
247 
248  gvFreeContext(gvc);
249  }
250  else
251  {
252  qCritical("gvContext() failed");
253  }
254 
255  fclose(fp);
256  }
257  else
258  {
259  qCritical("failed to open %s", filename.c_str());
260  }
261 }
262 
263 
264 QPolygonF
265 QY2Graph::makeShapeHelper(node_t* node) const
266 {
267  const polygon_t* poly = (polygon_t*) ND_shape_info(node);
268 
269  if (poly->peripheries != 1)
270  {
271  qWarning("unsupported number of peripheries %d", poly->peripheries);
272  }
273 
274  const int sides = poly->sides;
275  const pointf* vertices = poly->vertices;
276 
277  QPolygonF polygon;
278  for (int side = 0; side < sides; side++)
279  polygon.append(gToQ(vertices[side], false));
280  return polygon;
281 }
282 
283 
284 QPainterPath
285 QY2Graph::makeShape(node_t* node) const
286 {
287  QPainterPath path;
288 
289  const char* name = ND_shape(node)->name;
290 
291  if ((strcmp(name, "rectangle") == 0) ||
292  (strcmp(name, "box") == 0) ||
293  (strcmp(name, "hexagon") == 0) ||
294  (strcmp(name, "polygon") == 0) ||
295  (strcmp(name, "diamond") == 0))
296  {
297  QPolygonF polygon = makeShapeHelper(node);
298  polygon.append(polygon[0]);
299  path.addPolygon(polygon);
300  }
301  else if ((strcmp(name, "ellipse") == 0) ||
302  (strcmp(name, "circle") == 0))
303  {
304  QPolygonF polygon = makeShapeHelper(node);
305  path.addEllipse(QRectF(polygon[0], polygon[1]));
306  }
307  else
308  {
309  qWarning("unsupported shape %s", name);
310  }
311 
312  return path;
313 }
314 
315 
316 void
317 QY2Graph::drawLabel(const textlabel_t* textlabel, QPainter* painter) const
318 {
319  painter->setPen(textlabel->fontcolor);
320 
321  // Since I always just take the points from graphviz and pass them to Qt
322  // as pixel I also have to set the pixel size of the font.
323  QFont font(textlabel->fontname, textlabel->fontsize);
324  font.setPixelSize(textlabel->fontsize);
325 
326  if (!font.exactMatch())
327  {
328  QFontInfo fontinfo(font);
329  qWarning("replacing font \"%s\" by font \"%s\"", font.family().toUtf8().data(),
330  fontinfo.family().toUtf8().data());
331  }
332 
333  painter->setFont(font);
334 
335  QString text(QString::fromUtf8(textlabel->text));
336  QFontMetricsF fm(painter->fontMetrics());
337  QRectF rect(fm.boundingRect(text));
338  rect.moveCenter(gToQ(textlabel->pos, false));
339  painter->drawText(rect.adjusted(-2, -2, +2, +2), Qt::AlignCenter, text);
340 }
341 
342 
343 void
344 QY2Graph::clearGraph()
345 {
346  QList<QGraphicsItem*> items(scene->items());
347  while (!items.isEmpty())
348  delete items.takeFirst();
349 }
350 
351 
352 void
353 QY2Graph::renderGraph(graph_t* graph)
354 {
355  clearGraph();
356 
357  if (GD_charset(graph) != 0)
358  {
359  qWarning("unsupported charset");
360  }
361 
362  // don't use gToQ here since it adjusts the values
363  graphRect = QRectF(GD_bb(graph).LL.x, GD_bb(graph).LL.y, GD_bb(graph).UR.x, GD_bb(graph).UR.y);
364  scene->setSceneRect(graphRect.adjusted(-5, -5, +5, +5));
365 
366  scene->setBackgroundBrush(aggetToQColor(graph, "bgcolor", Qt::white));
367 
368  for (node_t* node = agfstnode(graph); node != NULL; node = agnxtnode(graph, node))
369  {
370  QPicture picture;
371  QPainter painter;
372 
373  painter.begin(&picture);
374  painter.initFrom(this);
375  drawLabel(ND_label(node), &painter);
376  painter.end();
377 
378  QY2Node* item = new QY2Node(makeShape(node), picture, node->name);
379 
380  item->setPos(gToQ(ND_coord(node)));
381 
382  QPen pen(aggetToQColor(node, "color", Qt::black));
383  pen.setWidthF(1.0);
384  item->setPen(pen);
385 
386  QBrush brush(aggetToQColor(node, "fillcolor", Qt::gray));
387  item->setBrush(brush);
388 
389  QString tooltip = aggetToQString(node, "tooltip", "");
390  if (!tooltip.isEmpty())
391  {
392  tooltip.replace("\\n", "\n");
393  item->setToolTip(tooltip);
394  }
395 
396  scene->addItem(item);
397 
398  for (edge_t* edge = agfstout(graph, node); edge != NULL; edge = agnxtout(graph, edge))
399  {
400  const splines* spl = ED_spl(edge);
401  if (spl == NULL)
402  continue;
403 
404  for (int i = 0; i < spl->size; ++i)
405  {
406  const bezier& bz = spl->list[i];
407 
408  QColor color(aggetToQColor(edge, "color", Qt::black));
409 
410  QPainterPath path(makeBezier(bz));
411 
412  QPicture picture;
413  QPainter painter;
414 
415  painter.begin(&picture);
416  if (bz.sflag)
417  drawArrow(QLineF(gToQ(bz.list[0]), gToQ(bz.sp)), color, &painter);
418  if (bz.eflag)
419  drawArrow(QLineF(gToQ(bz.list[bz.size-1]), gToQ(bz.ep)), color, &painter);
420  painter.end();
421 
422  QY2Edge* item = new QY2Edge(path, picture);
423 
424  QPen pen(color);
425  pen.setStyle(aggetToQPenStyle(edge, "style", Qt::SolidLine));
426  pen.setWidthF(1.0);
427  item->setPen(pen);
428 
429  item->setZValue(-1.0);
430 
431  scene->addItem(item);
432  }
433  }
434  }
435 }
436 
437 
438 QY2Node::QY2Node(const QPainterPath& path, const QPicture& picture, const QString& name)
439  : QGraphicsPathItem(path),
440  picture(picture),
441  name(name)
442 {
443 }
444 
445 
446 void
447 QY2Node::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
448 {
449  painter->save();
450  QGraphicsPathItem::paint(painter, option, widget);
451  painter->restore();
452 
453  picture.play(painter);
454 }
455 
456 
457 QY2Edge::QY2Edge(const QPainterPath& path, const QPicture& picture)
458  : QGraphicsPathItem(path),
459  picture(picture)
460 {
461 }
462 
463 
464 QRectF
465 QY2Edge::boundingRect() const
466 {
467  return QGraphicsPathItem::boundingRect().united(picture.boundingRect());
468 }
469 
470 
471 void
472 QY2Edge::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget)
473 {
474  painter->save();
475  QGraphicsPathItem::paint(painter, option, widget);
476  painter->restore();
477 
478  picture.play(painter);
479 }
480 
481 
482 #include "QY2Graph.moc"