/* dtkComposerSceneNodeComposite.cpp --- * * Author: Julien Wintz * Copyright (C) 2008-2011 - Julien Wintz, Inria. * Created: Fri Feb 3 14:01:41 2012 (+0100) * Version: $Id: 2aa8415cce50dfa6082e7ad04e26b52273ca3999 $ * Last-Updated: mer. févr. 5 10:17:29 2014 (+0100) * By: Nicolas Niclausse * Update #: 929 */ /* Commentary: * */ /* Change log: * */ #include #include "dtkComposerNodeComposite.h" #include "dtkComposerSceneEdge.h" #include "dtkComposerSceneNode.h" #include "dtkComposerSceneNodeComposite.h" #include "dtkComposerSceneNodeControl.h" #include "dtkComposerSceneNote.h" #include "dtkComposerScenePort.h" #include "dtkComposerWriter.h" #if defined(DTK_BUILD_SUPPORT_DISTRIBUTED) #include "dtkComposerNodeRemote.h" #endif #include #if defined(DTK_BUILD_SUPPORT_DISTRIBUTED) #include #include #endif // ///////////////////////////////////////////////////////////////// // dtkComposerSceneNodeComposite // ///////////////////////////////////////////////////////////////// class dtkComposerSceneNodeCompositePrivate { public: dtkComposerSceneNodeComposite *former; public: dtkComposerSceneNoteList notes; dtkComposerSceneNodeList nodes; dtkComposerSceneEdgeList edges; public: QPointF offset; QPointF unreveal_pos; public: QRectF rect; QRectF unreveal_rect; public: bool root; public: dtkComposerWriter writer; public: bool flattened; bool entered; bool revealed; bool obfuscated; }; dtkComposerSceneNodeComposite::dtkComposerSceneNodeComposite(void) : dtkComposerSceneNode(), d(new dtkComposerSceneNodeCompositePrivate) { d->former = NULL; d->rect = QRectF(0, 0, 150, 50); d->unreveal_rect = QRectF(0, 0, 150, 50); d->offset = QPointF(50, 50); d->root = false; d->flattened = false; d->entered = false; d->revealed = false; d->obfuscated = false; this->setAcceptDrops(true); this->layout(); this->setTitle("Composite"); } dtkComposerSceneNodeComposite::~dtkComposerSceneNodeComposite(void) { delete d; d = NULL; } void dtkComposerSceneNodeComposite::wrap(dtkComposerNode *wrapee) { dtkComposerNodeComposite *composite = dynamic_cast(wrapee); if (!composite) return; for (int i = 0; i < composite->receivers().count(); ++i) { dtkComposerScenePort *port = new dtkComposerScenePort(dtkComposerScenePort::Input, this); this->addInputPort(port); port->setLabel(composite->inputLabelHint(this->inputPorts().indexOf(port))); } for (int i = 0; i < composite->emitters().count(); ++i) { dtkComposerScenePort *port = new dtkComposerScenePort(dtkComposerScenePort::Output, this); this->addOutputPort(port); port->setLabel(composite->outputLabelHint(this->outputPorts().indexOf(port))); } dtkComposerSceneNode::wrap(wrapee); } void dtkComposerSceneNodeComposite::setFormer(dtkComposerSceneNodeComposite *former) { d->former = former; } dtkComposerSceneNodeComposite *dtkComposerSceneNodeComposite::former(void) { return d->former; } void dtkComposerSceneNodeComposite::addNote(dtkComposerSceneNote *note) { d->notes << note; } void dtkComposerSceneNodeComposite::removeNote(dtkComposerSceneNote *note) { d->notes.removeAll(note); } void dtkComposerSceneNodeComposite::addNode(dtkComposerSceneNode *node) { d->nodes << node; } void dtkComposerSceneNodeComposite::removeNode(dtkComposerSceneNode *node) { d->nodes.removeAll(node); } void dtkComposerSceneNodeComposite::addEdge(dtkComposerSceneEdge *edge) { d->edges << edge; } void dtkComposerSceneNodeComposite::removeEdge(dtkComposerSceneEdge *edge) { d->edges.removeAll(edge); } dtkComposerSceneNoteList dtkComposerSceneNodeComposite::notes(void) { return d->notes; } dtkComposerSceneNodeList dtkComposerSceneNodeComposite::nodes(void) { return d->nodes; } dtkComposerSceneEdgeList dtkComposerSceneNodeComposite::edges(void) { return d->edges; } int dtkComposerSceneNodeComposite::inputDegree(dtkComposerScenePort *port) { int degree = 0; foreach (dtkComposerSceneEdge *edge, d->edges) if (edge->destination() == port) degree++; return degree; } int dtkComposerSceneNodeComposite::outputDegree(dtkComposerScenePort *port) { int degree = 0; foreach (dtkComposerSceneEdge *edge, d->edges) if (edge->source() == port) degree++; return degree; } bool dtkComposerSceneNodeComposite::entered(void) { return d->entered; } bool dtkComposerSceneNodeComposite::flattened(void) { return d->flattened || d->root; } // return true if node is visible, ie, it is flatenned and all it's parent are ( or entered ) bool dtkComposerSceneNodeComposite::visible(void) { if (d->root) return true; if (dynamic_cast(this->parent())) return dynamic_cast(this->parent()->parent())->visible(); return (d->entered || ( d->flattened && (dynamic_cast(this->parent()))->visible())) ; } void dtkComposerSceneNodeComposite::enter(void) { d->entered = true; this->reveal(); } void dtkComposerSceneNodeComposite::leave(void) { d->entered = false; this->unreveal(); } void dtkComposerSceneNodeComposite::flatten(void) { d->flattened = true; this->reveal(); } void dtkComposerSceneNodeComposite::unflatten(void) { d->flattened = false; this->unreveal(); } void dtkComposerSceneNodeComposite::reveal(void) { d->revealed = true; if (this->embedded()) { this->obfuscate(); } else { this->resetPos(d->unreveal_pos, d->unreveal_rect); } if (!this->embedded() && !d->entered && d->flattened) { this->setFlag(QGraphicsItem::ItemIsMovable, true); this->setFlag(QGraphicsItem::ItemIsSelectable, true); } else { this->setFlag(QGraphicsItem::ItemIsMovable, false); this->setFlag(QGraphicsItem::ItemIsSelectable, false); } } void dtkComposerSceneNodeComposite::unreveal(void) { if (!d->flattened && !d->entered) d->revealed = false; if (this->embedded()) { foreach (dtkComposerSceneNode *node, d->nodes) node->setPos(d->unreveal_pos + (node->scenePos() - this->scenePos() - d->offset)); foreach (dtkComposerSceneNote *note, d->notes) note->setPos(d->unreveal_pos + (note->scenePos() - this->scenePos() - d->offset)); this->obfuscate(); } if (!d->flattened && !this->embedded() && !d->entered) { this->setFlag(QGraphicsItem::ItemIsMovable, true); this->setFlag(QGraphicsItem::ItemIsSelectable, true); } else { this->setFlag(QGraphicsItem::ItemIsMovable, false); this->setFlag(QGraphicsItem::ItemIsSelectable, false); } } void dtkComposerSceneNodeComposite::resetPos(const QPointF& pos, const QRectF& rect) { QPointF center(0, 0); QRectF box(0, 0, 0, 0); if (!pos.isNull()) { center = pos + QPointF(0.5 * rect.width(), 0.5 * rect.height()); foreach (dtkComposerSceneNode *node, d->nodes) box |= node->sceneBoundingRect(); } foreach (dtkComposerSceneNode *node, d->nodes) { node->setPos(node->scenePos() + (center - box.center())); if (dtkComposerSceneNodeControl *control = dynamic_cast(node)) foreach (dtkComposerSceneNodeComposite *block, control->blocks()) { block->resetPos(block->scenePos(), block->sceneBoundingRect()); } } foreach (dtkComposerSceneNote *note, d->notes) note->setPos(note->scenePos() + (center - box.center())); } void dtkComposerSceneNodeComposite::setUnrevealPos(const QPointF& pos) { d->unreveal_pos = pos; } void dtkComposerSceneNodeComposite::setUnrevealRect(const QRectF& rect) { d->unreveal_rect = rect; } QPointF dtkComposerSceneNodeComposite::unrevealPos(void) const { return d->unreveal_pos; } QRectF dtkComposerSceneNodeComposite::unrevealRect(void) const { return d->unreveal_rect; } bool dtkComposerSceneNodeComposite::root(void) { return d->root; } void dtkComposerSceneNodeComposite::setRoot(bool root) { d->root = root; } #include void dtkComposerSceneNodeComposite::layout(void) { if (this->embedded() && !d->entered) goto port_location; // ///////////////////////////////////////////////////////////////// // Rect calculation // ///////////////////////////////////////////////////////////////// if (!d->revealed) { d->rect = QRectF(0, 0, 150, 50); } else { qreal xmin, xmax; qreal ymin, ymax; this->boundingBox(xmin, xmax, ymin, ymax); qreal w = xmax - xmin; qreal h = ymax - ymin; w = qMax(w, 200.0) + 100; h = qMax(h, 100.0) + 100; d->rect = QRectF(QPointF(0, 0), QSizeF(w, h)); this->setPos(xmin - 50, ymin - 50); } // ///////////////////////////////////////////////////////////////// // Port location // ///////////////////////////////////////////////////////////////// port_location: int header = 15; int port_margin_top = 10; int port_margin_bottom = 10; int port_margin_left = 10; int port_spacing = 10; for (int i = 0; i < this->inputPorts().count(); i++) this->inputPorts().at(i)->setPos(QPointF(port_margin_left, i * this->inputPorts().at(i)->boundingRect().height() + i * port_spacing + port_margin_top + header)); for (int i = 0; i < this->outputPorts().count(); i++) this->outputPorts().at(i)->setPos(QPointF(d->rect.right() - port_margin_left - this->outputPorts().at(i)->boundingRect().width(), i * this->outputPorts().at(i)->boundingRect().height() + i * port_spacing + port_margin_top + header)); // ///////////////////////////////////////////////////////////////// // Redraw parent // ///////////////////////////////////////////////////////////////// if (dtkComposerSceneNodeComposite *parent = dynamic_cast(this->parent())) { if (!parent->root()) { if (parent->entered() || (parent->flattened() && !parent->embedded())) { parent->layout(); } } } if (this->embedded()) goto update; // ///////////////////////////////////////////////////////////////// // Height calculation // ///////////////////////////////////////////////////////////////// if (!d->revealed) { if (this->inputPorts().count() || this->outputPorts().count()) { if (this->inputPorts().count() >= this->outputPorts().count()) d->rect = QRectF(d->rect.topLeft(), QSize(d->rect.width(), this->inputPorts().count() * this->inputPorts().at(0)->boundingRect().height() + port_margin_top + port_margin_bottom + (this->inputPorts().count() - 1) * port_spacing + header)); else d->rect = QRectF(d->rect.topLeft(), QSize(d->rect.width(), this->outputPorts().count() * this->outputPorts().at(0)->boundingRect().height() + port_margin_top + port_margin_bottom + (this->outputPorts().count() - 1) * port_spacing + header)); } } update: // ///////////////////////////////////////////////////////////////// // Update edges geometry // ///////////////////////////////////////////////////////////////// QRectF updateRect; foreach (dtkComposerSceneEdge *edge, this->inputEdges()) { edge->adjust(); updateRect |= edge->boundingRect(); } foreach (dtkComposerSceneEdge *edge, d->edges) { edge->adjust(); updateRect |= edge->boundingRect(); } foreach (dtkComposerSceneEdge *edge, this->outputEdges()) { edge->adjust(); updateRect |= edge->boundingRect(); } this->update(updateRect); } void dtkComposerSceneNodeComposite::resize(const QSizeF& size) { d->rect = QRectF(d->rect.topLeft(), size); } void dtkComposerSceneNodeComposite::resize(qreal width, qreal height) { d->rect = QRectF(d->rect.topLeft(), QSizeF(width, height)); } void dtkComposerSceneNodeComposite::obfuscate(void) { QRectF rect = this->sceneBoundingRect(); d->obfuscated = false; if (!d->entered) { foreach (dtkComposerSceneNode *node, d->nodes) if (!rect.contains(node->sceneBoundingRect())) d->obfuscated = true; foreach (dtkComposerSceneNote *note, d->notes) if (!rect.contains(note->sceneBoundingRect())) d->obfuscated = true; } foreach (dtkComposerSceneNode *node, d->nodes) { node->setVisible(!d->obfuscated && this->isVisible() && d->flattened); if (dtkComposerSceneNodeComposite *composite = dynamic_cast(node)) { if (composite->flattened()) composite->obfuscate(); } else if (dtkComposerSceneNodeControl *control = dynamic_cast(node)) { foreach (dtkComposerSceneNodeComposite *block, control->blocks()) block->obfuscate(); } } foreach (dtkComposerSceneNote *note, d->notes) note->setVisible(!d->obfuscated && this->isVisible() && d->flattened); foreach (dtkComposerSceneEdge *edge, d->edges) edge->setVisible(!d->obfuscated && this->isVisible() && d->flattened); this->update(); } void dtkComposerSceneNodeComposite::boundingBox(qreal& x_min, qreal& x_max, qreal& y_min, qreal& y_max) { qreal xmin = d->unreveal_pos.x(); qreal xmax = d->unreveal_pos.x() + d->unreveal_rect.width(); qreal ymin = d->unreveal_pos.y(); qreal ymax = d->unreveal_pos.y() + d->unreveal_rect.height(); foreach (dtkComposerSceneNode *node, d->nodes) { xmin = qMin(xmin, node->scenePos().x()); xmax = qMax(xmax, node->scenePos().x() + node->boundingRect().width()); ymin = qMin(ymin, node->scenePos().y()); ymax = qMax(ymax, node->scenePos().y() + node->boundingRect().height()); } x_min = xmin; x_max = xmax; y_min = ymin; y_max = ymax; } QRectF dtkComposerSceneNodeComposite::boundingRect(void) const { return d->rect.adjusted(-2, -2, 2, 2); } void dtkComposerSceneNodeComposite::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option); Q_UNUSED(widget); qreal radius = this->embedded() ? 0.0 : 5.0; if (this->isSelected()) { painter->setPen(QPen(Qt::magenta, 3, Qt::SolidLine)); painter->setBrush(Qt::NoBrush); painter->drawRoundedRect(d->rect.adjusted(-1, -1, 1, 1), radius, radius); } if (d->revealed) { painter->setPen(QPen(Qt::darkGray, 1, Qt::DashLine)); painter->setBrush(QColor(10, 10, 10, 120)); } else { QLinearGradient gradiant(d->rect.left(), d->rect.top(), d->rect.left(), d->rect.bottom()); gradiant.setColorAt(0.0, QColor(Qt::darkGray)); gradiant.setColorAt(10 / d->rect.height(), QColor(Qt::darkGray).darker().darker()); gradiant.setColorAt(1.0, QColor(Qt::darkGray).darker().darker().darker()); painter->setPen(QPen(QColor(Qt::darkGray).darker().darker(), 1, Qt::SolidLine)); painter->setBrush(gradiant); } if (this->embedded() && !d->entered) { painter->setPen(Qt::NoPen); if (d->obfuscated) painter->setBrush(QColor(0, 0, 0, 127)); } painter->drawRoundedRect(d->rect, radius, radius); // Drawing node's title qreal margin = 5.0; QFont font = painter->font(); QFontMetricsF metrics(font); QString title_text = metrics.elidedText(this->title(), Qt::ElideMiddle, this->boundingRect().width() - 2 - 4 * margin); QPointF title_pos; if (!this->embedded() && !d->entered) title_pos = QPointF(2 * margin, 2 * margin + metrics.xHeight()); else title_pos = QPointF(d->rect.width() / 2.0 - metrics.width(title_text) / 2.0, d->rect.height() / 2.0 + metrics.xHeight() / 2.0); if ((this->embedded() && !d->entered) || (!this->embedded() && d->revealed)) painter->setPen(QPen(QColor(Qt::darkGray))); else painter->setPen(QPen(QColor(Qt::white))); painter->drawText(title_pos, title_text); if (d->obfuscated) { QFont oFont = QFont(font); oFont.setPointSizeF(font.pointSizeF() - 2); QString title = QString("Obfuscated content. (%1 notes, %2 nodes, %3 edges)") .arg(d->notes.count()) .arg(d->nodes.count()) .arg(d->edges.count()); title_text = metrics.elidedText(title, Qt::ElideRight, this->boundingRect().width() - 2 - 4 * margin); title_pos = QPointF(d->rect.width() / 2.0 - metrics.width(title_text) / 2.0, d->rect.height() / 2.0 + metrics.xHeight() / 2.0 + metrics.height() * 1.5); painter->setPen(QPen(QColor(Qt::darkGray))); painter->setFont(oFont); painter->drawText(title_pos, title_text); } } void dtkComposerSceneNodeComposite::dragEnterEvent(QGraphicsSceneDragDropEvent *event) { #if defined(DTK_BUILD_SUPPORT_DISTRIBUTED) dtkComposerNodeRemote *remote = dynamic_cast(this->wrapee()); if (!remote) { event->ignore(); return; } #endif if (event->mimeData()->hasText()) event->acceptProposedAction(); else event->ignore(); } void dtkComposerSceneNodeComposite::dragLeaveEvent(QGraphicsSceneDragDropEvent *event) { event->accept(); } void dtkComposerSceneNodeComposite::dragMoveEvent(QGraphicsSceneDragDropEvent *event) { #if defined(DTK_BUILD_SUPPORT_DISTRIBUTED) dtkComposerNodeRemote *remote = dynamic_cast(this->wrapee()); if (!remote) { event->ignore(); return; } #endif if (event->mimeData()->hasText()) event->acceptProposedAction(); else event->ignore(); } void dtkComposerSceneNodeComposite::dropEvent(QGraphicsSceneDragDropEvent *event) { #if defined(DTK_BUILD_SUPPORT_DISTRIBUTED) dtkComposerNodeRemote *remote = dynamic_cast(this->wrapee()); if (!remote) { event->ignore(); return; } const dtkDistributedMimeData *data = qobject_cast(event->mimeData()); if (!data) { dtkDebug() << "Unable to retrieve distributed mime data"; } QString job = data->text(); dtkDistributedController *controller = const_cast(data)->controller(); remote->setJob(job); remote->setController(controller); this->setTitle("Remote on " + job); event->acceptProposedAction(); this->update(); #else Q_UNUSED(event); #endif }