examples/qvdesigner0.2/slate/slatewindow.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 #include <QDebug>
00024 #include <iostream>
00025 
00026 #include "slatewindow.h"
00027 #include "sinclink.h"
00028 #include "asinclink.h"
00029 #include "node.h"
00030 #include "groupnode.h"
00031 #include "inputnode.h"
00032 #include "middlenode.h"
00033 #include "outputnode.h"
00034 #include "slateview.h"
00035 #include "insertaction.h"
00036 
00037 #include "facade/designergui.h"
00038 #include "facade/itemproperties.h"
00039 
00040 
00041 SlateWindow::SlateWindow(DesignerGUI *desig, QWidget * parent): QMainWindow(parent), designer(desig)
00042 {
00043     scene = new QGraphicsScene(0, 0, 800, 600);
00044 
00045     view = new SlateView;
00046     view->setScene(scene);
00047     view->setDragMode(QGraphicsView::RubberBandDrag);
00048     view->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
00049     view->setContextMenuPolicy(Qt::ActionsContextMenu);
00050     setCentralWidget(view);
00051 
00052     minZ = 0;
00053     maxZ = 0;
00054     seqNumber = 0;
00055 
00056     createMenus();
00057     createToolBars();
00058 
00059     connect(scene, SIGNAL(selectionChanged()), this, SLOT(updateActions()));
00060 
00061         scene->setBackgroundBrush(QPixmap(":/images/background1.png"));
00062 
00063     setWindowTitle(tr("Slate"));
00064     updateActions();
00065 }
00066 
00067 void SlateWindow::closeEvent(QCloseEvent *)
00068         {
00069         emit closed();
00070         }
00071 
00073 bool SlateWindow::createLink(Node *fromNode, int fromPoint, Node *toNode, int toPoint)
00074 {
00075         // si está en ejecución no se permite lincar
00076         if (stopAction->isEnabled()) return false;
00077 
00078         if (addSLinkAction->isChecked())
00079                 designer->addLink(fromNode->getName(), fromNode->propName(fromPoint), toNode->getName(), toNode->propName(toPoint), true);
00080         else
00081                 designer->addLink(fromNode->getName(), fromNode->propName(fromPoint), toNode->getName(), toNode->propName(toPoint), false);
00082 return true;
00083 }
00084 
00085 void SlateWindow::addLinkLine(QString fromNode, QString fromProp, QString toNode, QString toProp, bool sinc)
00086 {
00087         Link *link;
00088         QString linkName = fromNode + "[" + fromProp + "] => " + toNode + "[" + toProp + "]";
00089 
00090         if ( insertNodes.contains(fromNode) && insertNodes.contains(toNode) ) {
00091                 ItemNode *from = insertNodes.value(fromNode);
00092                 ItemNode *to = insertNodes.value(toNode);
00093 
00094                 if (sinc) {
00095                         link = new SincLink(from, fromProp, to, toProp, 0, scene);
00096                         insertLinks.insert(linkName, link);
00097                         link->trackNodes();
00098                 }
00099                 else {
00100                         link = new AsincLink(from, fromProp, to, toProp, 0, scene);
00101                         insertLinks.insert(linkName, link);
00102                         link->trackNodes();
00103                 }
00104         }
00105 }
00106 
00107 void SlateWindow::delLinkLine(QString fromNode, QString fromProp, QString toNode, QString toProp)
00108 {
00109         QString linkName = fromNode + "[" + fromProp + "] => " + toNode + "[" + toProp + "]";
00110         if (insertLinks.contains(linkName)) {
00111                 delete insertLinks.value(linkName);
00112                 insertLinks.remove(linkName);
00113         }
00114 }
00115 
00116 void SlateWindow::insertItem(QString type)
00117 {
00118         QString name = type + QString("_%1").arg(seqNumber);
00119 
00120         designer->addItem(type, name);
00121 }
00122 
00123 void SlateWindow::addItemNode(QString type, QString name, ItemProperties *item)
00124 {
00126         if (designer->getInputItemTypes().contains(type)) {
00127                 InputNode *node = new InputNode(*item, name, this, 0, scene);
00128                 insertNodes.insert(node->getName(), node);
00129                 setupNode(node);
00130                 if (insertNodesPos.contains(name))
00131                         node->setPos(insertNodesPos.value(name));
00132                 else
00133                         insertNodesPos.insert(name, node->pos());
00134         }
00135         else if (designer->getMiddleItemTypes().contains(type)) {
00136                 MiddleNode *node = new MiddleNode(*item, name, this, 0, scene);
00137                 insertNodes.insert(node->getName(), node);
00138                 setupNode(node);
00139                 if (insertNodesPos.contains(name))
00140                         node->setPos(insertNodesPos.value(name));
00141                 else
00142                         insertNodesPos.insert(name, node->pos());
00143         }
00144         else if (designer->getOutputItemTypes().contains(type)) {
00145                 OutputNode *node = new OutputNode(*item, name, this, 0, scene);
00146                 insertNodes.insert(node->getName(), node);
00147                 setupNode(node);
00148                 if (insertNodesPos.contains(name))
00149                         node->setPos(insertNodesPos.value(name));
00150                 else
00151                         insertNodesPos.insert(name, node->pos());
00152         }
00153 }
00154 
00155 void SlateWindow::delItemNode(QString name)
00156 {
00157         if (insertNodes.contains(name)) {
00158                 insertNodesPos.remove(name);
00159                 insertNodesPos.insert(name, insertNodes.value(name)->pos());
00160                 delete insertNodes.value(name);
00161                 insertNodes.remove(name);
00162         }
00163 }
00164 
00165 void SlateWindow::addProperty(QString srcName, QString propName, int type, bool in, bool out)
00166 {
00167         if (insertNodes.contains(srcName)) {
00168                 insertNodes.value(srcName)->insertProperty(propName, type, in, out);
00169         }
00170 }
00171 
00172 void SlateWindow::delProperty(QString srcName, QString propName)
00173 {
00174         if (insertNodes.contains(srcName)) {
00175                 insertNodes.value(srcName)->removeProperty(propName);
00176         }
00177 }
00178 
00179 void SlateWindow::del()
00180 {
00181         foreach(QGraphicsItem *item, scene->selectedItems()) {
00182         Link *link = dynamic_cast<Link *>(item);
00183                 Node *node = dynamic_cast<Node *>(item);
00184 
00185         if (link && (link->fromNode() != NULL) && (link->toNode() != NULL)) {
00186                         designer->delLink(link->fromNode()->getName(), link->fromProp(), link->toNode()->getName(), link->toProp());
00187         }
00188                 else if (node) {
00189                         designer->delItem(node->getName());
00190                 }
00191     }
00192 }
00193 
00194 void SlateWindow::join()
00195 {
00196         QList<QGraphicsItem *> group = selectedNodeGroup();
00197 
00198         // creo el nuevo grupo
00199     GroupNode *node = new GroupNode(0, scene);
00200     node->setText("Group");
00201 
00202         // si los items seleccionados estaban ya dentro de otro grupo, este se añade a el (sino a la escena)
00203         if ( group.first() && group.first()->parentItem() ) {
00204                 GroupNode *parentNode = dynamic_cast<GroupNode *>(group.first()->parentItem());
00205                 if (parentNode) parentNode->addNode(node);
00206         }
00207 
00208         // para cada item si es un nodo lo añado al nuevo grupo
00209         foreach (QGraphicsItem *item, group) {
00210                 Node *itemNode = dynamic_cast<Node *>(item);
00211                 if (itemNode) {
00212                         node->addNode(itemNode);
00213 
00214                         //se actualiza el tamaño de sus links
00215                         //y si la otra punta no se va a meter al grupo pasa a formar parte de este
00216                         foreach (Link *link, itemNode->getInLinks()) {
00217                                 Node *other = link->fromNode();
00218                                 if (group.contains(other)) {
00219                                         QPen pen = link->pen();
00220                                         pen.setWidthF(pen.width()*0.5);
00221                                         link->setPen(pen);
00222                                         link->update();
00223                                 }
00224 /*                              else {
00225                                                 link->changeToNode(node);
00226                                 }*/
00227                         }
00228                         foreach (Link *link, itemNode->getOutLinks()) {
00229                                 Node *other = link->toNode();
00230                                 if (group.contains(other)) {
00231                                         QPen pen = link->pen();
00232                                         pen.setWidthF(pen.width()*0.5);
00233                                         link->setPen(pen);
00234                                         link->update();
00235                                 }
00236 /*                              else {
00237                                                 link->changeFromNode(node);
00238                                 }*/
00239                         }
00240                         itemNode->updateLinksPos(); // para que se actualiza la posición y tamaño de los links del nodo
00241                 }
00242         }
00243 }
00244 
00245 void SlateWindow::bringToFront()
00246 {
00247     maxZ += 2; // dos, uno para el y otro para sus link que serán: el + 1
00248     setZValue(maxZ);
00249 }
00250 
00251 void SlateWindow::sendToBack()
00252 {
00253     minZ += 2; // dos, uno para el y otro para sus link que serán: el + 1
00254     setZValue(minZ);
00255 }
00256 
00257 void SlateWindow::run()
00258 {
00259         foreach (QAction *action, insertMenu->actions()) {
00260                 action->setEnabled(false);
00261         }
00262 
00263         deleteAction->setEnabled(false);
00264 
00265         runAction->setEnabled(false);
00266         stopAction->setEnabled(true);
00267         designer->run();
00268 }
00269 
00270 void SlateWindow::stop()
00271 {
00272         designer->stop();
00273         stopAction->setEnabled(false);
00274         runAction->setChecked(false);
00275         runAction->setEnabled(true);
00276 
00277     bool hasSelection = !scene->selectedItems().isEmpty();
00278         deleteAction->setEnabled(hasSelection);
00279 
00280         foreach (QAction *action, insertMenu->actions()) {
00281                 action->setEnabled(true);
00282         }
00283 }
00284 
00285 void SlateWindow::setZValue(int z)
00286 {
00287     Node *node = selectedNode();
00288     if (node) {
00289         node->setZValue(z);
00290                 node->updateLinksPos();
00291         }
00292 }
00293 
00294 void SlateWindow::showProperties()
00295 {
00296     Node *node = selectedNode();
00297 
00298     if (node) showProperties(node);
00299 }
00300 
00301 void SlateWindow::showProperties(Node *node)
00302 {
00303         designer->showProperties(node->getName());
00304 }
00305 
00306 void SlateWindow::updateActions()
00307 {
00308     bool hasSelection = !scene->selectedItems().isEmpty();
00309     bool isNode = (selectedNode() != 0);
00310 //      bool isNodePair = (selectedNodePair() != NodePair());
00311 //      bool isNodeGroup = (selectedNodeGroup() != QList<QGraphicsItem *>());
00312 
00313     joinAction->setEnabled(false); //joinAction->setEnabled(isNodeGroup);
00314         // si está en ejecución no se permite borrar
00315     if (runAction->isEnabled()) deleteAction->setEnabled(hasSelection);
00316     bringToFrontAction->setEnabled(isNode);
00317     sendToBackAction->setEnabled(isNode);
00318     propertiesAction->setEnabled(isNode);
00319 
00320     foreach (QAction *action, view->actions())
00321         view->removeAction(action);
00322 
00323     foreach (QAction *action, editMenu->actions()) {
00324         if (action->isEnabled())
00325             view->addAction(action);
00326     }
00327 
00328         // añado las acciones del menu insertar al desplegable del botón derecho (de dos formas distintas)
00329         QAction *ma = new QAction(tr("..."), this);
00330         ma->setMenu(new QMenu("insert_menu", this));
00331         ma->menu()->addMenu(insertMenu);
00332         view->addAction(ma);
00333 
00334         foreach (QAction *action, insertMenu->actions()) {
00335             view->addAction(action);
00336     }
00337 }
00338 
00339 void SlateWindow::setupNode(Node *node)
00340 {
00341     node->setPos( QPoint(10 + (seqNumber % 10) * 6, 20 + (seqNumber % 10) * 6) );
00342     ++seqNumber;
00343 
00344     scene->clearSelection();
00345     node->setSelected(true);
00346     bringToFront();
00347 }
00348 
00349 void SlateWindow::createMenus()
00350 {
00352     exitAction = new QAction(tr("E&xit"), this);
00353     exitAction->setShortcut(tr("Ctrl+Q"));
00354     connect(exitAction, SIGNAL(triggered()), this, SLOT(close()));
00355 
00356     addSLinkAction = new QAction(tr("Add &Synchronous Link"), this);
00357     addSLinkAction->setIcon(QIcon(":/images/slink.png"));
00358     addSLinkAction->setShortcut(tr("Ctrl+S"));
00359         addSLinkAction->setCheckable(true); // para que se mantenga pulsada
00360 
00361     addALinkAction = new QAction(tr("Add &Asynchronous Link"), this);
00362     addALinkAction->setIcon(QIcon(":/images/alink.png"));
00363     addALinkAction->setShortcut(tr("Ctrl+A"));
00364         addALinkAction->setCheckable(true); // para que se mantenga pulsada
00365         addALinkAction->setChecked(true); // por defecto está seleccionado asincrono
00366 
00367         // con esto conseguimos meterlas en un grupo exclusivo, o una o la otra
00368         linkGroup = new QActionGroup(this);
00369         linkGroup->setExclusive(true);
00370         linkGroup->addAction(addSLinkAction);
00371         linkGroup->addAction(addALinkAction);
00372 
00373 
00374     joinAction = new QAction(tr("&Join"), this);
00375     joinAction->setIcon(QIcon(":/images/join.png"));
00376     joinAction->setShortcut(tr("Ctrl+J"));
00377     connect(joinAction, SIGNAL(triggered()), this, SLOT(join()));
00378 
00379     deleteAction = new QAction(tr("&Delete"), this);
00380     deleteAction->setIcon(QIcon(":/images/delete.png"));
00381     deleteAction->setShortcut(tr("Del"));
00382         deleteAction->setEnabled(false);
00383     connect(deleteAction, SIGNAL(triggered()), this, SLOT(del()));
00384 
00385     bringToFrontAction = new QAction(tr("Bring to &Front"), this);
00386     bringToFrontAction->setIcon(QIcon(":/images/bringtofront.png"));
00387     connect(bringToFrontAction, SIGNAL(triggered()),
00388             this, SLOT(bringToFront()));
00389 
00390     sendToBackAction = new QAction(tr("&Send to Back"), this);
00391     sendToBackAction->setIcon(QIcon(":/images/sendtoback.png"));
00392     connect(sendToBackAction, SIGNAL(triggered()),
00393             this, SLOT(sendToBack()));
00394 
00395     propertiesAction = new QAction(tr("P&roperties..."), this);
00396     connect(propertiesAction, SIGNAL(triggered()),
00397             this, SLOT(showProperties()));
00398 
00399     runAction = new QAction(tr("&Run"), this);
00400     runAction->setIcon(QIcon(":/images/run.png"));
00401         runAction->setCheckable(true); // para que se mantenga pulsada
00402         runAction->setChecked(true); // porque comienza en ejecución
00403         runAction->setEnabled(false); // porque comienza en ejecución
00404     connect(runAction, SIGNAL(triggered()),
00405             this, SLOT(run()));
00406 
00407     stopAction = new QAction(tr("&Run"), this);
00408     stopAction->setIcon(QIcon(":/images/stop.png"));
00409         stopAction->setEnabled(true); // porque comienza en ejecución
00410     connect(stopAction, SIGNAL(triggered()),
00411             this, SLOT(stop()));
00412 
00413 
00414     fileMenu = menuBar()->addMenu(tr("&File"));
00415     fileMenu->addAction(exitAction);
00416 
00417     editMenu = menuBar()->addMenu(tr("&Edit"));
00418     editMenu->addAction(addSLinkAction);
00419     editMenu->addAction(addALinkAction);
00420 
00421     editMenu->addSeparator();
00422     editMenu->addAction(joinAction);
00423     editMenu->addAction(deleteAction);
00424     editMenu->addSeparator();
00425     editMenu->addAction(bringToFrontAction);
00426     editMenu->addAction(sendToBackAction);
00427     editMenu->addSeparator();
00428     editMenu->addAction(propertiesAction);
00429 
00430 
00431         // generamos el insert menu a partir de la lista que nos devuelve el controlador
00432         insertMenu = menuBar()->addMenu(tr("&Insert"));
00433 
00434         foreach (QString item, designer->getInputItemTypes())
00435                 {
00436                 InsertAction *action = new InsertAction(item, this);
00437                 action->setEnabled(false);
00438                 connect(action, SIGNAL(triggered(QString)), this, SLOT(insertItem(QString)));
00439                 insertMenu->addAction(action);
00440                 }
00441 
00442         foreach (QString item, designer->getMiddleItemTypes())
00443                 {
00444                 InsertAction *action = new InsertAction(item, this);
00445                 action->setEnabled(false);
00446                 connect(action, SIGNAL(triggered(QString)), this, SLOT(insertItem(QString)));
00447                 insertMenu->addAction(action);
00448                 }
00449 
00450         foreach (QString item, designer->getOutputItemTypes())
00451                 {
00452                 InsertAction *action = new InsertAction(item, this);
00453                 action->setEnabled(false);
00454                 connect(action, SIGNAL(triggered(QString)), this, SLOT(insertItem(QString)));
00455                 insertMenu->addAction(action);
00456                 }
00457 }
00458 
00459 void SlateWindow::createToolBars()
00460 {
00461     editToolBar = addToolBar(tr("Edit"));
00462     editToolBar->addAction(addSLinkAction);
00463     editToolBar->addAction(addALinkAction);
00464     editToolBar->addAction(joinAction);
00465     editToolBar->addAction(deleteAction);
00466     editToolBar->addSeparator();
00467     editToolBar->addSeparator();
00468     editToolBar->addAction(bringToFrontAction);
00469     editToolBar->addAction(sendToBackAction);
00470 
00471         editToolBar->addSeparator();
00472         editToolBar->addAction(runAction);
00473         editToolBar->addAction(stopAction);
00474 }
00475 
00476 Node *SlateWindow::selectedNode() const
00477 {
00478     QList<QGraphicsItem *> items = scene->selectedItems();
00479     if (items.count() == 1) {
00480         return dynamic_cast<Node *>(items.first());
00481     } else {
00482         return 0;
00483     }
00484 }
00485 
00486 Link *SlateWindow::selectedLink() const
00487 {
00488     QList<QGraphicsItem *> items = scene->selectedItems();
00489     if (items.count() == 1) {
00490         return dynamic_cast<Link *>(items.first());
00491     } else {
00492         return 0;
00493     }
00494 }
00495 
00496 SlateWindow::NodePair SlateWindow::selectedNodePair() const
00497 {
00498     QList<QGraphicsItem *> items = scene->selectedItems();
00499     if (items.count() == 2) {
00500         Node *first = dynamic_cast<Node *>(items.first());
00501         Node *second = dynamic_cast<Node *>(items.last());
00502         if (first && second)
00503             return NodePair(first, second);
00504     }
00505     return NodePair();
00506 }
00507 
00508 // devuelve la lista de onlyParents seleccionados, si son almenos 2 comparten padre (tambien devuelve los link de dicha lista)
00509 QList<QGraphicsItem *> SlateWindow::selectedNodeGroup() const
00510 {
00511     QList<QGraphicsItem *> items = onlyParents(scene->selectedItems());
00512 
00513         //si quedan almemos dos
00514     if (items.count() > 1) {
00515                 // y si comparten padre
00516                 QGraphicsItem * parent = 0;
00517                 QMutableListIterator<QGraphicsItem *> i(items);
00518                 if (i.hasNext()) parent = i.next()->parentItem();
00519 
00520                 while (i.hasNext())
00521                         if ( i.next()->parentItem() != parent) return QList<QGraphicsItem *>();
00522 
00523                 return items;
00524     }
00525     return QList<QGraphicsItem *>();
00526 }
00527 
00528 // obtiene los QGraphicsItem de la lista cuyo padre no pertenece a la lista
00529 QList<QGraphicsItem *> SlateWindow::onlyParents(QList<QGraphicsItem *> items) const
00530 {
00531     QList<QGraphicsItem *> parentItems;
00532 
00533     QMutableListIterator<QGraphicsItem *> i(items);
00534     while (i.hasNext()) {
00535                 i.next();
00536                 bool parent = true;
00537                 QMutableListIterator<QGraphicsItem *> j(items);
00538                 while (j.hasNext()) {
00539                         j.next();
00540                         if ( (i.value()) && (i.value()->parentItem() == j.value()) ) {
00541                                 parent = false;
00542                                 break;
00543                         }
00544                 }
00545                 if (parent) parentItems.append(i.value());
00546     }
00547 
00548         return parentItems;
00549 }
00550