File indexing completed on 2024-05-19 15:27:46
0001 /* This file is part of KGraphViewer. 0002 Copyright (C) 2005-2007 Gael de Chalendar <kleag@free.fr> 0003 0004 KGraphViewer is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU General Public 0006 License as published by the Free Software Foundation, version 2. 0007 0008 This program is distributed in the hope that it will be useful, 0009 but WITHOUT ANY WARRANTY; without even the implied warranty of 0010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0011 General Public License for more details. 0012 0013 You should have received a copy of the GNU General Public License 0014 along with this program; if not, write to the Free Software 0015 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0016 02110-1301, USA 0017 */ 0018 0019 #include "canvaselement.h" 0020 #include "FontsCache.h" 0021 #include "dot2qtconsts.h" 0022 #include "dotdefaults.h" 0023 #include "dotgraphview.h" 0024 #include "graphelement.h" 0025 #include "kgraphviewerlib_debug.h" 0026 0027 #include <iostream> 0028 #include <math.h> 0029 #include <stdlib.h> 0030 0031 #include <QGraphicsScene> 0032 #include <QGraphicsSceneMouseEvent> 0033 #include <QMenu> 0034 #include <QPainter> 0035 0036 #include <QAction> 0037 #include <QDebug> 0038 #include <klocalizedstring.h> 0039 0040 // comment out to get extended debug output during rendering 0041 // #define RENDER_DEBUG 1 0042 0043 #ifndef RENDER_DEBUG 0044 #define RENDER_DEBUG 0 0045 #endif 0046 0047 namespace KGraphViewer 0048 { 0049 CanvasElement::CanvasElement(DotGraphView *v, GraphElement *gelement, QGraphicsScene *c, QGraphicsItem *parent) 0050 : QObject() 0051 , QAbstractGraphicsShapeItem(parent) 0052 , m_scaleX(0) 0053 , m_scaleY(0) 0054 , m_xMargin(0) 0055 , m_yMargin(0) 0056 , m_gh(0) 0057 , m_element(gelement) 0058 , m_view(v) 0059 , m_font(nullptr) 0060 , m_pen(Dot2QtConsts::componentData().qtColor(gelement->fontColor())) 0061 , m_popup(new QMenu()) 0062 , m_hovered(false) 0063 , m_lastRenderOpRev(0) 0064 { 0065 // qCDebug(KGRAPHVIEWERLIB_LOG); 0066 m_font = FontsCache::changeable().fromName(gelement->fontName()); 0067 0068 /* qCDebug(KGRAPHVIEWERLIB_LOG) << "Creating CanvasElement for "<<gelement->id(); 0069 qCDebug(KGRAPHVIEWERLIB_LOG) << " data: " << wdhcf << "," << hdvcf << "," << gh << "," 0070 << scaleX << "," << scaleY << "," << xMargin << "," << yMargin << endl;*/ 0071 0072 if (element()->style() == "bold") { 0073 m_pen.setStyle(Qt::SolidLine); 0074 m_pen.setWidth(int(2 * ((m_scaleX + m_scaleY) / 2))); 0075 } else if (element()->style() != "filled") { 0076 m_pen.setStyle(Dot2QtConsts::componentData().qtPenStyle(m_element->style())); 0077 m_pen.setWidth(int((m_scaleX + m_scaleY) / 2)); 0078 if (element()->style().left(12) == "setlinewidth") { 0079 bool ok; 0080 uint lineWidth = element()->style().mid(13, m_element->style().length() - 1 - 13).toInt(&ok); 0081 m_pen.setWidth(lineWidth * int((m_scaleX + m_scaleY) / 2)); 0082 } 0083 } 0084 if (m_element->style() == "filled") { 0085 m_brush = Dot2QtConsts::componentData().qtColor(element()->backColor()); 0086 // QCanvasPolygon::drawShape(p); 0087 } else { 0088 m_brush = c->backgroundBrush(); 0089 } 0090 0091 // the message should be given (or possible to be given) by the part user 0092 QAction *removeElementAction = new QAction(i18n("Remove selected element(s)"), this); 0093 m_popup->addAction(removeElementAction); 0094 connect(removeElementAction, &QAction::triggered, this, &CanvasElement::slotRemoveElement); 0095 0096 connect(this, &CanvasElement::selected, v, &DotGraphView::slotElementSelected); 0097 0098 connect(this, &CanvasElement::elementContextMenuEvent, v, &DotGraphView::slotContextMenuEvent); 0099 0100 setAcceptHoverEvents(true); 0101 0102 connect(this, &CanvasElement::hoverEnter, v, static_cast<void (DotGraphView::*)(CanvasElement *)>(&DotGraphView::slotElementHoverEnter)); 0103 connect(this, &CanvasElement::hoverLeave, v, static_cast<void (DotGraphView::*)(CanvasElement *)>(&DotGraphView::slotElementHoverLeave)); 0104 } 0105 0106 CanvasElement::~CanvasElement() 0107 { 0108 delete m_popup; 0109 } 0110 0111 void CanvasElement::modelChanged() 0112 { 0113 qCDebug(KGRAPHVIEWERLIB_LOG); //<< id(); 0114 m_pen = QPen(Dot2QtConsts::componentData().qtColor(m_element->fontColor())); 0115 m_font = FontsCache::changeable().fromName(m_element->fontName()); 0116 prepareGeometryChange(); 0117 computeBoundingRect(); 0118 } 0119 0120 void CanvasElement::initialize(qreal scaleX, qreal scaleY, qreal xMargin, qreal yMargin, qreal gh) 0121 { 0122 Q_UNUSED(gh); 0123 // qCDebug(KGRAPHVIEWERLIB_LOG); 0124 setFlag(QGraphicsItem::ItemIsMovable, true); 0125 setFlag(QGraphicsItem::ItemIsSelectable, true); 0126 0127 m_scaleX = scaleX; 0128 m_scaleY = scaleY; 0129 m_xMargin = xMargin; 0130 m_yMargin = yMargin; 0131 // m_gh = gh; 0132 0133 setZValue(m_element->z()); 0134 0135 computeBoundingRect(); 0136 } 0137 0138 QRectF CanvasElement::boundingRect() const 0139 { 0140 return m_boundingRect; 0141 } 0142 0143 void CanvasElement::computeBoundingRect() 0144 { 0145 // qCDebug(KGRAPHVIEWERLIB_LOG) << element(); 0146 qCDebug(KGRAPHVIEWERLIB_LOG) << element()->id() << zValue(); 0147 0148 qreal adjust = 0.5; 0149 QRectF rect; 0150 if (element()->renderOperations().isEmpty()) { 0151 qCDebug(KGRAPHVIEWERLIB_LOG) << "no render operation"; 0152 rect = QRectF(0, 0, (m_view->defaultNewElementPixmap().size().width()) * m_scaleX, (m_view->defaultNewElementPixmap().size().height()) * m_scaleY); 0153 m_boundingRect = rect; 0154 } else { 0155 DotRenderOpVec::const_iterator it, it_end; 0156 it = element()->renderOperations().constBegin(); 0157 it_end = element()->renderOperations().constEnd(); 0158 for (; it != it_end; it++) { 0159 #if RENDER_DEBUG 0160 QString msg; 0161 QTextStream dd(&msg); 0162 dd << element()->id() << " an op: " << (*it).renderop << " "; 0163 for (int i : (*it).integers) { 0164 dd << i << " "; 0165 } 0166 dd << (*it).str; 0167 qCDebug(KGRAPHVIEWERLIB_LOG) << msg; 0168 #endif 0169 0170 if ((*it).renderop == "e" || (*it).renderop == "E") { 0171 // qCDebug(KGRAPHVIEWERLIB_LOG) << "integers[0]=" << (*it).integers[0] << "; 0172 qreal w = m_scaleX * (*it).integers[2] * 2; 0173 qreal h = m_scaleY * (*it).integers[3] * 2; 0174 qreal x = m_xMargin + (((*it).integers[0]) * m_scaleX) - w / 2; 0175 qreal y = ((m_gh - (*it).integers[1]) * m_scaleY) + m_yMargin - h / 2; 0176 m_boundingRect = QRectF(x - adjust, y - adjust, w + adjust, h + adjust); 0177 // qCDebug(KGRAPHVIEWERLIB_LOG) << "'" << element()->id() << "' set rect for ellipse to " << rect; 0178 } else if ((*it).renderop == "p" || (*it).renderop == "P") { 0179 QPolygonF polygon((*it).integers[0]); 0180 for (int i = 0; i < (*it).integers[0]; i++) { 0181 qreal x, y; 0182 x = (*it).integers[2 * i + 1]; 0183 y = (*it).integers[2 * i + 2]; 0184 { 0185 } 0186 QPointF p(x * m_scaleX + m_xMargin, (m_gh - y) * m_scaleY + m_yMargin); 0187 polygon[i] = p; 0188 } 0189 m_boundingRect = polygon.boundingRect(); 0190 // qCDebug(KGRAPHVIEWERLIB_LOG) << "'" << element()->id() << "' set rect for polygon to " << rect; 0191 } 0192 } 0193 } 0194 setPos(0, 0); 0195 } 0196 0197 /// TODO: optimize more! 0198 void CanvasElement::paint(QPainter *p, const QStyleOptionGraphicsItem *option, QWidget *widget) 0199 { 0200 if (m_lastRenderOpRev != element()->renderOperationsRevision()) { 0201 m_fontSizeCache.clear(); 0202 } 0203 0204 Q_UNUSED(option) 0205 Q_UNUSED(widget) 0206 /// computes the scaling of line width 0207 qreal widthScaleFactor = (m_scaleX + m_scaleY) / 2; 0208 if (widthScaleFactor < 1) { 0209 widthScaleFactor = 1; 0210 } 0211 0212 #if RENDER_DEBUG 0213 QString msg; 0214 QTextStream dd(&msg); 0215 for (const DotRenderOp &op : element()->renderOperations()) { 0216 dd << element()->id() << " an op: " << op.renderop << " "; 0217 for (int i : op.integers) { 0218 dd << i << " "; 0219 } 0220 dd << op.str << Qt::endl; 0221 } 0222 qCDebug(KGRAPHVIEWERLIB_LOG) << msg; 0223 #endif 0224 0225 if (element()->renderOperations().isEmpty() && m_view->isReadWrite()) { 0226 qCWarning(KGRAPHVIEWERLIB_LOG) << element()->id() << ": no render operation. This should not happen."; 0227 return; 0228 } 0229 0230 QListIterator<DotRenderOp> it(element()->renderOperations()); 0231 // it.toBack(); 0232 0233 QColor lineColor = Dot2QtConsts::componentData().qtColor(element()->lineColor()); 0234 QColor backColor = Dot2QtConsts::componentData().qtColor(element()->backColor()); 0235 if (m_hovered && m_view->highlighting()) { 0236 backColor = backColor.lighter(); 0237 } 0238 0239 const QPen oldPen = p->pen(); 0240 const QBrush oldBrush = p->brush(); 0241 const QFont oldFont = p->font(); 0242 0243 while (it.hasNext()) { 0244 const DotRenderOp &dro = it.next(); 0245 if (dro.renderop == "c") { 0246 QColor c(dro.str.mid(0, 7)); 0247 bool ok; 0248 c.setAlpha(255 - dro.str.mid(8).toInt(&ok, 16)); 0249 lineColor = c; 0250 // qCDebug(KGRAPHVIEWERLIB_LOG) << "c" << dro.str.mid(0,7) << lineColor; 0251 } else if (dro.renderop == "C") { 0252 QColor c(dro.str.mid(0, 7)); 0253 bool ok; 0254 c.setAlpha(255 - dro.str.mid(8).toInt(&ok, 16)); 0255 if (m_hovered && m_view->highlighting()) { 0256 c = c.lighter(); 0257 } 0258 backColor = c; 0259 // qCDebug(KGRAPHVIEWERLIB_LOG) << "C" << dro.str.mid(0,7) << backColor; 0260 } else if (dro.renderop == "e" || dro.renderop == "E") { 0261 QPen pen = oldPen; 0262 qreal w = m_scaleX * dro.integers[2] * 2; 0263 qreal h = m_scaleY * dro.integers[3] * 2; 0264 qreal x = m_xMargin + (dro.integers[0] * m_scaleX) - w / 2; 0265 qreal y = ((m_gh - dro.integers[1]) * m_scaleY) + m_yMargin - h / 2; 0266 QRectF rect(x, y, w, h); 0267 pen.setColor(lineColor); 0268 if (element()->attributes().contains("penwidth")) { 0269 bool ok; 0270 int lineWidth = element()->attributes()["penwidth"].toInt(&ok); 0271 pen.setWidth(int(lineWidth * widthScaleFactor)); 0272 } 0273 0274 p->setBrush(backColor); 0275 p->setPen(pen); 0276 0277 // qCDebug(KGRAPHVIEWERLIB_LOG) << element()->id() << "drawEllipse" << lineColor << backColor << rect; 0278 // rect = QRectF(0,0,100,100); 0279 p->drawEllipse(rect); 0280 } else if (dro.renderop == "p" || dro.renderop == "P") { 0281 // std::cerr << "Drawing polygon for node '"<<element()->id()<<"': "; 0282 QPolygonF points(dro.integers[0]); 0283 for (int i = 0; i < dro.integers[0]; i++) { 0284 qreal x, y; 0285 x = dro.integers[2 * i + 1]; 0286 y = dro.integers[2 * i + 2]; 0287 QPointF p((x * m_scaleX) + m_xMargin, ((m_gh - y) * m_scaleY) + m_yMargin); 0288 /* qCDebug(KGRAPHVIEWERLIB_LOG) << " point: (" << dro.integers[2*i+1] << "," 0289 << dro.integers[2*i+2] << ") " */ 0290 points[i] = p; 0291 } 0292 0293 QPen pen = oldPen; 0294 pen.setColor(lineColor); 0295 if (element()->style() == "bold") { 0296 pen.setStyle(Qt::SolidLine); 0297 pen.setWidth(2); 0298 } 0299 if (element()->attributes().contains("penwidth")) { 0300 bool ok; 0301 int lineWidth = element()->attributes()["penwidth"].toInt(&ok); 0302 pen.setWidth(int(lineWidth * widthScaleFactor)); 0303 } else if (element()->style() != "filled") { 0304 pen.setStyle(Dot2QtConsts::componentData().qtPenStyle(element()->style())); 0305 } 0306 if (element()->style().left(12) == "setlinewidth") { 0307 bool ok; 0308 uint lineWidth = element()->style().mid(12, element()->style().length() - 1 - 12).toInt(&ok); 0309 pen.setWidth(lineWidth); 0310 } 0311 p->setPen(pen); 0312 p->setBrush(backColor); 0313 /* if (element()->style() == "filled") 0314 { 0315 p->setBrush(Dot2QtConsts::componentData().qtColor(element()->backColor())); 0316 // QCanvasPolygon::paint(p); 0317 } 0318 else 0319 { 0320 p->setBrush(canvas()->backgroundColor()); 0321 }*/ 0322 // qCDebug(KGRAPHVIEWERLIB_LOG) << element()->id() << "drawPolygon" << points; 0323 p->drawPolygon(points); 0324 if (!element()->shapeFile().isEmpty()) { 0325 QPixmap pix(element()->shapeFile()); 0326 if (!pix.isNull()) { 0327 p->drawPixmap(int(points.boundingRect().left()), int(points.boundingRect().top()), pix); 0328 } 0329 } 0330 } 0331 } 0332 0333 p->setBrush(oldBrush); 0334 p->setPen(oldPen); 0335 0336 it.toFront(); 0337 while (it.hasNext()) { 0338 const DotRenderOp &dro = it.next(); 0339 if (dro.renderop == "c") { 0340 QColor c(dro.str.mid(0, 7)); 0341 bool ok; 0342 c.setAlpha(255 - dro.str.mid(8).toInt(&ok, 16)); 0343 lineColor = c; 0344 // qCDebug(KGRAPHVIEWERLIB_LOG) << "c" << dro.str.mid(0,7) << lineColor; 0345 } else if (dro.renderop == "C") { 0346 QColor c(dro.str.mid(0, 7)); 0347 bool ok; 0348 c.setAlpha(255 - dro.str.mid(8).toInt(&ok, 16)); 0349 if (m_hovered && m_view->highlighting()) { 0350 c = c.lighter(); 0351 } 0352 backColor = c; 0353 // qCDebug(KGRAPHVIEWERLIB_LOG) << "C" << dro.str.mid(0,7) << backColor; 0354 } else if (dro.renderop == "L") { 0355 // qCDebug(KGRAPHVIEWERLIB_LOG) << "Label"; 0356 QPolygonF points(dro.integers[0]); 0357 for (int i = 0; i < dro.integers[0]; i++) { 0358 qreal x, y; 0359 x = dro.integers[2 * i + 1]; 0360 y = dro.integers[2 * i + 2]; 0361 QPointF p((x * m_scaleX) + m_xMargin, ((m_gh - y) * m_scaleY) + m_yMargin); 0362 points[i] = p; 0363 } 0364 QPen pen(lineColor); 0365 if (element()->style() == "bold") { 0366 pen.setStyle(Qt::SolidLine); 0367 pen.setWidth(2); 0368 } else if (element()->style() != "filled") { 0369 pen.setStyle(Dot2QtConsts::componentData().qtPenStyle(element()->style())); 0370 } 0371 p->setPen(pen); 0372 // qCDebug(KGRAPHVIEWERLIB_LOG) << element()->id() << "drawPolyline" << points; 0373 p->drawPolyline(points); 0374 } 0375 } 0376 p->setPen(oldPen); 0377 0378 // qCDebug(KGRAPHVIEWERLIB_LOG) << "Drawing" << element()->id() << "labels"; 0379 QString color = lineColor.name(); 0380 it.toFront(); 0381 uint num_T = 0; 0382 while (it.hasNext()) { 0383 const DotRenderOp &dro = it.next(); 0384 if (dro.renderop == "c" || dro.renderop == "C") { 0385 color = dro.str.mid(0, 7); 0386 // qCDebug(KGRAPHVIEWERLIB_LOG) << dro.renderop << color; 0387 } else if (dro.renderop == "F") { 0388 element()->setFontName(dro.str); 0389 element()->setFontSize(dro.integers[0]); 0390 // qCDebug(KGRAPHVIEWERLIB_LOG) << "F" << element()->fontName() << element()->fontColor() << element()->fontSize(); 0391 } else if (dro.renderop == "T") { 0392 ++num_T; 0393 // we suppose here that the color has been set just before 0394 element()->setFontColor(color); 0395 // draw a label 0396 // qCDebug(KGRAPHVIEWERLIB_LOG) << "Drawing a label " << dro.integers[0] 0397 // << " " << dro.integers[1] << " " << dro.integers[2] 0398 // << " " << dro.integers[3] << " " << dro.str 0399 // << " (" << element()->fontName() << ", " << element()->fontSize() 0400 // << ", " << element()->fontColor() << ")"; 0401 0402 int fontWidth = 0; 0403 bool cacheValid = false; 0404 // qCDebug(KGRAPHVIEWERLIB_LOG) << element()->id() << " initial fontSize " << fontSize; 0405 if (m_lastRenderOpRev == element()->renderOperationsRevision()) { 0406 FontSizeCache::iterator cacheIt = m_fontSizeCache.find(num_T); 0407 if (cacheIt != m_fontSizeCache.end()) { 0408 m_font->setPointSize(cacheIt->first); 0409 fontWidth = cacheIt->second; 0410 cacheValid = true; 0411 } 0412 } 0413 if (!cacheValid) { 0414 int stringWidthGoal = int(dro.integers[3] * m_scaleX); 0415 int fontSize = element()->fontSize(); 0416 m_font->setPointSize(fontSize); 0417 0418 QFontMetrics fm(*m_font); 0419 fontWidth = fm.horizontalAdvance(dro.str); 0420 while (fontWidth > stringWidthGoal && fontSize > 1) { 0421 // use floor'ed extrapolated font size 0422 fontSize = double(stringWidthGoal) / fontWidth * fontSize; 0423 m_font->setPointSize(fontSize); 0424 fm = QFontMetrics(*m_font); 0425 fontWidth = fm.horizontalAdvance(dro.str); 0426 } 0427 m_fontSizeCache[num_T] = qMakePair(fontSize, fontWidth); 0428 } 0429 0430 p->setFont(*m_font); 0431 QPen pen(m_pen); 0432 pen.setColor(element()->fontColor()); 0433 p->setPen(pen); 0434 qreal x = (m_scaleX * ((dro.integers[0]) + (((-dro.integers[2]) * (fontWidth)) / 2) - ((fontWidth) / 2))) + m_xMargin; 0435 qreal y = ((m_gh - (dro.integers[1])) * m_scaleY) + m_yMargin; 0436 QPointF point(x, y); 0437 // qCDebug(KGRAPHVIEWERLIB_LOG) << element()->id() << "drawText" << point << " " << fontSize; 0438 p->drawText(point, dro.str); 0439 } 0440 } 0441 0442 if (element()->isSelected()) { 0443 // qCDebug(KGRAPHVIEWERLIB_LOG) << "element is selected: draw selection marks"; 0444 p->setBrush(Qt::black); 0445 p->setPen(Qt::black); 0446 p->drawRect(QRectF(m_boundingRect.topLeft(), QSizeF(6, 6))); 0447 p->drawRect(QRectF(m_boundingRect.topRight() - QPointF(6, 0), QSizeF(6, 6))); 0448 p->drawRect(QRectF(m_boundingRect.bottomLeft() - QPointF(0, 6), QSizeF(6, 6))); 0449 p->drawRect(QRectF(m_boundingRect.bottomRight() - QPointF(6, 6), QSizeF(6, 6))); 0450 } 0451 0452 p->setPen(oldPen); 0453 p->setBrush(oldBrush); 0454 p->setFont(oldFont); 0455 0456 m_lastRenderOpRev = element()->renderOperationsRevision(); 0457 } 0458 0459 void CanvasElement::mousePressEvent(QGraphicsSceneMouseEvent *event) 0460 { 0461 qCDebug(KGRAPHVIEWERLIB_LOG) << m_element->id() << boundingRect(); 0462 if (m_view->isReadOnly()) { 0463 return; 0464 } 0465 if (m_view->editingMode() == DotGraphView::AddNewEdge) { 0466 m_view->createNewEdgeDraftFrom(this); 0467 return; 0468 } else if (m_view->editingMode() == DotGraphView::DrawNewEdge) { 0469 m_view->finishNewEdgeTo(this); 0470 return; 0471 } 0472 if (event->button() == Qt::LeftButton) { 0473 m_element->setSelected(!m_element->isSelected()); 0474 if (m_element->isSelected()) { 0475 emit(selected(this, event->modifiers())); 0476 } 0477 update(); 0478 } else if (event->button() == Qt::RightButton) { 0479 // opens the selected edge contextual menu and if necessary select the edge 0480 if (!m_element->isSelected()) { 0481 m_element->setSelected(true); 0482 emit(selected(this, event->modifiers())); 0483 update(); 0484 } 0485 0486 // qCDebug(KGRAPHVIEWERLIB_LOG) << "opens the contextual menu"; 0487 // m_popup->exec(event->screenPos()); 0488 emit(elementContextMenuEvent(m_element->id(), event->screenPos())); 0489 } 0490 } 0491 0492 void CanvasElement::mouseMoveEvent(QGraphicsSceneMouseEvent *event) 0493 { 0494 Q_UNUSED(event) 0495 // qCDebug(KGRAPHVIEWERLIB_LOG) ; 0496 } 0497 0498 void CanvasElement::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) 0499 { 0500 Q_UNUSED(event) 0501 // qCDebug(KGRAPHVIEWERLIB_LOG) ; 0502 } 0503 0504 void CanvasElement::slotRemoveElement() 0505 { 0506 m_view->removeSelectedElements(); 0507 } 0508 0509 void CanvasElement::hoverEnterEvent(QGraphicsSceneHoverEvent *event) 0510 { 0511 Q_UNUSED(event) 0512 // qCDebug(KGRAPHVIEWERLIB_LOG); 0513 m_hovered = true; 0514 update(); 0515 emit hoverEnter(this); 0516 } 0517 0518 void CanvasElement::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) 0519 { 0520 Q_UNUSED(event) 0521 // qCDebug(KGRAPHVIEWERLIB_LOG); 0522 m_hovered = false; 0523 update(); 0524 emit hoverLeave(this); 0525 } 0526 0527 } 0528 0529 #include "moc_canvaselement.cpp"