PARP Research Group University of Murcia, Spain


src/qvgui/qvdesigner/slate/itemnode.cpp

00001 /*
00002  *      Copyright (C) 2008. 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 "itemnode.h"
00025 #include "slatewindow.h"
00026 
00027 ItemNode::ItemNode(ItemProperties item, QString _name, SlateWindow *wind, QGraphicsItem * parent, QGraphicsScene * scene): Node(parent, scene),
00028         itemProp(item), numProp(itemProp.getProperties().size()), window(wind), clickedPoint(-1), externalMarkedPoint(-1), markedPoint(-1)
00029 {
00030     myTextColor = Qt::darkGreen;
00031     myOutlineColor = Qt::darkBlue;
00032     myBackgroundColor = Qt::white;
00033 
00034     setFlags(ItemIsMovable | ItemIsSelectable);
00035 
00036 
00037         // obtiene el rectángulo exterior
00038     QFontMetricsF metrics = qApp->font();
00039         lineSpacing = metrics.lineSpacing();
00040     outlinerect = metrics.boundingRect(itemProp.getType());
00041 
00042         const QList<QString> props = itemProp.getProperties();
00043         for (int i = 0; i < props.size(); i++)
00044                 outlinerect |= metrics.boundingRect(props[i]);
00045 
00046     outlinerect.adjust(0.0, 0.0, lineSpacing*4, props.size()*lineSpacing);
00047 
00048 
00049         // obtiene el texto a escribir
00050         QString nameAndProp(itemProp.getType());
00051         for (int i = 0; i < props.size(); i++)
00052                 nameAndProp += "\n" + props[i];
00053 
00054         setText(nameAndProp);
00055 
00056         type = itemProp.getType();
00057         name = _name;
00058 }
00059 
00060 ItemNode::~ItemNode()
00061 {
00062      foreach (Link *link, getLinks())
00063         delete link;
00064 }
00065 
00066 void ItemNode::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget * /* widget */)
00067 {
00068     QPen pen(myOutlineColor);
00069     if (option->state & QStyle::State_Selected) {
00070         pen.setStyle(Qt::DotLine);
00071         pen.setWidth(2);
00072     }
00073     painter->setPen(pen);
00074     painter->setBrush(myBackgroundColor);
00075 
00076     QRectF rect = outlineRect();
00077         painter->drawRect(rect);
00078         painter->drawLine((int)rect.left(), (int)(rect.top() + lineSpacing), (int)rect.right(), (int)(rect.top() + lineSpacing));
00079 
00080     painter->setPen(myTextColor);
00081     painter->drawText(rect, Qt::AlignTop | Qt::AlignHCenter, myText);
00082 
00083         // y pinta los puntos de unión a ambos lados del texto
00084     painter->setPen(Qt::white);
00085         painter->setBrush(Qt::black);
00086         for (int i = 0; i < numProp; i++)
00087         {
00088                 if (itemProp.isInput(i)) // si tiene enlace de entrada lo pinto
00089                 painter->drawEllipse((int)(rect.left() + lineSpacing*0.25), (int)(rect.top() + lineSpacing*(i + 1.25)), (int)(lineSpacing*0.5), (int)(lineSpacing*0.5));
00090                 if (itemProp.isOutput(i)) // // si tiene enlace de salida lo pinto
00091                         painter->drawEllipse((int)(rect.right() - lineSpacing*0.75), (int)(rect.top() + lineSpacing*(i + 1.25)), (int)(lineSpacing*0.5), (int)(lineSpacing*0.5));
00092         }
00093 
00094         // si uno de los puntos está marcado, lo pinta dependiendo de su validez
00095         if (markedPoint >= 0)
00096         {
00097                 if (markedValidity) painter->setBrush(Qt::green);
00098                 else                            painter->setBrush(Qt::red);
00099 
00100                 if (markedPoint < numProp)
00101                         painter->drawEllipse((int)(rect.left() + lineSpacing*0.25), (int)(rect.top() + lineSpacing*(markedPoint + 1.25)), (int)(lineSpacing*0.5), (int)(lineSpacing*0.5));
00102                 else
00103                         painter->drawEllipse((int)(rect.right() - lineSpacing*0.75), (int)(rect.top() + lineSpacing*(markedPoint - numProp + 1.25)), (int)(lineSpacing*0.5), (int)(lineSpacing*0.5));
00104         }
00105 }
00106 
00107 // devuelve la posición (en cordenadas de la scena) del centro del punto indicado por el índice point, si se sale del rango devuelve la posición del nodo
00108 QPointF ItemNode::scenePointPos(int point) const
00109 {
00110         QRectF rect = outlineRect();
00111 
00112         if (point < 0) return this->scenePos();
00113         if (point < numProp) return mapToScene( QPointF(rect.left() + lineSpacing*0.5, rect.top() + lineSpacing*(point + 1.5)) );
00114         if (point < 2 * numProp) return mapToScene( QPointF(rect.right() - lineSpacing*0.5, rect.top() + lineSpacing*(point-numProp + 1.5)) );
00115         return this->scenePos();
00116 }
00117 
00118 QPointF ItemNode::scenePointPos(QString name, bool in) const
00119 {
00120         return scenePointPos(propPoint(name, in));
00121 }
00122 
00123 
00124 int ItemNode::numProps() const
00125 {
00126         return numProp;
00127 }
00128 
00129 
00130 void ItemNode::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *)
00131 {
00132         window->showProperties(this);
00133 }
00134 
00135 // estas funciones son para conseguir que los puntos se pinten de rojo cunado se pasa sobre ellos
00136 // y que se cree la linea al arrastar y creen los link cuando suelte en una posición correcta
00137 void ItemNode::mousePressEvent(QGraphicsSceneMouseEvent * event)
00138 {
00139         clickedPoint = pointAt(event->pos());
00140 
00141         if (clickedPoint < 0)
00142         {
00143                 // si no coincidía con un punto mueve el item normalmente
00144                 Node::mousePressEvent(event);
00145         }
00146         else
00147         {
00148                 // si coincide con un punto crea una línea indicadora del posible enlace
00149                 line = new QGraphicsLineItem( QLineF(event->scenePos(), event->scenePos()) );
00150                 line->setZValue(1000);
00151                 scene()->addItem(line);
00152         }
00153 }
00154 
00155 void ItemNode::mouseMoveEvent(QGraphicsSceneMouseEvent * event)
00156 {
00157         if (clickedPoint < 0)
00158         {
00159                 // si no coincidía con un punto mueve el item normalmente
00160                 Node::mouseMoveEvent(event);
00161         }
00162         else
00163         {
00164                 // desplazo la línea como el mouse, pero un poco (0.5) más corta, ya que sino cuando busquemos el item de justo
00165                 // debajo del cursor nos puede dar la própia línea en vez del que buscamos.
00166                 QLineF shortLine = QLineF(line->line().p1(), event->scenePos());
00167                 shortLine.setLength(shortLine.length() - 0.5);
00168                 line->setLine(shortLine);
00169 
00170                 // si había un punto marcado lo desmarco
00171                 if (externalMarkedPoint >= 0)
00172                 {
00173                         externalMarkedItem->unmarkPoint();
00174                         externalMarkedPoint = -1;
00175                 }
00176 
00177                 // y si está sobre otro punto válido para el enlace, lo marco
00178                 ItemNode *target = dynamic_cast<ItemNode *>(scene()->itemAt(event->scenePos())); // en un futuro tendrá que hacer el cast a Node
00179                 if (target)
00180                 {
00181                         // obtengo el punto concreto a partir de las coordenadas locales al otro item
00182                         int pointPos = target->pointAt(target->mapFromScene(event->scenePos()));
00183                         if (pointPos >= 0)
00184                         { // marco el nuevo punto en función de la validez del enlace que generaría
00185                                 externalMarkedPoint = pointPos;
00186                                 externalMarkedItem = target;
00187                                 externalMarkedItem->markPoint(externalMarkedPoint, isValidLink(this, clickedPoint, target, pointPos));
00188                         }
00189                 }
00190         }
00191 }
00192 
00193 void ItemNode::mouseReleaseEvent(QGraphicsSceneMouseEvent * event)
00194 {
00195         if (clickedPoint < 0)
00196         {
00197                 // si coincide con un punto válidosi no coincidía con un punto mueve el item normalmente
00198                 Node::mouseReleaseEvent(event);
00199         }
00200         else
00201         {
00202                 // elimino la línea indicadora
00203                 delete(line);
00204 
00205                 // si había un punto marcado lo desmarco
00206                 if (externalMarkedPoint >= 0)
00207                 {
00208                         externalMarkedItem->unmarkPoint();
00209                         externalMarkedPoint = -1;
00210                 }
00211 
00212                 // si está sobre un punto válido creo el enlace
00213                 ItemNode *target = dynamic_cast<ItemNode *>(scene()->itemAt(event->scenePos())); // en un futuro tendrá que hacer el cast a Node
00214                 if (target)
00215                 {
00216                         // obtiene el punto concreto a partir de las coordenadas locales al otro item
00217                         int pointPos = target->pointAt(target->mapFromScene(event->scenePos()));
00218 
00219                         // si es valido crea el enlace entre clickedPoint de this y pointPos de target
00220                         if ( (pointPos >= 0) && isValidLink(this, clickedPoint, target, pointPos) )
00221                         {
00222                                 if (clickedPoint >= numProp) // si clickedPoint es un punto de salida, el enlace parte de this
00223                                         window->createLink(this, clickedPoint, target, pointPos);
00224                                 else // si no, parte de target
00225                                         window->createLink(target, pointPos, this, clickedPoint);
00226                         }
00227                 }
00228 
00229                 clickedPoint = -1;
00230         }
00231 }
00232 
00233 void ItemNode::markPoint(int point, bool validity)
00234 {
00235         markedPoint = point;
00236         markedValidity = validity;
00237         update();
00238 }
00239 
00240 void ItemNode::unmarkPoint()
00241 {
00242         markedPoint = -1;
00243         update();
00244 }
00245 
00246 // indica si un enlace sería válido
00248 bool ItemNode::isValidLink(ItemNode *fromNode, int fromPoint, ItemNode *toNode, int toPoint) const
00249 {
00250         int fromNumProp = fromNode->numProp;
00251         int toNumProp = toNode->numProp;
00252 
00253         if (fromNode == toNode) return false; // si son iguales
00254         if (fromNode->parentItem() != toNode->parentItem()) return false; // si no están al mismo nivel
00255         if ( (fromPoint < 0) || (toPoint < 0) ) return false; // si alguno no es un punto
00256         if ( (fromPoint < fromNumProp) && (toPoint < toNumProp) ) return false; // si los dos son de entrada
00257         if ( (fromPoint >= fromNumProp) && (toPoint >= toNumProp) ) return false; // si los dos son de salida
00258         if ( (fromPoint >= 2 * fromNumProp) || (toPoint >= 2 * toNumProp) ) return false; // si alguno no es un punto existente
00259         return true;
00260 }
00261 
00262 int ItemNode::insertPos() const
00263 {
00264         return numProp;
00265 }
00266 
00267 void ItemNode::insertProperty(QString name, int type, bool input, bool output)
00268 {
00269         insertProperty(insertPos(), name, type, input, output);
00270 }
00271 
00272 void ItemNode::insertProperty(int pos, QString name, int type, bool input, bool output)
00273 {
00274         prepareGeometryChange();
00275         itemProp.insertProperty(pos, name, type, input, output);
00276         numProp = itemProp.getProperties().size();
00277 
00278         // recalcula el rectángulo exterior
00279     QFontMetricsF metrics = qApp->font();
00280         lineSpacing = metrics.lineSpacing();
00281     outlinerect = metrics.boundingRect(itemProp.getType());
00282 
00283         const QList<QString> props = itemProp.getProperties();
00284         for (int i = 0; i < props.size(); i++)
00285                 outlinerect |= metrics.boundingRect(props[i]);
00286 
00287     outlinerect.adjust(0.0, 0.0, lineSpacing*4, props.size()*metrics.lineSpacing());
00288 
00289 
00290         // recalcula el texto a escribir
00291         QRectF rect = outlineRect();
00292         QString nameAndProp(itemProp.getType());
00293         for (int i = 0; i < props.size(); i++)
00294                 nameAndProp += "\n" + props[i];
00295 
00296         setText(nameAndProp);
00297         updateLinksPos();
00298         update();
00299 }
00300 
00301 void ItemNode::removeProperty(QString name)
00302 {
00303         prepareGeometryChange();
00304         itemProp.deleteProperty(name);
00305         numProp = itemProp.getProperties().size();
00306 
00307         // recalcula el rectángulo exterior
00308     QFontMetricsF metrics = qApp->font();
00309         lineSpacing = metrics.lineSpacing();
00310     outlinerect = metrics.boundingRect(itemProp.getType());
00311 
00312         const QList<QString> props = itemProp.getProperties();
00313         for (int i = 0; i < props.size(); i++)
00314                 outlinerect |= metrics.boundingRect(props[i]);
00315 
00316     outlinerect.adjust(0.0, 0.0, lineSpacing*4, props.size()*metrics.lineSpacing());
00317 
00318 
00319         // recalcula el texto a escribir
00320         QRectF rect = outlineRect();
00321         QString nameAndProp(itemProp.getType());
00322         for (int i = 0; i < props.size(); i++)
00323                 nameAndProp += "\n" + props[i];
00324 
00325         setText(nameAndProp);
00326         updateLinksPos();
00327         update();
00328 }
00329 
00330 void ItemNode::deleteProperty(int pos)
00331 {
00332         prepareGeometryChange();
00333         itemProp.deleteProperty(pos);
00334         numProp = itemProp.getProperties().size();
00335 
00336         // recalcula el rectángulo exterior
00337     QFontMetricsF metrics = qApp->font();
00338         lineSpacing = metrics.lineSpacing();
00339     outlinerect = metrics.boundingRect(itemProp.getType());
00340 
00341         const QList<QString> props = itemProp.getProperties();
00342         for (int i = 0; i < props.size(); i++)
00343                 outlinerect |= metrics.boundingRect(props[i]);
00344 
00345     outlinerect.adjust(0.0, 0.0, lineSpacing*4, props.size()*metrics.lineSpacing());
00346 
00347 
00348         // recalcula el texto a escribir
00349         QRectF rect = outlineRect();
00350         QString nameAndProp(itemProp.getType());
00351         for (int i = 0; i < props.size(); i++)
00352                 nameAndProp += "\n" + props[i];
00353 
00354         setText(nameAndProp);
00355         updateLinksPos();
00356         update();
00357 }
00358 /*
00359 void ItemNode::addNewProp(ItemNode *toNode, int toPoint)
00360 {
00361         prepareGeometryChange();
00362         insertProperty(numProp - 1, toNode->propName(toPoint), toNode->propType(toPoint), true, false);
00363         update();
00364 }
00365 */
00366 void ItemNode::delLastProp()
00367 {
00368         prepareGeometryChange();
00369         deleteProperty(numProp - 2);
00370         update();
00371 }
00372 
00373 QVariant ItemNode::itemChange(GraphicsItemChange change,
00374                           const QVariant &value)
00375 {
00376     if (change == ItemPositionHasChanged)
00377                 updateLinksPos();
00378 
00379     return QGraphicsItem::itemChange(change, value);
00380 }
00381 
00382 void ItemNode::updateLinksPos()
00383 {
00384         foreach (Link *link, getLinks())
00385                 link->trackNodes();
00386 }
00387 
00388 QRectF ItemNode::outlineRect() const
00389 {
00390         return outlinerect;
00391 }
00392 
00393 int ItemNode::roundness(double size) const
00394 {
00395     const int Diameter = 12;
00396     return 100 * Diameter / int(size);
00397 }
00398 
00399 // Devuelve el indice del punto de enlace que hay debajo de la posición "pos", en coordenadas del workernode;
00400 // empieza a contar desde 0 y los de los de salida los cuenta a continuación de los de entrada;
00401 // si "pos" no está sobre ningún punto de enlace válido devuelve -1.
00402 int ItemNode::pointAt(QPointF pos) const
00403 {
00404         QRectF rect = outlineRect();
00405         if ((pos.x() >= rect.left() + lineSpacing*0.25) && (pos.x() <= rect.left() + lineSpacing*0.75)) // si está en la columna de entrada
00406         {
00407                 if (pos.y() - rect.top() >= lineSpacing*1.25) // si sestá por debajo del título
00408                 {
00409                         int relativePos = (int)(pos.y() - rect.top() - lineSpacing*1.25);
00410                         if (relativePos % (int)lineSpacing <= lineSpacing*0.5) // si está sobre el lugar del punto
00411                                 if (itemProp.isInput((int)(relativePos / lineSpacing))) return (int)(relativePos / lineSpacing); // si la propiedad tiene enlace de entrada, es valido
00412                 }
00413         }
00414         else if ((pos.x() >= rect.right() - lineSpacing*0.75) && (pos.x() <= rect.right() - lineSpacing*0.25)) // si está en la columna de salida
00415         {
00416                 if (pos.y() - rect.top() >= lineSpacing*1.25) // si está por debajo del título
00417                 {
00418                         int relativePos = (int)(pos.y() - rect.top() - lineSpacing*1.25);
00419                         if (relativePos % (int)lineSpacing <= lineSpacing*0.5) // si está sobre el lugar del punto
00420                                 if (itemProp.isOutput((int)(relativePos / lineSpacing))) return (int)(relativePos / lineSpacing + numProp);  // si la propiedad tiene enlace de salida, es valido
00421                 }
00422         }
00423         return -1;
00424 }
00425 
00426 QString ItemNode::propName(int point) const
00427 {
00428         if ( (point < 0) && (point >= 2 * numProp) ) return QString(); // si point se sale de rango
00429 
00430         if (point >= numProp) point = point - numProp; // ignoro si es de entrada o salida
00431         return itemProp.getProperties()[point];
00432 }
00433 
00434 int ItemNode::propPoint(QString name, bool in) const
00435 {
00436         int pos = itemProp.getProperties().indexOf(name);
00437         if (pos < 0) return pos;
00438         if (!in) return pos += numProp;
00439         return pos;
00440 }
00441 
00442 int ItemNode::propType(int point) const
00443 {
00444         if ( (point < 0) && (point >= 2 * numProp) ) return 0; // si point se sale de rango devuelvo "no type"
00445 
00446         if (point >= numProp) point = point - numProp; // ignoro si es de entrada o salida
00447         return itemProp.propertyType(point);
00448 }
00449 
00450 



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