PARP Research Group University of Murcia, Spain


src/qvgui/qvdesigner/slate/groupnode.cpp

00001 /*
00002  *      Copyright (C) 2008, 2009. PARP Research Group.
00003  *      <http://perception.inf.um.es>
00004  *      University of Murcia, Spain.
00005  *
00006  *      This file is part of the QVision library.
00007  *
00008  *      QVision is free software: you can redistribute it and/or modify
00009  *      it under the terms of the GNU Lesser General Public License as
00010  *      published by the Free Software Foundation, version 3 of the License.
00011  *
00012  *      QVision is distributed in the hope that it will be useful,
00013  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *      GNU Lesser General Public License for more details.
00016  *
00017  *      You should have received a copy of the GNU Lesser General Public
00018  *      License along with QVision. If not, see <http://www.gnu.org/licenses/>.
00019  */
00020 
00021 
00022 #include <QtGui>
00023 
00024 #include "groupnode.h"
00025 #include <QGraphicsSceneDragDropEvent>
00026 #include <iostream>
00027 #include "grouplink.h"
00028 
00029 
00030 uint GroupNode::maxId = 0;
00031 
00032 GroupNode::GroupNode(QString _name, SlateWindow *wind, QGraphicsItem * parent, QGraphicsScene * scene): Node(_name, wind, parent, scene), abstractPaint(false)
00033 {
00034     myTextColor = Qt::darkGreen;
00035     myOutlineColor = Qt::darkBlue;
00036     myBackgroundColor = Qt::white;
00037 
00038     QFontMetricsF metrics = qApp->font();
00039         lineSpacing = metrics.lineSpacing();
00040 
00041     setFlags(ItemIsMovable | ItemIsSelectable);
00042 
00043         type = "Group";
00044         name = type;
00045         id = getNewId(); // el identificador de subgrupo es independiente del de los Nodos
00046 }
00047 
00048 GroupNode::~GroupNode()
00049 {
00050         // quito los link internos, adaptando los links a los que servían
00051     foreach (Link *interLink, internalLinks.keys()) {
00052                 Link *exterLink = internalLinks.value(interLink);
00053                 if (exterLink) {
00054                         if (exterLink->toNode() == this)
00055                                 exterLink->changeToPoint(interLink->toNode(), interLink->toProp());
00056                         else
00057                                 exterLink->changeFromPoint(interLink->fromNode(), interLink->fromProp());
00058                 }
00059         delete interLink;
00060         }
00061 
00062         // saco a los hijos del grupo antes de borrar
00063     foreach (QGraphicsItem *child, children()) {
00064         child->setParentItem(this->parentItem());
00065                 child->scale(1.0 / SUBSCALE, 1.0 / SUBSCALE);
00066                 child->setPos(child->pos() / SUBSCALE);
00067         }
00068 }
00069 
00070 void GroupNode::addNode(Node *node)
00071 {
00072         node->setParentItem(this); //se actualiza cambio el padre
00073         node->scale(SUBSCALE, SUBSCALE); //se escala
00074         node->setPos(node->pos() * SUBSCALE); //se escala su posición para que no se separen cuando se unen
00075 
00076         // actualizamos las propiedades de entrada que tiene el grupo, en función de las que quedan lincadas hacia afuera
00077         foreach(Link *link, node->getInLinks()) {
00078                 changeLinkToGroup(link, true);
00079         }
00080 
00081         // actualizamos las propiedades de salida que tiene el grupo, en función de las que quedan lincadas hacia afuera
00082         foreach(Link *link, node->getOutLinks()) {
00083                 changeLinkToGroup(link, false);
00084         }
00085 }
00086 
00087 void GroupNode::changeLinkToGroup(Link *link, bool toNode)
00088 {
00089         Node *addedNode = (toNode) ? link->toNode() : link->fromNode();
00090         Node *linkedNode = (toNode) ? link->fromNode() : link->toNode();
00091         QString addedNodeProp = (toNode) ? link->toProp() : link->fromProp();
00092         QString linkedNodeProp = (toNode) ? link->fromProp() : link->toProp();
00093 
00094         bool isGroup = (dynamic_cast<GroupNode*>(addedNode)) ? true : false;
00095         if ( (isGroup) && (((GroupNode*)addedNode)->internalLinks.contains(link)) ) return; // no tengo en cuenta los link internos de los subgrupos
00096 
00097 //      std::cout << "LLamada para el link: " << QString("%1, addedNode:").arg((uint)link).toStdString() << QString("%1, addedNodeProp: ").arg(addedNode->getId()).toStdString() << addedNodeProp.toStdString() << ", linkedNode " << QString("%1, linkedNodeProp ").arg(linkedNode->getId()).toStdString() << linkedNodeProp.toStdString() << std::endl;
00098 
00099         if (linkedNode != this) { // si el otro nodo del link esta fuera del grupo, quiebro el link (pasando por el grupo)
00100                 int nodePos = addedNode->itemProp.getProperties().indexOf(addedNodeProp);
00101                 QString thisNameProp = ( (isGroup) ? QString("") : QString("%1: ").arg(addedNode->getId()) ) + addedNodeProp;
00102 //              std::cout << "añado para " << QString("%1:: ").arg(addedNode->getId()).toStdString() << " esta " << thisNameProp.toStdString() << std::endl;
00103                 insertProperty(thisNameProp, addedNode->itemProp.propertyType(nodePos), toNode, !toNode);
00104                 if (toNode) {
00105                         internalLinks.insert(new GroupLink(this, thisNameProp, addedNode, addedNodeProp, true, 0, scene()), link);
00106                         link->changeToPoint(this, thisNameProp);
00107                 }
00108                 else {
00109                         internalLinks.insert(new GroupLink(addedNode, addedNodeProp, this, thisNameProp, false, 0, scene()), link);
00110                         link->changeFromPoint(this, thisNameProp);
00111                 }
00112         }
00113         else { // sino borro la propiedad del grupo
00114 //              std::cout << "borro para " << QString("%1:: ").arg(addedNode->getId()).toStdString() << " esta " << linkedNodeProp.toStdString() << std::endl;
00115                 Link *interLink = internalLinks.key(link);
00116                 if (interLink) {
00117                         if (toNode) {
00118                                 link->changeFromPoint(interLink->fromNode(), interLink->fromProp());
00119                                 internalLinks.remove(interLink);
00120                                 removeProperty(interLink->toProp());
00121                                 delete interLink;
00122                         }
00123                         else {
00124                                 link->changeToPoint(interLink->toNode(), interLink->toProp());
00125                                 internalLinks.remove(interLink);
00126                                 removeProperty(interLink->fromProp());
00127                                 delete interLink;
00128                         }
00129                 }
00130 //              else std::cout << "ERROR-1" << std::endl;
00131         }
00132 }
00133 
00134 void GroupNode::removeLink(Link *link)
00135 {
00136         Node::removeLink(link);
00137 
00138         if (internalLinks.contains(link)) { // si era un link interno
00139                 internalLinks.remove(link);
00140         }
00141         if (internalLinks.values().contains(link)) { // si era un link asociado a un link interno
00142                 internalLinks[internalLinks.key(link)] = NULL;
00143         }
00144 }
00145 
00146 void GroupNode::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
00147 {
00148 //      abstractPaint = (option->levelOfDetail < 0.75) ? true : abstractMode;
00149 
00150         if (abstractPaint)
00151                 paintProps(painter, option, widget);
00152         else
00153                 paintGroup(painter, option, widget);
00154 }
00155 
00156 void GroupNode::paintProps(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * /* widget */)
00157 {
00158     QPen pen(myOutlineColor);
00159     if (option->state & QStyle::State_Selected) {
00160         pen.setStyle(Qt::DotLine);
00161         pen.setWidth(2);
00162     }
00163     painter->setPen(pen);
00164     painter->setBrush(myBackgroundColor);
00165 
00166     QRectF rect = outlineRect();
00167     painter->drawRoundRect(rect.adjusted(0.0, lineSpacing-2, 0.0, 0.0), roundness(rect.width()),
00168                            roundness(rect.height()));
00169 
00170     painter->setPen(myTextColor);
00171     painter->drawText(rect, Qt::AlignTop, name);
00172     painter->drawText(rect.adjusted(0.0, lineSpacing, 0.0, 0.0), Qt::AlignTop | Qt::AlignHCenter, myText);
00173 
00174         // muestra el boton redonde de abajo-derecha
00175         QRectF arrowRect((int)rect.right() - 15, (int)rect.bottom() - 10, 13, 8);
00176         drawCurvArrow(arrowRect, painter);
00177 
00178         // y pinta los puntos de unión a ambos lados del texto
00179     painter->setPen(Qt::white);
00180         painter->setBrush(Qt::black);
00181         for (int i = 0; i < numProp; i++) {
00182                 if (itemProp.isInput(i)) // si tiene enlace de entrada lo pinto
00183                 painter->drawEllipse((int)(rect.left() + lineSpacing*0.25), (int)(rect.top() + lineSpacing*(i + 1.25)), (int)(lineSpacing*0.5), (int)(lineSpacing*0.5));
00184                 if (itemProp.isOutput(i)) // // si tiene enlace de salida lo pinto
00185                         painter->drawEllipse((int)(rect.right() - lineSpacing*0.75), (int)(rect.top() + lineSpacing*(i + 1.25)), (int)(lineSpacing*0.5), (int)(lineSpacing*0.5));
00186         }
00187 
00188         // si uno de los puntos está marcado, lo pinta dependiendo de su validez
00189         if (markedPoint >= 0) {
00190                 if (markedValidity) painter->setBrush(Qt::green);
00191                 else                            painter->setBrush(Qt::red);
00192 
00193                 if (markedPoint < numProp)
00194                         painter->drawEllipse((int)(rect.left() + lineSpacing*0.25), (int)(rect.top() + lineSpacing*(markedPoint + 1.25)), (int)(lineSpacing*0.5), (int)(lineSpacing*0.5));
00195                 else
00196                         painter->drawEllipse((int)(rect.right() - lineSpacing*0.75), (int)(rect.top() + lineSpacing*(markedPoint - numProp + 1.25)), (int)(lineSpacing*0.5), (int)(lineSpacing*0.5));
00197         }
00198 }
00199 
00200 void GroupNode::paintGroup(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * /* widget */)
00201 {
00202     QPen pen(myOutlineColor);
00203     if (option->state & QStyle::State_Selected) {
00204         pen.setStyle(Qt::DotLine);
00205         pen.setWidth(2);
00206     }
00207     painter->setPen(pen);
00208     painter->setBrush(myBackgroundColor);
00209 
00210     QRectF rect = outlineRect();
00211     painter->drawRoundRect(rect.adjusted(0.0, lineSpacing-2, 0.0, .0), roundness(rect.width()),
00212                            roundness(rect.height()));
00213 
00214     painter->setPen(myTextColor);
00215     painter->drawText(rect, Qt::AlignTop, name);
00216 
00217         // muestra el boton redonde de abajo-derecha
00218         QRectF arrowRect((int)rect.right() - 15, (int)rect.bottom() - 10, 13, 8);
00219         drawCurvArrow(arrowRect, painter);
00220 
00221         // y pinta los puntos de unión a ambos lados del texto
00222     painter->setPen(Qt::white);
00223         painter->setBrush(Qt::black);
00224         for (int i = 0; i < numProp; i++) {
00225                 if (itemProp.isInput(i)) // si tiene enlace de entrada lo pinto
00226                 painter->drawEllipse((int)(rect.left() + lineSpacing*0.25), (int)(rect.top() + lineSpacing*(i + 1.25)), (int)(lineSpacing*0.5), (int)(lineSpacing*0.5));
00227                 if (itemProp.isOutput(i)) // // si tiene enlace de salida lo pinto
00228                         painter->drawEllipse((int)(rect.right() - lineSpacing*0.75), (int)(rect.top() + lineSpacing*(i + 1.25)), (int)(lineSpacing*0.5), (int)(lineSpacing*0.5));
00229         }
00230 
00231         // si uno de los puntos está marcado, lo pinta dependiendo de su validez
00232         if (markedPoint >= 0) {
00233                 if (markedValidity) painter->setBrush(Qt::green);
00234                 else                            painter->setBrush(Qt::red);
00235 
00236                 if (markedPoint < numProp)
00237                         painter->drawEllipse((int)(rect.left() + lineSpacing*0.25), (int)(rect.top() + lineSpacing*(markedPoint + 1.25)), (int)(lineSpacing*0.5), (int)(lineSpacing*0.5));
00238                 else
00239                         painter->drawEllipse((int)(rect.right() - lineSpacing*0.75), (int)(rect.top() + lineSpacing*(markedPoint - numProp + 1.25)), (int)(lineSpacing*0.5), (int)(lineSpacing*0.5));
00240         }
00241 
00242         // muestra los puntos de enlace a izquierda y derecha
00243     painter->setPen(Qt::white);
00244         painter->setBrush(Qt::black);
00245         painter->drawEllipse((int)rect.right() - 20 - (int)(lineSpacing*0.5), (int)rect.bottom() - 10, (int)(lineSpacing*0.5), (int)(lineSpacing*0.5));
00246         painter->drawEllipse((int)rect.left() + 20, (int)rect.bottom() - 10, (int)(lineSpacing*0.5), (int)(lineSpacing*0.5));
00247 }
00248 
00249 void GroupNode::drawCurvArrow(const QRectF rect, QPainter *painter)
00250 {
00251         QRectF smallCurveRect = QRectF(rect.x(), rect.y() - rect.height() * 2/5, rect.width() * 2/3, rect.height() * 4/5);
00252         QRectF bigCurveRect = QRectF(rect.x() - rect.width() * 1/3, rect.y() - rect.height() * 4/5, rect.width() * 4/3, rect.height() * 8/5);
00253 
00254         painter->drawArc(smallCurveRect, 270 * 16, 90 * 16);
00255         painter->drawArc(bigCurveRect, 270 * 16, 90 * 16);
00256 
00257         painter->drawLine(QPointF(rect.x() + rect.width() * 2/3, rect.y()), QPointF(rect.x() + rect.width() * 19/24, rect.y() + rect.height() * 1/5));
00258         painter->drawLine(QPointF(rect.x() + rect.width() * 19/24, rect.y() + rect.height() * 1/5), QPointF(rect.x() + rect.width(), rect.y()));
00259 
00260         painter->drawLine(QPointF(rect.x() + rect.width() * 1/3, rect.y() + rect.height() * 2/5), QPointF(rect.x() + rect.width() * 1/3, rect.y() + rect.height() * 1/5));
00261         painter->drawLine(QPointF(rect.x() + rect.width() * 1/3, rect.y() + rect.height() * 1/5), QPointF(rect.x(), rect.y() + rect.height() * 3/5));
00262         painter->drawLine(QPointF(rect.x(), rect.y() + rect.height() * 3/5), QPointF(rect.x() + rect.width() * 1/3, rect.y() + rect.height()));
00263         painter->drawLine(QPointF(rect.x() + rect.width() * 1/3, rect.y() + rect.height()), QPointF(rect.x() + rect.width() * 1/3, rect.y() + rect.height() * 4/5));
00264 }
00265 
00266 void GroupNode::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
00267 {
00268     QRectF rect = outlineRect();
00269         QPointF click = mapFromScene(event->scenePos());
00270 
00271         prepareHierarchy();
00272         if ((click.x() > rect.right() - 15) && (click.y() > rect.bottom() - 10)) {
00273                 // cambia de la vista de nodos unidos a los puntos laterales a la vista de los nombres de las propiedades de entrada y salida.
00274                 prepareGeometryChange();
00275                 abstractPaint = abstractPaint ? false : true;
00276                 foreach(QGraphicsItem *item, children()) {
00277                         if (dynamic_cast<Node *>(item))
00278                                 ((Node *)item)->setHide(abstractPaint);
00279                 }
00280                 update();
00281         }
00282         else {
00283                 QString text = QInputDialog::getText(event->widget(), tr("Edit Text"), tr("Enter new text:"), QLineEdit::Normal, name);
00284                 prepareGeometryChange();
00285                 name = text;
00286                 update();
00287         }
00288         updateLinksPos();
00289         updateHierarchy();
00290 }
00291 
00292 void GroupNode::abstractView(bool mode)
00293 {
00294         if (abstractPaint == mode) return;
00295 
00296         // cambia de la vista de nodos unidos a los puntos laterales a la vista de los nombres de las propiedades de entrada y salida.
00297         prepareHierarchy();
00298         prepareGeometryChange();
00299         abstractPaint = mode;
00300         foreach(QGraphicsItem *item, children()) {
00301                 if (dynamic_cast<Node *>(item))
00302                         ((Node *)item)->setHide(abstractPaint);
00303         }
00304         update();
00305         updateLinksPos();
00306         updateHierarchy();
00307 }
00308 
00309 void GroupNode::updateLinksPos()
00310 {
00311     foreach (Link *link, getLinks())
00312         link->trackNodes();
00313 
00314         foreach (QGraphicsItem *child, children()) {
00315                 Node *nodeChild = dynamic_cast<Node *>(child);
00316                 if (nodeChild)
00317                         nodeChild->updateLinksPos();
00318         }
00319 }
00320 
00321 QRectF GroupNode::outlineRect() const
00322 {
00323         if (abstractPaint) {
00324                 QFontMetricsF metrics = qApp->font();
00325                 double width = metrics.boundingRect(name).width();
00326         
00327                 const QList<QString> props = itemProp.getProperties();
00328                 for (int i = 0; i < props.size(); i++)
00329                         if (width < metrics.boundingRect(props[i]).width()) width = metrics.boundingRect(props[i]).width();
00330 
00331                 width += lineSpacing*4; // vale el ancho de la linea más grande más 4 espacios para los puntos de los lados
00332                 double height = (numProp+2)*lineSpacing; // vale el alto de todas las lineas
00333                 double xPos = childrenBoundingRect().left()-lineSpacing;
00334                 double yPos = childrenBoundingRect().top()-lineSpacing;
00335 
00336                 return QRectF(xPos, yPos, width, height);
00337         }
00338         else {
00339                 QRectF rect = childrenBoundingRect();
00340                 if (rect.height() < numProp*lineSpacing) rect.setHeight(numProp*lineSpacing);
00341                 return rect.adjusted(0.0-lineSpacing*2, 0.0-lineSpacing, lineSpacing*2, lineSpacing);
00342         }
00343 }
00344 
00345 
00346 void GroupNode::setHide(bool hide)
00347 {
00348         foreach(Link *link, getLinks())
00349                 if (internalLinks.contains(link))
00350                         link->setVisible(!hide && !abstractPaint);
00351                 else
00352                         link->setVisible(!hide);
00353 
00354         foreach(QGraphicsItem *item, children()) {
00355                 if (dynamic_cast<Node *>(item))
00356                         ((Node *)item)->setHide(hide || abstractPaint);
00357         }
00358 
00359         setVisible(!hide);
00360 }
00361 
00362 GroupInfo GroupNode::getInfo()
00363 {
00364         GroupInfo info(id, name);
00365 //      std::cout << "creo el grupo: " << QString("%1").arg(id).toStdString() << ", " << name.toStdString() << std::endl;
00366 
00367         foreach(QGraphicsItem *item, children()) {
00368                 if (dynamic_cast<Node *>(item)) {
00369                         if (dynamic_cast<GroupNode *>(item)) {
00370                                 info.addSubgroup( ((GroupNode *)item)->getId() );
00371 //                              std::cout << "     le añado el subgrupo: " << QString("%1").arg(((Node *)item)->getId()).toStdString() << std::endl;
00372                         }
00373                         else {
00374                                 info.addNode( ((Node *)item)->getId() );
00375 //                              std::cout << "     le añado el nodo: " << QString("%1").arg(((Node *)item)->getId()).toStdString() << std::endl;
00376                         }
00377                 }
00378         }
00379 
00380         foreach(Link *link, internalLinks.keys()) {
00381                 if (link->fromNode() == this) {
00382                         if (dynamic_cast<GroupNode *>(link->toNode()))
00383                                 info.addSubgroupLink(LinkInfo(link->toNode()->getId(), link->toProp(), true));
00384                         else
00385                                 info.addNodeLink(LinkInfo(link->toNode()->getId(), link->toProp(), true));
00386 //                      std::cout << "     le añado el link: " << QString("%1").arg(link->toNode()->getId()).toStdString() << "[" << link->toProp().toStdString() << "] (input)" << std::endl;
00387                 }
00388                 else {
00389                         if (dynamic_cast<GroupNode *>(link->fromNode()))
00390                                 info.addSubgroupLink(LinkInfo(link->fromNode()->getId(), link->fromProp(), false));
00391                         else
00392                                 info.addNodeLink(LinkInfo(link->fromNode()->getId(), link->fromProp(), false));
00393 //                      std::cout << "     le añado el link: " << QString("%1").arg(link->fromNode()->getId()).toStdString() << "[" << link->fromProp().toStdString() << "] (output)" << std::endl;
00394                 }
00395         }
00396 
00397         info.setPos(pos());
00398 //      std::cout << "     con la posición: [" << QString("%1").arg(pos().x()).toStdString() << ", " << QString("%1").arg(pos().y()).toStdString() << "]" << std::endl;
00399 
00400         return info;
00401 }
00402 
00403 



QVision framework. PARP research group, copyright 2007, 2008.