# # # add_dir "libs" # # add_dir "libs/qanava" # # add_dir "libs/qanava/build" # # add_dir "libs/qanava/build/qan" # # add_dir "libs/qanava/src" # # add_dir "libs/qanava/src/ui" # # add_file "guitone/res/forms/ancestry_graph.ui" # content [7e7f4aa6d5b1dc185e0b0ac8f5a05b5ed4c3bcf3] # # add_file "guitone/src/model/Graph.cpp" # content [35fe7a0ea20b3254f120f8c39b60e925af131097] # # add_file "guitone/src/model/Graph.h" # content [a2007e2c3765bc07a103e2172fc4340ad1eb997e] # # add_file "guitone/src/view/dialogs/AncestryGraph.cpp" # content [4d168d008bd0e6043f946d4c5728a84370b3eea0] # # add_file "guitone/src/view/dialogs/AncestryGraph.h" # content [8b3764f98c90d5c7fab0d8c529e97b92b92e4fc6] # # add_file "libs/libs.pro" # content [ca5044adbf8828221942c937a63e7b40638b4aea] # # add_file "libs/qanava/build/build.pro" # content [fea130f425d6c83a83f2843af84ced77e6e31b79] # # add_file "libs/qanava/build/qan/qan.pro" # content [d5ef733dbb9849633e57e042ad112df5df9185c9] # # add_file "libs/qanava/qanava.pro" # content [4b9d41233daf1c2fa2e1ed293eefed0b4d0b7252] # # add_file "libs/qanava/src/qanConfig.h" # content [3888e646fb6334e4e5fe0c89bf6ebeeea47e2736] # # add_file "libs/qanava/src/qanController.cpp" # content [2d05912d39496d3127db41a8099af62ba291288f] # # add_file "libs/qanava/src/qanController.h" # content [2f743c33036f4d4c664e491b786f1797cb2bd1f2] # # add_file "libs/qanava/src/qanEdge.cpp" # content [a6915d18182ee672105121cfeae13d2049a9805b] # # add_file "libs/qanava/src/qanEdge.h" # content [a5585d277d33f6bc9d1169e42a5b40224855efbd] # # add_file "libs/qanava/src/qanGraph.h" # content [f8253ed3de7c8c86c81686a8d558f1ded5d69f36] # # add_file "libs/qanava/src/qanGraph.hpp" # content [71f74c300686f7de87b05f7ef7a9974cb1e8d3d2] # # add_file "libs/qanava/src/qanGraphItemModel.cpp" # content [5589991ed19351c18ffe7c9b456a8bffcd5ce8a4] # # add_file "libs/qanava/src/qanGraphItemModel.h" # content [ad90da3f02f4b547d29c7cb8b378c7fb613781f6] # # add_file "libs/qanava/src/qanGraphItemView.cpp" # content [2650092cb13e790fe4e6e81a7476978b0fa8a2b6] # # add_file "libs/qanava/src/qanGraphItemView.h" # content [4ff6a1e3a993c364ed5dfb4eaee72c8919814ad0] # # add_file "libs/qanava/src/qanGraphicsView.cpp" # content [97aaefe368fdc15c63d293e9d40ef1630414fbea] # # add_file "libs/qanava/src/qanGraphicsView.h" # content [6f582f9c7e90a38cb818d892cd999b9b1d86969f] # # add_file "libs/qanava/src/qanGrid.h" # content [f98f7f90a1bf147fca5ccf385d06e3aaaf57b339] # # add_file "libs/qanava/src/qanGridItem.cpp" # content [a521aaca80231c4a3cc1cad98105e155687d6ec2] # # add_file "libs/qanava/src/qanGridItem.h" # content [14da4617240de3277b84e376e7652288b1e6ab07] # # add_file "libs/qanava/src/qanItemGeom.cpp" # content [4a53eff9f53c160ca690661510384b6266bcfa42] # # add_file "libs/qanava/src/qanItemGeom.h" # content [4a6d84dc2dd56022f8e1ef64327adc2e6f21bd6b] # # add_file "libs/qanava/src/qanLayout.cpp" # content [511aa22d12419f13286818772fc60ed1968f49a9] # # add_file "libs/qanava/src/qanLayout.h" # content [2f96b5189a86b669e54c072e9c621074ef76654f] # # add_file "libs/qanava/src/qanNode.cpp" # content [c8b9d52caf4a8465cc270b911eaeb020c91fb888] # # add_file "libs/qanava/src/qanNode.h" # content [5db78823a601423d53024a55532763ee5babcecd] # # add_file "libs/qanava/src/qanRepository.cpp" # content [aa82d566624ec78117cf1e9c488c056d492d383b] # # add_file "libs/qanava/src/qanRepository.h" # content [6ff55c5f0778c3d9c5befa905c9499497c70f738] # # add_file "libs/qanava/src/qanStyle.cpp" # content [e37b580f9069abc280b206b7c8ac6b548c4495b1] # # add_file "libs/qanava/src/qanStyle.h" # content [2a4c2efef1897cac58bad371e7d27bddab3f78a8] # # add_file "libs/qanava/src/qanTimeTree.cpp" # content [00ddcd23a8433e6b4cc8491051fb48ccc1b7ed13] # # add_file "libs/qanava/src/qanTimeTree.h" # content [0d123a00ba535d82f6e5707aa5b7fe1a33496d39] # # add_file "libs/qanava/src/qanVectorf.h" # content [9e02d50a23ddb577123d14ebb92fcb70220a04ea] # # add_file "libs/qanava/src/ui/uiNodesItemModel.cpp" # content [ce8ceeda6ffddcb4cc72a0ec843daec6550b337d] # # add_file "libs/qanava/src/ui/uiNodesItemModel.h" # content [c40b4d3800f8d2b99be3c2072ee15d9a8fff7945] # # add_file "libs/qanava/src/ui/uiStyleModel.cpp" # content [d93ce3111a03fcf80b343e6360915ea7d2b7d49b] # # add_file "libs/qanava/src/ui/uiStyleModel.h" # content [8f0c369542dfceb2099554d7d064a1169a72a668] # # add_file "libs/qanava/src/ui/uiStyleView.cpp" # content [ce449871831206f5bf55d96e801dae928444c284] # # add_file "libs/qanava/src/ui/uiStyleView.h" # content [1a99cd669c8250ec1a2e607bfdb7f811c3d2bdb2] # # add_file "libs/qanava/src/ui/uiUtil.h" # content [13f19d1cf4651914501e855d898f0ec26b5d54ab] # # patch "guitone/guitone.pro" # from [89ffc63cab6945f7c72dd91d072bfa7425ae9d33] # to [067355a047c27064dc3c1455a34579bf6554f0af] # # patch "guitone/res/forms/main_window.ui" # from [afaa9b341aa26361f28b3c8d857fa07473987959] # to [cfd66fbddffb6167fcf8240eb95c7a3e93042c87] # # patch "guitone/res/forms/preferences.ui" # from [50dcda14fc2c4f5147800b73b015bf988efd52f4] # to [5c8f89400f2da1fc0833738eb97b81ef5210caa2] # # patch "guitone/res/i18n/guitone_de.ts" # from [8a9f18c91d4da422143541bbe523025eb7a280d3] # to [afd926dcc7d88b42891b983cdeeff9f3d0f7bc8c] # # patch "guitone/src/view/MainWindow.cpp" # from [7c815e194238287d2253ae81d76627724e6fa733] # to [f0a536dfb4a7be366b0b1896dae37dacdad378eb] # # patch "guitone/src/view/MainWindow.h" # from [bf1f12efde46c96a0194070d00a9677c8f29e0cd] # to [777cac1855851bd2b95dfbc1ecab9c13e4ce6a82] # # patch "guitone.pro" # from [fa381aa2095c52bb3876fb5ffff635ce8c30ab32] # to [2297c37664f54796350e838adac98828c250f58a] # ============================================================ --- guitone/res/forms/ancestry_graph.ui 7e7f4aa6d5b1dc185e0b0ac8f5a05b5ed4c3bcf3 +++ guitone/res/forms/ancestry_graph.ui 7e7f4aa6d5b1dc185e0b0ac8f5a05b5ed4c3bcf3 @@ -0,0 +1,92 @@ + + AncestryGraph + + + Qt::NonModal + + + + 0 + 0 + 550 + 373 + + + + Ancestry Graph + + + + 9 + + + 6 + + + + + 0 + + + 6 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok + + + + + + + + + + + buttonBox + accepted() + AncestryGraph + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AncestryGraph + reject() + + + 316 + 260 + + + 286 + 274 + + + + + ============================================================ --- guitone/src/model/Graph.cpp 35fe7a0ea20b3254f120f8c39b60e925af131097 +++ guitone/src/model/Graph.cpp 35fe7a0ea20b3254f120f8c39b60e925af131097 @@ -0,0 +1,108 @@ +/*************************************************************************** + * Copyright (C) 2007 by Thomas Keller * + * address@hidden * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "Graph.h" + +Graph::Graph(QObject *parent) : QObject(parent) +{ + graph = new qan::Graph(); + mtnDelegate = new MonotoneDelegate(this); +} + +Graph::~Graph() +{ + delete graph; + delete mtnDelegate; +} + +bool Graph::readGraph() +{ + QStringList cmd; + cmd.append("graph"); + + return mtnDelegate->triggerCommand(cmd); +} + +void Graph::parseOutput() +{ + QStringList revisionList = AutomateCommand::data.split("\n"); + + QMap nodeMap; + QMap parentMap; + + qan::Node *curNode, *parentNode; + + // 1st pass: create node objects for every rev and add it to the graph + for (int i=0, j=revisionList.size(); i 0); + QString rev = revs.takeFirst(); + // for whatever reason inserting every node as root node creates a + // big memory hole + curNode = graph->insertNode(rev, -1, NULL); + + // remember those for the 2nd pass + nodeMap[rev] = curNode; + parentMap[rev] = revs; + + if (i % 100 == 0) + { + qDebug("added %d nodes", i); + } + } + + qDebug("Graph: created nodes"); + + revisionList.clear(); + QStringList singleRevs = nodeMap.keys(); + + // 2nd pass: now that we have all nodes create the edges + for (int i=0, j=singleRevs.size(); iinsertEdge(*parentNode, *curNode); + } + } + if (i % 100 == 0) + { + qDebug("added edges for %d nodes", i); + } + } + + qDebug("Graph: created edges"); + + graph->generateRootNodes(); + + qDebug("Graph: generated root nodes"); + + emit graphCreated(); +} + ============================================================ --- guitone/src/model/Graph.h a2007e2c3765bc07a103e2172fc4340ad1eb997e +++ guitone/src/model/Graph.h a2007e2c3765bc07a103e2172fc4340ad1eb997e @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2007 by Thomas Keller * + * address@hidden * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef GRAPH_H +#define GRAPH_H + +#include "AutomateCommand.h" +#include "MonotoneDelegate.h" +#include "qanGraph.h" + +#include + +class Graph : public QObject, public AutomateCommand +{ + Q_OBJECT +public: + Graph(QObject *); + virtual ~Graph(); + + qan::Graph * getGraph() { return graph; } + +public slots: + bool readGraph(); + + +signals: + void graphCreated(); + +private: + void parseOutput(); + MonotoneDelegate * mtnDelegate; + qan::Graph * graph; +}; + +#endif ============================================================ --- guitone/src/view/dialogs/AncestryGraph.cpp 4d168d008bd0e6043f946d4c5728a84370b3eea0 +++ guitone/src/view/dialogs/AncestryGraph.cpp 4d168d008bd0e6043f946d4c5728a84370b3eea0 @@ -0,0 +1,82 @@ +/*************************************************************************** + * Copyright (C) 2007 by Thomas Keller * + * address@hidden * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "AncestryGraph.h" +#include "qanGrid.h" + +AncestryGraph::AncestryGraph(QWidget* parent) + : Dialog(parent) +{ + setupUi(this); + + graph = new Graph(this); + graph->readGraph(); + + QHBoxLayout *hbox = new QHBoxLayout( graphFrame ); + hbox->setMargin(0); + hbox->setSpacing(2); + + labelWait = new QLabel(tr("Please wait while the graph is being created...")); + labelWait->setAlignment(Qt::AlignCenter); + + graphItemView = new qan::GraphItemView(graphFrame); + graphItemView->setModel(graph->getGraph()); + graphItemView->hide(); + + hbox->addWidget(labelWait); + hbox->addWidget(graphItemView); + + // create and layout the graphview if all nodes have been created + connect(graph, SIGNAL(graphCreated()), + this, SLOT(createGraphView())); +} + +AncestryGraph::~AncestryGraph() +{ + if (graph) delete graph; + if (graphItemView) delete graphItemView; + if (labelWait) delete labelWait; +} + +void AncestryGraph::createGraphView( ) +{ + qan::VectorF origin(2); + origin(0) = 10.f; + origin(1) = 10.f; + + qan::VectorF spacing(2); + spacing(0) = 120.f; + spacing(1) = 55.f; + + qan::Layout* layout = new qan::DirectedTree( + origin, + spacing, + qan::DirectedTree::HORIZONTAL + ); + + graphItemView->setGraphLayout(layout); + graphItemView->layoutGraph(); + + // hide the wait label and show the graph view + labelWait->hide(); + graphItemView->show(); + +} + ============================================================ --- guitone/src/view/dialogs/AncestryGraph.h 8b3764f98c90d5c7fab0d8c529e97b92b92e4fc6 +++ guitone/src/view/dialogs/AncestryGraph.h 8b3764f98c90d5c7fab0d8c529e97b92b92e4fc6 @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright (C) 2007 by Thomas Keller * + * address@hidden * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef ANCESTRY_GRAPH_H +#define ANCESTRY_GRAPH_H + +#include + +#include "ui_ancestry_graph.h" + +#include "Dialog.h" +#include "Graph.h" + +#include "qanGraphItemView.h" +#include "qanLayout.h" + +class AncestryGraph : public Dialog, private Ui::AncestryGraph +{ + Q_OBJECT + +public: + AncestryGraph(QWidget*); + ~AncestryGraph(); + +private: + QHBoxLayout *hbox; + QLabel *labelWait; + Graph *graph; + qan::GraphItemView *graphItemView; + +protected slots: + + void createGraphView(); +}; + +#endif + ============================================================ --- libs/libs.pro ca5044adbf8828221942c937a63e7b40638b4aea +++ libs/libs.pro ca5044adbf8828221942c937a63e7b40638b4aea @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = qanava + ============================================================ --- libs/qanava/build/build.pro fea130f425d6c83a83f2843af84ced77e6e31b79 +++ libs/qanava/build/build.pro fea130f425d6c83a83f2843af84ced77e6e31b79 @@ -0,0 +1,2 @@ +TEMPLATE=subdirs +SUBDIRS=qan ============================================================ --- libs/qanava/build/qan/qan.pro d5ef733dbb9849633e57e042ad112df5df9185c9 +++ libs/qanava/build/qan/qan.pro d5ef733dbb9849633e57e042ad112df5df9185c9 @@ -0,0 +1,61 @@ + +LANGUAGE = C++ +DEFINES += QANAVA +TARGET = qanava +DESTDIR = ../ +CONFIG += debug \ + warn_on \ + qt \ + thread staticlib +QT += xml +TEMPLATE = lib +HEADERS += ../../src/qanConfig.h \ + ../../src/qanEdge.h \ + ../../src/qanGraph.h \ + ../../src/qanGraph.hpp \ + ../../src/qanGrid.h \ + ../../src/qanLayout.h \ + ../../src/qanNode.h \ + ../../src/qanVectorf.h \ + ../../src/qanRepository.h \ + ../../src/qanTimeTree.h \ + ../../src/qanController.h \ + ../../src/qanItemGeom.h \ + ../../src/qanStyle.h \ + ../../src/qanGraphicsView.h \ + ../../src/qanGraphItemView.h \ + ../../src/qanGraphItemModel.h \ + ../../src/qanGridItem.h \ + ../../src/ui/uiStyleView.h \ + ../../src/ui/uiStyleModel.h \ + ../../src/ui/uiNodesItemModel.h \ + ../../src/ui/uiUtil.h + +SOURCES += ../../src/qanEdge.cpp \ + ../../src/qanLayout.cpp \ + ../../src/qanNode.cpp \ + ../../src/qanRepository.cpp \ + ../../src/qanTimeTree.cpp \ + ../../src/qanController.cpp \ + ../../src/qanItemGeom.cpp \ + ../../src/qanStyle.cpp \ + ../../src/qanGraphicsView.cpp \ + ../../src/qanGraphItemView.cpp \ + ../../src/qanGraphItemModel.cpp \ + ../../src/qanGridItem.cpp \ + ../../src/ui/uiStyleView.cpp \ + ../../src/ui/uiStyleModel.cpp \ + ../../src/ui/uiNodesItemModel.cpp + +macx | unix { + QMAKE_CXXFLAGS_WARN_ON += -Wno-unused-parameter + UI_DIR = .ui + MOC_DIR = .moc + OBJECTS_DIR = .obj + DEFINES += QANAVA_UNIX + CONFIG += ppc x86 +} +win32{ + OBJECTS_DIR = ./Debug +} + ============================================================ --- libs/qanava/qanava.pro 4b9d41233daf1c2fa2e1ed293eefed0b4d0b7252 +++ libs/qanava/qanava.pro 4b9d41233daf1c2fa2e1ed293eefed0b4d0b7252 @@ -0,0 +1,2 @@ +TEMPLATE=subdirs +SUBDIRS=build ============================================================ --- libs/qanava/src/qanConfig.h 3888e646fb6334e4e5fe0c89bf6ebeeea47e2736 +++ libs/qanava/src/qanConfig.h 3888e646fb6334e4e5fe0c89bf6ebeeea47e2736 @@ -0,0 +1,57 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava and LTM software. +// +// \file utlConfig.h +// \author Benoit Autheman (address@hidden) +// \date 2004 September 21 +//----------------------------------------------------------------------------- + + +#ifndef qan_utlConfig_h +#define qan_utlConfig_h + + +// Configure this file either for LTM or Qanava +#undef QAN_UTL_ROOT_NAMESPACE +#define QAN_UTL_ROOT_NAMESPACE qan // Uncomment for Qanava +//#define QAN_UTL_ROOT_NAMESPACE ltm // Uncomment for LTM + +#include + +#ifndef QANAVA_UNIX + +#pragma warning( disable:4786 ) // VC6: Too long template name in debug informations + +#pragma warning( disable:4251 ) // VC7.1: BOOST dll interface warning + +#pragma warning( disable:4275 ) // VC7.1: BOOST dll interface warning + +#pragma warning( disable:4290 ) // VC7.1: Explicit C++ exception specification + +#pragma warning( disable:4100 ) // VC7.1: Unreferenced formal parameter + +#pragma warning( disable:4244 ) // VC7.1: BOOST date and time warning + +#endif + +#endif // utlConfig_h + ============================================================ --- libs/qanava/src/qanController.cpp 2d05912d39496d3127db41a8099af62ba291288f +++ libs/qanava/src/qanController.cpp 2d05912d39496d3127db41a8099af62ba291288f @@ -0,0 +1,529 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file canController.cpp +// \author Benoit Autheman (address@hidden) +// \date 2003 August 13 +//----------------------------------------------------------------------------- + + +// Qanava headers +#include "./qanController.h" +#include "./qanGraphicsView.h" + + +// QT headers +#include +#include +#include +#include +#include +#include +#include + + +namespace qan { // ::qan + + +bool Controller::Manager::keyPressEvent( QKeyEvent* e ) +{ + for ( Controller::Manager::iterator controllerIter = begin( ); controllerIter != end( ); controllerIter++ ) + { + if ( ( *controllerIter )->keyPressEvent( e ) ) + return true; + } + return false; +} + +bool Controller::Manager::mousePressEvent( QMouseEvent* e ) +{ + for ( Controller::Manager::iterator controllerIter = begin( ); controllerIter != end( ); controllerIter++ ) + { + if ( ( *controllerIter )->mousePressEvent( e ) ) + return true; + } + return false; +} + +bool Controller::Manager::mouseReleaseEvent( QMouseEvent* e ) +{ + for ( Controller::Manager::iterator controllerIter = begin( ); controllerIter != end( ); controllerIter++ ) + { + if ( ( *controllerIter )->mouseReleaseEvent( e ) ) + return true; + } + return false; +} + +bool Controller::Manager::mouseMoveEvent( QMouseEvent* e ) +{ + for ( Controller::Manager::iterator controllerIter = begin( ); controllerIter != end( ); controllerIter++ ) + { + if ( ( *controllerIter )->mouseMoveEvent( e ) ) + return true; + } + return false; +} + +bool Controller::Manager::mouseDoubleClickEvent( QMouseEvent* e ) +{ + for ( Controller::Manager::iterator controllerIter = begin( ); controllerIter != end( ); controllerIter++ ) + { + if ( ( *controllerIter )->mouseDoubleClickEvent( e ) ) + return true; + } + return false; +} + +bool Controller::Manager::wheelEvent( QWheelEvent* e ) +{ + for ( Controller::Manager::iterator controllerIter = begin( ); controllerIter != end( ); controllerIter++ ) + { + if ( ( *controllerIter )->wheelEvent( e ) ) + return true; + } + return false; +} + +Controller* Controller::Manager::getController( QString name ) +{ + for ( Controller::Manager::iterator controllerIter = begin( ); controllerIter != end( ); controllerIter++ ) + { + if ( ( *controllerIter )->getName( ) == name ) + return ( *controllerIter ); + } + return 0; +} + +void Controller::Manager::registerController( Controller* controller ) +{ + if ( controller == 0 ) + return; + + push_back( controller ); + QAction* controllerAction = controller->getAction( ); + if ( controllerAction->isCheckable( ) ) + _actionGroup->addAction( controllerAction ); // Only one checkable action active at a time +} + +Controller::Controller( QString name, GraphicsView& graphicsView ) : + _graphicsView( graphicsView ), + _action( 0 ), + _name( name ) +{ + +} + + +/* PanController Constructor/Destructor *///----------------------------------- +PanController::PanController( GraphicsView& graphicsView ) : + Controller( "pan_controller", graphicsView ), + _keyboardNavigation( true ), + _keyboardNavigationIntensity( 10.0 ), + _start( 0., 0. ) +{ + QAction* action = new QAction( this ); + action->setCheckable( true ); + action->setText( "Pan" ); + action->setVisible( true ); + action->setIcon( QIcon( "images/qanava_pan.png" ) ); + connect( action, SIGNAL( toggled( bool ) ), this, SLOT( toggled( bool ) ) ); + setAction( action ); + + setKeyboardNavigation( true ); +} + +void PanController::toggled( bool state ) +{ + setMode( state ? PAN : NONE ); +} +//----------------------------------------------------------------------------- + + + +/* PanController Keyboard Management *///-------------------------------------- +bool PanController::keyPressEvent( QKeyEvent* e ) +{ + getGraphicsView( ).setFocusPolicy( Qt::ClickFocus ); + if ( getKeyboardNavigation( ) ) + { + switch ( e->key( ) ) + { + case Qt::Key_Left: + getGraphicsView( ).translate( ( int )-_keyboardNavigationIntensity, 0 ); + break; + + case Qt::Key_Up: + getGraphicsView( ).translate( 0, ( int )-_keyboardNavigationIntensity ); + break; + + case Qt::Key_Right: + getGraphicsView( ).translate( ( int )_keyboardNavigationIntensity, 0 ); + break; + + case Qt::Key_Down: + getGraphicsView( ).translate( 0, ( int )_keyboardNavigationIntensity ); + break; + + default: + e->ignore( ); + break; + } + } + return false; +} + +/*! To use keyboard navigation, ensure that this widget parent widget has at least a 'Click' + focus policy (in Designer or with the QWidget::setFocusPolicy() method). +*/ +void PanController::setKeyboardNavigation( bool state ) +{ + _keyboardNavigation = state; + if ( state ) + { + getGraphicsView( ).viewport( )->setFocusPolicy( Qt::StrongFocus ); + getGraphicsView( ).setFocusPolicy( Qt::StrongFocus ); + } +} +//----------------------------------------------------------------------------- + + + +/* PanController Mouse Management *///----------------------------------------- +bool PanController::mousePressEvent( QMouseEvent* e ) +{ + // Navigation must not occurs if a node is under the mouse + QPointF p = getGraphicsView( ).mapToScene( e->pos( ) ); + QList< QGraphicsItem* > items = getGraphicsView( ).scene( )->items( p ); + if ( items.size( ) == 0 ) + { // TODO: prendre les elements de grille en compte (et tous ceux qui ne sont pas manipulables) + if ( getMode( ) == PAN ) + { + _start = e->pos( ); + getGraphicsView( ).viewport( )->setCursor( QCursor( Qt::SizeAllCursor ) ); + setState( PANNING ); + return true; + } + } + + return false; +} + +bool PanController::mouseReleaseEvent( QMouseEvent* e ) +{ + getGraphicsView( ).viewport( )->setCursor( QCursor( Qt::ArrowCursor ) ); + + if ( getMode( ) == PAN && getState( ) == PANNING ) + setState( NONE ); // Stop panning until next click + + return false; +} + +bool PanController::mouseMoveEvent( QMouseEvent* e ) +{ + if ( getMode( ) == PAN && getState( ) == PANNING ) + { + QPointF p = e->pos( ); // No mapping, we move scrollbars in their coordinate system + //QPointF p = getGraphicsView( ).mapToScene( e->pos( ) ); + + double accel = 1.0; + double dx = ( _start.x( ) - p.x( ) ) * accel; + double dy = ( _start.y( ) - p.y( ) ) * accel; + + int valueY = ( int )( getGraphicsView( ).verticalScrollBar( )->value( ) + dy ); + getGraphicsView( ).verticalScrollBar( )->setValue( valueY ); + int valueX = ( int )( getGraphicsView( ).horizontalScrollBar( )->value( ) + dx ); + getGraphicsView( ).horizontalScrollBar( )->setValue( valueX ); + + _start = p; + return true; + } + return false; +} +//----------------------------------------------------------------------------- + + + +/* ZoomWindowController Constructor/Destructor *///------------------------------- +ZoomWindowController::ZoomWindowController( GraphicsView& graphicsView ) : + Controller( "zoom_window_controller", graphicsView ), + _start( 0., 0. ), + _zoomRectItem( 0 ) +{ + QAction* action = new QAction( this ); + action->setCheckable( true ); + action->setText( "Zoom Window" ); + action->setVisible( true ); + action->setIcon( QIcon( "images/qanava_zoomwindow.png" ) ); + connect( action, SIGNAL( toggled( bool ) ), this, SLOT( toggled( bool ) ) ); + setAction( action ); + + _zoomRectItem = new QGraphicsRectItem( QRectF( 0., 0., 50., 50. ), 0, getGraphicsView( ).scene( ) ); + _zoomRectItem->setZValue( 100. ); + QPen p; + p.setStyle( Qt::DotLine ); + p.setWidth( 1 ); + p.setColor( QColor( 55, 55, 55 ) ); + _zoomRectItem->setPen( p ); + _zoomRectItem->hide( ); +} + +void ZoomWindowController::toggled( bool state ) +{ + setMode( state ? ZOOM : NONE ); +} +//----------------------------------------------------------------------------- + + + +/* ZoomWindowController Zooming Management *///-------------------------------- +bool ZoomWindowController::mousePressEvent( QMouseEvent* e ) +{ + // Navigation must not occurs if a node is under the mouse + QPointF p = getGraphicsView( ).mapToScene( e->pos( ) ); + QList< QGraphicsItem* > items = getGraphicsView( ).scene( )->items( p ); + if ( items.size( ) == 0 ) + { // TODO: prendre les elements de grille en compte (et tous ceux qui ne sont pas manipulables) + if ( getMode( ) == ZOOM ) + { + _start = getGraphicsView( ).mapToScene( e->pos( ) ); + setState( ZOOMING ); + return true; + } + } + + return false; +} + +bool ZoomWindowController::mouseReleaseEvent( QMouseEvent* e ) +{ + if ( getMode( ) == ZOOM && getState( ) == ZOOMING ) + { + getGraphicsView( ).fitInView( _zoomRectItem->rect( ), Qt::KeepAspectRatioByExpanding ); + _zoomRectItem->hide( ); + setState( NONE ); // Stop zooming until next click + return true; + } + + return false; +} + +bool ZoomWindowController::mouseMoveEvent( QMouseEvent* e ) +{ + if ( getMode( ) == ZOOM && getState( ) == ZOOMING && _zoomRectItem != 0 ) + { + QPointF p = getGraphicsView( ).mapToScene( e->pos( ) ); + + QRectF r( _start.x( ), _start.y( ), + p.x( ) - _start.x( ), p.y( ) - _start.y( ) ); + _zoomRectItem->setRect( r ); + _zoomRectItem->show( ); + return true; + } + return false; +} +//----------------------------------------------------------------------------- + + + +/* ZoomController Constructor/Destructor *///---------------------------------- +ZoomController::ZoomController( GraphicsView& graphicsView ) : + Controller( "zoom_controller", graphicsView ), + _actionZoomIn( new QAction( this ) ), + _actionZoomOut( new QAction( this ) ) +{ + QAction* action = new QAction( this ); + action->setCheckable( true ); + action->setText( "Zoom" ); + action->setVisible( true ); + action->setIcon( QIcon( "images/qanava_zoom.png" ) ); + setAction( action ); + + _actionZoomIn->setText( "Zoom In" ); + _actionZoomIn->setVisible( true ); + _actionZoomIn->setIcon( QIcon( "images/qanava_zoomin.png" ) ); + connect( _actionZoomIn, SIGNAL( triggered( bool ) ), this, SLOT( zoomIn( bool ) ) ); + + _actionZoomOut->setText( "Zoom Out" ); + _actionZoomOut->setVisible( true ); + _actionZoomOut->setIcon( QIcon( "images/qanava_zoomout.png" ) ); + connect( _actionZoomOut, SIGNAL( triggered( bool ) ), this, SLOT( zoomOut( bool ) ) ); +} +//----------------------------------------------------------------------------- + + + +/* ZoomController Zooming Management *///-------------------------------------- +void ZoomController::zoomIn( bool state ) +{ + getGraphicsView( ).setZoom( getGraphicsView( ).getZoom( ) + 0.2 ); +} + +void ZoomController::zoomOut( bool state ) +{ + getGraphicsView( ).setZoom( getGraphicsView( ).getZoom( ) - 0.2 ); +} + +bool ZoomController::wheelEvent( QWheelEvent* e ) +{ + return false; +} + +QAction* ZoomController::getAction( QString name ) +{ + if ( name == "zoom_in" ) + return _actionZoomIn; + else if ( name == "zoom_out" ) + return _actionZoomOut; + + return 0; +} +//----------------------------------------------------------------------------- + + + +/* Selection Controller Management *///------------------------------------------ +/*SelectionController::SelectionController( GraphicsView& graphicsView ) : + Controller( graphicsView ) +{ +} + +bool SelectionController::mouseDoubleClickEvent( QMouseEvent* e ) +{*/ + // FIXME +/* QPointF p = getGraphicsView( ).mapToScene( e->pos( ) ); + Item::List collisions; + getGraphicsView( ).getCanvas( )->getCollisions( p, collisions ); + for ( Item::List::iterator itemIter = collisions.begin( ); itemIter != collisions.end( ); itemIter++ ) + { + Item* item = *itemIter; + if ( getGraphicsView( ).getCanvas( )->isFreed( item ) ) + break; + + Node* node = getGraphicsView( ).getGraphicItemNode( item ); + if ( node != 0 ) + emit nodeSelected( node, p ); + }*/ + +/* return false; +}*/ +//----------------------------------------------------------------------------- + + + +/* Tooltip Controller Management *///-------------------------------------------- +/*TooltipController::TooltipController( GraphicsView& graphicsView ) : + Controller( graphicsView ), + //_tipItem( 0 ), + _tipPos( 0, 0 ), + _timer( 0 ) +{ + _timer = new QTimer( this ); + connect( _timer, SIGNAL( timeout( ) ), SLOT( tipTimeout( ) ) ); +}*/ + +/*void TooltipController::tipTimeout( ) +{*/ + // FIXME + // Don't show tooltips in case of error or if another controller is already active +/* if ( _tipItem == 0 || getGraphicsView( ).getControllerManager( ).isControllerActive( ) ) + { + _timer->stop( ); + return; + } + + // Test if the mouse is still on the item that has started the tooltip demand (tooltip delay) milliseconds ago + Item::List collisions; + getGraphicsView( ).getCanvas( )->getCollisions( _tipPos, collisions ); + for ( Item::List::iterator itemIter = collisions.begin( ); itemIter != collisions.end( ); itemIter++ ) + { + Item* item = *itemIter; + + if ( item == _tipItem ) + { + Node* node = getGraphicsView( ).getGraphicItemNode( _tipItem ); + if ( node != 0 ) + { + // Compute the real screen viewport coordinates + QPoint viewportTipPosContent = getGraphicsView( ).mapFromCanvas( _tipPos ); + emit nodeTipped( node, getGraphicsView( ).viewport( )->mapToGlobal( viewportTipPosContent ) ); + } + } + } + _timer->stop( ); + _tipItem = 0;*/ +//} + +//bool TooltipController::mouseMoveEvent( QMouseEvent* e ) +//{ + // FIXME +/* QPointF p = getGraphicsView( ).mapToScene( e->pos( ) ); + _tipPos = p; + + // If no button is pressed, there is no manipulation or navigation operation going on, so tooltip can be displayed + if ( e->button() != Qt::LeftButton ) + { + Item::List collisions; + getGraphicsView( ).getCanvas( )->getCollisions( _tipPos, collisions ); + for ( Item::List::iterator itemIter = collisions.begin( ); itemIter != collisions.end( ); itemIter++ ) + { + Item* item = *itemIter; + if ( item == _tipItem && _timer->isActive( ) ) // Avoid the timer to be restarted when moving in the same item + break; + + _tipPos = p; + if ( getGraphicsView( ).getCanvas( )->isFreed( item ) ) + break; + + Node* node = getGraphicsView( ).getGraphicItemNode( item ); + if ( node != 0 ) + { + _timer->start( 800 ); + _tipItem = item; + return true; + } + break; // Take just the first intersecting item to avoid deep z selection + } + } + else + { + // There is a complex navigation or manipulation operation going on, don't display tooltips + _timer->stop( ); + _tipItem = 0; + } + */ +// return false; +//} + +//bool TooltipController::mouseDoubleClickEvent( QMouseEvent* e ) +//{ + // Avoid interference with other controllers (such as simultaneous tip and double click node selection) + // FIXME + /*_timer->stop( ); + _tipItem = 0;*/ +// return false; +//} +//----------------------------------------------------------------------------- + + +} // ::qan + ============================================================ --- libs/qanava/src/qanController.h 2f743c33036f4d4c664e491b786f1797cb2bd1f2 +++ libs/qanava/src/qanController.h 2f743c33036f4d4c664e491b786f1797cb2bd1f2 @@ -0,0 +1,352 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file canController.h +// \author Benoit Autheman (address@hidden) +// \date 2003 August 13 +//----------------------------------------------------------------------------- + + +#ifndef canController_h +#define canController_h + + +// Qanava headers +#include "./qanNode.h" + + +// STD headers +#include + + +// QT headers +#include +#include +#include +#include +#include + + +//----------------------------------------------------------------------------- +namespace qan { // ::qan + + class GraphicsView; + + //! Manage complex view modification operations in an AbstractItemView object (for example, panning, selection, etc.). + /*! + \nosubgrouping + */ + class Controller : public QObject + { + Q_OBJECT + + public: + + class Manager : public QList< Controller* > + { + public: + + Manager( QObject* parent ) : _actionGroup( new QActionGroup( parent ) ) { } + + virtual ~Manager( ) { } + + virtual bool keyPressEvent( QKeyEvent* e ); + + virtual bool mousePressEvent( QMouseEvent* e ); + + virtual bool mouseReleaseEvent( QMouseEvent* e ); + + virtual bool mouseMoveEvent( QMouseEvent* e ); + + virtual bool mouseDoubleClickEvent( QMouseEvent* e ); + + virtual bool wheelEvent( QWheelEvent* e ); + + void registerController( Controller* controller ); + + Controller* getController( QString name ); + + private: + + QActionGroup* _actionGroup; + }; + + Controller( QString name, GraphicsView& graphicsView ); + + virtual ~Controller( ) { } + + virtual bool keyPressEvent( QKeyEvent* e ) { return false; } + + virtual bool mousePressEvent( QMouseEvent* e ) { return false; } + + virtual bool mouseReleaseEvent( QMouseEvent* e ) { return false; } + + virtual bool mouseMoveEvent( QMouseEvent* e ) { return false; } + + virtual bool mouseDoubleClickEvent( QMouseEvent* e ) { return false; } + + virtual bool wheelEvent( QWheelEvent* e ) { return false; } + + QString getName( ) { return _name; } + + protected: + + GraphicsView& getGraphicsView( ) { return _graphicsView; } + + private: + + GraphicsView& _graphicsView; + + QAction* _action; + + QString _name; + + protected: + + void setAction( QAction* action ) { _action = action; } + + public: + + //! Get a custom action by name in this controller. + virtual QAction* getAction( QString name ) { return 0; } + + public slots: + + QAction* getAction( ) { return _action; } + + signals: + + //! Emmitted when the controller action (must) state changes. + void toggled( bool on ); + }; + + + + //! . + /*! + \nosubgrouping + */ + class PanController : public Controller + { + Q_OBJECT + + /*! \name PanController Constructor/Destructor *///-------------------- + //@{ + public: + + //! PanController constructor with associed graphics view initialization. + PanController( GraphicsView& graphicsView ); + + //! PanController virtual destructor. + virtual ~PanController( ) { } + //@} + //--------------------------------------------------------------------- + + + //! . + enum State + { + NONE = 0, + KEYBOARD = 1, + PAN = 2, + PANNING = 8, + }; + + /*! \name Keyboard Navigation Management *///-------------------------- + //@{ + public: + + //! . + virtual bool keyPressEvent( QKeyEvent* e ); + + //! Enable or disable keyboard navigation (with arrow keys). + void setKeyboardNavigation( bool state ); + + //! Get the current keyboard navigation state. + bool getKeyboardNavigation( ) const { return _keyboardNavigation; } + + private: + + //! Keyboard navigation state. + bool _keyboardNavigation; + + //! Keyboard navigation intensity. + double _keyboardNavigationIntensity; + //@} + //--------------------------------------------------------------------- + + + + /*! \name Panning and Zooming Management *///-------------------------- + //@{ + public: + virtual bool mousePressEvent( QMouseEvent* e ); + + virtual bool mouseReleaseEvent( QMouseEvent* e ); + + virtual bool mouseMoveEvent( QMouseEvent* e ); + + public: + + State getMode( ) const { return _mode; } + + State getState( ) const { return _state; } + + void setState( State state ) { _state = state; } + + void setMode( State mode ) { _mode = mode; } + + protected slots: + + void toggled( bool state ); + + private: + + //! . + QPointF _start; + + State _mode; + + State _state; + //@} + //--------------------------------------------------------------------- + }; + + + //! + /*! + \nosubgrouping + */ + class ZoomWindowController : public Controller + { + Q_OBJECT + + /*! \name ZoomWindowController Constructor/Destructor *///------------- + //@{ + public: + + //! ZoomWindowController constructor with associed graphics view initialization. + ZoomWindowController( GraphicsView& graphicsView ); + + //! ZoomWindowController virtual destructor. + virtual ~ZoomWindowController( ) { } + //@} + //--------------------------------------------------------------------- + + //! . + enum State + { + NONE = 0, + ZOOM = 4, + ZOOMING = 16 + }; + + /*! \name Panning and Zooming Management *///-------------------------- + //@{ + public: + + virtual bool mousePressEvent( QMouseEvent* e ); + + virtual bool mouseReleaseEvent( QMouseEvent* e ); + + virtual bool mouseMoveEvent( QMouseEvent* e ); + + public: + + State getMode( ) const { return _mode; } + + State getState( ) const { return _state; } + + void setState( State state ) { _state = state; } + + void setMode( State mode ) { _mode = mode; } + + protected slots: + + void toggled( bool state ); + + private: + + //! . + QPointF _start; + + //! Geometric item used to model the dashed zoom selection window . + QGraphicsRectItem* _zoomRectItem; + + State _mode; + + State _state; + //@} + //--------------------------------------------------------------------- + }; + + + //! + /*! + \nosubgrouping + */ + class ZoomController : public Controller + { + Q_OBJECT + + /*! \name ZoomController Constructor/Destructor *///------------------- + //@{ + public: + + //! ZoomController constructor with associed graphics view initialization. + ZoomController( GraphicsView& graphicsView ); + + //! ZoomController virtual destructor. + virtual ~ZoomController( ) { } + //@} + //--------------------------------------------------------------------- + + + + /*! \name Panning and Zooming Management *///-------------------------- + //@{ + public: + + virtual bool wheelEvent( QWheelEvent* e ); + + virtual QAction* getAction( QString name ); + + protected slots: + + void zoomIn( bool state ); + + void zoomOut( bool state ); + + private: + + QAction* _actionZoomIn; + + QAction* _actionZoomOut; + //@} + //--------------------------------------------------------------------- + }; +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // canController_h + ============================================================ --- libs/qanava/src/qanEdge.cpp a6915d18182ee672105121cfeae13d2049a9805b +++ libs/qanava/src/qanEdge.cpp a6915d18182ee672105121cfeae13d2049a9805b @@ -0,0 +1,46 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file laEdge.cpp +// \author Benoit Autheman (address@hidden) +// \date 2004 February 15 +//----------------------------------------------------------------------------- + + +// Qanava headers +#include "./qanEdge.h" +#include "./qanNode.h" + + +namespace qan { // ::qan + +void Edge::set( Node* src, Node* dst ) +{ + if ( src != 0 && dst != 0 ) + { + _src = src; _dst = dst; + src->addOutEdge( *this ); + dst->addInEdge( *this ); + } +} + +} // ::qan ============================================================ --- libs/qanava/src/qanEdge.h a5585d277d33f6bc9d1169e42a5b40224855efbd +++ libs/qanava/src/qanEdge.h a5585d277d33f6bc9d1169e42a5b40224855efbd @@ -0,0 +1,139 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file laEdge.cpp +// \author Benoit Autheman (address@hidden) +// \date 2004 February 15 +//----------------------------------------------------------------------------- + + +#ifndef laEdge_h +#define laEdge_h + + +// Qanava headers +#include "./qanConfig.h" + + +// Standard headers +#include +#include +#include + + +// QT headers +#include +#include + + +//----------------------------------------------------------------------------- +namespace qan { // ::qan + + class Node; + + //! Model a weighted directed edge between two nodes. + /*! + \nosubgrouping + */ + class Edge + { + /*! \name Generator Constructor/Destructor *///-------------------- + //@{ + public: + + //! Node constructor with source and destination initialization. + Edge( Node& src, Node& dst, float weight = 1.f ) : + _src( &src ), + _dst( &dst ), + _weight( weight ) + { + + } + + //! Node constructor with source and destination initialization. + Edge( Node* src, Node* dst, float weight = 1.f ) : + _src( src ), + _dst( dst ), + _weight( weight ) + { + + } + + //! Typedef for a QT list of Edge. + typedef QList< Edge* > List; + + //! Typedef for a QT set of Edge. + typedef QSet< Edge* > Set; + //@} + //----------------------------------------------------------------- + + + + /*! \name Source/Destination Management *///----------------------- + //@{ + public: + + //! Get edge source. + bool hasSrc( ) const { return ( _src != 0 ); } + + //! Get edge destination. + bool hasDst( ) const { return ( _dst != 0 ); } + + //! Get edge source. + Node& getSrc( ) { return *_src; } + + //! Get edge destination. + Node& getDst( ) { return *_dst; } + + //! Get const reference on the edge source. + const Node& getSrc( ) const { return *_src; } + + //! Get const reference on the edge destination. + const Node& getDst( ) const { return *_dst; } + + //! Get edge's weight. + float getWeight( ) const { return _weight; } + + //! Set edge's weight. + void setWeight( float weight ) { _weight = weight; } + + //! Set edge source and destination (use this method carefully it is normally reserved for serialization implementation). + void set( Node* src, Node* dst ); + + private: + + //! Edge source. + Node* _src; + + //! Edge destination. + Node* _dst; + + //! Edge weight. + float _weight; + //@} + //----------------------------------------------------------------- + }; +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // laEdge_h ============================================================ --- libs/qanava/src/qanGraph.h f8253ed3de7c8c86c81686a8d558f1ded5d69f36 +++ libs/qanava/src/qanGraph.h f8253ed3de7c8c86c81686a8d558f1ded5d69f36 @@ -0,0 +1,305 @@ +/* +Qanava - GraphT drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file laGraph.h +// \author Benoit Autheman (address@hidden) +// \date 2004 February 15 +//----------------------------------------------------------------------------- + + +#ifndef laGraph_h +#define laGraph_h + + +// Qanava headers +#include "./qanEdge.h" +#include "./qanNode.h" +#include "./qanGraphItemModel.h" + + +// QT headers +#include +#include +#include + + +//----------------------------------------------------------------------------- +namespace qan { // ::qan + + //! Model a standard weighted directed graph using a node-list, edge-list representation. + /*! + \nosubgrouping + */ + template < class M > + class GraphT : public M + { + public: + + /*! \name GraphT Constructor/Destructor *///------------------------ + //@{ + public: + + //! GraphT default constructor. + GraphT( ); + + //! GraphT default destructor. All registered nodes and edges are invalid after graph destruction. + ~GraphT( ); + + private: + + //! GraphT empty private copy constructor. + GraphT( const GraphT& graph ); + //@} + //----------------------------------------------------------------- + + + + /*! \name Edge/Node Management *///-------------------------------- + //@{ + public: + + //! Clear the graph from all its edges and nodes. + void clear( ); + + //! Set a node object unique key. + void setNodeObject( Node& node, void* object ); + + void modifyNode( Node& node, const QString& name, int type = -1, void* object = 0 ); + + void modifyEdge( Edge& edge, Node& a, Node& b, float weight = 1.f ); + + typedef QList< GraphT< M >* > List; + //@} + //----------------------------------------------------------------- + + + /*! \name Edge/Node Management *///-------------------------------- + //@{ + public: + + void addNode( Node* node, void* object = 0 ); + + Node* addNode( const QString& name, int type = -1, void* object = 0 ); + + void insertNode( Node* node, void* object = 0 ); + + Node* insertNode( const QString& name, int type = -1, void* object = 0 ); + + void removeNode( Node& node ); + + + void addEdge( Edge* edge ); + + Edge* addEdge( Node& a, Node& b, float weight = 1.f ); + + void insertEdge( Edge* edge ); + + Edge* insertEdge( Node& a, Node& b, float weight = 1.f ); + + //! Insert a new leaf node in this graph using an existing node as the super node, and return an edge from the super node to node. + /*! + \param node Node that must be inserted in the graph (graph will take ownership of the node). + \param superNode Already existing node that will be used as a super node, and the source of the returned edge. + */ + Edge* insertNode( Node* node, Node& superNode ); + + void removeEdge( Edge& edge ); + + bool hasEdge( Node& a, Node& b ) const; + + + private: + + //! List of nodes currently registered in this graph. + Node::List _nodes; + + //! Set of nodes currently registered in this graph (used for fast node search). + Node::Set _nodesSet; + + //! Map an object to its associed node. + typedef QMap< void*, Node* > ObjectNodeMap; + + //! . + typedef QMap< Node*, void* > NodeObjectMap; + + //! Map an object to its associed graph node. + ObjectNodeMap _objectNodeMap; + + //! . + NodeObjectMap _nodeObjectMap; + + //! List of edges currently connecting the registered nodes in this graph. + Edge::List _edges; + //@} + //----------------------------------------------------------------- + + + + + /*! \name GraphT Consultation Management *///----------------------- + //@{ + public: + + //! Get graph node count. + unsigned int getNodeCount( ) const { return _nodes.size( ); } + + //! Get graph's nodes list (list must be used read-only). + Node::List& getNodes( ) { return _nodes; } + + //! Get graph's edges list (list must be used read-only). + Edge::List& getEdges( ) { return _edges; } + + //! Collect a set of unique node registered in this graph. + void collectNodes( Node::Set& nodes ) const; + //@} + //----------------------------------------------------------------- + + + + /*! \name Search Management *///----------------------------------- + //@{ + public: + + //! Find a node of a given label in the graph. + Node* findNode( const QString& label ); + + //! Find a node given its associed key memory object (if such key has been specified during node adjunction). + Node* findNode( void* object ); + + //! Find the nth registered node in this graph (For internal use only). + /*! This method should only be used in repositories just after node loading when their initial order has not been altered. */ + Node* findNode( int node ); + + //! Get the index where a node is currently registered in this graph. + /*! This method should only be used in repositories just after node loading when their initial order has not been altered. */ + int findNode( const Node& node ) const; + + //! Find a node object for a specific node (if such an object has been specified during node adjunction). + void* findObject( Node* node ); + + //! Search for a specific node in this graph and return true if the node is found. + bool hasNode( Node* node ) const; + //@} + //----------------------------------------------------------------- + + + + /*! \name Root Node Management *///-------------------------------- + //@{ + public: + + //! Add a root to this graph (ie a node that doesn't have super nodes.) + /*! \param node must be already registered in the graph. */ + void addRootNode( Node& node ) { _rootNodes << &node; _rootNodesSet << &node; } + + //! Remove a node from the root node list + void removeRootNode( Node& node ) { _rootNodes.removeAll( &node ); _rootNodesSet.remove( &node ); } + + //! Get the currently registered root node for this graph. + Node::List& getRootNodes( ) { return _rootNodes; } + + //! Get the currently registered root node for this graph. + Node::Set& getRootNodesSet( ) { return _rootNodesSet; } + + //! Generate the root set by automatically looking for nodes with no super nodes (O(n), n numbers of nodes). + void generateRootNodes( ); + + //! Test if a given node is a graph root node. + bool isRootNode( const Node& node ) const; + + private: + + //! Ordered root nodes for this graph's subgraphs. + Node::List _rootNodes; + + //! Root nodes of graph's subgraphs (used only to test root node existence). + Node::Set _rootNodesSet; + //@} + //----------------------------------------------------------------- + + + /*! \name Node Attributes Management *///-------------------------- + //@{ + public: + + template < typename T > + void addAttribute( const QString& name, T t ) + { + _attributesNames.push_back( name ); + for ( Node::List::iterator nodeIter = getNodes( ).begin( ); nodeIter != getNodes( ).end( ); nodeIter++ ) + ( *nodeIter )->addAttribute< T >( t ); + } + + QString getAttributeName( int role ) + { + return ( role < _attributesNames.size( ) ? _attributesNames[ role ] : QString( "" ) ); + } + + template < typename T > + void setAttribute( Node* node, const QString& name, T& value ) + { + int role = hasAttribute( name ); + if ( role >= 0) + setAttribute< T >( node, role, value ); + } + + template < typename T > + void setAttribute( Node* node, unsigned int role, T& value ) + { + node->setAttribute< T >( role, value ); + } + + int hasAttribute( const QString& name ) const + { + int r = 0; + Strings::const_iterator nameIter = _attributesNames.begin( ); + for ( ; nameIter != _attributesNames.end( ); nameIter++, r++ ) + if ( *nameIter == name ) + return r; + return -1; + } + + unsigned int getAttributesCount( ) const { return _attributesNames.size( ); } + + protected: + + void initNodeAttributes( Node& node ) + { + node.initAttributes( _attributesNames.size( ) ); + } + + typedef QList< QString > Strings; + + Strings _attributesNames; + //@} + //----------------------------------------------------------------- + }; + + typedef GraphT< GraphItemModel > Graph; +} // ::qan + +#include "./qanGraph.hpp" +//----------------------------------------------------------------------------- + + +#endif // laGraph_h + ============================================================ --- libs/qanava/src/qanGraph.hpp 71f74c300686f7de87b05f7ef7a9974cb1e8d3d2 +++ libs/qanava/src/qanGraph.hpp 71f74c300686f7de87b05f7ef7a9974cb1e8d3d2 @@ -0,0 +1,414 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file laGraph.cpp +// \author Benoit Autheman (address@hidden) +// \date 2004 February 15 +//----------------------------------------------------------------------------- + +// Qt headers +#include + + +namespace qan { // ::qan + + +/* GraphT Constructor/Destructor *///------------------------------------------- +template < class M > +GraphT< M >::GraphT( ) : + M( _rootNodes ) +{ + M::init( _rootNodes ); + + addAttribute< int >( "Type", 0 ); + addAttribute< QString >( "Label", QString( "" ) ); + addAttribute< VectorF >( "Position", VectorF( 2 ) ); + addAttribute< VectorF >( "Dimension", VectorF( 2 ) ); + addAttribute< QDateTime >( "Date", QDateTime( ) ); +} + +template < class M > +GraphT< M >::~GraphT( ) +{ + clear( ); +} +//----------------------------------------------------------------------------- + + +template < class M > +void GraphT< M >::modifyNode( Node& node, const QString& name, int type, void* object ) +{ + node.setLabel( name ); + + if ( type != -1 ) + node.setType( type ); + + if ( object != 0 ) + setNodeObject( node, object ); + + M::nodeChanged( node ); +} + +template < class M > +void GraphT< M >::setNodeObject( Node& node, void* object ) +{ + // Map the registered node to a given memory object that act as a key + if ( object != 0 ) + { + _objectNodeMap.insert( object, &node ); + _nodeObjectMap.insert( &node, object ); + } +} + + + +/* Edge/Node Management *///--------------------------------------------------- +template < class M > +void GraphT< M >::addNode( Node* node, void* object ) +{ + assert( node != 0 ); + initNodeAttributes( *node ); + + _nodes.push_back( node ); + _nodesSet.insert( node ); + + if ( object != 0 ) + setNodeObject( *node, object ); + + if ( node->getInDegree( ) == 0 ) + addRootNode( *node ); +} + +template < class M > +Node* GraphT< M >::addNode( const QString& name, int type, void* object ) +{ + Node* node = new Node( name, type ); + addNode( node, object ); + return node; +} + +template < class M > +void GraphT< M >::insertNode( Node* node, void* object ) +{ + assert( node != 0 ); + M::nodeInsertedBegin( *node ); + addNode( node, object ); + M::nodeInserted( *node ); +} + +template < class M > +Node* GraphT< M >::insertNode( const QString& name, int type, void* object ) +{ + Node* node = new Node( name, type ); + insertNode( node, object ); + return node; +} + + +template < class M > +void GraphT< M >::removeNode( Node& node ) +{ + // Test if node subnodes will eventually became root nodes after their super node removal + Node::List rootNodes; + Node::List outNodes; node.collectOutNodes( outNodes ); + for ( Node::List::iterator outNodeIter = outNodes.begin( ); outNodeIter != outNodes.end( ); outNodeIter++ ) + { + Node* outNode = *outNodeIter; + if ( !isRootNode( *outNode ) && outNode->getInDegree( ) <= 1 ) + rootNodes.push_back( outNode ); + } + + // Add orphan out nodes as root node (node is removed to force view update and maintain graph coherency for listeners) + // Oprhan out nodes are removed before 'node' so that they still have a valid parent in + // an eventual associed view (for example in a QAbstractItem view, removing node would cause + // the model to have a gap preventing node out nodes to be destroyed). + for ( Node::List::iterator rootNodeIter = rootNodes.begin( ); rootNodeIter != rootNodes.end( ); rootNodeIter++ ) + { + Node& rootNode = **rootNodeIter; + M::nodeInsertedBegin( rootNode ); + addRootNode( rootNode ); + M::nodeInserted( rootNode ); + } + + + M::nodeRemovedBegin( node ); + + // Disconnect node from its "in edges". + Edge::List::iterator inEdgeIter = node.getInEdges( ).begin( ); + for ( ; inEdgeIter != node.getInEdges( ).end( ); inEdgeIter++ ) + { + Edge& inEdge = ( **inEdgeIter ); + inEdge.getSrc( ).getOutEdges( ).removeAll( &inEdge ); + } + node.getInEdges( ).clear( ); + + // Disconnect node from its "out edges". + Edge::List::iterator outEdgeIter = node.getOutEdges( ).begin( ); + for ( ; outEdgeIter != node.getOutEdges( ).end( ); outEdgeIter++ ) + { + Edge& outEdge = ( **outEdgeIter ); + outEdge.getDst( ).getInEdges( ).removeAll( &outEdge ); + } + node.getOutEdges( ).clear( ); + + // Remove node from the graph various node list + _nodes.removeAll( &node ); + _nodesSet.remove( &node ); + void* object = findObject( &node ); + if ( object != 0 ) + _objectNodeMap.remove( object ); + _nodeObjectMap.remove( &node ); + removeRootNode( node ); + + M::nodeRemoved( node ); +} + + + +template < class M > +void GraphT< M >::addEdge( Edge* edge ) +{ + assert( edge != 0 ); + if ( edge->hasSrc( ) ) + edge->getSrc( ).addOutEdge( *edge ); + if ( edge->hasDst( ) ) + { + edge->getDst( ).addInEdge( *edge ); + removeRootNode( edge->getDst( ) ); // Dst can't be a root node, supress it to maintain coherency + } + _edges.push_back( edge ); +} + +template < class M > +Edge* GraphT< M >::addEdge( Node& a, Node& b, float weight ) +{ + Edge* edge = new Edge( &a, &b, weight ); + addEdge( edge ); + return edge; +} + + +template < class M > +void GraphT< M >::insertEdge( Edge* edge ) +{ + +} + +template < class M > +Edge* GraphT< M >::insertEdge( Node& src, Node& dst, float weight ) +{ + // Save destination node topology. + Edge::List inEdges; + std::copy( dst.getInEdges( ).begin( ), dst.getInEdges( ).end( ), std::back_insert_iterator< Edge::List >( inEdges ) ); + Edge::List outEdges; + std::copy( dst.getOutEdges( ).begin( ), dst.getOutEdges( ).end( ), std::back_insert_iterator< Edge::List >( outEdges ) ); + + // Delete the node to force view update (and eventually move the node to its correct position) + void* object = findObject( &dst ); + removeNode( dst ); + + // Restore destination node in and out edges (and reconnect theses edges to their extremity node). + for ( Edge::List::iterator inEdgeIter = inEdges.begin( ); inEdgeIter != inEdges.end( ); inEdgeIter++ ) + { + Edge* inEdge = *inEdgeIter; + dst.addInEdge( *inEdge ); + inEdge->getSrc( ).addOutEdge( *inEdge ); + } + for ( Edge::List::iterator outEdgeIter = outEdges.begin( ); outEdgeIter != outEdges.end( ); outEdgeIter++ ) + { + Edge* outEdge = *outEdgeIter; + dst.addOutEdge( *outEdge ); + outEdge->getDst( ).addInEdge( *outEdge ); + + // Remove (eventually) out node from root node + removeRootNode( outEdge->getDst( ) ); + } + + // Insert back the node to its new correct view position + Edge* edge = addEdge( src, dst, weight ); + insertNode( &dst, object ); + return edge; +} + +template < class M > +Edge* GraphT< M >::insertNode( Node* node, Node& superNode ) +{ + return 0; +} + +/*! + \return true is there is an edge between a and b (whatever is orientation is), false otherwise. + */ +template < class M > +bool GraphT< M >::hasEdge( Node& a, Node& b ) const +{ + Node::Set adjacentA; + a.getAdjacentNodesSet( adjacentA ); + if ( adjacentA.find( &b ) != adjacentA.end( ) ) + return true; + + Node::Set adjacentB; + b.getAdjacentNodesSet( adjacentB ); + if ( adjacentB.find( &a ) != adjacentB.end( ) ) + return true; + + return false; +} + +/*! + \return a pointer on the node of request label. 0 if no such node exists. +*/ +template < class M > +Node* GraphT< M >::findNode( const QString& label ) +{ + for ( Node::List::iterator nodeIter = _nodes.begin( ); nodeIter != _nodes.end( ); nodeIter++ ) + { + Node* node = *nodeIter; + if ( node->getLabel( ) == label ) + return node; + } + return 0; +} + +template < class M > +Node* GraphT< M >::findNode( void* object ) +{ + return _objectNodeMap.value( object, 0 ); +} + +template < class M > +Node* GraphT< M >::findNode( int node ) +{ + Node::List::iterator nodeIter = _nodes.begin( ); + int n = 0; + while ( n < node ) + { + nodeIter++; + n++; + } + if ( nodeIter != _nodes.end( ) ) + return *nodeIter; + return 0; +} + +/*! + \result The node index if node is registered in this graph, -1 if there is no node or index. + */ +template < class M > +int GraphT< M >::findNode( const Node& node ) const +{ + Node::List::const_iterator nodeIter = _nodes.begin( ); + int n = 0; + while ( nodeIter != _nodes.end( ) && ( *nodeIter != &node ) ) + { + nodeIter++; + n++; + } + if ( nodeIter != _nodes.end( ) ) + return n; + return -1; +} + +template < class M > +void* GraphT< M >::findObject( Node* node ) +{ + NodeObjectMap::iterator objectIter = _nodeObjectMap.find( node ); + return ( objectIter != _nodeObjectMap.end( ) ? objectIter.value( ) : 0 ); +} + +/*! + \param node Node to be searched in this graph. + \return true if the node is found, false otherwise. + */ +template < class M > +bool GraphT< M >::hasNode( Node* node ) const +{ + return ( _nodesSet.find( node ) != _nodesSet.end( ) ); +} + +template < class M > +void GraphT< M >::collectNodes( Node::Set& nodes ) const +{ + nodes.reserve( _nodes.size( ) ); + foreach ( Node* node, _nodes ) + nodes.insert( node ); + // FIXME + /*std::copy( _nodes.begin( ), _nodes.end( ), + std::insert_iterator< Node::Set >( nodes, nodes.begin( ) ) );*/ +} + +/*! This method clear the nodes, the fast node search cache, the node object mapping + system and the edge list. Registered nodes and edges are not only dereferenced, but + destroyed with a call to delete. + */ +template < class M > +void GraphT< M >::clear( ) +{ + _objectNodeMap.clear( ); + _nodeObjectMap.clear( ); + + for ( Edge::List::iterator edgeIter = _edges.begin( ); edgeIter != _edges.end( ); edgeIter++ ) + delete *edgeIter; + _edges.clear( ); + + _nodesSet.clear( ); + for ( Node::List::iterator nodeIter = _nodes.begin( ); nodeIter != _nodes.end( ); nodeIter++ ) + delete *nodeIter; + _nodes.clear( ); + + + _rootNodes.clear( ); + _rootNodesSet.clear( ); +} +//----------------------------------------------------------------------------- + + +/* Root Node Management *///--------------------------------------------------- +/*! + Root nodes manually inserted via addRoot() are cleared from the root nodes (and eventually + automatically re-added if needed). + */ +template < class M > +void GraphT< M >::generateRootNodes( ) +{ + _rootNodes.clear( ); + for ( Node::List::iterator nodeIter = _nodes.begin( ); nodeIter != _nodes.end( ); nodeIter++ ) + { + if ( ( *nodeIter )->getInDegree( ) == 0 ) + addRootNode( **nodeIter ); + } +} + +/*! + \param node node that must be tested against the root nodes set (must be a graph node). + \return true if the node is a graph root node, false otherwise. + */ +template < class M > +bool GraphT< M >::isRootNode( const Node& node ) const +{ + return ( _rootNodesSet.find( const_cast< Node* >( &node ) ) != _rootNodesSet.end( ) ); +} +//----------------------------------------------------------------------------- + + +} // ::qan + ============================================================ --- libs/qanava/src/qanGraphItemModel.cpp 5589991ed19351c18ffe7c9b456a8bffcd5ce8a4 +++ libs/qanava/src/qanGraphItemModel.cpp 5589991ed19351c18ffe7c9b456a8bffcd5ce8a4 @@ -0,0 +1,399 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file canGraphItemModel.h +// \author Benoit Autheman (address@hidden) +// \date 2005 November 22 +//----------------------------------------------------------------------------- + + +// Qanava headers +#include "./qanStyle.h" +#include "./qanGraphItemModel.h" + + +//----------------------------------------------------------------------------- +namespace qan { // ::qan + + +/* Custom Graph Model Interface *///------------------------------------------- +GraphItemModel::GraphItemModel( Node::List& rootNodes ) : + QAbstractItemModel( ), + _rootNodes( rootNodes ), + _styleManager( 0 ) +{ + +} + +void GraphItemModel::init( Node::List& rootNodes ) +{ + _rootNodes = rootNodes; + + // Generate persistent model indexes for all graph nodes + Node::List::iterator nodeIter = _rootNodes.begin( ); + //for ( int row = 0; nodeIter != _rootNodes.end( ); row++, nodeIter++ ) + // visit( **nodeIter, row, 0, 500 ); + + emit layoutChanged( ); +} + +void GraphItemModel::visit( Node& node, int row, int column, int maxDepth ) +{ + Node::Set marked; + visit( node, row, column, marked, 500 ); +} + +void GraphItemModel::visit( Node& node, int row, int column, Node::Set& marked, int maxDepth ) +{ + if ( maxDepth == 0 ) + return; + maxDepth--; + + if ( marked.find( &node ) != marked.end( ) ) + return; + marked.insert( &node ); + + QModelIndex nodeIndex = createIndex( row, column, &node ); + QPersistentModelIndex* nodePersistentIndex = getNodePersistentIndex( &node ); + + if ( nodePersistentIndex != 0 ) + { + // Update a pre existing persistent index + changePersistentIndex( *nodePersistentIndex, nodeIndex ); + } + else + { + // Create a new persistent index + nodePersistentIndex = new QPersistentModelIndex( nodeIndex ); + _nodePersistentIndexMap.insert( std::pair< Node*, QPersistentModelIndex* >( &node, nodePersistentIndex ) ); + } + + column = 0; + + // visit sub nodes + Edge::List& edgeList = node.getOutEdges( ); + row = 0; + for ( Edge::List::iterator edgeIter = edgeList.begin( ); edgeIter != edgeList.end( ); edgeIter++ ) + { + Edge* edge = *edgeIter; + visit( edge->getDst( ), row++, column, marked, maxDepth ); + } +} + +QPersistentModelIndex* GraphItemModel::getNodePersistentIndex( Node* node ) const +{ + NodePersistentIndexMap::const_iterator nodeIter = _nodePersistentIndexMap.find( node ); + if ( nodeIter != _nodePersistentIndexMap.end( ) ) + return nodeIter->second; + return 0; +} +//----------------------------------------------------------------------------- + + + +/* QT Model Interface Management *///------------------------------------------ +QVariant GraphItemModel::data( const QModelIndex &index, int role ) const +{ + if ( !index.isValid( ) ) + return QVariant( "Index invalid" ); + + Node* node = static_cast< Node* >( index.internalPointer( ) ); + if ( node == 0 ) + return QVariant( ); + + const Style* style = 0; + if ( _styleManager != 0 && node != 0 ) + { + style = _styleManager->getStyle( node ); + if ( _styleManager->getStyle( node->getType( ) ) ) + style = _styleManager->getStyle( node->getType( ) ); + } + + QVariant result; + switch ( role ) + { + case Qt::DisplayRole: + result = node->getLabel( ); + break; + case Qt::DecorationRole: + if ( style != 0 && style->has( "icon" ) ) + { + QIcon icon( style->getIcon( "icon" ) ); + if ( !icon.isNull( ) ) + result = icon; + } + break; + case Qt::BackgroundColorRole: + { + if ( style != 0 && style->has( "backcolor" ) ) + { + QColor backColor = style->getColor( "backcolor" ); + if ( backColor.isValid( ) ) + { + QColor bc( backColor ); + bc.setAlpha( 50 ); + result = bc; + } + } + } + break; + case Qt::TextColorRole: + { + if ( style != 0 ) + { + QColor backColor = style->getColor( "textcolor" ); + if ( backColor.isValid( ) ) + result = backColor; + } + } + break; + + case Qt::UserRole + POSITION_X: + result = node->getPosition( )( 0 ); + break; + case Qt::UserRole + POSITION_Y: + result = node->getPosition( )( 1 ); + break; + case Qt::UserRole + DIMENSION_X: + result = node->getDimension( )( 0 ); + break; + case Qt::UserRole + DIMENSION_Y: + result = node->getDimension( )( 1 ); + break; + default: + break; + } + return result; +} + +Qt::ItemFlags GraphItemModel::flags( const QModelIndex& index ) const +{ + return ( Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled ); +} + +QModelIndex GraphItemModel::index( int row, int column, const QModelIndex &parent ) const +{ + if ( !parent.isValid( ) ) + { + if ( row < _rootNodes.size( ) ) + { + Node* node = _rootNodes.at( row ); + QPersistentModelIndex* index = getNodePersistentIndex( node ); + if ( index == 0 ) + index = new QPersistentModelIndex( createIndex( row, column, node ) ); + return ( index != 0 ? QModelIndex( *index ) : QModelIndex( ) ); + } + } + else + { + Node* node = static_cast< Node* >( parent.internalPointer( ) ); + if ( node != 0 && row < node->getOutEdges( ).size( ) ) + { + Edge* edge = node->getOutEdges( ).at( row ); + Node& outNode = edge->getDst( ); + + QPersistentModelIndex* persistentIndex = getNodePersistentIndex( &outNode ); + if ( persistentIndex == 0 ) + persistentIndex = new QPersistentModelIndex( createIndex( row, column, &outNode ) ); + return ( persistentIndex != 0 ? QModelIndex( *persistentIndex ) : QModelIndex( ) ); + } + } + return QModelIndex( ); +} + +QModelIndex GraphItemModel::parent( const QModelIndex &index ) const +{ + if ( !index.isValid( ) ) + return QModelIndex( ); + else + { + Node* node = static_cast< Node* >( index.internalPointer( ) ); + Node* superNode = 0; + if ( node != 0 && ( node->getInEdges( ).size( ) > 0 ) ) + superNode = &( *node->getInEdges( ).begin( ) )->getSrc( ); + + if ( superNode != 0 && superNode->getOutEdges( ).size( ) > 0 ) + { + QPersistentModelIndex* index = getNodePersistentIndex( superNode ); + if ( index == 0 ) + index = new QPersistentModelIndex( createIndex( 0, 0, superNode ) ); + return ( index != 0 ? QModelIndex( *index ) : QModelIndex( ) ); + } + } + return QModelIndex( ); +} + +bool GraphItemModel::hasChildren( const QModelIndex& parent ) const +{ + return ( rowCount( parent ) > 0 ); +} + +int GraphItemModel::rowCount( const QModelIndex& parent ) const +{ + if ( !parent.isValid( ) ) + return _rootNodes.size( ); + else + { + Node* node = static_cast< Node* >( parent.internalPointer( ) ); + if ( node != 0 ) + return node->getOutEdges( ).size( ); + } + return 0; +} + +int GraphItemModel::columnCount( const QModelIndex& parent ) const +{ + return 1; +} + +bool GraphItemModel::setData ( const QModelIndex& index, const QVariant& value, int role ) +{ + if ( !index.isValid( ) ) + return true; + + if ( value.type( ) == QVariant::String ) + { + QString name = value.toString( ); + Node* node = static_cast< Node* >( index.internalPointer( ) ); + //_graph.modifyNode( *node, name.toStdString( ), node->getType( ), _graph.findObject( node ) ); + // FIXME + } + return true; +} +//----------------------------------------------------------------------------- + + +void GraphItemModel::nodeInsertedBegin( Node& node ) +{ + Node::List inNodes; node.collectInNodes( inNodes ); + if ( inNodes.size( ) == 0 ) // Root node + //beginInsertRows( QModelIndex( ), _rootNodes.size( ), _rootNodes.size( ) + 1 ); + emit rowsAboutToBeInserted( QModelIndex( ), _rootNodes.size( ), _rootNodes.size( ) + 1 ); + else + { + for ( Node::List::iterator inNodeIter = inNodes.begin( ); inNodeIter != inNodes.end( ); inNodeIter++ ) + { + Node* inNode = *inNodeIter; + QPersistentModelIndex* parentPersistentIndex = getNodePersistentIndex( inNode ); + QModelIndex parentIndex( parentPersistentIndex != 0 ? QModelIndex( *parentPersistentIndex ) : QModelIndex( ) ); + //beginInsertRows( parentIndex, inNode->getOutDegree( ) /*- 1*/, inNode->getOutDegree( ) /*- 1*/ + 1 ); + emit rowsAboutToBeInserted( parentIndex, inNode->getOutDegree( ) /*- 1*/, inNode->getOutDegree( ) /*- 1*/ + 1 ); + } + } +} + +void GraphItemModel::nodeInserted( Node& node ) +{ + Node::List inNodes; node.collectInNodes( inNodes ); + if ( inNodes.size( ) == 0 /*&& _graph.isRootNode( node )*/ ) + { + // Generate persistent model indexes for all graph root nodes + Node::List::iterator nodeIter = _rootNodes.begin( ); + for ( int row = 0; nodeIter != _rootNodes.end( ); row++, nodeIter++ ) + visit( **nodeIter, row, 0, 1 ); + //emit rowsInserted( QModelIndex( ), _rootNodes.size( ), _rootNodes.size( ) + 1 ); + emit rowsInserted( QModelIndex( ), _rootNodes.size( ) - 1, _rootNodes.size( ) ); + } + else + { + int nodeRow = 0; + for ( Node::List::iterator inNodeIter = inNodes.begin( ); inNodeIter != inNodes.end( ); nodeRow++, inNodeIter++ ) + { + Node* inNode = *inNodeIter; + QPersistentModelIndex* parentPersistentIndex = getNodePersistentIndex( inNode ); + QModelIndex parentIndex( parentPersistentIndex != 0 ? QModelIndex( *parentPersistentIndex ) : QModelIndex( ) ); + + visit( *inNode, parentIndex.row( ), 0, 2 ); + + QPersistentModelIndex* nodePersistentIndex = getNodePersistentIndex( &node ); + int nodeRow = 0; + if ( nodePersistentIndex != 0 && nodePersistentIndex->isValid( ) ) + nodeRow = nodePersistentIndex->row( ); + emit rowsInserted( parentIndex, nodeRow, nodeRow + 1 ); + } + } + +// endInsertRows( ); // Bug in QT 4.1.0 + //emit layoutChanged( ); +} + +void GraphItemModel::nodeRemovedBegin( Node& node ) +{ + NodePersistentIndexMap::iterator nodeIter = _nodePersistentIndexMap.find( &node ); + if ( nodeIter != _nodePersistentIndexMap.end( ) ) + { + QPersistentModelIndex* nodePersistentIndex = nodeIter->second; + int row = nodePersistentIndex->row( ); + QModelIndex parentIndex = nodePersistentIndex->parent( ); + //beginRemoveRows( parentIndex, row, row + 1 ); + emit rowsAboutToBeRemoved( parentIndex, row, row + 1 ); + } +} + +void GraphItemModel::nodeRemoved( Node& node ) +{ + QPersistentModelIndex* nodePersistentIndex = getNodePersistentIndex( &node ); + + // Adjust other concerned node model index (rows and columns index may now be invalid) + { + Node::List inNodes; node.collectInNodes( inNodes ); + if ( inNodes.size( ) == 0 ) // A removed node should always have no in or out nodes + { + // Generate persistent model indexes for all graph nodes + Node::List::iterator nodeIter = _rootNodes.begin( ); + for ( int row = 0; nodeIter != _rootNodes.end( ); row++, nodeIter++ ) + visit( **nodeIter, row, 0, 1 ); + } + } + + _nodePersistentIndexMap.erase( &node ); + + QModelIndex parentIndex; + int nodeRow = 0; + if ( nodePersistentIndex != 0 ) + { + parentIndex = nodePersistentIndex->parent( ); + nodeRow = nodePersistentIndex->row( ); + } + + if ( nodeRow != -1 ) + emit rowsRemoved( parentIndex, nodeRow, nodeRow + 1 ); + + //endRemoveRows( ); // Bug in QT 4.1.0 + emit layoutChanged( ); +} + +void GraphItemModel::nodeChanged( qan::Node& node ) +{ + QPersistentModelIndex* persistentNodeIndex = getNodePersistentIndex( &node ); + if ( persistentNodeIndex != 0 ) + { + QModelIndex nodeIndex( *persistentNodeIndex ); + if ( nodeIndex.isValid( ) ) + emit dataChanged( nodeIndex, nodeIndex ); + } +} + +} // ::qan +//----------------------------------------------------------------------------- + + ============================================================ --- libs/qanava/src/qanGraphItemModel.h ad90da3f02f4b547d29c7cb8b378c7fb613781f6 +++ libs/qanava/src/qanGraphItemModel.h ad90da3f02f4b547d29c7cb8b378c7fb613781f6 @@ -0,0 +1,183 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file canGraphItemModel.h +// \author Benoit Autheman (address@hidden) +// \date 2005 November 22 +//----------------------------------------------------------------------------- + + +#ifndef canGraphItemModel_h +#define canGraphItemModel_h + + +// Qanava headers +#include "./qanNode.h" +#include "./qanStyle.h" + + +// QT headers +#include +#include +#include +#include + + +//----------------------------------------------------------------------------- +namespace qan { // ::qan + +/* class GraphItemListener : public QObject, public Graph::Listener + { + Q_OBJECT + + public: + + GraphItemListener( Graph& graph, QObject* parent = 0 ) : QObject( parent ), Graph::Listener( graph ) { } + + virtual void nodeInsertedBegin( Node& node ) { emit nodeInsertedBeginSig( node ); } + + virtual void nodeInserted( Node& node ) { emit nodeInsertedSig( node ); } + + virtual void nodeRemovedBegin( Node& node ) { emit nodeRemovedBeginSig( node ); } + + virtual void nodeRemoved( Node& node ) { emit nodeRemovedSig( node ); } + + virtual void nodeChanged( Node& node ) { emit nodeChangedSig( node ); } + + signals: + + void nodeInsertedBeginSig( qan::Node& node ); + + void nodeInsertedSig( qan::Node& node ); + + void nodeRemovedBeginSig( qan::Node& node ); + + void nodeRemovedSig( qan::Node& node ); + + void nodeChangedSig( qan::Node& node ); + };*/ + + //! Provide access to a Graph trough a QT interview compatible interface. + /*! This model is useable with any classes based on QAbstractItemView, even if some + view might eventually not support (or have never been tested with) general graphs + with circular depedencies. + + \nosubgrouping + */ + class GraphItemModel : public QAbstractItemModel + { + Q_OBJECT + + public: + + //! Value to be added to the Qt::UserRole constant to get corresponding GraphItemModel data role. + enum DataRole + { + POSITION_X = 1, + POSITION_Y = 2, + DIMENSION_X = 3, + DIMENSION_Y = 4 + }; + + /*! \name Custom Graph Model Interface *///---------------------------- + //@{ + public: + + GraphItemModel( Node::List& rootNodes ); + + void init( Node::List& rootNodes ); + + void visit( Node& node, int row = 0, int column = 0, int maxDepth = 1 ); + + void visit( Node& node, int row, int column, Node::Set& marked, int maxDepth = 1 ); + + QPersistentModelIndex* getNodePersistentIndex( Node* node ) const; + + void setStyleManager( Style::Manager* styleManager ) { _styleManager = styleManager; } + + typedef std::map< Node*, QPersistentModelIndex* > NodePersistentIndexMap; + + protected: + + NodePersistentIndexMap _nodePersistentIndexMap; + + Node::List& _rootNodes; + + Style::Manager* _styleManager; + //@} + //--------------------------------------------------------------------- + + + + /*! \name QT Model Interface Management *///--------------------------- + //@{ + public: + + virtual QVariant data( const QModelIndex& index, int role ) const; + + virtual bool hasChildren( const QModelIndex & parent = QModelIndex( ) ) const; + + virtual Qt::ItemFlags flags( const QModelIndex& index ) const; + + virtual QModelIndex index( int row, int column, const QModelIndex &parent = QModelIndex( ) ) const; + + virtual QModelIndex parent( const QModelIndex& index ) const; + + virtual int rowCount( const QModelIndex& parent = QModelIndex( ) ) const; + + virtual int columnCount( const QModelIndex& parent = QModelIndex( ) ) const; + + virtual bool setData ( const QModelIndex& index, const QVariant& value, int role = Qt::EditRole ); + + virtual void reset( ) { QAbstractItemModel::reset( ); } + //@} + //--------------------------------------------------------------------- + + signals: + + void rowsAboutToBeInserted( const QModelIndex& parent, int start, int end ); + + void rowsAboutToBeRemoved( const QModelIndex& parent, int start, int end ); + + void rowsInserted( const QModelIndex& parent, int start, int end ); + + void rowsRemoved( const QModelIndex& parent, int start, int end ); + + public: + + void nodeInsertedBegin( qan::Node& node ); + + void nodeInserted( qan::Node& node ); + + void nodeRemovedBegin( qan::Node& node ); + + void nodeRemoved( qan::Node& node ); + + void nodeChanged( qan::Node& node ); + }; +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // canGraphItemModel_h + + ============================================================ --- libs/qanava/src/qanGraphItemView.cpp 2650092cb13e790fe4e6e81a7476978b0fa8a2b6 +++ libs/qanava/src/qanGraphItemView.cpp 2650092cb13e790fe4e6e81a7476978b0fa8a2b6 @@ -0,0 +1,642 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file canGraphItemView.cpp +// \author Benoit Autheman (address@hidden) +// \date 2005 November 22 +//----------------------------------------------------------------------------- + + +// Qanava headers +#include "./qanGraph.h" +#include "./qanEdge.h" +#include "./qanGraphItemView.h" +#include "./qanGraphItemModel.h" +#include "./qanGrid.h" + + +// STD headers +#include + + +// QT headers +#include +#include +#include + + +//----------------------------------------------------------------------------- +namespace qan { // ::qan + + +/* View Construction *///------------------------------------------------------ +/*! + \param graph Already available graph used in this view GraphItemModel. + \param backgroundColor Graph canvas background color. + \param autoExpand Model are fully explored when auto expansion is activated (default), + if false, only the first hierarchy level of the model is viewed, + and node must be expanded manually with method expandNode(). + */ +GraphItemView::GraphItemView( QWidget* parent, Graph* graph, QColor backgroundColor, QSize size, bool autoExpand ) : + QAbstractItemView( parent ), + _graphicsScene( 0 ), + _graphicsView( 0 ), + _graph( graph ), + _autoExpand( autoExpand ), + _layout( new Random( ) ) +{ + if ( _graph == 0 ) + _graph = new Graph( ); + + registerGraphicNodeFactory( new NodeItem::DefaultFactory( ) ); + + _graphicsScene = new QGraphicsScene( this ); + _graphicsView = new GraphicsView( _graphicsScene, this ); + connect( _graphicsView, SIGNAL(itemDoubleClicked(QGraphicsItem*)), this, SLOT(graphicItemDoubleClicked(QGraphicsItem*)) ); + + QPalette palette; + palette.setColor( backgroundRole( ), backgroundColor ); + viewport( )->setPalette( palette ); + + setHorizontalScrollBarPolicy( Qt::ScrollBarAsNeeded ); + setVerticalScrollBarPolicy( Qt::ScrollBarAsNeeded ); + + setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + clear( ); + update( ); +} + +GraphItemView::~GraphItemView( ) +{ + _styleManager.clear( ); +} + +/*! + \note Style manager is not cleared since this method original aim in GraphicsView only concern geometry. + */ +void GraphItemView::clear( ) +{ + // Clear all mappings + _modelIndexNodeMap.clear( ); + _modelIndexGraphicItemMap.clear( ); + _graphicItemModelIndexMap.clear( ); + _nodeGraphicItemMap.clear( ); + + // Clear all items in the graphic view + // FIXME: all items are removed, not just the one modelling the actual graph + QList< QGraphicsItem* > items = _graphicsView->scene( )->items( ); + QList< QGraphicsItem* >::iterator itemIterator; + for( itemIterator = items.begin( ); itemIterator != items.end( ); ++itemIterator ) + _graphicsView->scene( )->removeItem( *itemIterator ); + + if ( _layout != 0 ) + delete _layout; + _layout = new Random( ); +} +//----------------------------------------------------------------------------- + + + +/* QT View Interface Management *///------------------------------------------- +void GraphItemView::setModel ( QAbstractItemModel* model ) +{ + // Clear this view (we must clear before setting the new model since model will be set to 0) + clear( ); + QAbstractItemView::setModel( model ); +} + +QModelIndex GraphItemView::indexAt( const QPoint& p ) const +{ + return QModelIndex( ); +} + +void GraphItemView::visitModelIndex( QAbstractItemModel& model, QModelIndex& modelIndex, QModelIndex& parentIndex, int depth ) +{ + if ( depth-- == 0 ) + return; + + if ( !modelIndex.isValid( ) ) + return; + + if ( GraphItemView::getModelIndexNode( modelIndex ) != 0 ) + return; + + mapIndex( modelIndex, parentIndex ); + + for ( int i = 0; i < model.rowCount( modelIndex ); i++ ) + { + QModelIndex index = model.index( i, 0, modelIndex ); + visitModelIndex( model, index, modelIndex, depth ); + } +} + +void GraphItemView::mapIndex( QModelIndex& index, QModelIndex& parent ) +{ + if ( !index.isValid( ) ) + return; + + // Check if the index isn't already mapped + if ( _modelIndexNodeMap.find( index.internalPointer( ) ) != _modelIndexNodeMap.end( ) ) + return; + + if ( index.internalPointer( ) != 0 && _graph != 0 ) + { + // Map a model index to a graph node + Node* node = static_cast< Node* >( index.internalPointer( ) ); + if ( node == 0 || !_graph->hasNode( node ) ) + { + // Create a new graph node and map it + node = _graph->insertNode( "" ); + updateModelIndexNode( index, node ); + + // Create an edge between this node and its parent node + Node* parentNode = getModelIndexNode( parent ); + if ( node != 0 && parentNode != 0 ) + _graph->addEdge( *parentNode, *node ); + } + + + // Map a model index to a graphic item + if ( node != 0 ) + { + _modelIndexNodeMap.insert( index.internalPointer( ), node ); + + // Get node type associed style + Style* style = _styleManager.getEmptyStyle( ); + Style* nodeStyle = _styleManager.getStyle( node ); + if ( nodeStyle != 0 ) + style = nodeStyle; + nodeStyle = _styleManager.getStyle( node->getType( ) ); + if ( nodeStyle != 0 ) + style = nodeStyle; + + AbstractNodeItem* nodeItem = 0; + if ( style != 0 ) + nodeItem = createGraphicNode( *node, _styleManager, *style, 0, _graphicsScene, QPoint( 1, 1 ), node->getLabel( ) ); + if ( nodeItem != 0 ) + { + _modelIndexGraphicItemMap.insert( index.internalPointer( ), nodeItem ); + _nodeGraphicItemMap.insert( node, nodeItem ); + QGraphicsItem* gi = nodeItem->getGraphicsItem( ); + _graphicItemModelIndexMap.insert( nodeItem->getGraphicsItem( ), index.internalPointer( ) ); + _nodeModelIndexMap.insert( node, index ); + } + + // Generate arrows for the model + Edge::Set edges = Edge::Set::fromList( node->getInEdges( ) ); + edges.unite( Edge::Set::fromList( node->getOutEdges( ) ) ); + foreach ( Edge* edge, edges ) + { + EdgeGraphicItemMap::iterator canvasEdgeIter = _edgeGraphicItemMap.find( edge ); + if ( canvasEdgeIter == _edgeGraphicItemMap.end( ) ) + { + NodeItem* src = static_cast< NodeItem* >( getNodeGraphicItem( &edge->getSrc( ) ) ); + NodeItem* dst = static_cast< NodeItem* >( getNodeGraphicItem( &edge->getDst( ) ) ); + if ( src != 0 && dst != 0 ) + { + EdgeItem* edgeItem = new EdgeItem( 0, _graphicsScene, src, dst ); + _edgeGraphicItemMap.insert( edge, edgeItem ); + } + } + else + canvasEdgeIter.value( )->show( ); // The arrow might have been previously hiden, so set it visible again + } + } + } +} + +void GraphItemView::updateModelIndexGraphicItem( QModelIndex& index ) +{ + if ( !index.isValid( ) ) + return; + +} + +/*! This method is usefull when this view model is a QT model (ex QDirModel) where items are + not internally mapped to an existing graph node. For example, if a node has just been + created for a specific model index, the notification mecanism of the model has eventually + not been used, this method can be used to force the associed node data update). + + \param index Item index in this view model that must be used to refresh its associed node internal data. + \param node Allow user to manually provide the node mapped to the given model item index. + */ +void GraphItemView::updateModelIndexNode( QModelIndex& index, Node* node ) +{ + if ( !index.isValid( ) ) + return; + + if ( index.column( ) != 0 ) + return; + + if ( node == 0 ) + node = getModelIndexNode( index ); + if ( node != 0 ) + { + double positionX( 0.0 ); + double positionY( 0.0 ); + double dimensionX( 75.0 ); + double dimensionY( 45.0 ); + + QVariant v = model( )->data( index, Qt::DisplayRole ); + QString content = ( v.type( ) == QVariant::String ? v.toString( ).toAscii( ) : "ERROR" ); + + v = model( )->data( index, Qt::UserRole + GraphItemModel::POSITION_X ); + positionX = ( v.type( ) == QVariant::Double ? v.toDouble( ) : 0.0 ); + v = model( )->data( index, Qt::UserRole + GraphItemModel::POSITION_Y ); + positionY = ( v.type( ) == QVariant::Double ? v.toDouble( ) : 0.0 ); + + v = model( )->data( index, Qt::UserRole + GraphItemModel::DIMENSION_X ); + dimensionX = ( v.type( ) == QVariant::Double ? v.toDouble( ) : dimensionX ); + v = model( )->data( index, Qt::UserRole + GraphItemModel::DIMENSION_Y ); + dimensionY = ( v.type( ) == QVariant::Double ? v.toDouble( ) : dimensionY ); + + node->setLabel( content ); + node->setPosition( positionX, positionY ); + node->setDimension( dimensionX, dimensionY ); + + // Update node style (if such a style is present) + Style* style = _styleManager.getStyle( node ); + if ( style == 0 ) + { + style = new Style( "" ); + _styleManager.setStyle( node, *style ); + } + + if ( style != 0 ) + { + v = model( )->data( index, Qt::BackgroundColorRole ); + QColor c = ( v.type( ) == QVariant::Color ? v.value( ) : QColor( ) ); + if ( c.isValid( ) ) + style->addColor( "backcolor", c.red( ), c.green( ), c.blue( ) ); + + v = model( )->data( index, Qt::DecorationRole ); + QIcon i = ( v.type( ) == QVariant::Icon ? v.value( ) : QIcon( ) ); + if ( !i.isNull( ) ) + style->addIcon( "icon", i ); + } + } + update( ); // TODO: update uniquement pour la zone graphique du node +} + +void GraphItemView::expand( const Node* node ) +{ + if ( model( ) != 0 ) + { + QModelIndex noItem; + QModelIndex nodeIndex = _nodeModelIndexMap.value( node, QModelIndex( ) ); + if ( nodeIndex.isValid( ) ) + { + for ( int row = 0; row < model( )->rowCount( nodeIndex ); row++ ) + { + QModelIndex subNodeIndex = model( )->index( row, 0, nodeIndex ); + if ( subNodeIndex.isValid( ) ) + visitModelIndex( *model( ), subNodeIndex, nodeIndex, ( _autoExpand ? 5000 : 1 ) ); + } + } + } +} + +void GraphItemView::graphicItemDoubleClicked( QGraphicsItem* item ) +{ + Node* node = getGraphicItemNode( item ); + if ( node != 0 ) + emit nodeDoubleClicked( node ); +} + +void GraphItemView::reset( ) +{ + QAbstractItemView::reset( ); + + if ( model( ) != 0 ) + { + // Explore model and map it items to graphical elements + for ( int i = 0; i < model( )->rowCount( ); i++ ) + { + QModelIndex index = model( )->index( i, 0 ); + QModelIndex noItem; + visitModelIndex( *model( ), index, noItem, ( _autoExpand ? 5000 : 1 ) ); + } + + connect( model( ), SIGNAL( rowsInserted(const QModelIndex&,int,int) ), this, SLOT( rowsInserted(const QModelIndex&,int,int) ) ); + connect( model( ), SIGNAL( rowsAboutToBeInserted(const QModelIndex&,int,int) ), this, SLOT( rowsAboutToBeInserted(const QModelIndex&,int,int) ) ); + connect( model( ), SIGNAL( rowsRemoved(const QModelIndex&,int,int) ), this, SLOT( rowsRemoved(const QModelIndex&,int,int) ) ); + connect( model( ), SIGNAL( rowsAboutToBeRemoved(const QModelIndex&,int,int) ), this, SLOT( rowsAboutToBeRemoved(const QModelIndex&,int,int) ) ); + } +} + +void GraphItemView::rowsAboutToBeInserted( const QModelIndex& parent, int start, int end ) +{ +} + +void GraphItemView::rowsInserted( const QModelIndex& parent, int start, int end ) +{ + QModelIndex index( parent ); + QAbstractItemView::rowsInserted( parent, start, end ); + + for ( int row = start; row < end; row++ ) + { + QModelIndex nodeIndex = model( )->index( row, 0, parent ); + visitModelIndex( *model( ), nodeIndex, const_cast< QModelIndex& >( parent ), ( _autoExpand ? 5000 : 1 ) ); + } +} + +void GraphItemView::rowsAboutToBeRemoved( const QModelIndex & parent, int start, int end ) +{ + //QAbstractItemView::rowsAboutToBeRemoved( parent, start, end ); + for ( int row = start; row < end; row++ ) + { + QModelIndex nodeIndex = model( )->index( row, 0, parent ); + if ( !nodeIndex.isValid( ) ) + continue; + ModelIndexGraphicItemMap::iterator indexIter = _modelIndexGraphicItemMap.find( nodeIndex.internalPointer( ) ); + if ( indexIter != _modelIndexGraphicItemMap.end( ) ) + { + NodeItem* nodeItem = static_cast< NodeItem* >( indexIter.value( ) ); + + Node* node = getGraphicItemNode( nodeItem ); + if ( node != 0 ) + { + // Remove arrows + Edge::List edges; + std::copy( node->getInEdges( ).begin( ), node->getInEdges( ).end( ), std::back_insert_iterator< Edge::List >( edges ) ); + std::copy( node->getOutEdges( ).begin( ), node->getOutEdges( ).end( ), std::back_insert_iterator< Edge::List >( edges ) ); + for ( Edge::List::iterator edgeIter = edges.begin( ); edgeIter != edges.end( ); edgeIter++ ) + { + EdgeGraphicItemMap::iterator canvasEdgeIter = _edgeGraphicItemMap.find( *edgeIter ); + if ( canvasEdgeIter != _edgeGraphicItemMap.end( ) ) + { + ( canvasEdgeIter.value( ) )->update( 0, 0 ); + _edgeGraphicItemMap.remove( *edgeIter ); + } + } + + _nodeGraphicItemMap.remove( node ); + } + + _modelIndexNodeMap.remove( nodeIndex.internalPointer( ) ); + _modelIndexGraphicItemMap.remove( nodeIndex.internalPointer( ) ); + _graphicItemModelIndexMap.remove( nodeItem->getGraphicsItem( ) ); + delete nodeItem; + } + } +} + +void GraphItemView::rowsRemoved( const QModelIndex& parent, int first, int last ) +{ + +} + +void GraphItemView::dataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ) +{ + if ( !topLeft.isValid( ) ) + return; + + QModelIndex changedDataIndex( topLeft ); + Node* topLeftNode = getModelIndexNode( changedDataIndex ); + if ( topLeftNode != 0 ) + { + NodeItem* topLeftItem = static_cast< NodeItem* >( getNodeGraphicItem( topLeftNode ) ); + if ( topLeftItem != 0 ) + { + Style* style = _styleManager.getStyle( topLeftNode ); + if ( style == 0 ) + style = _styleManager.getStyle( topLeftNode->getType( ) ); + if ( style != 0 ) + { + //topLeftItem->update( topLeftNode->getLabel( ), *style ); // FIXME + //topLeftItem->updateGeometry( *style ); + } + else + { + Style emptyStyle( "empty" ); + //topLeftItem->update( topLeftNode->getLabel( ), emptyStyle ); // FIXME + //topLeftItem->updateGeometry( emptyStyle ); + } + } + } + + update( ); +} +//----------------------------------------------------------------------------- + + + +/* Model Index and Graphic Item Management *///-------------------------------- +Node* GraphItemView::getGraphicItemNode( const QGraphicsItem* item ) +{ + if ( item == 0 ) + return 0; + GraphicItemModelIndexMap::const_iterator graphicItemIter = _graphicItemModelIndexMap.find( item ); + if ( graphicItemIter != _graphicItemModelIndexMap.end( ) ) + { + // Return the graph node associed to the model index found + ModelIndexNodeMap::iterator nodeIter = _modelIndexNodeMap.find( graphicItemIter.value( ) ); + if ( _modelIndexNodeMap.contains( graphicItemIter.value( ) ) ) + return _modelIndexNodeMap.value( graphicItemIter.value( ) ); + } + return 0; +} + +QGraphicsItem* GraphItemView::getNodeGraphicItem( const Node* node ) +{ + if ( node == 0 ) + return 0; + NodeGraphicItemMap::const_iterator nodeIter = _nodeGraphicItemMap.find( node ); + if ( nodeIter != _nodeGraphicItemMap.end( ) ) + return ( nodeIter.value( ) )->getGraphicsItem( ); + return 0; +} + +Node* GraphItemView::getModelIndexNode( QModelIndex& index ) +{ + if ( !index.isValid( ) ) + return 0; + + if ( _modelIndexNodeMap.contains( index.internalPointer( ) ) ) + return _modelIndexNodeMap.value( index.internalPointer( ), 0 ); + return 0; +} + +Node* GraphItemView::getCurrentNode( QPoint& p ) +{ + /*Item::List collisions; + //getCanvas( )->getCollisions( p, collisions ); // FIXME + if ( collisions.begin( ) != collisions.end( ) ) + { + Item* item = *collisions.begin( ); + //if ( getCanvas( ) != 0 && !getCanvas( )->isFreed( item ) ) // FIXME + // return getGraphicItemNode( item ); + }*/ + return 0; +} +//----------------------------------------------------------------------------- + + + +/* Style Management *///------------------------------------------------------- +void GraphItemView::applyStyle( Node* node, Style* style ) +{ + if ( node != 0 && style != 0 ) + { + _styleManager.setStyle( node, *style ); + + // Update node graphic item with the style content + NodeGraphicItemMap::const_iterator nodeIter = _nodeGraphicItemMap.find( node ); + if ( nodeIter != _nodeGraphicItemMap.end( ) ) + { + AbstractNodeItem* item = nodeIter.value( ); + item->setStyle( style ); + } + } +} +//----------------------------------------------------------------------------- + + + +/* Layout Management *///------------------------------------------------------ +/*! + \note The ownership of the given layout object is transferred to this view. + \param layout New layout to use with this view (must not be 0). + */ +void GraphItemView::setGraphLayout( Layout* layout, QProgressDialog* progress ) +{ + if ( layout == 0 ) + return; + + if ( _layout != 0 ) + { + //delete _layout; + _layout = 0; + } + _layout = layout; +} + +void GraphItemView::layoutGraph( QProgressDialog* progress, Layout* layout, int step, Node* except ) +{ + if ( _graph == 0 ) + return; + if ( _layout == 0 && layout == 0 ) + return; + + QRectF r = getGraphicsView( )->sceneRect( ); + r.setWidth( std::max( r.width( ), ( double )width( ) ) ); + r.setHeight( std::max( r.height( ), ( double )height( ) ) ); + + if ( layout != 0 ) + layout->layout( *_graph, *getGraphicsView( )->getGrid( ), r, progress, step ); + else if ( _layout != 0 && getGraphicsView( )->getGrid( ) != 0 ) + _layout->layout( *_graph, *getGraphicsView( )->getGrid( ), r, progress, step ); + + // Update all nodes positions + { + NodeGraphicItemMap::iterator nodeIter = _nodeGraphicItemMap.begin( ); + for ( ; nodeIter != _nodeGraphicItemMap.end( ); nodeIter++ ) + { + const Node* node = nodeIter.key( ); + AbstractNodeItem* item = nodeIter.value( ); + if ( node != 0 && item != 0 && node != except ) + item->getGraphicsItem( )->setPos( node->getPosition( )( 0 ), node->getPosition( )( 1 ) ); + } + } + _graphicsView->update( ); + update( ); +} + +VectorF GraphItemView::getBoundingBox( const Node::List& nodes ) +{ + VectorF bbox( 2 ); + bbox( 0 ) = 0; + bbox( 1 ) = 0; + + Node::List::const_iterator nodeIter = nodes.begin( ); + for ( ; nodeIter != nodes.end( ); nodeIter++ ) + { + const Node* node = *nodeIter; + const VectorF& position = node->getPosition( ); + const VectorF& dimension = node->getDimension( ); + + if ( position( 0 ) + dimension( 0 ) > bbox( 0 ) ) + bbox( 0 ) = position( 0 ) + dimension( 0 ); + if ( position( 1 ) + dimension( 1 ) > bbox( 1 ) ) + bbox( 1 ) = position( 1 ) + dimension( 1 ); + } + return bbox; +} +//----------------------------------------------------------------------------- + + + +/* ScrollArea Management *///-------------------------------------------------- +void GraphItemView::resizeEvent( QResizeEvent* event ) +{ + int w = width( ); + int h = height( ); + _graphicsView->resize( w, h ); +} + +bool GraphItemView::eventFilter( QObject *o, QEvent *e ) +{ + return false; +} +//----------------------------------------------------------------------------- + + + +/* Graphic Node Factory Management *///---------------------------------------- +/*! Ownership for the factory is transfrerred to this graph item view. + \param factory Graphic node factory that must be used when generating node graphic counterpart. + */ +void GraphItemView::registerGraphicNodeFactory( AbstractNodeItem::AbstractFactory* factory ) +{ + assert( factory != 0 ); + if ( factory->isDefaultFactory( ) ) + _factories.push_back( factory ); // Default factories must be tested after standard ones + else + _factories.push_front( factory ); +} + +/*! + \return A adequat graphic counterpart for node, or a node from a default factory, or 0 if no (default) factories are registered. + */ +AbstractNodeItem* GraphItemView::createGraphicNode( Node& node, Style::Manager& styleManager, Style& style, + QGraphicsItem* parent, QGraphicsScene* scene, + QPoint origin, const QString& label ) +{ + AbstractNodeItem::AbstractFactory::List::iterator factoryIter = _factories.begin( ); + for ( ; factoryIter != _factories.end( ); factoryIter++ ) + { + AbstractNodeItem::AbstractFactory* factory = *factoryIter; + if ( factory->isDefaultFactory( ) ) // Default factories create graphic node whatever the node type is + return factory->create( node, styleManager, style, parent, scene, origin, label ); + + if ( node.getType( ) == factory->getNodeType( ) ) + return factory->create( node, styleManager, style, parent, scene, origin, label ); + } + return 0; // No adequat nor default factory found +} +//----------------------------------------------------------------------------- + + +} // ::qan +//----------------------------------------------------------------------------- + + ============================================================ --- libs/qanava/src/qanGraphItemView.h 4ff6a1e3a993c364ed5dfb4eaee72c8919814ad0 +++ libs/qanava/src/qanGraphItemView.h 4ff6a1e3a993c364ed5dfb4eaee72c8919814ad0 @@ -0,0 +1,295 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file canGraphItemView.h +// \author Benoit Autheman (address@hidden) +// \date 2005 November 22 +//----------------------------------------------------------------------------- + + +#ifndef canGraphItemView_h +#define canGraphItemView_h + + +// Qanava headers +#include "./qanNode.h" +#include "./qanGraph.h" +#include "./qanLayout.h" +#include "./qanItemGeom.h" +#include "./qanGraphicsView.h" + + +// QT headers +#include +#include +#include +#include +#include +#include + + +//----------------------------------------------------------------------------- +namespace qan { // ::qan + + class Grid; + + + //! Display a graph provided as a QT Interview based model. + /*! + \nosubgrouping + */ + class GraphItemView : public QAbstractItemView + { + Q_OBJECT + + /*! \name View Construction *///--------------------------------------- + //@{ + public: + + //! GraphItemView constructor with optionnal settings for graph widget initialization. + GraphItemView( QWidget* parent, Graph* graph = 0, + QColor backColor = QColor( 170, 171, 205 ), QSize size = QSize( 300, 150 ), + bool autoExpand = true ); + + //! GraphItemView virtual destructor. + virtual ~GraphItemView( ); + + //! Clear this item view of all elements and set its model to 0. + void clear( ); + + QGraphicsScene* getGraphicsScene( ) { return _graphicsScene; } + + GraphicsView* getGraphicsView( ) { return _graphicsView; } + + void setGrid( GridItem* grid ) { _graphicsView->setGrid( grid ); } + + virtual QSize sizeHint( ) const { return QSize( 300, 250 ); } + + private: + + QGraphicsScene* _graphicsScene; + + GraphicsView* _graphicsView; + + Graph* _graph; + //@} + //--------------------------------------------------------------------- + + + + /*! \name QT View Interface Management *///---------------------------- + //@{ + public: + + virtual void setModel ( QAbstractItemModel* model ); + + virtual QModelIndex indexAt( const QPoint& p ) const; + + virtual void scrollTo( const QModelIndex& index, ScrollHint hint = EnsureVisible ) { } + + virtual QRect visualRect( const QModelIndex& index ) const { return QRect( 0, 0, 0, 0 ); } + + virtual int horizontalOffset( ) const { return 0; } + + virtual bool isIndexHidden ( const QModelIndex & index ) const { return false; } + + virtual QModelIndex moveCursor ( CursorAction cursorAction, Qt::KeyboardModifiers modifiers ) { return QModelIndex( ); } + + virtual void setSelection ( const QRect& rect, QItemSelectionModel::SelectionFlags flags ) { } + + virtual int verticalOffset( ) const { return 0; } + + virtual QRegion visualRegionForSelection ( const QItemSelection& selection ) const { return QRegion( 0, 0, 0, 0 ); } + + protected: + + //! Visit a model index and its child index, and map encoutered indexes. + void visitModelIndex( QAbstractItemModel& model, QModelIndex& modelIndex, QModelIndex& parentIndex, int depth = -1 ); + + //! Map a given model index to a graph node and a canvas geometric items. + void mapIndex( QModelIndex& index, QModelIndex& parent ); + + //! Update an index associed graphic item (and eventually node) with the current index data. + void updateModelIndexGraphicItem( QModelIndex& index ); + + //! Update a node from data comming from its associed model index (assuming the model data are more accurate than the node ones). + void updateModelIndexNode( QModelIndex& index, Node* node = 0 ); + + public slots: + + void expand( const Node* node ); + + protected slots: + + void graphicItemDoubleClicked( QGraphicsItem* item ); + + virtual void reset( ); + + virtual void rowsAboutToBeInserted( const QModelIndex& parent, int start, int end ); + + virtual void rowsInserted( const QModelIndex& parent, int start, int end ); + + virtual void rowsAboutToBeRemoved( const QModelIndex & parent, int start, int end ); + + virtual void rowsRemoved( const QModelIndex &parent, int first, int last ); + + virtual void dataChanged( const QModelIndex& topLeft, const QModelIndex& bottomRight ); + //@} + //--------------------------------------------------------------------- + + + + /*! \name Model Index and Graphic Item Management *///----------------- + //@{ + public: + + //! Get a graph node associed to a given graphic item. + Node* getGraphicItemNode( const QGraphicsItem* item ); + + //! Get a graphic item associed to a given graph node. + QGraphicsItem* getNodeGraphicItem( const Node* node ); + + //! Get a graph node associed to a given model item index. + Node* getModelIndexNode( QModelIndex& index ); + + //! Get the node that is currently under the mouse pointer. + Node* getCurrentNode( QPoint& p ); + + signals: + + void nodeDoubleClicked( qan::Node* node ); + + protected: + + typedef QMap< void*, Node* > ModelIndexNodeMap; + + typedef QMap< void*, AbstractNodeItem* > ModelIndexGraphicItemMap; + + typedef QMap< const Edge*, EdgeItem* > EdgeGraphicItemMap; + + typedef QMap< const Node*, AbstractNodeItem* > NodeGraphicItemMap; + + typedef QMap< const QGraphicsItem*, void* > GraphicItemModelIndexMap; + + typedef QMap< const Node*, QModelIndex > NodeModelIndexMap; + + ModelIndexNodeMap _modelIndexNodeMap; + + ModelIndexGraphicItemMap _modelIndexGraphicItemMap; + + EdgeGraphicItemMap _edgeGraphicItemMap; + + NodeGraphicItemMap _nodeGraphicItemMap; + + GraphicItemModelIndexMap _graphicItemModelIndexMap; + + NodeModelIndexMap _nodeModelIndexMap; + + bool _autoExpand; + //@} + //--------------------------------------------------------------------- + + + + /*! \name Style Management *///---------------------------------------- + //@{ + public: + + //! Get the graph view style manager. + Style::Manager& getStyleManager( ) { return _styleManager; } + + //! Set a node style. + void applyStyle( Node* node, Style* style ); + + protected: + + //! Graph view style manager. + Style::Manager _styleManager; + //@} + //--------------------------------------------------------------------- + + + + /*! \name Layout Management *///--------------------------------------- + //@{ + public: + + //! . + void setGraphLayout( Layout* layout, QProgressDialog* progress = 0 ); + + //! . + Layout* getGraphLayout( Layout& layout ) { return _layout; } + + //! . + const Layout* getGraphLayout( Layout& layout ) const { return _layout; } + + //! . + void layoutGraph( QProgressDialog* progress = 0, Layout* layout = 0, int step = -1, Node* except = 0 ); + + //! Get bounding box for a group of nodes. + static VectorF getBoundingBox( const Node::List& nodes ); + + protected: + + //! . + Layout* _layout; + //@} + //--------------------------------------------------------------------- + + + + /*! \name ScrollArea Management *///----------------------------------- + //@{ + protected: + + void resizeEvent( QResizeEvent* event ); + + bool eventFilter( QObject* o, QEvent* e ); + //@} + //--------------------------------------------------------------------- + + + + /*! \name Graphic Node Factory Management *///------------------------- + //@{ + public: + + //! Register a graphic node factory for a certain type of nodes (from graph topology to graph graphic visualization). + void registerGraphicNodeFactory( AbstractNodeItem::AbstractFactory* factory ); + + //! Create a graphic node from a topological node using the currently registered graphic node factories. + AbstractNodeItem* createGraphicNode( Node& node, Style::Manager& styleManager, Style& style, + QGraphicsItem* parent, QGraphicsScene* scene, + QPoint origin, const QString& label ); + + private: + + AbstractNodeItem::AbstractFactory::List _factories; + //@} + //--------------------------------------------------------------------- + }; +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // canGraphItemView_h + ============================================================ --- libs/qanava/src/qanGraphicsView.cpp 97aaefe368fdc15c63d293e9d40ef1630414fbea +++ libs/qanava/src/qanGraphicsView.cpp 97aaefe368fdc15c63d293e9d40ef1630414fbea @@ -0,0 +1,207 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file canGraphicsView.cpp +// \author Benoit Autheman (address@hidden) +// \date 2006 July 29 +//----------------------------------------------------------------------------- + + +// Qanava headers +#include "./qanGraphicsView.h" +#include "./qanGridItem.h" + + +// QT headers +#include + + +//----------------------------------------------------------------------------- +namespace qan { // ::qan + + +/* GraphicsView Constructors/Destructors *///---------------------------------- +GraphicsView::GraphicsView( QWidget* parent ) : + QGraphicsView( parent ), + _grid( 0 ), + _controllerManager( this ), + _zoom( 1.0 ), + _zoomMaxFactor( 2 ) +{ + setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); + setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); + setAlignment( Qt::AlignLeft | Qt::AlignTop ); + + getControllerManager( ).registerController( new PanController( *this ) ); + getControllerManager( ).registerController( new ZoomWindowController( *this ) ); + getControllerManager( ).registerController( new ZoomController( *this ) ); + + setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); +} + +GraphicsView::GraphicsView( QGraphicsScene* scene, QWidget* parent ) : + QGraphicsView( scene, parent ), + _grid( 0 ), + _controllerManager( this ), + _zoom( 1.0 ), + _zoomMaxFactor( 2 ) +{ + setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); + setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); + setAlignment( Qt::AlignLeft | Qt::AlignTop ); + + getControllerManager( ).registerController( new PanController( *this ) ); + getControllerManager( ).registerController( new ZoomWindowController( *this ) ); + getControllerManager( ).registerController( new ZoomController( *this ) ); + + setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); + if ( scene != 0 ) + scene->setItemIndexMethod( QGraphicsScene::NoIndex ); +} +//----------------------------------------------------------------------------- + + + +/* Grid Management *///-------------------------------------------------------- +void GraphicsView::drawBackground( QPainter* painter, const QRectF& rect ) +{ + if ( _grid != 0 ) + _grid->drawBackground( *painter, rect ); +} +//----------------------------------------------------------------------------- + + + +/* View Controller Management *///----------------------------------------------- +void GraphicsView::keyPressEvent( QKeyEvent* e ) +{ + QGraphicsView::keyPressEvent( e ); + + if ( _controllerManager.keyPressEvent( e ) ) + viewport( )->update( ); +} + +void GraphicsView::mousePressEvent( QMouseEvent* e ) +{ + QGraphicsView::mousePressEvent( e ); + + if ( _controllerManager.mousePressEvent( e ) ) + viewport( )->update( ); +} + +void GraphicsView::mouseReleaseEvent( QMouseEvent* e ) +{ + QGraphicsView::mouseReleaseEvent( e ); + + if ( _controllerManager.mouseReleaseEvent( e ) ) + viewport( )->update( ); +} + +void GraphicsView::mouseMoveEvent( QMouseEvent* e ) +{ + QGraphicsView::mouseMoveEvent( e ); + + if ( _controllerManager.mouseMoveEvent( e ) ) + viewport( )->update( ); +} + +void GraphicsView::mouseDoubleClickEvent( QMouseEvent* e ) +{ + QGraphicsView::mouseDoubleClickEvent( e ); + + if ( _controllerManager.mouseDoubleClickEvent( e ) ) + viewport( )->update( ); + + QGraphicsItem* item = itemAt( e->pos( ) ); + if ( item != 0 ) + emit itemDoubleClicked( item->parentItem( ) != 0 ? item->parentItem( ) : item ); +} + +void GraphicsView::wheelEvent( QWheelEvent* e ) +{ + QGraphicsView::wheelEvent( e ); + + if ( _controllerManager.wheelEvent( e ) ) + viewport( )->update( ); +} + +QAction* GraphicsView::getAction( QString name ) +{ + Controller* controller = _controllerManager.getController( name ); + if ( controller != 0 ) + return controller->getAction( ); + + Controller::Manager::iterator controllerIter = _controllerManager.begin( ); + for ( ; controllerIter != _controllerManager.end( ); controllerIter++ ) + { + QAction* action = ( *controllerIter )->getAction( name ); // Create an action by name + if ( action != 0 ) + return action; + } + return 0; +} +//----------------------------------------------------------------------------- + + + +/* Zoom Management *///------------------------------------------------------- +void GraphicsView::setZoom( double zoom ) +{ + qreal factor = matrix( ).scale( zoom, zoom ).mapRect( QRectF( 0., 0., 1., 1.) ).width( ); + if ( factor < 0.07 || factor > 100 ) + return; + + if ( zoom > 0.1 ) + { + _zoom = zoom; + QMatrix m; + m.scale( zoom, zoom ); + setMatrix( m ); + } +} + +/*! + With a maximum zoom factor of 2 (this default value can be changed with setZoomMaxFactor() method), a + normal zoom (1) is obtained by setting the zoom to 50% (half of the maximum magnification + factor). + + \param value Zoom value in percent between (almost) 0 and the max zoom magnification (must be 0_1 to 100). + */ +void GraphicsView::setZoomPercent( int value ) +{ + double zoomPercent = value; + if ( zoomPercent == 0 ) + zoomPercent = 1; + if ( zoomPercent > 100 ) + zoomPercent = 100; + + // Set zoom factor + double zoomMaxFactor = ( _zoomMaxFactor < 0.1 ? 0.1 : _zoomMaxFactor ); + setZoom( zoomPercent / 100.f * zoomMaxFactor ); +} +//----------------------------------------------------------------------------- + + +} // ::qan +//----------------------------------------------------------------------------- + + ============================================================ --- libs/qanava/src/qanGraphicsView.h 6f582f9c7e90a38cb818d892cd999b9b1d86969f +++ libs/qanava/src/qanGraphicsView.h 6f582f9c7e90a38cb818d892cd999b9b1d86969f @@ -0,0 +1,152 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file canGraphicsView.h +// \author Benoit Autheman (address@hidden) +// \date 2006 July 29 +//----------------------------------------------------------------------------- + + +#ifndef canGraphicsView_h +#define canGraphicsView_h + + +// Qanava headers +#include "./qanController.h" + + +// QT headers +#include +#include +#include + + +//----------------------------------------------------------------------------- +namespace qan { // ::qan + + class GridItem; + + class GraphicsView : public QGraphicsView + { + Q_OBJECT + + /*! \name GraphicsView Constructors/Destructors *///------------------- + //@{ + public: + + GraphicsView( QWidget* parent = 0 ); + + GraphicsView( QGraphicsScene* scene, QWidget* parent = 0 ); + + virtual QSize sizeHint( ) const { return QSize( 300, 250 ); } + //@} + //--------------------------------------------------------------------- + + + + /*! \name Grid Management *///----------------------------------------- + //@{ + public: + + void setGrid( GridItem* grid ) { _grid = grid; } + + GridItem* getGrid( ) { return _grid; } + + protected: + + virtual void drawBackground( QPainter* painter, const QRectF& rect ); + + private: + + GridItem* _grid; + //@} + //--------------------------------------------------------------------- + + + + /*! \name View Controllers Management *///----------------------------- + //@{ + protected: + + virtual void keyPressEvent( QKeyEvent* e ); + + virtual void mousePressEvent( QMouseEvent* e ); + + virtual void mouseReleaseEvent( QMouseEvent* e ); + + virtual void mouseMoveEvent( QMouseEvent* e ); + + virtual void mouseDoubleClickEvent( QMouseEvent* e ); + + virtual void wheelEvent( QWheelEvent* e ); + + signals: + + void itemDoubleClicked( QGraphicsItem* item ); + + public: + + //! Get this item view controller manager. + Controller::Manager& getControllerManager( ) { return _controllerManager; } + + //! Get the action for a controller with a given name. + QAction* getAction( QString name ); + + private: + + Controller::Manager _controllerManager; + //@} + //--------------------------------------------------------------------- + + + + /*! \name Zoom Management *///---------------------------------------- + //@{ + public: + + //! Set the maximum zoom factor that can be sets with the setZoomValue() method. + void setZoomMaxFactor( double zoomMaxFactor ) { _zoomMaxFactor = zoomMaxFactor; } + + double getZoom( ) const { return _zoom; } + + void setZoom( double zoom ); + + public slots: + + //! Set the current zoom factor value in percents (from 1 to 100, 100 sets the maximum zoom factor). + void setZoomPercent( int value ); + + private: + + double _zoom; + + //! Maximum zoom factor (default = 2). + double _zoomMaxFactor; + //@} + //--------------------------------------------------------------------- + }; +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // canGraphicsView_h + ============================================================ --- libs/qanava/src/qanGrid.h f98f7f90a1bf147fca5ccf385d06e3aaaf57b339 +++ libs/qanava/src/qanGrid.h f98f7f90a1bf147fca5ccf385d06e3aaaf57b339 @@ -0,0 +1,108 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file laGrid.h +// \author Benoit Autheman (address@hidden) +// \date 2004 December 05 +//----------------------------------------------------------------------------- + + +#ifndef qanGrid_h +#define qanGrid_h + + +// QT headers +#include +#include +#include +#include + + +//----------------------------------------------------------------------------- +namespace qan { // ::qan + + //! Defines the necessary methods to build a grid on a virtual canvas (draw lines, text, manage resizing, etc.). + /*! + Grid geometry is added dynamically using virtual methods addLine(), addRectangle() and addText() implemented + in a concrete grid for a particular graphic subsystem (for exemple QT GraphicsView and GridCheckBoardItem). + + \sa GridCheckBoardItem + \nosubgrouping + */ + class Grid + { + /*! \name Grid Constructor and Destructor *///------------------------- + //@{ + public: + + //! Grid constructor with eventual layout initialization. + Grid( ) { } + + //! Grid destructor. + virtual ~Grid( ) { } + + private: + + Grid( const Grid& g ); + //@} + //--------------------------------------------------------------------- + + + + /*! \name Layout and Size Management *///----------------------------- + //@{ + public: + + //! Resize the grid (ie, take care of resizing the specified horizontal and vertical lines, and notify the grid layout of the resize). + virtual void resize( QSizeF s ) { } + //@} + //--------------------------------------------------------------------- + + + + /*! \name Grid Construction Management *///--------------------------- + //@{ + public: + + //! Add a grid line on the canvas with specific line settings. + virtual void addLine( QLineF l, float w = 1, bool dash = false, bool dot = false ) = 0; + + //! Add a grid rectangle on the canvas with specific size and color settings. + virtual void addRectangle( QRectF r, QColor c ) = 0; + + //! Add a grid text label on the canvas. + virtual void addText( const QString& text, QPointF p, bool bold = false ) = 0; + + //! Add an horizontal line to the grid canvas (horizontal lines are automatically resized when the grid canvas is resized). + virtual void addHorizontalLine( QLineF l, int w = 1, bool dash = false, bool dot = false ) = 0; + + //! Add a vertical line to the grid canvas (vertical lines are automatically resized when the grid canvas is resized). + virtual void addVerticalLine( QLineF l, int w = 1, bool dash = false, bool dot = false ) = 0; + //@} + //--------------------------------------------------------------------- + }; +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // qanGrid_h + ============================================================ --- libs/qanava/src/qanGridItem.cpp a521aaca80231c4a3cc1cad98105e155687d6ec2 +++ libs/qanava/src/qanGridItem.cpp a521aaca80231c4a3cc1cad98105e155687d6ec2 @@ -0,0 +1,154 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file canGrid.cpp +// \author Benoit Autheman (address@hidden) +// \date 2004 December 05 +//----------------------------------------------------------------------------- + + +// Qanava headers +#include "./qanGridItem.h" + + +// QT headers +#include +#include +#include + + +// STD headers +//#include + + +namespace qan { // ::qan + + +/* Grid Constructor/Destructor *///------------------------------------------- +/*! + */ +GridItem::GridItem( GraphicsView* graphicsView ) : + QGraphicsItem( 0, graphicsView->scene( ) ) +{ + //connect( graphView->getGraphicsScene( ), SIGNAL( sceneRectChanged(const QRectF&) ), + // this, SLOT( sceneRectChanged(const QRectF&) ) ); + + graphicsView->setGrid( this ); +} + +GridItem::~GridItem( ) +{ + +} +//----------------------------------------------------------------------------- + + +/* Graphics View Grid Implementation *///-------------------------------------- +QRectF GridItem::boundingRect( ) const +{ + return QRectF( QPointF( 0., 0. ), QSizeF( 1., 1. ) ); +} + +void GridItem::drawBackground( QPainter& painter, const QRectF& rect ) +{ + +} +//----------------------------------------------------------------------------- + + + +/* Grid Content Management *///----------------------------------------------- +void GridItem::addLine( QLineF l, float w, bool dash, bool dot ) +{ + QGraphicsLineItem* line = new QGraphicsLineItem( l, this, scene( ) ); + + // TODO: dash, dot, w +} + +void GridItem::addRectangle( QRectF r, QColor c ) +{ +} + +void GridItem::addText( const QString& text, QPointF p, bool bold ) +{ +} + +void GridItem::addHorizontalLine( QLineF l, int w, bool dash, bool dot ) +{ +} + +void GridItem::addVerticalLine( QLineF l, int w, bool dash, bool dot ) +{ +} +//----------------------------------------------------------------------------- + + + +/* Regular Grid Management *///----------------------------------------------- +GridRegularItem::GridRegularItem( GraphicsView* graphicsView, int spacing ) : + GridItem( graphicsView ), + _spacing( spacing ), + _sizeMax( 0, 0 ), + _gradCount( 0, 0 ) +{ + +} +//----------------------------------------------------------------------------- + + + +/* CheckBoard Grid Management *///-------------------------------------------- +GridCheckBoardItem::GridCheckBoardItem( GraphicsView* graphicsView, QColor white, QColor black, qreal length ) : + GridItem( graphicsView ), + _white( white ), + _black( black ) +{ + _squaresPattern = QPixmap( length * 2, length * 2 ); + QPainter painter( &_squaresPattern ); + painter.fillRect( 0, 0, length, length, white ); + painter.fillRect( length, 0, length, length, black ); + painter.fillRect( 0, length, length, length, black ); + painter.fillRect( length, length, length, length, white ); + painter.end( ); +} + +void GridCheckBoardItem::drawBackground( QPainter& painter, const QRectF& rect ) +{ + GridItem::drawBackground( painter, rect ); + + int left = ( int )rect.left( ); + int top = ( int )rect.top( ); + int x = ( ( left / 100 ) * 100 ); + int y = ( ( top / 100 ) * 100 ); + + if ( left < 0 ) + x = x - 100; + if ( top < 0 ) + y = y - 100; + + if ( !_squaresPattern.isNull( ) ) + painter.drawTiledPixmap( x, y, rect.width( ) + 100, rect.height( ) + 100, _squaresPattern ); +} +//----------------------------------------------------------------------------- + + +} // ::qan ============================================================ --- libs/qanava/src/qanGridItem.h 14da4617240de3277b84e376e7652288b1e6ab07 +++ libs/qanava/src/qanGridItem.h 14da4617240de3277b84e376e7652288b1e6ab07 @@ -0,0 +1,174 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file qanGridItem.h +// \author Benoit Autheman (address@hidden) +// \date 2004 December 05 +//----------------------------------------------------------------------------- + + +#ifndef qanGridItem_h +#define qanGridItem_h + + +// Qanava headers +#include "./qanGrid.h" +#include "./qanItemGeom.h" +#include "./qanGraphicsView.h" + + +// QT headers +#include +#include + + +//----------------------------------------------------------------------------- +namespace qan { // ::qan + + class Canvas; + + + //! Implements the abstract methods defined in class Grid. + /*! + \nosubgrouping + */ + class GridItem : public QObject, public QGraphicsItem, public Grid + { + Q_OBJECT + + /*! \name Grid Constructor/Destructor *///---------------------------- + //@{ + public: + + //! Grid constructor with canvas and backcolor initialization (backcolor is not drawn, except for optimized line's backcolor). + GridItem( GraphicsView* graphicsView ); + + //! Grid destructor, once destroyed, all graphic items are cleared from the grid canvas. + virtual ~GridItem( ); + //@} + //--------------------------------------------------------------------- + + + + /*! \name Graphics View Grid Implementation *///----------------------- + //@{ + public: + + QRectF boundingRect( ) const; + + virtual void paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0 ) { } + + virtual void drawBackground( QPainter& painter, const QRectF& rect ); + //@} + //--------------------------------------------------------------------- + + + + /*! \name Grid Construction Management *///--------------------------- + //@{ + public: + + //! \copydoc Grid::addLine() + virtual void addLine( QLineF l, float w = 1, bool dash = false, bool dot = false ); + + //! \copydoc Grid::addRectangle() + virtual void addRectangle( QRectF r, QColor c ); + + //! \copydoc Grid::addText() + virtual void addText( const QString& text, QPointF p, bool bold = false ); + + //! \copydoc Grid::addHorizontalLine() + virtual void addHorizontalLine( QLineF l, int w = 1, bool dash = false, bool dot = false ); + + //! \copydoc Grid::addVerticalLine() + virtual void addVerticalLine( QLineF l, int w = 1, bool dash = false, bool dot = false ); + //@} + //--------------------------------------------------------------------- + }; + + + + //! Regular grid draws a square pattern of horizontal and vertical dotted lines (ie a standard grid!). + /*! + Qanava regular grid + + \nosubgrouping + */ + class GridRegularItem : public GridItem + { + /*! \name Regular Grid Management *///-------------------------------- + //@{ + public: + + //! GridRegularItem constructor with grid line spacing initialization. + GridRegularItem( GraphicsView* graphicsView, int spacing = 60 ); + + virtual ~GridRegularItem( ) { } + + private: + + int _spacing; + + QSize _sizeMax; + + QPoint _gradCount; + //@} + //--------------------------------------------------------------------- + }; + + + //! Draw a bicolor checkboard pattern regular grid. + /*! + \nosubgrouping + */ + class GridCheckBoardItem : public GridItem + { + Q_OBJECT + + /*! \name CheckBoard Grid Management *///----------------------------- + //@{ + public: + + //! GridCheckBoardItem constructor with black, white color and square size initialization. + GridCheckBoardItem( GraphicsView* graphicsView, QColor white, QColor black, qreal length = 50. ); + + virtual ~GridCheckBoardItem( ) { } + + virtual void drawBackground( QPainter& painter, const QRectF& rect ); + + private: + + QPixmap _squaresPattern; + + QColor _white; + + QColor _black; + //@} + //--------------------------------------------------------------------- + }; + +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // qanGridItem_h + ============================================================ --- libs/qanava/src/qanItemGeom.cpp 4a53eff9f53c160ca690661510384b6266bcfa42 +++ libs/qanava/src/qanItemGeom.cpp 4a53eff9f53c160ca690661510384b6266bcfa42 @@ -0,0 +1,425 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file canItemGeom.cpp +// \author Benoit Autheman (address@hidden) +// \date 2004 October 13 +//----------------------------------------------------------------------------- + + +// Qanava headers +#include "./qanItemGeom.h" + + +// QT headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// STD headers +#include +#include + + +namespace qan { + + +/* Arrow Constructor/Destructor *///------------------------------------------- +EdgeItem::EdgeItem( QGraphicsItem* parent, QGraphicsScene* scene, NodeItem* src, NodeItem* dst ) : + QGraphicsItem( parent, scene ), + _src( src ), + _dst( dst ), + _hasArrow( true ) +{ + setZValue( 1. ); + + if ( src != 0 && dst != 0 ) + { + src->addEdge( this ); + dst->addEdge( this ); + update( src, dst ); + } +} + +EdgeItem::~EdgeItem( ) +{ + // FIXME: segfault with node dtor + /*if ( _src != 0 ) + _src->removeEdge( this ); + if ( _dst != 0 ) + _dst->removeEdge( this );*/ +} + +QRectF EdgeItem::boundingRect( ) const +{ + if ( _src == 0 || _dst == 0 ) + return QRectF( ); + + QPointF src = _src->mapToScene( _src->boundingRect( ).center( ) ); + QPointF dst = _dst->mapToScene( _dst->boundingRect( ).center( ) ); + + QPointF topLeft( std::min( src.x( ), dst.x( ) ), // tl=(minx, miny) + std::min( src.y( ), dst.y( ) ) ); + QPointF bottomRight( std::max( src.x( ), dst.x( ) ), // br=(maxx, maxy) + std::max( src.y( ), dst.y( ) ) ); + + return QRectF( 0., 0., bottomRight.x( ) - topLeft.x( ), bottomRight.y( ) - topLeft.y( ) ); +} + +void EdgeItem::update( NodeItem* src, NodeItem* dst ) +{ + _src = src; + _dst = dst; + + if ( _src == 0 || _dst == 0 ) + return; + + QPointF srcF = _src->mapToScene( _src->boundingRect( ).center( ) ); + QPointF dstF = _dst->mapToScene( _dst->boundingRect( ).center( ) ); + QPointF topLeft( std::min( srcF.x( ), dstF.x( ) ), // tl=(minx, miny) + std::min( srcF.y( ), dstF.y( ) ) ); + + setPos( topLeft ); // setPos should have been called update on the old garbage area ? + + QGraphicsItem::update( ); + scene( )->update( ); +} + +void EdgeItem::paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget ) +{ + if ( _src == 0 || _dst == 0 ) + return; + + QPointF srcF = _src->mapToScene( _src->boundingRect( ).center( ) ); + QPointF dstF = _dst->mapToScene( _dst->boundingRect( ).center( ) ); + QLineF line( mapFromScene( srcF ), mapFromScene( dstF ) ); + + if ( option->levelOfDetail > 0.25 && + _hasArrow ) + { + const double Pi = 3.141592653; + double TwoPi = 2.0 * Pi; + double angle = ::acos(line.dx() / line.length()); + if (line.dy() >= 0) + angle = TwoPi - angle; + + // Get the intersection between arrow and dst node + QRectF br = mapFromItem( _dst, _dst->boundingRect( ) ).boundingRect( ); + + // Test intersection with all borders + QLineF top( br.topLeft( ), br.topRight( ) ); + QLineF right( br.topRight( ), br.bottomRight( ) ); + QLineF bottom( br.bottomLeft( ), br.bottomRight( ) ); + QLineF left( br.topLeft( ), br.bottomLeft( ) ); + + QPointF i; + if ( line.intersect( top, &i ) == QLineF::BoundedIntersection || + line.intersect( right, &i ) == QLineF::BoundedIntersection || + line.intersect( bottom, &i ) == QLineF::BoundedIntersection || + line.intersect( left, &i ) == QLineF::BoundedIntersection ) + { + QLineF shortLine( mapFromScene( srcF ), i ); + painter->setPen( QPen( Qt::black, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) ); + painter->drawLine( shortLine ); + + painter->setRenderHint( QPainter::Antialiasing ); + painter->setPen( QPen( Qt::black, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) ); + painter->setBrush( QBrush( QColor( 0, 0, 0 ) ) ); + + double angle = ::acos( line.dx( ) / line.length( ) ); + if ( line.dy( ) <= 0 ) + angle = TwoPi - angle; + painter->save( ); + painter->translate( i ); + painter->rotate( angle * 180. / Pi ); + + double arrowSize = 4.; + double arrowLength = 8.; + i = QPointF( -arrowLength - 1., 0. ); + QPolygonF poly; + poly << QPointF( i.x( ), i.y( ) - arrowSize ) + << QPointF( i.x( ) + arrowLength, i.y( ) ) + << QPointF( i.x( ), i.y( ) + arrowSize ) << QPointF( i.x( ), i.y( ) - arrowSize ); + painter->drawPolygon( poly ); + painter->restore( ); + } + } + else + { + painter->setPen( QPen( Qt::black, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) ); + painter->drawLine( line ); + } +} + + +/* Node Constructor/Destructor *///-------------------------------------------- +/*! + The following style options are supported: +
    +
  • 'backcolor': Background color, when there is no background image defined. +
  • 'bordercolor': Color of the item border. +
  • 'backimage': Background image (scaled to fit the item size). +
  • 'maximumwidth': Maximum width of the item, content is cropped to fit this with limit. +
  • 'maximumheight': Maximum height of the item, content is cropped to fit this height limit. +
  • 'fontsize': Base size for the font used to display the item label. +
  • 'icon': Display an icon centered in the left of the item. +
  • 'hasshadow': Set this value to false to supress the node shadow. +
+ + An item with an empty style is transparent with no background nor border. + + \param origin Uper left corner of the rectangular item. + \param label Text label to be displayed in the node text area (can be multi line HTML tagged text). + \param style Advanced style to pass optionnal display parameters. + */ +NodeItem::NodeItem( Node& node, Style::Manager& styleManager, Style& style, + QGraphicsItem* parent, QGraphicsScene* scene, + QPoint origin, const QString& label ) : + AbstractNodeItem( node, styleManager, &style ), + QGraphicsRectItem( parent, scene ), + _dimension( 170.0, 45.0 ), + _label( label ), + _shadowColor( ), + _shadowOffset( 1.0 ), + _labelItem( 0 ) +{ + setRect( origin.x( ), origin.y( ), _dimension.x( ), _dimension.y( ) ); + setFlag( QGraphicsItem::ItemIsMovable ); + setZValue( 2.0 ); + + updateNode( ); +} + +NodeItem::~NodeItem( ) +{ + // FIXME: segfault with edge dtor + // foreach ( EdgeItem* edge, _edges ) + // edge->update( 0, 0 ); +} + +QRectF NodeItem::boundingRect( ) const +{ + return QGraphicsRectItem::boundingRect( ); +} + +void NodeItem::paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget ) +{ + { + QBrush b; + b.setStyle( Qt::SolidPattern ); + b.setColor( _shadowColor ); + painter->setBrush( b ); + + QPen p( Qt::red ); + painter->setPen( p ); + + QRectF r = boundingRect( ); + painter->drawRect( r ); + } + + // Draw background pixmap + if ( !_backPixmap.isNull( ) ) + { + QBrush b = brush( ); + b.setStyle( Qt::NoBrush ); + setBrush( b ); // Remove default backcolor (usefull if we have an alpha channel) + + QRectF backRect = boundingRect( ); + //backRect.adjust( 0., 0., 1., 1. ); + QRectF srcRect( 0., 0., _backPixmap.width( ), _backPixmap.height( ) ); + painter->drawPixmap( backRect, _backPixmap, srcRect ); + } + + QGraphicsRectItem::paint( painter, option, widget ); + + // Draw the item icon centered vertically + if ( !_icon.isNull( ) ) + { + QPointF iconPos( 1.0, 1.0 ); + if ( _dimension.y( ) > _icon.height( ) ) + iconPos.ry( ) += ( _dimension.y( ) - _icon.height( ) ) / 2.; + painter->drawPixmap( iconPos, _icon ); + } +} + +void NodeItem::updateNode( ) +{ + if ( getStyle( ) == 0 ) + return; + + QColor backColor = QColor( 255, 255, 255 ); + + if ( !getStyle( )->has( "nobackground" ) ) + { + QBrush b = brush( ); + b.setStyle( Qt::NoBrush ); + setBrush( b ); + } + + if ( getStyle( )->has( "backcolor" ) ) + { + backColor = getStyle( )->getColor( "backcolor" ); + QBrush b = brush( ); + b.setStyle( Qt::SolidPattern ); + b.setColor( backColor ); + setBrush( b ); + } + else + setBrush( QColor( 255, 255, 255 ) ); + + QColor borderColor = QColor( 0, 0, 0 ); + if ( getStyle( )->has( "bordercolor" ) ) + { + borderColor = getStyle( )->getColor( "bordercolor" ); + setPen( borderColor ); + } + + // Compute the _label size once it is laid out as rich text html + if ( _label.size( ) > 0 ) + { + QFont font; + if ( getStyle( )->has( "fontsize" ) ) + { + int fontSize = getStyle( )->getT< int >( "fontsize" ); + font.setPointSize( fontSize > 0 ? fontSize : 11 ); + } + _labelItem = new QGraphicsTextItem( this, scene( ) ); + _labelItem->setHtml( _label ); + _labelItem->setFont( font ); + } + + + // Updating node geometry and content + { + // Check for item maximum dimension (if specified) + double maximumWidth = -1.; + double maximumHeight = -1.; + if ( getStyle( )->has( "maximumwidth" ) ) + maximumWidth = getStyle( )->getT< int >( "maximumwidth" ); + if ( getStyle( )->has( "maximumheight" ) ) + maximumHeight = getStyle( )->getT< int >( "maximumheight" ); + + // Compute the item height according to the _label size once formatted and displayed + if ( _labelItem != 0 /*_labelDocument != 0 && _labelLayout != 0*/ ) + { + // Do not resize the item larger than its maximum allowed size + double textLayoutWidth = _labelItem->boundingRect( ).width( ) + 2.; + double textLayoutHeight = _labelItem->boundingRect( ).height( ) + 2.; + _dimension.setX( maximumWidth > 0. ? std::min( textLayoutWidth, maximumWidth ) : textLayoutWidth ); + _dimension.setY( maximumHeight > 0. ? std::min( textLayoutHeight, maximumHeight ) : textLayoutHeight ); + } + + // Resize the whole item to fit (eventual) icon size + if ( getStyle( )->has( "icon" ) ) + { + QImage icon = getStyleManager( ).getImage( getStyle( )->getImageName( "icon" ) ); + if ( !icon.isNull( ) ) + _icon = QPixmap::fromImage( icon, Qt::OrderedAlphaDither ); + } + + if ( !_icon.isNull( ) ) + { + double w = _dimension.x( ) + _icon.width( ) + 1.; + _dimension.setX( maximumWidth != -1 ? std::min( w, maximumWidth ) : w ); + + double h = std::max( _icon.height( ) + 1., _dimension.y( ) ); + _dimension.setY( maximumHeight != -1 ? std::min( h, maximumHeight ) : h ); + + double textMarginX = 2; + double textMarginY = 0; + double textX = textMarginX; + if ( !_icon.isNull( ) ) // Draw the text right of the icon + textX += _icon.width( ); + double textY = textMarginY; + + QRectF clipRect( textX, textY, _dimension.x( ) - textMarginX, _dimension.y( ) - textMarginY ); + _labelItem->translate( textX, textY ); + } + + // Update the background image + if ( getStyle( )->has( "backimage" ) ) + { + QImage backImage = getStyleManager( ).getImage( getStyle( )->getImageName( "backimage" ) ); + if ( !backImage.isNull( ) ) + { + QImage image = backImage.scaled( ( int )_dimension.x( ) - 1, ( int )_dimension.y( ) - 1 ); // -1 for the border + if ( !image.isNull( ) ) + _backPixmap = QPixmap::fromImage( image, Qt::OrderedAlphaDither ); + } + } + + if ( getStyle( )->has( "hasshadow" ) && getStyle( )->getT< bool >( "hasshadow" ) ) + { + if ( getStyle( )->has( "shadowcolor" ) ) + _shadowColor = getStyle( )->getColor( "shadowcolor" ); + else + _shadowColor = QColor( 105, 105, 105 ); + if ( getStyle( )->has( "shadowoffset" ) ) + _shadowOffset = getStyle( )->getT< int >( "shadowoffset" ); + else + _shadowOffset = 3.; + + // Setup the two rectangle sub items who are modelling shadow + // Two rect are needed, because a unique subitem modelling shadow cannot have + // a zvalue inferior to the rect item modelling the node (BTW, it may be faster + // to render two small rect than a big one) + QGraphicsRectItem* shadowBottom = new QGraphicsRectItem( this, scene( ) ); + shadowBottom->setRect( _shadowOffset, _dimension.y( ) + 1.0, _dimension.x( ), _shadowOffset ); + QBrush b; + b.setStyle( Qt::SolidPattern ); + b.setColor( _shadowColor ); + shadowBottom->setBrush( b ); + shadowBottom->setPen( QPen( _shadowColor ) ); + + QGraphicsRectItem* shadowRight = new QGraphicsRectItem( this, scene( ) ); + shadowRight->setRect( _dimension.x( ) + 1.0, _shadowOffset, _shadowOffset, _dimension.y( ) + 1.0 ); + shadowRight->setBrush( b ); + shadowRight->setPen( QPen( _shadowColor ) ); + } + else + _shadowColor = QColor( ); // Invalid color since there is no shadow + + // Set item geometry + setRect( 0., 0., _dimension.x( ), _dimension.y( ) ); + } +} + +QVariant NodeItem::itemChange( GraphicsItemChange change, const QVariant& value ) +{ + if ( change == ItemPositionChange ) + { + updateEdges( ); + } + return QGraphicsItem::itemChange( change, value ); +} +//----------------------------------------------------------------------------- + +} + ============================================================ --- libs/qanava/src/qanItemGeom.h 4a6d84dc2dd56022f8e1ef64327adc2e6f21bd6b +++ libs/qanava/src/qanItemGeom.h 4a6d84dc2dd56022f8e1ef64327adc2e6f21bd6b @@ -0,0 +1,265 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file canItemGeom.h +// \author Benoit Autheman (address@hidden) +// \date 2004 October 13 +//----------------------------------------------------------------------------- + + +#ifndef canItemGeom_h +#define canItemGeom_h + + +// Qanava headers +#include "./qanNode.h" +#include "./qanStyle.h" + + +// QT headers +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +//----------------------------------------------------------------------------- +namespace qan { // ::qan + + class NodeItem; + + class EdgeItem : public QGraphicsItem + { + public: + + EdgeItem( QGraphicsItem* parent, QGraphicsScene* scene, + NodeItem* src, NodeItem* dst ); + + virtual ~EdgeItem( ); + + QRectF boundingRect( ) const; + + void paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0 ); + + virtual void update( NodeItem* src, NodeItem* dst ); + + NodeItem* getSrc( ) { return _src; } + + NodeItem* getDst( ) { return _dst; } + + private: + + NodeItem* _src; + + NodeItem* _dst; + + QPointF _sourcePoint; + + QPointF _destinationPoint; + + bool _hasArrow; + }; + + + class AbstractNodeItem : public QObject + { + Q_OBJECT + + public: + + AbstractNodeItem( Node& node, Style::Manager& styleManager, Style* style ) : + _node( node ), + _styleManager( styleManager ), + _style( style ) + { + if ( style != 0 ) + connect( style, SIGNAL( modified() ), this, SLOT( updateNode() ) ); + } + + virtual ~AbstractNodeItem( ) + { + } + + //! Usually, return this casted to a QGraphicsItem. + virtual QGraphicsItem* getGraphicsItem( ) = 0; + + public slots: + + virtual void updateNode( ) = 0; + + public: + + Node& getNode( ) { return _node; } + + Style::Manager& getStyleManager( ) { return _styleManager; } + + void setStyle( Style* style ) { _style = style; updateNode( ); } + + Style* getStyle( ) { return _style; } + + private: + + Node& _node; + + Style::Manager& _styleManager; + + Style* _style; + + public: + + void addEdge( EdgeItem* edge ) { _edges << edge; } + + void removeEdge( EdgeItem* edge ) { _edges.removeAll( edge ); } + + void updateEdges( ) + { + foreach ( EdgeItem* edge, _edges ) + edge->update( edge->getSrc( ), edge->getDst( ) ); + } + + protected: + + QList< EdgeItem* > _edges; + + public: + + class AbstractFactory + { + public: + + AbstractFactory( int nodeType, bool defaultFactory = false ) : + _nodeType( nodeType ), _defaultFactory( defaultFactory ) { }; + + virtual ~AbstractFactory( ) { } + + virtual AbstractNodeItem* create( Node& node, Style::Manager& styleManager, Style& style, + QGraphicsItem* parent, QGraphicsScene* scene, + QPoint origin, const QString& label) = 0; + + int getNodeType( ) const { return _nodeType; } + + bool isDefaultFactory( ) const { return _defaultFactory; } + + typedef QList< AbstractFactory* > List; + + private: + + int _nodeType; + + bool _defaultFactory; + }; + + template < typename T > + class Factory : public AbstractFactory + { + public: + + Factory( int nodeType, bool defaultFactory = false ) : + AbstractFactory( nodeType, defaultFactory ) { } + + virtual AbstractNodeItem* create( Node& node, Style::Manager& styleManager, Style& style, + QGraphicsItem* parent, QGraphicsScene* scene, + QPoint origin, const QString& label ) + { return new T( node, styleManager, style, parent, scene, origin, label ); } + }; + }; + + + + //! Model a rectangular node item on a QT graphics view. + /*! + The following style options are supported: +
    +
  • 'backcolor': Background color, when there is no background image defined. +
  • 'bordercolor': Color of the item border. +
  • 'backimage': Background image (scaled to fit the item size). +
  • 'maximumwidth': Maximum width of the item, content is cropped to fit this with limit. +
  • 'maximumheight': Maximum height of the item, content is cropped to fit this height limit. +
  • 'fontsize': Base size for the font used to display the item label. +
  • 'icon': Display an icon centered in the left of the item. +
  • 'hasshadow': Set this value to false to supress the node shadow. +
+ \nosubgrouping + */ + class NodeItem : public AbstractNodeItem, public QGraphicsRectItem + { + Q_OBJECT + + public: + + NodeItem( Node& node, Style::Manager& styleManager, Style& style, + QGraphicsItem* parent, QGraphicsScene* scene, + QPoint origin, const QString& label ); + + virtual ~NodeItem( ); + + QRectF boundingRect() const; + + void paint( QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0 ); + + struct DefaultFactory : public AbstractNodeItem::Factory< NodeItem > + { + DefaultFactory( ) : AbstractNodeItem::Factory< NodeItem >( -1, true ) { } + }; + + private: + + bool hasShadow( ) const { return _shadowColor.isValid( ); } + + public slots: + + virtual void updateNode( ); + + virtual QGraphicsItem* getGraphicsItem( ) { return static_cast< QGraphicsItem* >( this ); } + + private: + + QPointF _dimension; + + QString _label; + + QPixmap _backPixmap; + + QColor _shadowColor; + + double _shadowOffset; + + QGraphicsTextItem* _labelItem; + + QPixmap _icon; + + protected: + + QVariant itemChange( GraphicsItemChange change, const QVariant& value ); + }; + +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // canItemGeom_h + ============================================================ --- libs/qanava/src/qanLayout.cpp 511aa22d12419f13286818772fc60ed1968f49a9 +++ libs/qanava/src/qanLayout.cpp 511aa22d12419f13286818772fc60ed1968f49a9 @@ -0,0 +1,414 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file laLayout.cpp +// \author Benoit Autheman (address@hidden) +// \date 2004 May 22 +//----------------------------------------------------------------------------- + + +// Qanava headers +#include "./qanLayout.h" + + +// Std headers +#include +#include +#include +#include + + +namespace qan { // ::qan + + +/* Layout Generation Management *///------------------------------------------- +void Layout::resetNodesPositions( Graph& graph ) +{ + Node::List& nodes = graph.getNodes( ); + for ( Node::List::iterator nodeIter = nodes.begin( ); nodeIter != nodes.end( ); nodeIter++ ) + ( *nodeIter )->setPosition( -1.0f, -1.0f ); +} +//----------------------------------------------------------------------------- + + + +/* Random Layout Generation Management *///------------------------------------ +void Random::layout( Graph& graph, GridItem& gridItem, QRectF r, QProgressDialog* progress, int step ) +{ + if ( progress != 0 ) + { + progress->setMaximum( graph.getNodeCount( ) ); + progress->setValue( 0 ); + } + + srand( QDateTime::currentDateTime( ).toTime_t( ) ); + + Node::List& nodes = graph.getNodes( ); + for ( Node::List::iterator nodeIter = nodes.begin( ); nodeIter != nodes.end( ); nodeIter++ ) + { + Node* node = *nodeIter; + + float w = ( float )( ( rand( ) % 100 ) / 100.0 * r.width( ) ); + float h = ( float )( ( rand( ) % 100 ) / 100.0 * r.height( ) ); + node->setPosition( w, h ); + + if ( progress != 0 ) + progress->setValue( progress->value( ) + 1 ); + } + + if ( progress != 0 ) + progress->close( ); +} +//----------------------------------------------------------------------------- + + + +/* Concentric Layout Management *///------------------------------------------- +void Concentric::layout( Graph& graph, GridItem& gridItem, QRectF r, QProgressDialog* progress, int step ) +{ + // Configure the progress monitor + if ( progress != 0 ) + { + progress->setMaximum( graph.getNodeCount( ) ); + progress->setValue( 0 ); + } + + QPointF center = r.center( ); + int nodesPerCircle = ( int )( 360. / _azimutDelta ); + Node::List::iterator nodeIter = graph.getNodes( ).begin( ); + for ( int n = 0; n < ( int )graph.getNodes( ).size( ); n++, nodeIter++ ) + { + Node& node = **nodeIter; + double azimutIndex = ( n % nodesPerCircle ); + double azimut = azimutIndex * _azimutDelta; + + int circleIndex = 1 + ( n / nodesPerCircle ); + double cx = sin( azimut * 3.14156 / 180. ) * ( circleIndex * _circleInterval ); + double cy = cos( azimut * 3.14156 / 180. ) * ( circleIndex * _circleInterval ); + + node.getPosition( )( 0 ) = center.x( ) + cx; + node.getPosition( )( 1 ) = center.y( ) + cy; + if ( progress != 0 ) + progress->setValue( n ); + } + if ( progress != 0 ) + progress->close( ); +} +//----------------------------------------------------------------------------- + + + +/* Colimacon Layout Management *///-------------------------------------------- +void Colimacon::layout( Graph& graph, GridItem& gridItem, QRectF r, QProgressDialog* progress, int step ) +{ + // Configure the progress monitor + if ( progress != 0 ) + { + progress->setMaximum( graph.getNodeCount( ) ); + progress->setValue( 0 ); + } + + QPointF center = r.center( ); + int nodesPerCircle = ( int )( 360. / _azimutDelta ); + Node::List::iterator nodeIter = graph.getNodes( ).begin( ); + for ( int n = 0; n < ( int )graph.getNodes( ).size( ); n++, nodeIter++ ) + { + Node& node = **nodeIter; + //double azimutIndex = ( double )( n % nodesPerCircle ); + double azimut = n * _azimutDelta; + + //int circleIndex = 1 + ( int )( n / nodesPerCircle ); + double cx = sin( azimut * 3.14156 / 180. ) * ( log( 1. + n ) * 10 * _circleInterval ); + double cy = cos( azimut * 3.14156 / 180. ) * ( log( 1. + n ) * 10 * _circleInterval ); + + node.getPosition( )( 0 ) = center.x( ) + cx; + node.getPosition( )( 1 ) = center.y( ) + cy; + + if ( progress != 0 ) + progress->setValue( n ); + } + if ( progress != 0 ) + progress->close( ); +} +//----------------------------------------------------------------------------- + + + +/* Hierarchy Layout Generation Management *///--------------------------------- +void DirectedTree::layout( Graph& graph, GridItem&, QRectF r, QProgressDialog* progress, int step ) +{ + // Configure the progress monitor + if ( progress != 0 ) + { + progress->setMaximum( graph.getNodeCount( ) ); + progress->setValue( 0 ); + } + + // Reset the graph nodes positions (If position are not resetted, nodes are all considered + // already placed + resetNodesPositions( graph ); + + // Configure tree bounding box + VectorF& bbox = *new VectorF( 4 ); + bbox( 0 ) = getXOrigin( ); + bbox( 1 ) = getYOrigin( ); + + // Layout all graph subgraph + Node::List& rootNodes = graph.getRootNodes( ); + for ( Node::List::iterator rootsIter = rootNodes.begin( ); rootsIter != rootNodes.end( ); rootsIter++ ) + { + layout( **rootsIter, bbox, 0, progress ); + bbox( 0 ) += getXSpacing( ); + } + + // Set graph oritentation + if ( _orientation == HORIZONTAL ) + transpose( graph ); + delete &bbox; + + if ( progress != 0 ) + progress->close( ); +} + +void DirectedTree::layout( Node& node, VectorF& bbox, int depth, QProgressDialog* progress, int step ) +{ + if ( _marked.find( &node ) != _marked.end( ) ) + return; + _marked.insert( &node ); + + if ( getStopRecusrion( ) ) + return; + + float xStart = bbox( 0 ); + + // Depth first + int laidOut = 0; + Node::Set outNodes; node.collectOutNodesSet( outNodes ); + for ( Node::Set::iterator outNodesIter = outNodes.begin( ); outNodesIter != outNodes.end( ); outNodesIter++ ) + { + Node& subNode = **outNodesIter; + + // Detect if the node has already been placed + if ( subNode.getPosition( )( 0 ) < 0.f && + subNode.getPosition( )( 1 ) < 0.f ) + { + // A leaf node must be drawn at x+dx if one or more nodes has previously been laid out (!=begin). + if ( laidOut > 0 ) + bbox( 0 ) += getXSpacing( ); + + layout( subNode, bbox, depth + 1, progress ); + laidOut++; + } + } + + float xEnd = bbox( 0 ); + + // Detect leaf node + float x = 0.f; + float y = getYOrigin( ) + ( getYSpacing( ) * depth ); + if ( node.isLeaf( ) ) + x = xStart; + else + x = xStart + ( ( xEnd - xStart ) / 2.f ); + node.setPosition( x, y ); + + // Updated progress monitor + if ( progress != 0 && progress->wasCanceled( ) ) + stopRecursion( ); + if ( progress != 0 ) + progress->setValue( progress->value( ) + 1 ); +} + +void DirectedTree::transpose( Graph& graph ) +{ + Node::List& nodes = graph.getNodes( ); + for ( Node::List::iterator nodeIter = nodes.begin( ); nodeIter != nodes.end( ); nodeIter++ ) + { + Node& node = **nodeIter; + VectorF position = node.getPosition( ); + node.setPosition( position( 1 ), position( 0 ) ); + } +} +//----------------------------------------------------------------------------- + + + +/* Force Layout Generation Management *///------------------------------------- +/*! Initial positions will be generated using a fixed layout (random or colimacon) if step is + superior of 1. + */ +void UndirectedGraph::layout( Graph& graph, GridItem& gridItem, QRectF r, QProgressDialog* progress, int step ) +{ + int runCount = 50; + if ( step != -1 ) + runCount = step; + if ( progress != 0 ) + { + progress->setMaximum( runCount ); + progress->setValue( 0 ); + } + + // Generate a random layout for algorithm initialisation + if ( runCount > 1 ) + { + //Random initalLayout; + Colimacon initalLayout; + initalLayout.layout( graph, gridItem, r ); + } + + // Apply the spring force algorithm + std::vector< VectorF > positions; + positions.reserve( graph.getNodeCount( ) + 1 ); // Last position reserved for a virtual center node + unsigned int n = 0; + for ( ; n < graph.getNodeCount( ) + 1; n++ ) + positions.push_back( VectorF( 2 ) ); + Node::Set nodesSet; graph.collectNodes( nodesSet ); + + _center.setPosition( r.width( ) / 2, r.height( ) / 2 ); + //_center.setPosition( 100., 100. ); + Node::List& nodes = graph.getNodes( ); + for ( int iter = 0; iter < runCount; iter++ ) + { + double modification = 0.; + + // Compute new nodes positions using the spring embedder model + Node::List::iterator nodeIter = nodes.begin( ); + for ( n = 0; nodeIter != nodes.end( ); n++, nodeIter++ ) + { + Node& node = **nodeIter; + + Node::Set adjacentNodes; + node.getAdjacentNodesSet( adjacentNodes ); + if ( graph.isRootNode( node ) ) + adjacentNodes.insert( &_center ); + VectorF fspring = computeSpringForce( node, adjacentNodes, graph ); + VectorF frep = computeRepulseForce( node, adjacentNodes, nodesSet, graph ); + + VectorF delta( 2 ); + delta = ( frep + fspring ) / ( float )( nodes.size( ) + 1.f ); + positions[ n ] = node.getPosition( ) + delta; + modification += length2( delta ); + } + + // Apply modifications for a virtual center node connected to all root nodes + { + n = nodes.size( ); + VectorF fspring = computeSpringForce( _center, graph.getRootNodesSet( ), graph ); + VectorF frep = computeRepulseForce( _center, graph.getRootNodesSet( ), nodesSet, graph ); + + VectorF delta( 2 ); + delta = ( frep + fspring ) / ( float )( nodes.size( ) + 1.f ); + positions[ n ] = _center.getPosition( ) + delta; + } + + // Move the nodes to their new positions + int p = 0; + for ( nodeIter = nodes.begin( ); nodeIter != nodes.end( ); nodeIter++, p++ ) + ( *nodeIter )->setPosition( positions[ p ] ); + _center.setPosition( positions[ nodes.size( ) ] ); + + // Stop iterating if node have converged to a fixed position + if ( modification < ( 5. * nodes.size( ) ) ) + break; + + // Update progress bar + if ( progress != 0 ) + progress->setValue( iter ); + + // Detect process interruption + if ( progress != 0 && progress->wasCanceled( ) ) + break; + } + + if ( progress != 0 ) + progress->close( ); +} + +VectorF UndirectedGraph::computeRepulseForce( Node& u, Node::Set& adjacentNodes, Node::Set& nodes, Graph& graph ) +{ + VectorF force( 2 ); + force( 0 ) = force( 1 ) = 0.f; + + // Compute repulsion forces for all non adjacent nodes + Node::Set nonAdjacentNodes( nodes ); + nonAdjacentNodes.subtract( adjacentNodes ); + if ( !graph.isRootNode( u ) ) + nonAdjacentNodes.insert( &_center ); + + Node::Set::iterator nonAdjacentNodeIter = nonAdjacentNodes.begin( ); + for ( ; nonAdjacentNodeIter != nonAdjacentNodes.end( ); nonAdjacentNodeIter++ ) + { + Node& v = **nonAdjacentNodeIter; + if ( &v == &u ) + continue; + + // Compute repulsion forces between item and + VectorF& pu = u.getPosition( ); + VectorF& pv = v.getPosition( ); + VectorF uv = pv - pu; + uv *= - ( 80 * 80 ) / ( 1.0 + length2( uv ) ); + force += uv; + } + return force; +} + +VectorF UndirectedGraph::computeSpringForce( Node& u, Node::Set& adjacentNodes, Graph& graph ) +{ + VectorF force( 2 ); + force( 0 ) = 0.f; force( 1 ) = 0.f; + + // Compute attraction forces for all adjacent nodes (roots nodes are considered adjacents) + Node::Set::iterator adjacentNodeIter = adjacentNodes.begin( ); + for ( ; adjacentNodeIter != adjacentNodes.end( ); adjacentNodeIter++ ) + { + Node& v = **adjacentNodeIter; + + // Compute attraction forces between item and + VectorF& pu = u.getPosition( ); + VectorF& pv = v.getPosition( ); + VectorF uv = pv - pu; + + double l = length( uv ) / 100.f; + double size = 1.; + if ( l > 1.0 ) + size = 2.f * log( l ); + + uv *= size; + force += uv; + } + + return force; +} + +float UndirectedGraph::length( const VectorF& v ) +{ + return sqrt( ( v( 0 ) * v( 0 ) ) + ( v( 1 ) * v( 1 ) ) ); +} + +float UndirectedGraph::length2( const VectorF& v ) +{ + return ( v( 0 ) * v( 0 ) ) + ( v( 1 ) * v( 1 ) ); +} +//----------------------------------------------------------------------------- + + +} // ::qan ============================================================ --- libs/qanava/src/qanLayout.h 2f96b5189a86b669e54c072e9c621074ef76654f +++ libs/qanava/src/qanLayout.h 2f96b5189a86b669e54c072e9c621074ef76654f @@ -0,0 +1,337 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file laLayout.h +// \author Benoit Autheman (address@hidden) +// \date 2004 May 22 +//----------------------------------------------------------------------------- + + +#ifndef laLayout_h +#define laLayout_h + + +// Qanava headers +#include "./qanGraph.h" +#include "./qanGridItem.h" + + +// STD headers +#include + + +// QT headers +#include +#include + + +//----------------------------------------------------------------------------- +namespace qan { // ::qan + + //! Abstract interface defining a graph layout algorithm (placement of node in space). + /*! + \nosubgrouping + */ + class Layout + { + public: + + /*! \name Layout Constructor/Destructor *///--------------------------- + //@{ + //! Layout constructor. + Layout( ) { } + + //! Layout virtual destructor. + virtual ~Layout( ) { } + + private: + + Layout( const Layout& l ); + //@} + //--------------------------------------------------------------------- + + + + /*! \name Layout Generation Management *///---------------------------- + //@{ + public: + + //! Layout nodes from a given graph using r as a clipping rect, and update grid. + virtual void layout( Graph& graph, GridItem& gridItem, + QRectF r, QProgressDialog* progress = 0, int step = -1 ) = 0; + + protected: + + //! Reset all nodes positions. + static void resetNodesPositions( Graph& graph ); + //@} + //--------------------------------------------------------------------- + }; + + + + //! Randomly layout an undirected graph. + /*! + \nosubgrouping + */ + class Random : public Layout + { + /*! \name Random Constructor/Destructor *///--------------------------- + //@{ + public: + + //! Random constructor. + Random( ) : Layout( ) { } + //@} + //--------------------------------------------------------------------- + + + + /*! \name Random Layout Generation Management *///--------------------- + //@{ + public: + + //! . + virtual void layout( Graph& graph, GridItem& gridItem, + QRectF r, QProgressDialog* progress = 0, int step = -1 ); + //@} + //--------------------------------------------------------------------- + }; + + + //! Layout a graph as concentric circles of nodes. + /*! + \nosubgrouping + */ + class Concentric : public Layout + { + /*! \name Concentric Constructor/Destructor *///----------------------- + //@{ + public: + + //! Concentric constructor. + Concentric( double azimutDelta = 45., double circleInterval = 50. ) : + Layout( ), _azimutDelta( azimutDelta ), _circleInterval( circleInterval ) { } + + private: + + double _azimutDelta; + + double _circleInterval; + //@} + //--------------------------------------------------------------------- + + + + /*! \name Concentric Layout Management *///---------------------------- + //@{ + public: + + //! . + virtual void layout( Graph& graph, GridItem& gridItem, + QRectF r, QProgressDialog* progress = 0, int step = -1 ); + //@} + //--------------------------------------------------------------------- + }; + + + //! Layout nodes in a (logarithm) colimacon. + /*! + \nosubgrouping + */ + class Colimacon : public Layout + { + /*! \name Colimacon Constructor/Destructor *///------------------------ + //@{ + public: + + //! Colimacon constructor. + Colimacon( double azimutDelta = 15., double circleInterval = 10. ) : + Layout( ), _azimutDelta( azimutDelta ), _circleInterval( circleInterval ) { } + + private: + + double _azimutDelta; + + double _circleInterval; + //@} + //--------------------------------------------------------------------- + + + + /*! \name Colimacon Layout Management *///----------------------------- + //@{ + public: + + //! . + virtual void layout( Graph& graph, GridItem& gridItem, + QRectF r, QProgressDialog* progress = 0, int step = -1 ); + //@} + //--------------------------------------------------------------------- + }; + + + //! Layout an undirected graph as a directed top-down tree. + /*! + \nosubgrouping + */ + class DirectedTree : public Layout + { + /*! \name DirectedTree Constructor/Destructor *///--------------------- + //@{ + public: + + //! Define layout orientation. + enum Orientation + { + //! . + NONE = 0, + + //! . + HORIZONTAL = 1, + + //! . + VERTICAL = 2 + }; + + //! DirectedTree constructor with orientation initialization. + /*! \param spacing spacing between node on x and y (ex: 120, 70 with VERTICAL). */ + DirectedTree( const VectorF& spacing, Orientation orientation ) : + Layout( ), _origin( 2 ), _spacing( spacing ), _orientation( orientation ), _stopRecursion( false ) { _origin( 0 ) = 0.f; _origin( 1 ) = 0.f; } + + //! DirectedTree constructor with origin and orientation initialization. + /*! \param spacing spacing between node on x and y (ex: 120, 70 with VERTICAL). */ + DirectedTree( const VectorF& origin, const VectorF& spacing, Orientation orientation ) : + Layout( ), _origin( origin ), _spacing( spacing ), _orientation( orientation ), _stopRecursion( false ) { } + + private: + + VectorF _origin; + //@} + //--------------------------------------------------------------------- + + + + /*! \name Hierarchy Layout Generation Management *///------------------ + //@{ + public: + + //! Layout a graph as a hierarchy tree. + virtual void layout( Graph& graph, GridItem& gridItem, + QRectF r, QProgressDialog* progress = 0, int step = -1 ); + + protected: + + //! Layout a node hierarchy as a hierarchy tree. + void layout( Node& node, VectorF& bbox, int depth, QProgressDialog* progress = 0, int step = 0 ); + + //! Invert the x/y coordinates to set the desired orientation. + static void transpose( Graph& graph ); + + //! Spacing on x and y between tree nodes. + VectorF _spacing; + + //! Generated tree orientation. + Orientation _orientation; + + //! Container for already laid out nodes. + Node::Set _marked; + + private: + + //! Get the 'x' horizontal spacing according to the current orientation. + float getXSpacing( ) const { return ( _orientation == HORIZONTAL ? _spacing( 1 ) : _spacing( 0 ) ); } + + //! Get the 'y' vertical spacing according to the current orientation. + float getYSpacing( ) const{ return ( _orientation == HORIZONTAL ? _spacing( 0 ) : _spacing( 1 ) ); } + + //! Get the 'x' horizontal origin according to the current orientation. + float getXOrigin( ) const { return ( _orientation == HORIZONTAL ? _origin( 1 ) : _origin( 0 ) ); } + + //! Get the 'y' vertical origin according to the current orientation. + float getYOrigin( ) const { return ( _orientation == HORIZONTAL ? _origin( 0 ) : _origin( 1 ) ); } + + //! Force the tree layout algorithm to stop recursion. + void stopRecursion( ) { _stopRecursion = true; } + + //! Return the value of the stop recursion flag. + bool getStopRecusrion( ) const { return _stopRecursion; } + + //! Indicate that the algorithm execution is canceled and that the layout algorithm recusrion must stop. + bool _stopRecursion; + //@} + //--------------------------------------------------------------------- + }; + + + + //! Layout an undirected graph using a spring force algorithm. + /*! + \nosubgrouping + */ + class UndirectedGraph : public Layout + { + /*! \name UndirectedGraph Constructor/Destructor *///------------------ + //@{ + public: + + //! UndirectedGraph constructor. + UndirectedGraph( ) : Layout( ), _center( "" ) { } + //@} + //--------------------------------------------------------------------- + + + + /*! \name Force Layout Generation Management *///---------------------- + //@{ + public: + + //! Layout 'graph' using a spring force algorithm. + virtual void layout( Graph& graph, GridItem& gridItem, + QRectF r, QProgressDialog* progress = 0, int step = -1 ); + + static void add( VectorF& a, const VectorF& b ); + + static void scale( VectorF& p, float scale ); + + static VectorF vector( VectorF a, VectorF b ); + + static float length( const VectorF& v ); + + static float length2( const VectorF& v ); + + private: + + VectorF computeRepulseForce( Node& node, Node::Set& adjacentNodes, Node::Set& nodes, Graph& graph ); + + VectorF computeSpringForce( Node& node, Node::Set& adjacentNodes, Graph& graph ); + + Node _center; + //@} + //--------------------------------------------------------------------- + }; +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // laLayout_h + ============================================================ --- libs/qanava/src/qanNode.cpp c8b9d52caf4a8465cc270b911eaeb020c91fb888 +++ libs/qanava/src/qanNode.cpp c8b9d52caf4a8465cc270b911eaeb020c91fb888 @@ -0,0 +1,132 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file laNode.cpp +// \author Benoit Autheman (address@hidden) +// \date 2004 February 15 +//----------------------------------------------------------------------------- + + +// Qanava headers +#include "./qanNode.h" +#include "./qanGraph.h" + + +// Std headers +#include +#include + + +namespace qan { // ::qan + + +/* Node Constructor/Destructor *///-------------------------------------------- +Node::Node( const QString& label ) +{ + initAttributes( 5 ); + setLabel( label ); + setType( -1 ); + VectorF v( 2 ); v( 0 ) = 0.f; v( 0 ) = 0.f; + setPosition( v ); + setDimension( v ); +} + +Node::Node( const QString& label, int type ) +{ + initAttributes( 5 ); + setLabel( label ); + setType( type ); + VectorF v( 2 ); v( 0 ) = 0.f; v( 0 ) = 0.f; + setPosition( v ); + setDimension( v ); +} +//----------------------------------------------------------------------------- + + + +/* Node Edges Management *///-------------------------------------------------- +void Node::collectOutNodes( Node::List& outNodes ) +{ + foreach ( Edge* outEdge, _outEdges ) + outNodes << &outEdge->getDst( ); + //for ( Edge::List::iterator outEdgeIter = _outEdges.begin( ); outEdgeIter != _outEdges.end( ); outEdgeIter++ ) + //outNodes << &( ( *outEdgeIter )->getDst( ) ); +} + +void Node::collectInNodes( Node::List& inNodes ) +{ + for ( Edge::List::iterator intEdgeIter = _inEdges.begin( ); intEdgeIter != _inEdges.end( ); intEdgeIter++ ) + inNodes << &( ( *intEdgeIter )->getSrc( ) ); +} + +void Node::collectOutNodesSet( Node::Set& outNodes ) const +{ + // Add all edge destination to the out nodes set + const Edge::List& edges = getOutEdges( ); + Edge::List::const_iterator edgeIter = edges.begin( ); + for ( ; edgeIter != edges.end( ); edgeIter++ ) + outNodes.insert( &( *edgeIter )->getDst( ) ); +} + +void Node::collectInNodesSet( Node::Set& inNodes ) const +{ + // Add all edge destination to the out nodes set + const Edge::List& edges = getInEdges( ); + Edge::List::const_iterator edgeIter = edges.begin( ); + for ( ; edgeIter != edges.end( ); edgeIter++ ) + inNodes.insert( &( *edgeIter )->getSrc( ) ); +} + +void Node::getAdjacentNodesSet( Node::Set& adjacentNodes ) const +{ + Node::Set outNodes; collectOutNodesSet( outNodes ); + Node::Set inNodes; collectInNodesSet( inNodes ); + + adjacentNodes.unite( outNodes ); + adjacentNodes.unite( inNodes ); +} + +void Node::getNonAdjacentNodesSet( Node::Set& nonAdjacentNodes, const Node::Set& graphNodes ) const +{ + Node::Set adjacentNodes; getAdjacentNodesSet( adjacentNodes ); + nonAdjacentNodes.unite( graphNodes ); + nonAdjacentNodes.subtract( adjacentNodes ); + + // Remove the node idself from to non adjacent nodes list + nonAdjacentNodes.remove( const_cast< Node* >( this ) ); +} +//----------------------------------------------------------------------------- + + + +/* Node Attributes Management *///--------------------------------------------- +void Node::setDate( const QString& date ) +{ + QDateTime dt = QDateTime::fromString( date.toAscii( ), Qt::ISODate ); + if ( dt.isValid( ) ) + setAttribute< QDateTime >( Node::DATE, dt ); +} +//----------------------------------------------------------------------------- + + +} // ::qan + ============================================================ --- libs/qanava/src/qanNode.h 5db78823a601423d53024a55532763ee5babcecd +++ libs/qanava/src/qanNode.h 5db78823a601423d53024a55532763ee5babcecd @@ -0,0 +1,264 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file laNode.h +// \author Benoit Autheman (address@hidden) +// \date 2004 February 15 +//----------------------------------------------------------------------------- + + +#ifndef laNode_h +#define laNode_h + + +// Qanava headers +#include "./qanEdge.h" +#include "./qanVectorf.h" + + +// Standard headers +#include + + +// QT headers +#include +#include +#include + + +//----------------------------------------------------------------------------- +//! Root qanava namespace +namespace qan { // ::qan + + //! Model a node in a standard weighted and directed graph. + /*! + \nosubgrouping + */ + class Node + { + /*! \name Node Constructor/Destructor *///------------------------- + //@{ + public: + + //! Node constructor with label initialisation. + Node( const QString& label ); + + //! Node constructor with label and type initialisation. + Node( const QString& label, int type ); + + //! Node destructor. + ~Node( ) { } + + private: + + Node( const Node& n ); + //@} + //----------------------------------------------------------------- + + + + /*! \name Node Attributes Management *///-------------------------- + //@{ + public: + + template < typename T > + void addAttribute( T t ) + { + _attributes.push_back( new T( t ) ); + } + + template < typename T > + T* getAttribute( int role ) + { + assert( role < _attributes.size( ) ); + return ( _attributes[ role ] != 0 ? static_cast< T* >( _attributes[ role ] ) : 0 ); + } + + template < typename T > + const T* getAttribute( int role ) const + { + assert( role < _attributes.size( ) ); + return ( _attributes[ role ] != 0 ? static_cast< const T* >( _attributes[ role ] ) : 0 ); + } + + template < typename T > + void setAttribute( int role, const T& t ) + { + assert( role < _attributes.size( ) ); + if ( _attributes[ role ] == 0 ) + _attributes[ role ] = new T( t ); + else + *static_cast< T* >( _attributes[ role ] ) = t; + } + + void initAttributes( unsigned int attributeCount ) + { + if ( _attributes.size( ) < ( int )attributeCount ) // Add attributes to fit the requested attribute count + { + for ( unsigned int attribute = _attributes.size( ); attribute < attributeCount; attribute++ ) + _attributes.push_back( 0 ); + } + } + + private: + + QList< void* > _attributes; + //@} + //----------------------------------------------------------------- + + + + /*! \name Node Edges Management *///------------------------------- + //@{ + public: + + //! Typedef for a QT list of pointer on Node. + typedef QList< Node* > List; + + //! Typedef for a QT set of pointer on Node. + typedef QSet< Node* > Set; + + //! Get a list of all nodes pointing to this node. + const Edge::List& getInEdges( ) const { return _inEdges; } + + //! Get a list of all nodes pointing to this node. + Edge::List& getInEdges( ) { return _inEdges; } + + //! Get a list of all node pointed by this node. + const Edge::List& getOutEdges( ) const { return _outEdges; } + + //! Get a list of all node pointed by this node. + Edge::List& getOutEdges( ) { return _outEdges; } + + //! Collect a list of this node sub nodes. + void collectOutNodes( Node::List& outNodes ); + + //! Collect a list of this node in nodes. + void collectInNodes( Node::List& outNodes ); + + //! Collect a set of unique nodes pointed by this node. + void collectOutNodesSet( Node::Set& outNodes ) const; + + //! Collect a set of unique nodes referencing this node. + void collectInNodesSet( Node::Set& nodes ) const; + + //! Add an in edge. + void addInEdge( Edge& edge ) { _inEdges << &edge; } + + //! Add an out edge. + void addOutEdge( Edge& edge ) { _outEdges << &edge; } + + //! Get node in degree. + unsigned int getInDegree( ) const { return _inEdges.size( ); } + + //! Get node out degree. + unsigned int getOutDegree( ) const { return _outEdges.size( ); } + + //! Get a list of nodes adjacent to this (all in and out nodes, without this). + void getAdjacentNodesSet( Node::Set& nodes ) const; + + //! Get a list of nodes non adjacent to this (all nodes minus the adjacent node set collected with getAdjacentNodesSet()). + void getNonAdjacentNodesSet( Node::Set& nonAdjacentNodes, const Node::Set& graphNodes ) const; + + //! Return true if this node is a "leaf" (ie has no out edges). + bool isLeaf( ) const { return _outEdges.size( ) == 0; } + + private: + + //! Input edges. + Edge::List _inEdges; + + //! Output edges. + Edge::List _outEdges; + //@} + //----------------------------------------------------------------- + + + + /*! \name Node Property Management *///---------------------------- + //@{ + public: + + //! Attribute role. + enum Role + { + TYPE = 1, + LABEL = 2, + POSITION = 3, + DIMENSION = 4, + DATE = 5, + USER = 6 + }; + + enum { StdAttributeCount = 5 }; + + //! Get this node label. + const QString& getLabel( ) const { return *getAttribute< QString >( Node::LABEL ); } + + //! Set this node label. + void setLabel( const QString& label ) { setAttribute< QString >( Node::LABEL, label ); } + + //! Set this node's user defined type. + void setType( int type ) { setAttribute< int >( Node::TYPE, type ); } + + //! Get this node's user defined type. + int getType( ) const { return *getAttribute< int >( Node::TYPE ); } + + //! . + VectorF& getPosition( ) { return *getAttribute< VectorF >( Node::POSITION ); } + + //! . + const VectorF& getPosition( ) const { return *getAttribute< VectorF >( Node::POSITION ); } + + //! . + void setPosition( VectorF& position ) { setAttribute< VectorF >( Node::POSITION, position ); } + + //! . + void setPosition( float x, float y ) + { + VectorF& position = getPosition( ); + position( 0 ) = x; position( 1 ) = y; + } + + const VectorF& getDimension( ) const { return *getAttribute< VectorF >( Node::DIMENSION ); } + + void setDimension( const VectorF& dimension ) { setAttribute< VectorF >( Node::DIMENSION, dimension ); } + + void setDimension( float x, float y ) + { + VectorF& dimension = *getAttribute< VectorF >( Node::DIMENSION ); + dimension( 0 ) = x; dimension( 1 ) = y; + } + + //! Set the node date from a text string with the Posix Time format (ex: 2002-Jan-01 10:00:01). + void setDate( const QString& date ); + + //! Return the node date under Posix Time format (0 if the date is undefined). + const QDateTime* getDate( ) const { return getAttribute< QDateTime >( Node::DATE ); } + //@} + //----------------------------------------------------------------- + }; +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // laNode_h ============================================================ --- libs/qanava/src/qanRepository.cpp aa82d566624ec78117cf1e9c488c056d492d383b +++ libs/qanava/src/qanRepository.cpp aa82d566624ec78117cf1e9c488c056d492d383b @@ -0,0 +1,323 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file qanRepository.cpp +// \author Benoit Autheman (address@hidden) +// \date 2005 December 23 +//----------------------------------------------------------------------------- + + +// Qanava headers +#include "./qanRepository.h" + + +// Standard headers +#include +#include +#include + + +// QT headers +#include + + +namespace qan { // ::qan + + +/* Graph Serialization Management *///----------------------------------------- +void PajekRepository::load( Graph* graph ) +{ + std::ifstream netFile( getName( ).c_str( ) ); + if ( !netFile.is_open( ) ) + return; + + Mode mode = UNDEFINED; + while ( !netFile.eof( ) ) + { + std::string line; + std::getline( netFile, line ); // Parse the file line by line + + std::string header( "" ); + { + std::stringstream lineStream; + lineStream << line; + lineStream >> header; + } + + Mode oldMode = mode; + if ( header == "*Vertices" ) + mode = VERTICES; + else if ( header == "*Arcs" ) + mode = ARCS; + else if ( header == "*Edges" ) + mode = EDGES; + if ( oldMode != mode ) + continue; // A header line has just been read, jump to the next line + + std::stringstream lineStream; + lineStream << line; + + switch ( mode ) + { + case VERTICES: + { + int nodeId( -1 ); + lineStream >> nodeId; + + std::string nodeName( "" ); + lineStream >> nodeName; + + if ( ( nodeId > 0 ) && nodeName.size( ) > 0 ) + { + graph->insertNode( QString( nodeName.c_str( ) ) ); + } + } + break; + + case ARCS: + { + int nodeSrcId( -1 ); + lineStream >> nodeSrcId; + + int nodeDstId( -1 ); + lineStream >> nodeDstId; + + if ( nodeSrcId > 0 && nodeDstId > 0 ) + { + Node* nodeSrc = graph->findNode( nodeSrcId - 1 ); + Node* nodeDst = graph->findNode( nodeDstId - 1 ); + if ( nodeSrc != 0 && nodeDst != 0 ) + graph->addEdge( *nodeSrc, *nodeDst ); + } + } + break; + + case EDGES: + { + int nodeSrcId( -1 ); + lineStream >> nodeSrcId; + + int nodeDstId( -1 ); + lineStream >> nodeDstId; + + if ( nodeSrcId > 0 && nodeDstId > 0 ) + { + Node* nodeSrc = graph->findNode( nodeSrcId - 1 ); + Node* nodeDst = graph->findNode( nodeDstId - 1 ); + if ( nodeSrc != 0 && nodeDst != 0 ) + graph->addEdge( *nodeSrc, *nodeDst ); + } + } + break; + + case UNDEFINED: + default: + break; + } + } + netFile.close( ); + graph->generateRootNodes( ); +} + +void PajekRepository::save( Graph* graph ) +{ + +} +//----------------------------------------------------------------------------- + + +/* Graph Serialization Management *///----------------------------------------- +void GraphvizRepository::load( Graph* graph ) +{ + +} + +void GraphvizRepository::save( Graph* graph ) +{ + std::ofstream ofs; + ofs.open( getName( ).c_str( ) ); + if ( !ofs.is_open( ) ) + return; + + ofs << "graph g {\n"; + ofs << "\t center=true; \n"; + ofs << "#\t page=\"18,18\"; \n"; + ofs << "\t size=\"20,20\"; \n"; + ofs << "#\t ratio=compress; \n"; + ofs << "#\t concentrate=true; \n"; + ofs << "\t overlap=true; \n"; + ofs << "\t orientation=paysage; \n"; + + ofs << "\t node [ shape=box, style=filled, fontsize=10, height=0.2, width=0.4 ];\n"; + ofs << "\t edge [ len=1.5 ];\n"; + + // Dump nodes + Node::List::iterator nodeIter = graph->getNodes( ).begin( ); + for ( int n = 0; nodeIter != graph->getNodes( ).end( ); nodeIter++ ) + { + Node* node = *nodeIter; + //ofs << "\t" << n++/*node->getId( )*/ << " [label=\"" << node->getLabel( ).c_str( ) << "\"];\n"; + assert( false ); // FIXME + } + + // Dump nodes edges + for ( nodeIter = graph->getNodes( ).begin( ); nodeIter != graph->getNodes( ).end( ); nodeIter++ ) + { + Node* node = *nodeIter; + const Edge::List& edges = node->getOutEdges( ); + Edge::List::const_iterator edgeIter = edges.begin( ); + for ( ; edgeIter != edges.end( ); edgeIter++ ) + { + //Edge* edge = *edgeIter; + ofs << "\t" << 0/*edge->getSrc( ).getId( )*/ << " -- " << 0/*edge->getDst( ).getId( )*/ << "\n"; + } + } + ofs << "}\n"; + ofs.close( ); +} +//----------------------------------------------------------------------------- + + +/* GML Graph Serialization Management *///------------------------------------- +GMLRepository::GMLRepository( const std::string& name ) : + Repository( name ) +{ + +} + +void GMLRepository::load( Graph* graph ) +{ + if ( getName( ).size( ) <= 0 ) + return; + + QFile file( getName( ).c_str( ) ); + if ( !file.open( QFile::ReadOnly | QFile::Text ) ) + return; + + QString errorStr( "" ); + int errorLine = -1; + int errorColumn = -1; + + QDomDocument domDocument; + if ( !domDocument.setContent( &file, true, &errorStr, &errorLine, &errorColumn ) ) + return; + + QDomElement root = domDocument.documentElement( ); + if ( root.tagName( ) != "graphml" ) + return; + + QDomElement graphChild = root.firstChildElement( "graph" ); + if ( !graphChild.isNull( ) ) + parseGraph( domDocument, graphChild, graph ); +} + +void GMLRepository::parseGraph( QDomDocument domDocument, QDomElement element, Graph* graph ) +{ + if ( element.tagName( ) != "graph" ) + return; + + // Parse nodes + QDomNodeList nodes = element.elementsByTagName( "node" ); + for( unsigned int n = 0; n < nodes.length( ); n++ ) + { + QDomElement node = nodes.item( n ).toElement( ); + if ( node.isNull( ) ) + continue; + QString nodeId = node.attribute( "id" ); + Node* laNode = graph->insertNode( nodeId ); + + + // Parse data elements + QDomNodeList dataNodes = node.elementsByTagName( "data" ); + for ( unsigned int dn = 0; dn < dataNodes.length( ); dn++ ) + { + QDomElement dataNode = dataNodes.item( dn ).toElement( ); + if ( dataNode.isNull( ) ) + continue; + + QString dataKey = dataNode.attribute( "key" ); + QString dataText = dataNode.text( ); + + if ( dataKey.length( ) <= 0 ) + continue; + + if ( graph->hasAttribute( dataKey ) == -1 ) + graph->addAttribute< QString >( dataKey, QString( "" ) ); + graph->setAttribute< QString >( laNode, dataKey, dataText ); + } + } + + graph->generateRootNodes( ); + + //return; + // Parse edges + QDomNodeList edges = element.elementsByTagName( "edge" ); + for( unsigned int e = 0; e < edges.length( ); e++ ) + { + QDomElement edge = edges.item( e ).toElement( ); + if ( edge.isNull( ) ) + continue; + QString edgeSource = edge.attribute( "source" ); + QString edgeTarget = edge.attribute( "target" ); + + + // Parse data elements + double weight = 0.; + QDomNodeList dataNodes = edge.elementsByTagName( "data" ); + for ( unsigned int dn = 0; dn < dataNodes.length( ); dn++ ) + { + QDomElement dataNode = dataNodes.item( dn ).toElement( ); + if ( dataNode.isNull( ) ) + continue; + + QString dataKey = dataNode.attribute( "key" ); + QString dataText = dataNode.text( ); + + if ( dataKey.length( ) <= 0 ) + continue; + + if ( dataKey == "weight" ) + weight = dataText.toDouble( ); + } + + if ( ( edgeSource.length( ) > 0 ) && ( edgeTarget.length( ) > 0 ) ) + { + Node* laSrc = graph->findNode( edgeSource ); + Node* laDst = graph->findNode( edgeTarget ); + if ( ( laSrc != 0 ) && ( laDst != 0 ) ) + graph->addEdge( *laSrc, *laDst, weight ); + } + //break; + } + + graph->generateRootNodes( ); +} + +void GMLRepository::save( Graph* graph ) +{ + +} +//----------------------------------------------------------------------------- + + +} // ::qan + ============================================================ --- libs/qanava/src/qanRepository.h 6ff55c5f0778c3d9c5befa905c9499497c70f738 +++ libs/qanava/src/qanRepository.h 6ff55c5f0778c3d9c5befa905c9499497c70f738 @@ -0,0 +1,174 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file qanRepository.h +// \author Benoit Autheman (address@hidden) +// \date 2005 December 23 +//----------------------------------------------------------------------------- + + +#ifndef qanRepository_h +#define qanRepository_h + + +// Qanava headers +#include "./qanConfig.h" +#include "./qanGraph.h" + + +// STD headers +#include + + +// QT headers +#include + + +//----------------------------------------------------------------------------- +namespace qan { // ::qan + + //! Model an abstract storage object for graphs. + /*! + \nosubgrouping + */ + class Repository + { + /*! \name Graph Serialization Management *///---------------------- + //@{ + public: + + //! Repository constructor with name initialization. + Repository( const std::string& name ) : _name( name ) { } + + //! Repsoitory standard virtual destructor. + virtual ~Repository( ) { } + + //! . + virtual void load( Graph* graph ) = 0; + + //! . + virtual void save( Graph* graph ) = 0; + + //! Get the repository name. + const std::string& getName( ) const { return _name; } + + private: + + //! Repository name (usually a file name). + std::string _name; + //@} + //----------------------------------------------------------------- + }; + + + + //! + /*! + \nosubgrouping + */ + class PajekRepository : public Repository + { + /*! \name Graph Serialization Management *///---------------------- + //@{ + public: + + PajekRepository( const std::string& name ) : Repository( name ) { } + + virtual ~PajekRepository( ) { } + + //! . + virtual void load( Graph* graph ); + + //! . + virtual void save( Graph* graph ); + + private: + + //! Current data mode (Pajek Vertices, Arcs or Edges) + enum Mode + { + UNDEFINED = 1, + VERTICES = 2, + ARCS = 4, + EDGES = 8 + }; + //@} + //----------------------------------------------------------------- + }; + + + + //! DEPRECATED + /*! DEPRECATED, used for debugging purposes only. + \nosubgrouping + */ + class GraphvizRepository : public Repository + { + /*! \name Graph Serialization Management *///---------------------- + //@{ + public: + + GraphvizRepository( const std::string& name ) : Repository( name ) { } + + virtual ~GraphvizRepository( ) { } + + //! . + virtual void load( Graph* graph ); + + //! . + virtual void save( Graph* graph ); + //@} + //----------------------------------------------------------------- + }; + + //! GML (XML based Graph Markup Language) repository. + /*! + \nosubgrouping + */ + class GMLRepository : public Repository + { + /*! \name Graph Serialization Management *///---------------------- + //@{ + public: + + GMLRepository( const std::string& name ); + + virtual ~GMLRepository( ) { } + + //! . + virtual void load( Graph* graph ); + + //! . + virtual void save( Graph* graph ); + + private: + + void parseGraph( QDomDocument domDocument, QDomElement graphChild, Graph* graph ); + //@} + //----------------------------------------------------------------- + }; +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // qanRepository_h + ============================================================ --- libs/qanava/src/qanStyle.cpp e37b580f9069abc280b206b7c8ac6b548c4495b1 +++ libs/qanava/src/qanStyle.cpp e37b580f9069abc280b206b7c8ac6b548c4495b1 @@ -0,0 +1,373 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qarte software. +// +// \file qanStyle.cpp +// \author Benoit Autheman (address@hidden) +// \date 2005 January 03 +//----------------------------------------------------------------------------- + + +// Qanava headers +#include "./qanStyle.h" + + +// QT headers +#include +#include +#include +#include + + +namespace qan { // ::qan + + +/* Style Manager Constructor/Destructor *///----------------------------------- +Style::Manager::Manager( ) : + _empty( new Style( "empty" ) ) +{ + +} + +Style::Manager::~Manager( ) +{ + if ( _empty != 0 ) + delete _empty; + clear( ); +} +//----------------------------------------------------------------------------- + + + +/* Style Management *///------------------------------------------------------- +void Style::Manager::clear( ) +{ + QSet< Style* > styles = QSet< Style* >::fromList( _objectStyleMap.values( ) ); + foreach ( Style* style, styles ) + delete style; + _objectStyleMap.clear( ); + _typeStyleMap.clear( ); + clearImages( ); +} + +void Style::Manager::setStyle( void* object, Style& style ) +{ + if ( !_objectStyleMap.contains( object ) ) + _objectStyleMap.insert( object, &style ); +} + +Style* Style::Manager::getStyle( void* object ) +{ + return _objectStyleMap.value( object, 0 ); +} + +const Style* Style::Manager::getStyle( const void* object ) const +{ + return _objectStyleMap.value( const_cast< void* >( object ), 0 ); +} + +void Style::Manager::setStyle( int type, Style& style ) +{ + // Set the style if the type don't already have a style + if ( !_typeStyleMap.contains( type ) ) + _typeStyleMap.insert( type, &style ); +} + +Style* Style::Manager::getStyle( int type ) +{ + return _typeStyleMap.value( type, 0 ); +} + +const Style* Style::Manager::getStyle( int type ) const +{ + return _typeStyleMap.value( type, 0 ); +} +//----------------------------------------------------------------------------- + + +/* Image Caching Management *///----------------------------------------------- +/*! + \return A cached image loaded from the given filename. If fileName is invalid, the returned image can be queried for isNull(). + */ +QImage Style::Manager::getImage( QString fileName ) +{ + NameImageMap::iterator imageIter = _nameImageMap.find( fileName ); + if ( imageIter != _nameImageMap.end( ) ) + return imageIter.value( ); + else + { + QImage image( fileName ); + if ( image.isNull( ) ) + return image; + else + { + _nameImageMap.insert( fileName, image ); + return image; + } + } + + return QImage(); +} + +void Style::Manager::clearImages( ) +{ + _nameImageMap.clear( ); +} +//----------------------------------------------------------------------------- + + + +/* Attribute Management *///--------------------------------------------------- +void Style::add( QString name, QVariant variant ) +{ + _nameValueMap.insert( name, variant ); + reset( ); // Force coherent Interview update whitout using (begin/end)Insert() + emit modified( ); +} + +void Style::remove( QString name ) +{ + _nameValueMap.remove( name ); + _imageNames.removeAll( name ); + reset( ); // Force coherent Interview update whitout using (begin/end)Insert() + emit modified( ); +} + +/*! + \return true if the attribute has been renamed, false otherwise (for example, the name was already used by another attribute). + */ +bool Style::rename( QString name, QString newName ) +{ + if ( has( newName ) ) + return false; + + QVariant value = get( name ); + remove( name ); + add( newName, value ); + reset( ); // Force coherent Interview update + return true; +} + +/*! + \return true if a QVariant is registered under the given name, false otherwise. + */ +bool Style::has( QString name ) const +{ + NameValueMap::const_iterator nameIter = _nameValueMap.find( name ); + return ( nameIter != _nameValueMap.end( ) ); +} + +/*! + \return A QVariant registered under a given name, or an invalid QVariant if the name is unknown. + */ +QVariant Style::get( QString name ) +{ + return _nameValueMap.value( name, QVariant( ) ); +} + +/*! + \return A const QVariant registered under a given name, or an invalid QVariant if the name is unknown. + */ +const QVariant Style::get( QString name ) const +{ + /*QVariant result; + NameValueMap::const_iterator nameIter = _nameValueMap.find( name ); + if ( nameIter != _nameValueMap.end( ) ) + result = nameIter->second; + return result;*/ + return _nameValueMap.value( name, QVariant( ) ); +} + +void Style::addColor( QString name, int r, int g, int b ) +{ + QVariant value; + value = QColor( r, g, b ); // QVariant does not support QColor in its ctor (non gui only types) + add( name, value ); +} + +/*! + \return The color registered with name, an invalid Qcolor otherwise. + */ +QColor Style::getColor( QString name ) const +{ + QColor color; + const QVariant& value = get( name ); + if ( value.isValid( ) ) + color = value.value< QColor >( ); + return color; +} + +void Style::addIcon( QString name, QIcon& icon ) +{ + QVariant value; + value = icon; // QVariant does not support gui types in its ctor + add( name, value ); +} + +QIcon Style::getIcon( QString name ) const +{ + QIcon icon; + const QVariant& value = get( name ); + if ( value.isValid( ) ) + icon = value.value< QIcon >( ); + return icon; +} + +void Style::addImage( QString name, QImage image ) +{ + QVariant value; + value = image; // QVariant does not support gui types in its ctor + add( name, value ); +} + +void Style::addImageName( QString name, QString fileName ) +{ + add( name, QVariant( fileName ) ); + _imageNames.push_back( name ); +} +bool Style::hasImageName( QString name ) const +{ + return ( _imageNames.contains( name ) ); +} + +QString Style::getImageName( QString name ) const +{ + if ( hasImageName( name ) ) // Check if name is an image name type + { + QVariant imageName = get( name ); + if ( imageName.type( ) == QVariant::String ) + return imageName.toString( ); + } + return QString( ); +} +//----------------------------------------------------------------------------- + + + +/* Qt Model Interface *///----------------------------------------------------- +QVariant Style::data( const QModelIndex& index, int role ) const +{ + if ( !index.isValid( ) ) + return QVariant( "index invalid" ); + + QVariant d; + + if ( ( role == Qt::FontRole ) && ( index.column( ) == 0 ) ) + { + QFont font; + font.setWeight( QFont::Bold ); + d = font; + } + + if ( role == Qt::DisplayRole ) + { + if ( index.column( ) == 0 ) + { + if ( index.row( ) == 0 ) + d = QString( "name" ); + else + d = _nameValueMap.keys( ).at( index.row( ) - 1 ); + } + else if ( index.column( ) == 1 ) + { + if ( index.row( ) == 0 ) + d = getName( ); + else + d = _nameValueMap.values( ).at( index.row( ) - 1 ); + } + } + + return d; +} + +bool Style::setData( const QModelIndex& index, const QVariant& value, int role ) +{ + if ( index.row( ) == 1 ) + return QAbstractListModel::setData( index, value, role ); + + if ( role == Qt::EditRole && index.column( ) == 1 ) // Edit values + { + QString k = _nameValueMap.keys( ).at( index.row( ) - 1 ); + QVariant v = _nameValueMap.values( ).at( index.row( ) - 1 ); + if ( v.type( ) == value.type( ) ) + { + v = value; + _nameValueMap.insert( k, v ); + emit dataChanged( index, index ); + emit modified( ); + return true; + } + } + if ( role == Qt::EditRole && index.column( ) == 0 ) // Edit names + { + if ( value.type( ) == QVariant::String ) + { + QString k = _nameValueMap.keys( ).at( index.row( ) - 1 ); + if ( rename( k, value.toString( ) ) ) // Rename old name with the editor content and eventually validate the input + { + emit dataChanged( index, index ); + emit modified( ); + return true; + } + } + } + return QAbstractListModel::setData( index, value, role ); +} + +Qt::ItemFlags Style::flags( const QModelIndex &index ) const +{ + if ( !index.isValid( ) ) + return Qt::ItemIsEnabled; + + if ( index.row( ) == 0 ) + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; // First row (style name) is not editable + else + return Qt::ItemIsEnabled | Qt::ItemIsEditable; // Other rows are editable in both the name and value columns + + return Qt::ItemIsEnabled; +} + +QVariant Style::headerData( int section, Qt::Orientation orientation, int role ) const +{ + if ( orientation == Qt::Horizontal && role == Qt::DisplayRole ) + { + if ( section == 0 ) + return QString( "Property" ); + else if ( section == 1 ) + return QString( "Value" ); + } + return QVariant( ); +} + +int Style::rowCount( const QModelIndex& parent ) const +{ + return 1 + size( ); +} + +int Style::columnCount( const QModelIndex& parent ) const +{ + return 2; +} +//----------------------------------------------------------------------------- + + +} // ::qan + ============================================================ --- libs/qanava/src/qanStyle.h 2a4c2efef1897cac58bad371e7d27bddab3f78a8 +++ libs/qanava/src/qanStyle.h 2a4c2efef1897cac58bad371e7d27bddab3f78a8 @@ -0,0 +1,303 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qarte software. +// +// \file qanStyle.h +// \author Benoit Autheman (address@hidden) +// \date 2005 January 03 +//----------------------------------------------------------------------------- + + +#ifndef qan_can_Style_h +#define qan_can_Style_h + + +// QT headers +#include +#include +#include +#include +#include + + +//----------------------------------------------------------------------------- +namespace qan // ::qan +{ + //! Specify graphic and other attributes for a specific primitive (usually a Canvas Item). + /*! + \nosubgrouping + */ + class Style : public QAbstractListModel + { + Q_OBJECT + + public: + + //! Manage styles for a set of objects (usually graphics items). + /*! + If you are applying style to nodes, it is usually safer to use the dedicated methods + from GraphItemView such as applyStyle() rather than accessing this style manager + directly. + + \sa GraphItemView + \nosubgrouping + */ + class Manager + { + /*! \name Manager Constructor/Destructor *///------------------ + //@{ + public: + + Manager( ); + + virtual ~Manager( ); + //@} + //------------------------------------------------------------- + + + + /*! \name Style Management *///-------------------------------- + //@{ + public: + + //! Clear this manager from all mapping and delete registered styles. + void clear( ); + + //! Set the style for a specific object. + void setStyle( void* object, Style& style ); + + //! Set the style for a specific node type. + void setStyle( int type, Style& style ); + + //! Get the style for a specific object. + Style* getStyle( void* object ); + + //! Get the style for a specific object. + const Style* getStyle( const void* object ) const; + + //! Get the style for a specific node type. + Style* getStyle( int type ); + + //! Get the style for a specific node type. + const Style* getStyle( int type ) const; + + //! Get a void style (empty style is owned by this manager, but not registered in). + Style* getEmptyStyle( ) const { return _empty; } + + protected: + + //! Map style to their target object. + typedef QMap< void*, Style* > ObjectStyleMap; + + //! Map style to their target object. + typedef QMap< int, Style* > TypeStyleMap; + + //! . + ObjectStyleMap _objectStyleMap; + + //! . + TypeStyleMap _typeStyleMap; + + Style* _empty; + //@} + //------------------------------------------------------------- + + + + /*! \name Image Caching Management *///------------------------ + //@{ + public: + + //! Get an image from its file name or its cached version the second call of the same filename. + QImage getImage( QString fileName ); + + //! Clear this image manager mappings and data (images). + void clearImages( ); + + protected: + + typedef QMap< QString, QImage > NameImageMap; + + //! Map image filename to concrete QT images. + NameImageMap _nameImageMap; + //@} + //------------------------------------------------------------- + }; + + + /*! \name Style Constructor/Destructor *///------------------------ + //@{ + public: + + //! Style constructor. + Style( ) : _name( "" ) { } + + //! Style constructor with name initialisation. + Style( QString name ) : _name( name ) { } + + private: + + //! Style empty private copy constructor. + Style( const Style& style ); + //@} + //----------------------------------------------------------------- + + + + /*! \name Style Name Management *///------------------------------- + //@{ + public: + + //! Get the style name. + QString getName( ) const { return _name; } + + protected: + + //! Style name. + QString _name; + //@} + //----------------------------------------------------------------- + + + + /*! \name Attribute Management *///-------------------------------- + //@{ + public: + + //! Add an attribute and register its name. + template < typename T > + void addT( QString name, T value ) + { + add( name, QVariant( value ) ); + } + + template < typename T > + T getT( QString name ) const + { + QVariant value = get( name ); + if ( value.isValid( ) ) + return value.value< T >( ); + return T( ); + } + + //! Return the number of attributes/value couples registered in this style. + unsigned int size( ) const { return _nameValueMap.size( ); } + + //! Add an attribute and register its name. + void add( QString name, QVariant value ); + + //! Remove a style attribute by name. + void remove( QString name ); + + //! Change an attribute name (changing to an already existing name, has no effect). + bool rename( QString name, QString newName ); + + //! Test if an attribute with a specific name has been set. + bool has( QString name ) const; + + //! Get an attribute by name (invalide QVariant is returned if the name does not exists). + QVariant get( QString name ); + + //! Get an attribute by name (invalide QVariant is returned if the name does not exists). + const QVariant get( QString name ) const; + + //! Specialized version of add() for QColor objects. + void addColor( QString name, int r, int g, int b ); + + //! Get a previously registered QColor by name. + QColor getColor( QString name ) const; + + //! Specialized version of add() for QIcon objects. + void addIcon( QString name, QIcon& icon ); + + //! Get a previously registered QIcon by name. + QIcon getIcon( QString name ) const; + + //! Specialized version of add() for QImage objects. + void addImage( QString name, QImage image ); + + //! Add an image attribute, image loading is delayed until display, and images shared accross styles. + void addImageName( QString name, QString fileName ); + + //! Test if an image name with a specific name has been set. + bool hasImageName( QString name ) const; + + //! . + QString getImageName( QString name ) const; + + protected: + + typedef QMap< QString, QVariant > NameValueMap; + + bool isImageName( QString name ) { return _imageNames.contains( name ); } + + QList< QString > _imageNames; + + //! . + NameValueMap _nameValueMap; + //@} + //----------------------------------------------------------------- + + + /*! \name Qt Model Interface *///---------------------------------- + //@{ + public: + + //! Define the article attribute offset (row) in table. + enum AttributeOffset + { + TITLE = 0, + AUTHOR = 1, + SOURCE = 2, + SUB = 3, + SUP = 4 + }; + + virtual QVariant data( const QModelIndex &index, int role ) const; + + virtual bool setData( const QModelIndex& index, const QVariant& value, int role = Qt::EditRole ); + + virtual Qt::ItemFlags flags( const QModelIndex &index ) const; + + virtual QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + virtual int rowCount( const QModelIndex& parent = QModelIndex( ) ) const; + + virtual int columnCount( const QModelIndex& parent = QModelIndex( ) ) const; + //@} + //----------------------------------------------------------------- + + + /*! \name Model Signals Management *///---------------------------- + //@{ + signals: + + void modified( ); + //@} + //----------------------------------------------------------------- + }; +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // qan_can_Style_h + + ============================================================ --- libs/qanava/src/qanTimeTree.cpp 00ddcd23a8433e6b4cc8491051fb48ccc1b7ed13 +++ libs/qanava/src/qanTimeTree.cpp 00ddcd23a8433e6b4cc8491051fb48ccc1b7ed13 @@ -0,0 +1,628 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file laLayout.cpp +// \author Benoit Autheman (address@hidden) +// \date 2004 December 05 +//----------------------------------------------------------------------------- + + +// Qanava haders +#include "./qanTimeTree.h" + + +// Std headers +#include + + +namespace qan { // ::qan + + +bool TimeTree::NodeDateComp::operator()( const Node* n1, const Node* n2 ) const +{ + const QDateTime* d1 = n1->getDate( ); + const QDateTime* d2 = n2->getDate( ); + if ( d1 == 0 || d2 == 0 ) + return false; + return ( *d1 < *d2 ); +} + +/* TimeTree Grid Management *///----------------------------------------------- +/*! + \param cy Y coordinate of the cluster top left corner + \param w Width of the cluster. + \param h Height of the cluster. + */ +void TimeTree::GridBuilder::buildClusterGrid( int cy, int w, int h, int r, int g, int b ) +{ + _grid.addLine( QLineF( 0, cy + h, w, cy + h ), 1, false, false ); + _grid.addRectangle( QRectF( 0, cy, w, h + 1 ), QColor( r, g, b ) ); +} + +/*! + \param sortedNodes Set of time sorted nodes already placed on the canvas. + \param maxX, maxY Maximum coordinate for the nodes in the current layout. +*/ +void TimeTree::GridBuilder::buildTimeGrid( NodeSet& nodes, int, int height ) +{ + // Draw vertical time grid lines + float x = 10.f; + const QDateTime* lastNodeDate = 0; + for ( NodeSet::iterator nodeIter = nodes.begin( ); nodeIter != nodes.end( ); nodeIter++ ) + { + // Set default separation line settings + bool dash = false; + bool dot = true; + + const QDateTime* nodeDate = ( *nodeIter )->getDate( ); + if ( nodeDate != 0 && lastNodeDate != 0 && ( *nodeDate == *lastNodeDate ) ) + x -= incX; + + // Different days are separated with a strong line + if ( ( nodeDate != 0 && lastNodeDate == 0 ) || // First day + ( nodeDate != 0 && lastNodeDate != 0 && ( nodeDate->date( ) > lastNodeDate->date( ) ) ) ) // New Day + { + dash = true; + dot = false; + + // Add the date text + QString dateText( "" ); + try + { + //dateText = nodeDate->date( ).toString( "yyyy-MMM-dd" ).latin1( ); + dateText = nodeDate->date( ).toString( "yyyy-MMM-dd" ); + } catch( std::exception& ) { } + _grid.addText( dateText, QPointF( ( int )x, 2 ), true ); + _grid.addText( dateText, QPointF( ( int )x, height - 17 ), true ); + } + + // Add separation line to grid + _grid.addLine( QLineF( ( int )x - 5, 0, ( int )x - 5, height ), 1, dash, dot ); + + lastNodeDate = nodeDate; + x += incX; + } +} + +void TimeTree::GridBuilder::buildTimeLabel( Node& node, int x, int y ) +{ + const QDateTime* nodeDate = node.getDate( ); + if ( nodeDate != 0 ) + { + // Add date label to grid + QString timeText( "" ); + try + { + timeText = nodeDate->toString( QString( "hh:mm::ss" ) ); + } catch( std::exception& ) { timeText = "00:01:00"; } + _grid.addText( timeText, QPointF( x, y ) ); + } +} +//----------------------------------------------------------------------------- + +bool TimeTree::Group::GroupMedDateComp::operator()( const Group* g1, const Group* g2 ) const +{ + QDateTime tg1 = g1->getMedianDate( ); + QDateTime tg2 = g2->getMedianDate( ); + return ( tg1 < tg2 ); +} + +/*! + \return A pointer to the line node list, 0 if line index is negative or superior to the group lines count. + */ +Node::List* TimeTree::Group::getLine( unsigned int line ) +{ + if ( line >= _lines.size( ) ) + return 0; + return _lines[ line ]; +} + +void TimeTree::Group::collectNodes( Node::List& nodes ) const +{ + for ( Lines::const_iterator lineIter = _lines.begin( ); lineIter != _lines.end( ); lineIter++ ) + { + const Node::List* lineNodes = *lineIter; + std::copy( lineNodes->begin( ), lineNodes->end( ), + std::back_insert_iterator< Node::List >( nodes ) ); + } +} + +void TimeTree::Group::setNodesType( int type ) +{ + Node::List nodes; + collectNodes( nodes ); + for ( Node::List::iterator nodeIter = nodes.begin( ); nodeIter != nodes.end( ); nodeIter++ ) + ( *nodeIter )->setType( type ); +} + +/*! + To avoid aggressive merge during layout, the method return true when a node with no date + occurs. + */ +bool TimeTree::Group::intersect( const Group& group ) const +{ + for ( Lines::const_iterator lineIter = _lines.begin( ); lineIter != _lines.end( ); lineIter++ ) + { + const Node::List* lineNodes = *lineIter; + if ( lineNodes->size( ) < 1 ) + continue; + const Node* nodeFirst = lineNodes->front( ); + const Node* nodeLast = lineNodes->back( ); + + const QDateTime* nodeFirstDate = nodeFirst->getDate( ); + const QDateTime* nodeLastDate = nodeLast->getDate( ); + if ( nodeFirstDate == 0 || nodeLastDate == 0 ) + continue; + + // Test the current line time interval with the given group ones + for ( Lines::const_iterator lineDstIter = group._lines.begin( ); lineDstIter != group._lines.end( ); lineDstIter++ ) + { + const Node::List* lineDstNodes = *lineDstIter; + if ( lineDstNodes->size( ) < 1 ) + continue; + const Node* nodeDstFirst = lineDstNodes->front( ); + const Node* nodeDstLast = lineDstNodes->back( ); + + const QDateTime* nodeDstFirstDate = nodeDstFirst->getDate( ); + const QDateTime* nodeDstLastDate = nodeDstLast->getDate( ); + if ( nodeDstFirstDate == 0 || nodeDstLastDate == 0 ) + continue; + + // Test mutual intersection + + // Dst node area has an extremity in the node area + if ( ( ( *nodeDstFirstDate >= *nodeFirstDate ) && ( *nodeDstFirstDate <= *nodeLastDate ) ) || + ( ( *nodeDstLastDate >= *nodeFirstDate ) && ( *nodeDstLastDate <= *nodeLastDate ) ) ) + return true; + + // Dst node area is larger than the node are + if ( ( *nodeDstFirstDate <= *nodeFirstDate ) && ( *nodeDstLastDate >= *nodeLastDate ) ) + return true; + } + } + return false; +} + +QDateTime TimeTree::Group::getMedianDate( ) const +{ + Node::List groupNodes; + collectNodes( groupNodes ); + + // Get the min and max date in the cluster articles + QDateTime dateMin( QDate( 3000, 1, 1 ), QTime( 0, 0 ) ); + QDateTime dateMax( QDate( 0, 1, 1 ), QTime( 0, 0 ) ); + for ( Node::List::const_iterator nodeIter = groupNodes.begin( ); nodeIter != groupNodes.end( ); nodeIter++ ) + { + const QDateTime* nodeDate = ( *nodeIter )->getDate( ); + if ( nodeDate != 0 ) + { + if ( *nodeDate < dateMin ) + dateMin = *nodeDate; + if ( *nodeDate > dateMax ) + dateMax = *nodeDate; + } + } + + // Get the median time + //time_duration groupDuration = dateMax - dateMin; + //return dateMin + ( groupDuration / 2 ); + int dayToMedian = dateMin.daysTo( dateMax ) / 2; + QDateTime result = dateMin.addDays( dayToMedian ); + // BUG: prendre l'heure en compte et pas seulement les days + return result; +} + + +/*! + This method assumer that the groups inside the two track are disjoint within their own track groups. + */ +bool TimeTree::Track::isDisjointOf( Track& track ) +{ + // Check that given track groups are disjoint from thie track groups + Group::Groups::iterator groupIter = track.getGroups( ).begin( ); + for ( ; groupIter != track.getGroups( ).end( ); groupIter++ ) + { + Group::Groups::iterator groupDstIter = getGroups( ).begin( ); + for ( ; groupDstIter != getGroups( ).end( ); groupDstIter++ ) + { + if ( ( *groupIter )->intersect( **groupDstIter ) ) + return false; + } + } + return true; +} + +/*! This method will merge tracks even if they have intersecting groups, so please verfiy track intersection if + such behaviour is not desired. + */ +void TimeTree::Track::mergeTrack( Track& track ) +{ + Group::Groups& trackGroups = track.getGroups( ); + for ( Group::Groups::iterator groupIter = trackGroups.begin( ); groupIter != trackGroups.end( ); groupIter++ ) + _groups.push_front( *groupIter ); + trackGroups.clear( ); +} + +/*! + \return Maximum number of lines in all track node groups, 0 if there is no lines. + */ +int TimeTree::Track::getLineCount( ) const +{ + int lineCount = 0; + for ( Group::Groups::const_iterator groupIter = _groups.begin( ); groupIter != _groups.end( ); groupIter++ ) + { + if ( ( *groupIter )->getLineCount( ) > lineCount ) + lineCount = ( *groupIter )->getLineCount( ); + } + return lineCount; +} + +/*! + \return Height of the track after the layout. + */ +float TimeTree::Track::layout( float trackY, float width, GridBuilder& gridBuilder ) +{ + float trackYStart = trackY; + trackY += trackIntervalY; + int lineCount = getLineCount( ); + for ( int line = 0; line < lineCount; line++ ) + { + Node::List lineNodes; + + // Regroup track group current line as one line + for ( Group::Groups::iterator groupIter = getGroups( ).begin( ); groupIter != getGroups( ).end( ); groupIter++ ) + { + Node::List* groupLineNodes = ( *groupIter )->getLine( line ); + if ( groupLineNodes == 0 ) + continue; + std::copy( groupLineNodes->begin( ), groupLineNodes->end( ), + std::back_insert_iterator< Node::List >( lineNodes ) ); + } + + // Get current line maximum height + float lineHeight = 0.f; + for ( Node::List::iterator lineNodeIter = lineNodes.begin( ); lineNodeIter != lineNodes.end( ); lineNodeIter++ ) + { + Node* lineNode = ( *lineNodeIter ); + if ( lineNode->getDimension( )( 1 ) > lineHeight ) + lineHeight = lineNode->getDimension( )( 1 ); + } + + // Center line nodes vertically + for ( Node::List::iterator lineNodeIter = lineNodes.begin( ); lineNodeIter != lineNodes.end( ); lineNodeIter++ ) + { + Node* lineNode = ( *lineNodeIter ); + gridBuilder.buildTimeLabel( *lineNode, ( int )lineNode->getPosition( )( 0 ), ( int )trackY - 15 ); + float deltaY = ( ( lineHeight - lineNode->getDimension( )( 1 ) ) / 2.f ); + lineNode->getPosition( )( 1 ) = trackY + deltaY; + } + + // Switch to next line + trackY += lineHeight; + if ( line < lineCount - 1 ) + { + trackY += 2; + + // Add line grid separation line + gridBuilder.getGrid( ).addLine( QLineF( 0, ( int )trackY, ( int )width, ( int )trackY ), 1, false, true ); + + trackY += lineIntervalY; + } + } + trackY += 2; + return ( trackY - trackYStart ); +} + +void TimeTree::Track::collectGroups( Group::Groups& groups ) +{ + // Sort this track groups according to their average date thanks to a sorted multiset + Group::GroupSet groupSet; + std::copy( _groups.begin( ), _groups.end( ), std::inserter( groupSet, groupSet.begin( ) ) ); + + // Copy the sorted groups to the groups collection list + std::copy( groupSet.begin( ), groupSet.end( ), std::back_insert_iterator< Group::Groups >( groups ) ); +} + +int TimeTree::Track::getNodeCount( ) const +{ + int nodeCount = 0; + Group::Groups::const_iterator groupIter = _groups.begin( ); + for ( ; groupIter != _groups.end( ); groupIter++ ) + { + Node::List nodes; + ( *groupIter )->collectNodes( nodes ); + nodeCount += nodes.size( ); + nodes.clear( ); + } + return nodeCount; +} + +void TimeTree::Tracks::addTrack( Track* track ) +{ + _tracks.push_back( track ); +} + +void TimeTree::Tracks::mergeTracks( ) +{ + std::set< Track* > tracksMerged; + + // Try to insert each tracks in the other ones + for ( Track::Tracks::iterator trackIter = _tracks.begin( ); trackIter != _tracks.end( ); trackIter++ ) + { + Track* track = *trackIter; + + Track::Tracks::iterator trackDstIter = _tracks.begin( ); + for ( ; trackDstIter != _tracks.end( ); trackDstIter++ ) + { + Track* trackDst = *trackDstIter; + if ( track == trackDst ) + continue; + + // Don't merge to a track that is merged and no longer exists + if ( tracksMerged.find( trackDst ) != tracksMerged.end( ) ) + continue; + + // Test if track content can be merged in dst track + if ( trackDst->isDisjointOf( *track ) ) + { + trackDst->mergeTrack( *track ); + tracksMerged.insert( track ); + } + } + } + + // Supress merged tracks + { + std::set< Track* >::iterator trackIter = tracksMerged.begin( ); + for ( ; trackIter != tracksMerged.end( ); trackIter++ ) + _tracks.remove( *trackIter ); + } +} + +/*! + \return Height of the resulting layout. + */ +float TimeTree::Tracks::layout( float width, GridBuilder& gridBuilder ) +{ + // Try to insert each tracks in the other ones + float trackY = 0.f; + unsigned int trackId = 0; + for ( Track::Tracks::iterator trackIter = _tracks.begin( ); trackIter != _tracks.end( ); trackId++, trackIter++ ) + { + Track* track = *trackIter; + + // For the first cluster increase space for the top days labels + float trackStart = trackY; + if ( trackId == 0 ) + trackY += trackIntervalY + 2; + + // Layout track groups + float height = track->layout( trackY, width, gridBuilder ); + trackY += height; + + int r = ( trackId % 2 == 0 ) ? 105 : 170; + int g = ( trackId % 2 == 0 ) ? 105 : 171; + int b = ( trackId % 2 == 0 ) ? 175 : 205; + + // For the last cluster increase space for the top days labels + if ( trackId >= _tracks.size( ) - 1 ) + trackY += trackIntervalY + 2; + float trackEnd = trackY; + + gridBuilder.buildClusterGrid( ( int )trackStart, ( int )width, ( int )( trackEnd - trackStart ), r, g, b ); + } + return trackY; +} + +void TimeTree::Tracks::collectGroups( Group::Groups& groups ) +{ + // Collect tracks in their natural order (in fact they are garanteed to be sorted as they have been + // added (and they have been added according to their group average date) + for ( Track::Tracks::iterator trackIter = _tracks.begin( ); trackIter != _tracks.end( ); trackIter++ ) + ( *trackIter )->collectGroups( groups ); +} + + +bool TimeTree::TrackDensityComp::operator()( const TimeTree::Track* t1, const TimeTree::Track* t2 ) const +{ + + return ( t1->getNodeCount( ) / t1->getLineCount( ) ) > ( t2->getNodeCount( ) / t2->getLineCount( ) ); +} + + +void TimeTree::Tracks::sortByNodeDensity( ) +{ + std::set< TimeTree::Track*, TrackDensityComp > tracksSetDensity; + std::copy( _tracks.begin( ), _tracks.end( ), std::inserter( tracksSetDensity, tracksSetDensity.begin( ) ) ); + _tracks.clear( ); + std::copy( tracksSetDensity.begin( ), tracksSetDensity.end( ), + std::back_insert_iterator< Track::Tracks >( _tracks ) ); +} + +// Layout settings +float TimeTree::incX = 83.f; + +float TimeTree::lineIntervalY = 16.f; + +float TimeTree::trackIntervalY = 16.f; + + +/* TimeTree Layout Generation Management *///---------------------------------- +/*! + \note If cluster track merge option is enabled, then a valid style manager must + have been provided in the TimeTree object constructor, otherwise, consecutive groups (clusters) + will eventually be affected the same graphic style, creating undiscernable groups. + */ +void TimeTree::layout( Graph& graph, Grid& grid, int /*width*/, int /*height*/, QProgressDialog* ) +{ + // Layout the nodes horizontally + NodeSet nodes; + std::copy( graph.getNodes( ).begin( ), graph.getNodes( ).end( ), + std::inserter( nodes, nodes.begin( ) ) ); + float layoutWidth = layoutNodesX( nodes ); + + // Generate groups for all nodes set to layout + Group::GroupSet groups; + NodeSetManager::iterator nodeSetIter = _nodeSetManager.begin( ); + for ( ; nodeSetIter != _nodeSetManager.end( ); nodeSetIter++ ) + { + Group* clusterGroup = buildGroup( **nodeSetIter ); + groups.insert( clusterGroup ); + } + + // Create a track per group (tracks are sorted by time, since groups already are) + Tracks* tracks = new Tracks( ); + for ( Group::GroupSet::iterator groupIter = groups.begin( ); groupIter != groups.end( ); groupIter++ ) + { + Track* track = new Track( ); + track->addGroup( *groupIter ); + tracks->addTrack( track ); + } + + // Optimize tracks placement + tracks->mergeTracks( ); + + // Sort tracks per average node density (to maximize node count on first page) +// tracks->sortByNodeDensity( ); + + // Affect styles such that two consecutive groups on a track do not have the same style + { + int groupId = 0; + Group::Groups groups; + tracks->collectGroups( groups ); + for ( Group::Groups::iterator groupIter = groups.begin( ); groupIter != groups.end( ); groupId++, groupIter++ ) + ( *groupIter )->setNodesType( 1 + ( groupId % 3 ) ); + } + + // Fix the tracks layout, ie assign concrete coordinate to graph nodes + GridBuilder gridBuilder( grid ); + float layoutHeight = tracks->layout( layoutWidth, gridBuilder ); + + // Draw the time grid + gridBuilder.buildTimeGrid( nodes, ( int )layoutWidth, ( int )layoutHeight ); +} + +float TimeTree::layoutNodesX( NodeSet& nodes ) +{ + // Set nodes horizontal (x) position according to their date + float x = 10.f; + Node* lastNode = 0; + for ( NodeSet::iterator sortedNodeIter = nodes.begin( ); sortedNodeIter != nodes.end( ); sortedNodeIter++ ) + { + Node* node = *sortedNodeIter; + if ( lastNode != 0 ) + { + const QDateTime* nodeDate = node->getDate( ); + const QDateTime* lastNodeDate = lastNode->getDate( ); + if ( nodeDate != 0 && lastNodeDate != 0 && ( *nodeDate == *lastNodeDate ) ) + x -= incX; + } + + node->setPosition( x, -1.0f ); // y=-1 used later to indicate the node as still not been placed + x += incX; + + lastNode = node; + } + return x; +} + +TimeTree::Group* TimeTree::buildGroup( NodeSet& clusterNodes ) +{ + Group* clusterGroup = new Group( ); + + Node::List nodes; + std::copy( clusterNodes.begin( ), clusterNodes.end( ), + std::back_insert_iterator< Node::List >( nodes ) ); + + // Consume all nodes, ie affect all nodes to a line + unsigned int runCount = 0; + while ( ( nodes.size( ) > 0 ) && ( runCount < clusterNodes.size( ) ) ) + { + // Consume as many nodes as possible for a line (ie all non simultaneous nodes or a node with a subnode in this cluster) + Node::List* lineNodes = new Node::List( ); + Node* prevNode = 0; + for ( Node::List::iterator nodeIter = nodes.begin( ); nodeIter != nodes.end( ); nodeIter++ ) + { + Node* node = *nodeIter; + + // Detect if the node hasn't already been positionned (ex: it is a sub node of an existing node in this cluster) + if ( node->getPosition( )( 1 ) >= 0.f ) + continue; + + if ( ( prevNode == 0 ) || + ( prevNode != 0 && ( node->getPosition( )( 0 ) > prevNode->getPosition( )( 0 ) ) ) ) + { + // Consume the node + lineNodes->push_back( node ); + node->getPosition( )( 1 ) = 1.0f; + prevNode = node; + + // Consume the node subnodes (and sort subnodes by date) + NodeSet sortedOutNodes; + Node::Set outNodes; node->collectOutNodesSet( outNodes ); + std::copy( outNodes.begin( ), outNodes.end( ), std::inserter( sortedOutNodes, sortedOutNodes.begin( ) ) ); + + for ( NodeSet::iterator outNodeIter = sortedOutNodes.begin( ); outNodeIter != sortedOutNodes.end( ); outNodeIter++ ) + { + Node* outNode = *outNodeIter; + if ( clusterNodes.find( outNode ) == clusterNodes.end( ) ) + continue; // Dismiss nodes not in this cluster (is not in the cluster sorted nodes set) + + if ( outNode->getPosition( )( 1 ) >= 0.f ) + continue; + + // Test that a node with a previous same date is not already in this line. Ex: Two sub + // articles have the same date, so the same x position, they must be on different lines. + bool skipNode = false; + Node::List::iterator lineNodeIter = lineNodes->begin( ); + for ( ; lineNodeIter != lineNodes->end( ); lineNodeIter++ ) + { + if ( outNode->getPosition( )( 0 ) == ( *lineNodeIter )->getPosition( )( 0 ) ) + skipNode = true; + } + if ( skipNode ) + continue; + + lineNodes->push_back( outNode ); + outNode->getPosition( )( 1 ) = 1.0f; + prevNode = outNode; + } + } + } + + // Supress all line nodes + for ( Node::List::iterator lineNodeIter = lineNodes->begin( ); lineNodeIter != lineNodes->end( ); lineNodeIter++ ) + nodes.removeAll( *lineNodeIter ); + + clusterGroup->addLine( lineNodes ); + + // Avoid infinite recursion, allow no more runs than there is nodes + runCount++; + } + + return clusterGroup; +} +//----------------------------------------------------------------------------- + + +} // ::qan + ============================================================ --- libs/qanava/src/qanTimeTree.h 0d123a00ba535d82f6e5707aa5b7fe1a33496d39 +++ libs/qanava/src/qanTimeTree.h 0d123a00ba535d82f6e5707aa5b7fe1a33496d39 @@ -0,0 +1,287 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file laTimeTree.h +// \author Benoit Autheman (address@hidden) +// \date 2004 December 05 +//----------------------------------------------------------------------------- + + +#ifndef laTimeTree_h +#define laTimeTree_h + + +// Qanava headers +#include "./qanLayout.h" +#include "./qanGrid.h" + + +//----------------------------------------------------------------------------- +namespace qan { // ::qan + + //! Layout a graph in space taking into account group membership (clusters) and respecting a chronological order. + /*! + + Qanava timetree sample + + \nosubgrouping + */ + class TimeTree : public Layout + { + public: + + //! Sort nodes on the basis of their date (older first). + struct NodeDateComp + { + bool operator()( const Node* n1, const Node* n2 ) const; + }; + + //! STL set sorting node by date. + typedef std::multiset< Node*, NodeDateComp > NodeSet; + + //! Manager of NodeSet objects. + typedef std::list< NodeSet* > NodeSetManager; + + protected: + + //! Construct the grid for a cluster based time tree graph layout. + /*! + \nosubgrouping + */ + class GridBuilder + { + /*! \name TimeTree Grid Management *///---------------------------- + //@{ + public: + + //! . + GridBuilder( Grid& grid ) : _grid( grid ) { } + + public: + + //! Add _one_ grid element for a given cluster coordinates. + void buildClusterGrid( int cy, int w, int h, int r, int g, int b ); + + //! Add all horizontal time grid elements for the given (sorted) nodes. + void buildTimeGrid( NodeSet& nodes, int width, int height ); + + //! . + void buildTimeLabel( Node& node, int x, int y ); + + //! . + Grid& getGrid( ) { return _grid; } + + private: + + //! . + Grid& _grid; + //@} + //----------------------------------------------------------------- + }; + + public: + + //! Models a group of nodes in the same track (usually a group equals a cluster of nodes). + /*! + \nosubgrouping + */ + class Group + { + private: + + class GroupMedDateComp + { + public: + bool operator()( const Group* g1, const Group* g2 ) const; + }; + + public: + + //! . + Group( ) { } + + //! . + ~Group( ) { } + + //! Add a group of nodes corresponding to a line of nodes for a given cluster. + /*! The node must be sorted by date ascending order. */ + void addLine( Node::List* lineNodes ) { _lines.push_back( lineNodes ); } + + //! Get the number of lines in this group. + int getLineCount( ) const { return _lines.size( ); } + + //! Get the ith line in this group. + Node::List* getLine( unsigned int line ); + + //! Collect this group nodes to a given node list (list can be non emtpy). + void collectNodes( Node::List& nodes ) const; + + //! Affect a given style type to all of this group nodes. + void setNodesType( int type ); + + //! Test if a group intersect with this group time interval. + bool intersect( const Group& group ) const; + + //! Get this group median date. + QDateTime getMedianDate( ) const; + + typedef std::list< Group* > Groups; + + typedef std::vector< Node::List* > Lines; + + //! STL multi set with median date group sorting. + typedef std::multiset< Group*, GroupMedDateComp > GroupSet; + + protected: + + //! Nodes in each lines must be stored in . + Lines _lines; + }; + + + protected: + + //! Models a line of related groups of nodes (the track form a line, but the group in the track can take ultiple single lines). + /*! + \nosubgrouping + */ + class Track + { + public: + + Track( ) { } + + void addGroup( Group* group ) { _groups.push_back( group ); } + + //! Test if a given track time duration interval is disjoint from this track own duration. + bool isDisjointOf( Track& track ); + + //! Merge a given track groups to this track. + void mergeTrack( Track& track ); + + typedef std::list< Track* > Tracks; + + int getLineCount( ) const; + + float layout( float trackY, float width, GridBuilder& gridBuilder ); + + //! Collect groups in this track sorted by group's average date. + void collectGroups( Group::Groups& groups ); + + Group::Groups& getGroups( ) { return _groups; } + + int getNodeCount( ) const; + + protected: + + private: + + Group::Groups _groups; + }; + + + //! Sort tracks on the basis of their node (spatial) density. + struct TrackDensityComp + { + bool operator()( const Track* t1, const Track* t2 ) const; + }; + + + //! Model a series of tracks. + /*! + \nosubgrouping + */ + class Tracks + { + public: + + Tracks( ) { } + + void addTrack( Track* track ); + + void mergeTracks( ); + + float layout( float width, GridBuilder& gridBuilder ); + + //! Collect all tracks group to a group list in the left-right top to bottom order. + void collectGroups( Group::Groups& groups ); + + void sortByNodeDensity( ); + + protected: + + Track::Tracks _tracks; + }; + + + /*! \name TimeTree Constructor/Destructor *///------------------------- + //@{ + public: + + //! TimeTree constructor. + TimeTree( NodeSetManager& nodeSetManager ) : + Layout( ), + _nodeSetManager( nodeSetManager ) { } + + ~TimeTree( ) + { + + } + + private: + + //! . + NodeSetManager& _nodeSetManager; + //@} + //--------------------------------------------------------------------- + + + + /*! \name TimeTree Layout Generation Management *///------------------- + //@{ + public: + + static float incX; + + static float lineIntervalY; + + static float trackIntervalY; + + //! . + virtual void layout( Graph& graph, Grid& grid, int width, int height, QProgressDialog* progress = 0 ); + + protected: + + //! Set nodes horizontal position, and return the resulting layout width. + float layoutNodesX( NodeSet& nodes ); + + //! . + Group* buildGroup( NodeSet& clusterNodes ); + //@} + //--------------------------------------------------------------------- + }; +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // laTimeTree_h + ============================================================ --- libs/qanava/src/qanVectorf.h 9e02d50a23ddb577123d14ebb92fcb70220a04ea +++ libs/qanava/src/qanVectorf.h 9e02d50a23ddb577123d14ebb92fcb70220a04ea @@ -0,0 +1,172 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file laVectorF.h +// \author Benoit Autheman (address@hidden) +// \date 2004 February 15 +//----------------------------------------------------------------------------- + + +#ifndef laVectorF_h +#define laVectorF_h + + +// Qanava headers +#include "./qanConfig.h" + + +// Standard headers +#include +#include + + +//----------------------------------------------------------------------------- +//! Root qanava namespace +namespace qan { // ::qan + + class VectorF + { + public: + + VectorF( ) : _length( 0 ), _data( 0 ) { } + + VectorF( unsigned int length ) : _length( length ), _data( 0 ) { _data = new double[ length ]; } + + VectorF( const VectorF& v ) + { + _length = v._length; + _data = new double[ v._length ]; + for ( unsigned int i = 0; i < v._length; i++ ) + _data[ i ] = v._data[ i ]; + } + + ~VectorF( ) { if ( _data != 0 ) delete _data; } + + double& operator()( unsigned int i ) { return _data[ i ]; } + + const double& operator()( unsigned int i ) const { return _data[ i ]; } + + double operator[]( unsigned int i ) const { return _data[ i ]; } + + void operator=( const VectorF& v ) + { + if ( _length == v._length && _data != 0 ) + { + for ( unsigned int i = 0; i < v._length; i++ ) + _data[ i ] = v._data[ i ]; + } + else + { + //if ( _data != 0 ) + // delete _data; + _length = v._length; + _data = new double[ v._length ]; + for ( unsigned int i = 0; i < v._length; i++ ) + _data[ i ] = v._data[ i ]; + } + } + + void operator/=( double f ) + { + for ( unsigned int i = 0; i < _length; i++ ) + _data[ i ] /= f; + } + + void operator*=( double f ) + { + for ( unsigned int i = 0; i < _length; i++ ) + _data[ i ] *= f; + } + + void operator+=( const VectorF& b ) + { + for ( unsigned int i = 0; i < _length; i++ ) + _data[ i ] += b._data[ i ]; + } + + void operator+=( double v ) + { + for ( unsigned int i = 0; i < _length; i++ ) + _data[ i ] += v; + } + + void operator-=( const VectorF& b ) + { + for ( unsigned int i = 0; i < _length; i++ ) + _data[ i ] -= b._data[ i ]; + } + + void operator/=( const VectorF& b ) + { + for ( unsigned int i = 0; i < _length; i++ ) + _data[ i ] /= b._data[ i ]; + } + + void operator*=( const VectorF& b ) + { + for ( unsigned int i = 0; i < _length; i++ ) + _data[ i ] *= b._data[ i ]; + } + + VectorF operator-( const VectorF& b ) const + { + VectorF result( _length ); + for ( unsigned int i = 0; i < _length; i++ ) + result._data[ i ] = _data[ i ] - b._data[ i ]; + return result; + } + + VectorF operator+( const VectorF& b ) const + { + VectorF result( _length ); + for ( unsigned int i = 0; i < _length; i++ ) + result._data[ i ] = _data[ i ] + b._data[ i ]; + return result; + } + + VectorF operator/( const VectorF& b ) const + { + VectorF result( _length ); + for ( unsigned int i = 0; i < _length; i++ ) + result._data[ i ] = _data[ i ] / b._data[ i ]; + return result; + } + + VectorF operator/( double f ) const + { + VectorF result( _length ); + for ( unsigned int i = 0; i < _length; i++ ) + result._data[ i ] = _data[ i ] / f; + return result; + } + + private: + + unsigned int _length; + + double* _data; + }; +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // laVectorF_h ============================================================ --- libs/qanava/src/ui/uiNodesItemModel.cpp ce8ceeda6ffddcb4cc72a0ec843daec6550b337d +++ libs/qanava/src/ui/uiNodesItemModel.cpp ce8ceeda6ffddcb4cc72a0ec843daec6550b337d @@ -0,0 +1,175 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file uiNodesItemModel.cpp +// \author Benoit Autheman (address@hidden) +// \date 2006 August 30 +//----------------------------------------------------------------------------- + + +// Qanava headers +#include "./uiNodesItemModel.h" + + +// QT headers +#include + + +namespace qan { // ::qan +namespace ui { // ::qan::ui + + +//----------------------------------------------------------------------------- +NodesItemModel::NodesItemModel( Graph& graph, QObject *parent ) : + _graph( graph ) +{ + connect( &graph, SIGNAL( rowAboutToBeInserted(const QModelIndex&,int,int) ), this, SLOT( nodeAboutToBeInserted(const QModelIndex&,int,int) ) ); + connect( &graph, SIGNAL( rowAboutToBeRemoved(const QModelIndex&,int,int) ), this, SLOT( nodeAboutToBeRemoved(const QModelIndex& parent,int,int) ) ); + connect( &graph, SIGNAL( rowInserted(const QModelIndex&,int,int) ), this, SLOT( nodeInserted(const QModelIndex&,int,int) ) ); + connect( &graph, SIGNAL( rowRemoved(const QModelIndex&,int,int) ), this, SLOT( nodeRemoved(const QModelIndex&,int,int) ) ); +} + + +QVariant NodesItemModel::data( const QModelIndex &index, int role ) const +{ + if ( !index.isValid( ) ) + return QVariant( "index invalid" ); + + QVariant d; + + if ( ( role == Qt::FontRole ) && ( index.column( ) == 0 ) ) + { + QFont font; + font.setWeight( QFont::Bold ); + d = font; + } + + if ( role == Qt::DisplayRole ) + { + Node* node = _graph.findNode( index.row( ) ); + if ( node != 0 ) + { + if ( index.column( ) == 0 ) + d = QString( node->getLabel( ) ); + if ( index.column( ) > 0 ) + { + int attributeRole = index.column( ) - 1 + Node::StdAttributeCount; + if ( attributeRole < ( int )_graph.getAttributesCount( ) ) + { + QString* attribute = node->getAttribute< QString >( attributeRole ); + d = ( attribute != 0 ? QVariant( *attribute ) : QVariant( "" ) ); + } + } + } + } + return d; +} + +bool NodesItemModel::hasChildren( const QModelIndex& parent ) const +{ + return false; +} + +Qt::ItemFlags NodesItemModel::flags( const QModelIndex& index ) const +{ + return ( index.isValid( ) ? Qt::ItemIsSelectable | Qt::ItemIsEnabled : Qt::ItemIsEnabled ); +} + +QVariant NodesItemModel::headerData( int section, Qt::Orientation orientation, int role ) const +{ + QVariant d; + if ( role == Qt::DisplayRole ) + { + int attributeRole = section - 1 + Node::StdAttributeCount; + if ( section == 0 ) + return QString( "Label" ); + else if ( attributeRole >= 0 && attributeRole < ( int )_graph.getAttributesCount( ) ) + d = _graph.getAttributeName( attributeRole ); + } + return d; +} + +QModelIndex NodesItemModel::index(int row, int column, const QModelIndex& parent ) const +{ + return createIndex( row, column, ( row * 10000 ) + column ); +} + +QModelIndex NodesItemModel::parent( const QModelIndex &index ) const +{ + return QModelIndex( ); +} + +int NodesItemModel::rowCount( const QModelIndex& parent ) const +{ + return _graph.getNodeCount( ); +} + +int NodesItemModel::columnCount( const QModelIndex& parent ) const +{ + return ( 1 + _graph.getAttributesCount( ) - Node::StdAttributeCount ); +} + +void NodesItemModel::nodeAboutToBeInserted( const QModelIndex& parent, int start, int end ) +{ + QModelIndex nodeItem = _graph.index( start, 0, parent ); + if ( nodeItem.isValid( ) ) + { + Node* node = static_cast< Node* >( nodeItem.internalPointer( ) ); + int id = _graph.findNode( *node ); + if ( id != -1 ) + beginInsertRows( QModelIndex( ), id, id + 1 ); + } +} + +void NodesItemModel::nodeAboutToBeRemoved( const QModelIndex& parent, int start, int end ) +{ +/* int id = _graph.findNode( node ); + if ( id != -1 ) + beginRemoveRows( QModelIndex( ), id, id + 1 );*/ +} + +void NodesItemModel::nodeInserted( const QModelIndex& parent, int start, int end ) +{ + QModelIndex nodeItem = _graph.index( start, 0, parent ); + if ( nodeItem.isValid( ) ) + { + Node* node = static_cast< Node* >( nodeItem.internalPointer( ) ); + int id = _graph.findNode( *node ); + if ( id != -1 ) + emit rowsInserted( QModelIndex( ), id, id + 1 ); + emit layoutChanged( ); + } +} + +void NodesItemModel::nodeRemoved( const QModelIndex& parent, int start, int end ) +{ + /*int id = _graph.findNode( node ); + if ( id != -1 ) + endRemoveRows( ); + emit layoutChanged( );*/ +} +//----------------------------------------------------------------------------- + + +} // ::qan::ui +} // ::qan + ============================================================ --- libs/qanava/src/ui/uiNodesItemModel.h c40b4d3800f8d2b99be3c2072ee15d9a8fff7945 +++ libs/qanava/src/ui/uiNodesItemModel.h c40b4d3800f8d2b99be3c2072ee15d9a8fff7945 @@ -0,0 +1,105 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file uiNodesItemModel.h +// \author Benoit Autheman (address@hidden) +// \date 2006 August 30 +//----------------------------------------------------------------------------- + + +#ifndef uiNodesItemModel_h +#define uiNodeSItemModel_h + + +// Qanava headers +#include "../qanGraph.h" +#include "../qanGraphItemModel.h" + + +// QT headers +#include + + +//----------------------------------------------------------------------------- +namespace qan // ::qan +{ + namespace ui // ::qan::ui + { + //! . + /*! + \nosubgrouping + */ + class NodesItemModel : public QAbstractItemModel + { + Q_OBJECT + + public: + + NodesItemModel( Graph& graph, QObject *parent = 0 ); + + virtual QVariant data( const QModelIndex &index, int role ) const; + + virtual bool hasChildren( const QModelIndex & parent = QModelIndex( ) ) const; + + virtual Qt::ItemFlags flags( const QModelIndex &index ) const; + + virtual QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const; + + virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + + virtual QModelIndex parent( const QModelIndex &index ) const; + + virtual int rowCount( const QModelIndex& parent = QModelIndex( ) ) const; + + virtual int columnCount( const QModelIndex& parent = QModelIndex( ) ) const; + + signals: + + void rowsInserted( const QModelIndex& parent, int start, int end ); + + void rowsRemoved( const QModelIndex& parent, int start, int end ); + + protected slots: + + void nodeAboutToBeInserted( const QModelIndex& parent, int start, int end ); + + void nodeAboutToBeRemoved( const QModelIndex& parent, int start, int end ); + + void nodeInserted( const QModelIndex& parent, int start, int end ); + + void nodeRemoved( const QModelIndex& parent, int start, int end ); + + private: + + Graph& _graph; + + bool getShowStdAttributes( ) const { return _showStdAttributes; } + + bool _showStdAttributes; + }; + } // ::qan::ui +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // uiStyleEditor_h + ============================================================ --- libs/qanava/src/ui/uiStyleModel.cpp d93ce3111a03fcf80b343e6360915ea7d2b7d49b +++ libs/qanava/src/ui/uiStyleModel.cpp d93ce3111a03fcf80b343e6360915ea7d2b7d49b @@ -0,0 +1,151 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Mysire software. +// +// \file uiStyleItemModel.cpp +// \author Benoit Autheman (address@hidden) +// \date 2006 January 04 +//----------------------------------------------------------------------------- + + +// Qanava headers +#include "./uiStyleModel.h" + + +namespace qan { // ::qan +namespace ui { // ::qan::ui + + +//----------------------------------------------------------------------------- + StyleItemModel::StyleItemModel( Style& style, QObject *parent ) : + QAbstractItemModel( parent ), + _style( style ) +{ + +} +//----------------------------------------------------------------------------- + + + +/* Qt Model Interface *///----------------------------------------------------- +QVariant StyleItemModel::data( const QModelIndex &index, int role ) const +{ + if ( !index.isValid( ) ) + return QVariant( "index invalid" ); + + QVariant d; + + if ( ( role == Qt::FontRole ) && ( index.column( ) == 0 ) ) + { + QFont font; + font.setWeight( QFont::Bold ); + d = font; + } + + if ( role == Qt::DisplayRole ) + { + if ( index.column( ) == 0 ) + { + d = QString( "ATTR NAME" ); + } + else if ( index.column( ) == 1 ) + { + d = QString( "FIXME: StyleItemModel::data()!" ); + } + } + + return d; +} + +bool StyleItemModel::hasChildren( const QModelIndex & parent ) const +{ + return false; // No hierarchy for articles +} + +Qt::ItemFlags StyleItemModel::flags( const QModelIndex &index ) const +{ + if ( !index.isValid( ) ) + return Qt::ItemIsEnabled; + + if ( index.column( ) == 0 ) + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; + else if ( index.column( ) == 1 ) + return Qt::ItemIsEnabled | Qt::ItemIsEditable; + + return Qt::ItemIsEnabled; +} + +QVariant StyleItemModel::headerData( int section, Qt::Orientation orientation, int role ) const +{ + if ( orientation == Qt::Horizontal && role == Qt::DisplayRole ) + { + if ( section == 0 ) + return QString( "Property" ); + else if ( section == 1 ) + return QString( "Value" ); + } + return QVariant( ); +} + +QModelIndex StyleItemModel::index( int row, int column, const QModelIndex &parent ) const +{ + if ( !parent.isValid( ) ) + { + const void* internalPointer = 0; + int internalId = 0; + + //Style::Attribute* attribute = _style.getAttributeManager( ).get( row ); + //if ( attribute != 0 ) + //{ + if ( column == 0 ) + internalId = 100 + row; + else if ( column == 1 ) + internalId = 200 + row; + // FIXME internalPointer = 200 + row; // FIXME attribute; + //} + + if ( internalPointer != 0 ) + return createIndex( row, column, const_cast< void* >( internalPointer ) ); + else if ( internalId != 0 ) + return createIndex( row, column, internalId ); + } + return QModelIndex( ); +} + +QModelIndex StyleItemModel::parent( const QModelIndex &index ) const +{ + return QModelIndex( ); // No hierarchy for articles +} + +int StyleItemModel::rowCount( const QModelIndex& parent ) const +{ + return _style.size( ); +} + +int StyleItemModel::columnCount( const QModelIndex& parent ) const +{ + return 2; +} +//----------------------------------------------------------------------------- + + +} // ::qan::ui +} // ::qan ============================================================ --- libs/qanava/src/ui/uiStyleModel.h 8f0c369542dfceb2099554d7d064a1169a72a668 +++ libs/qanava/src/ui/uiStyleModel.h 8f0c369542dfceb2099554d7d064a1169a72a668 @@ -0,0 +1,103 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Mysire software. +// +// \file uiStyleItemModel.h +// \author Benoit Autheman (address@hidden) +// \date 2006 January 04 +//----------------------------------------------------------------------------- + + +#ifndef uiStyleItemModel_h +#define uiStyleItemModel_h + + +// Qanava headers +#include "../qanGraph.h" +#include "../qanStyle.h" + + +// QT headers +#include +#include +#include + + +//----------------------------------------------------------------------------- +namespace qan // ::qan +{ + namespace ui // ::qan::ui + { + //! . + /*! + \nosubgrouping + */ + class StyleItemModel : public QAbstractItemModel + { + Q_OBJECT + + public: + + StyleItemModel( Style& style, QObject *parent = 0 ); + + private: + + Style& _style; + + /*! \name Qt Model Interface *///---------------------------------- + //@{ + public: + + //! Define the article attribute offset (row) in table. + enum AttributeOffset + { + TITLE = 0, + AUTHOR = 1, + SOURCE = 2, + SUB = 3, + SUP = 4 + }; + + virtual QVariant data( const QModelIndex &index, int role ) const; + + virtual bool hasChildren( const QModelIndex & parent = QModelIndex( ) ) const; + + virtual Qt::ItemFlags flags( const QModelIndex &index ) const; + + virtual QVariant headerData( int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + + virtual QModelIndex parent( const QModelIndex &index ) const; + + virtual int rowCount( const QModelIndex& parent = QModelIndex( ) ) const; + + virtual int columnCount( const QModelIndex& parent = QModelIndex( ) ) const; + //@} + //----------------------------------------------------------------- + }; + } // ::qan::ui +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // uiStyleItemModel_h + ============================================================ --- libs/qanava/src/ui/uiStyleView.cpp ce449871831206f5bf55d96e801dae928444c284 +++ libs/qanava/src/ui/uiStyleView.cpp ce449871831206f5bf55d96e801dae928444c284 @@ -0,0 +1,383 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file uiStyleView.cpp +// \author Benoit Autheman (address@hidden) +// \date 2005 December 23 +//----------------------------------------------------------------------------- + + +// Qanava headers +#include "./uiStyleView.h" +#include "../qanStyle.h" + + +// QT headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace qan { // ::qan + + +ColorEditWidget::ColorEditWidget( QWidget* parent, QColor color ) : + QWidget( parent ), + _label( 0 ), + _color( color ) +{ + setAutoFillBackground( true ); // Delegate widget are transparent by default + + QHBoxLayout* hbox = new QHBoxLayout( this ); + hbox->setMargin( 0 ); + hbox->setSpacing( 1 ); + + _label = new QLabel( this ); + _label->setText( _color.name( ) ); + + QPushButton* b = new QPushButton( "...", parent ); + b->setText( "..." ); + b->setMaximumWidth( 40 ); + connect( b, SIGNAL( clicked() ), this, SLOT( selectColor() ) ); + + hbox->addSpacing( 2 ); + hbox->addWidget( _label ); + hbox->addWidget( b ); + + setColor( color ); +} + +void ColorEditWidget::setColor( QColor color ) +{ + _color = color; + + QPalette palette; + palette.setColor( backgroundRole( ), _color ); + setPalette( palette ); + + QColor colorHsv = color.toHsv( ); + QColor textColor( colorHsv.saturation( ) < 127 ? Qt::black : Qt::white ); + palette = _label->palette( ); + palette.setColor( QPalette::Text, textColor ); + _label->setPalette( palette ); + _label->setText( _color.name( ) ); +} + +void ColorEditWidget::selectColor( ) +{ + QColor color = QColorDialog::getColor( _color, this ); + setColor( color ); + close( ); + /*if ( _delegate != 0 ) + emit _delegate->commitData( this );*/ +} + + +StringEditWidget::StringEditWidget( QWidget* parent, QString s ) : + QWidget( parent ), + _edit( 0 ), + _string( s ) +{ + setAutoFillBackground( true ); // Delegate widget are transparent by default + + QHBoxLayout* hbox = new QHBoxLayout( this ); + hbox->setMargin( 0 ); + hbox->setSpacing( 1 ); + + _edit = new QLineEdit( this ); + _edit->setFrame( false ); + + QPushButton* b = new QPushButton( "...", parent ); + b->setText( "..." ); + b->setMaximumWidth( 40 ); + connect( b, SIGNAL( clicked() ), this, SLOT( selectString() ) ); + + hbox->addSpacing( 2 ); + hbox->addWidget( _edit ); + hbox->addWidget( b ); + connect( _edit, SIGNAL( editingFinished() ), this, SLOT( editingFinished() ) ); + setString( s ); +} + +void StringEditWidget::setString( QString s ) +{ + _edit->setText( s ); + _edit->selectAll( ); + _string = s; +} + +void StringEditWidget::selectString( ) +{ + QString s = QFileDialog::getOpenFileName( this, "Choose an image file", "./", "Images (*.png *.jpg)" ); + if ( !s.isEmpty( ) ) + setString( s ); +} + +void StringEditWidget::editingFinished( ) +{ + setString( _edit->text( ) ); +} + + +StyleDelegate::StyleDelegate( QAbstractItemModel& model ) : + _model( model ) +{ + setItemEditorFactory( new QItemEditorFactory( ) ); +} + +QWidget* StyleDelegate::createEditor ( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + QWidget* editor = 0; + + QPalette palette( parent->palette( ) ); + + QVariant attribute = _model.data( index ); + switch ( attribute.type( ) ) + { + case QVariant::String: + { + QString s = attribute.toString( ); + editor = new StringEditWidget( parent, s ); + editor->setPalette( palette ); + } + break; + case QVariant::Color: + { + QColor color = attribute.value< QColor >( ); + editor = new ColorEditWidget( parent, color ); + } + break; + default: + editor = itemEditorFactory( )->createEditor( attribute.type( ), parent ); + break; + }; + + if ( editor != 0 ) + editor->installEventFilter( const_cast< StyleDelegate* >( this ) ); + else + return QItemDelegate::createEditor( parent, option, index ); + + return editor; +} + +void StyleDelegate::paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const +{ + if ( !index.isValid( ) || index.row( ) == 0 || index.column( ) != 1 ) + { + QItemDelegate::paint( painter, option, index ); + return; + } + + QVariant attribute = _model.data( index ); + switch ( attribute.type( ) ) + { + case QVariant::Color: + { + QColor color = attribute.value< QColor >( ); + + painter->save( ); // StyleOption option does not work for back color + painter->setPen( Qt::NoPen ); + painter->setBrush( color ); + painter->drawRect( option.rect ); + painter->restore( ); + + // Invert text color to avoid text beeing unreadable + QColor colorHsv = color.toHsv( ); + QColor textColor( colorHsv.saturation( ) < 127 ? Qt::black : Qt::white ); + QStyleOptionViewItem customOption( option ); + customOption.palette.setColor( QPalette::Text, textColor ); + drawDisplay( painter, customOption, option.rect, color.name( ) ); + break; + } + default: + QItemDelegate::paint( painter, option, index ); + break; + }; +} + +void StyleDelegate::setEditorData( QWidget* editor, const QModelIndex& index ) const +{ + QVariant attribute = _model.data( index ); + + switch ( attribute.type( ) ) + { + case QVariant::String: + { + StringEditWidget* w = static_cast< StringEditWidget* >( editor ); + QVariant attribute = _model.data( index ); + w->setString( attribute.toString( ) ); + } + break; + case QVariant::Color: + { + ColorEditWidget* w = static_cast< ColorEditWidget* >( editor ); + QVariant attribute = _model.data( index ); + w->setColor( attribute.value< QColor >( ) ); + } + break; + default: + QItemDelegate::setEditorData( editor, index ); + break; + }; +} + +void StyleDelegate::setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index ) const +{ + QVariant attribute = model->data( index ); + + switch ( attribute.type( ) ) + { + case QVariant::String: + { + StringEditWidget* w = static_cast< StringEditWidget* >( editor ); + model->setData( index, w->getString( ) ); + } + break; + case QVariant::Color: + { + ColorEditWidget* w = static_cast< ColorEditWidget* >( editor ); + model->setData( index, w->getColor( ) ); + } + break; + default: + QItemDelegate::setModelData( editor, model, index ); + break; + }; +} + +QSize StyleDelegate::sizeHint ( const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + return QSize( 50, 15 ); +} + + + +//----------------------------------------------------------------------------- +StyleEditor::StyleEditor( QWidget* parent, Style* style ) : + QMainWindow( parent ), + _style( style ), + _tableView( 0 ), + _cbType( 0 ) +{ + if ( layout( ) != 0 ) + { + layout( )->setMargin( 0 ); + layout( )->setSpacing( 1 ); + } + QToolBar* toolBar = addToolBar( "Tools" ); + if ( toolBar->layout( ) != 0 ) + toolBar->layout( )->setMargin( 2 ); + toolBar->setIconSize( QSize( 16, 16 ) ); + + _cbType = new QComboBox( toolBar ); + _cbType->addItem( QIcon(), "<< Type >>" ); + _cbType->addItem( QIcon(), "Color" ); + _cbType->addItem( QIcon(), "String" ); + _cbType->addItem( QIcon(), "Image" ); + _cbType->addItem( QIcon(), "Int" ); + _cbType->addItem( QIcon(), "Double" ); + + QToolButton* addAttribute = new QToolButton( toolBar ); + addAttribute->setIcon( QIcon( "images/qanava_plus.png" ) ); + addAttribute->setAutoRaise( true ); + connect( addAttribute, SIGNAL( clicked() ), this, SLOT( addAttribute() ) ); + + QToolButton* removeAttribute = new QToolButton( parent ); + removeAttribute->setIcon( QIcon( "images/qanava_minus.png" ) ); + removeAttribute->setAutoRaise( true ); + connect( removeAttribute, SIGNAL( clicked() ), this, SLOT( removeAttribute() ) ); + + toolBar->addWidget( _cbType ); + //toolBar->addSeparator( ); + toolBar->addWidget( addAttribute ); + toolBar->addWidget( removeAttribute ); + + _tableView = new QTableView( this ); + _tableView->setAlternatingRowColors( true ); + _tableView->setMaximumWidth( 200 ); + _tableView->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + _tableView->horizontalHeader( )->resizeSections( QHeaderView::Fixed ); // Fixed + _tableView->horizontalHeader( )->setStretchLastSection( true ); + _tableView->verticalHeader( )->resizeSections( QHeaderView::Fixed ); + _tableView->verticalHeader( )->setDefaultSectionSize( 20 ); + _tableView->verticalHeader( )->hide( ); + + setCentralWidget( _tableView ); + setStyle( style ); +} + +void StyleEditor::setStyle( Style* style ) +{ + if ( style != 0 ) + { + _style = style; + _tableView->setDisabled( false ); + _tableView->setItemDelegate( new StyleDelegate( *style ) ); + _tableView->setModel( style ); + } + else + { + _style = 0; + _tableView->reset( ); + _tableView->setDisabled( true ); + } +} + +void StyleEditor::addAttribute( ) +{ + QString attrName( "New attribute " ); + attrName += QString::number( _style->size( ) ); + switch ( _cbType->currentIndex( ) ) + { + case 1: // Color + _style->add( attrName, Qt::black ); + break; + case 2: // String + _style->add( attrName, QString( "" ) ); + break; + case 3: // Image + break; + case 4: // Int + break; + case 5: // Double + break; + default: + break; + } +} + +void StyleEditor::removeAttribute( ) +{ + +} +//----------------------------------------------------------------------------- + + +} // ::qan + ============================================================ --- libs/qanava/src/ui/uiStyleView.h 1a99cd669c8250ec1a2e607bfdb7f811c3d2bdb2 +++ libs/qanava/src/ui/uiStyleView.h 1a99cd669c8250ec1a2e607bfdb7f811c3d2bdb2 @@ -0,0 +1,167 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file uiStyleView.h +// \author Benoit Autheman (address@hidden) +// \date 2005 December 23 +//----------------------------------------------------------------------------- + + +#ifndef uiStyleEditor_h +#define uiStyleEditor_h + + +// Qanava headers +#include "../qanStyle.h" + + +// QT headers +#include +#include +#include +#include +#include +#include +#include +#include + + +//----------------------------------------------------------------------------- +namespace qan // ::qan +{ + class ColorEditWidget : public QWidget + { + Q_OBJECT + + public: + + ColorEditWidget( QWidget* parent, QColor color ); + + QColor getColor( ) { return _color; } + + void setColor( QColor color ); + + protected: + + QLabel* _label; + + QColor _color; + + private slots: + + void selectColor( ); + }; + + class StringEditWidget : public QWidget + { + Q_OBJECT + + public: + + StringEditWidget( QWidget* parent, QString s ); + + QString getString( ) { return _string; } + + void setString( QString s ); + + protected: + + QLineEdit* _edit; + + QString _string; + + private slots: + + void selectString( ); + + void editingFinished( ); + }; + + class StyleDelegate : public QItemDelegate + { + public: + + StyleDelegate( QAbstractItemModel& model /*QAbstractItemView& tableView*/ ); + + virtual QWidget* createEditor ( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const; + + virtual void paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const; + + virtual void setEditorData( QWidget* editor, const QModelIndex& index ) const; + + virtual void setModelData( QWidget* editor, QAbstractItemModel* model, const QModelIndex& index ) const; + + virtual QSize sizeHint ( const QStyleOptionViewItem& option, const QModelIndex& index ) const; + + protected: + + QAbstractItemModel& _model; + }; + + + + //! + /*! + \nosubgrouping + */ + class StyleEditor : public QMainWindow + { + Q_OBJECT + + public: + + StyleEditor( QWidget* parent = 0, Style* style = 0 ); + + virtual ~StyleEditor( ) { } + + private: + + StyleEditor( const StyleEditor& ); + + StyleEditor& operator=( const StyleEditor& ); + + public: + + void setStyle( Style* style ); + + virtual QSize sizeHint( ) const { return QSize( 150, 200 ); } + + protected slots: + + void addAttribute( ); + + void removeAttribute( ); + + protected: + + Style* _style; + + QTableView* _tableView; + + QComboBox* _cbType; + }; +} // ::qan +//----------------------------------------------------------------------------- + + +#endif // uiStyleEditor_h + ============================================================ --- libs/qanava/src/ui/uiUtil.h 13f19d1cf4651914501e855d898f0ec26b5d54ab +++ libs/qanava/src/ui/uiUtil.h 13f19d1cf4651914501e855d898f0ec26b5d54ab @@ -0,0 +1,34 @@ +/* +Qanava - Graph drawing library for QT +Copyright (C) 2006 Benoit AUTHEMAN + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +//----------------------------------------------------------------------------- +// This file is a part of the Qanava software. +// +// \file uiUtil.h +// \author Benoit Autheman (address@hidden) +// \date 2006 January 07 +//----------------------------------------------------------------------------- + + +#ifndef uiUtil_h +#define uiUtil_h + + +#endif // uiUtil_h + ============================================================ --- guitone/guitone.pro 89ffc63cab6945f7c72dd91d072bfa7425ae9d33 +++ guitone/guitone.pro 067355a047c27064dc3c1455a34579bf6554f0af @@ -28,6 +28,7 @@ HEADERS += src/view/MainWindow.h \ src/view/dialogs/GenerateKeypair.h \ src/view/dialogs/About.h \ src/view/dialogs/DatabaseView.h \ + src/view/dialogs/AncestryGraph.h \ src/monotone/Monotone.h \ src/model/MonotoneDelegate.h \ src/model/Inventory.h \ @@ -45,6 +46,7 @@ HEADERS += src/view/MainWindow.h \ src/model/Changeset.h \ src/model/ChangesetModel.h \ src/model/Toposort.h \ + src/model/Graph.h \ src/util/IconProvider.h \ src/util/StanzaParser.h \ src/util/Settings.h \ @@ -69,6 +71,7 @@ SOURCES += src/view/MainWindow.cpp \ src/view/dialogs/GenerateKeypair.cpp \ src/view/dialogs/About.cpp \ src/view/dialogs/DatabaseView.cpp \ + src/view/dialogs/AncestryGraph.cpp \ src/monotone/Monotone.cpp \ src/model/MonotoneDelegate.cpp \ src/model/Inventory.cpp \ @@ -86,6 +89,7 @@ SOURCES += src/view/MainWindow.cpp \ src/model/Changeset.cpp \ src/model/ChangesetModel.cpp \ src/model/Toposort.cpp \ + src/model/Graph.cpp \ src/util/IconProvider.cpp \ src/util/StanzaParser.cpp \ src/util/Settings.cpp \ @@ -104,7 +108,8 @@ FORMS += res/forms/switch_workspace.ui res/forms/generate_keypair.ui \ res/forms/about.ui \ res/forms/main_window.ui \ - res/forms/databaseview.ui + res/forms/databaseview.ui \ + res/forms/ancestry_graph.ui UI_DIR = tmp OBJECTS_DIR = tmp @@ -145,3 +150,17 @@ macx { QMAKE_MAC_SDK = /Developer/SDKs/MacOSX10.4u.sdk } +message($$system(pwd)) + +# +# libqanava configuration +# +QANAVADIR = "../libs/qanava" +macx | unix { + LIBS += -L$${QANAVADIR}/build -lqanava + DEFINES += QANAVA_UNIX +} +win32:LIBS += $${QANAVADIR}/build/qanava.lib +INCLUDEPATH += $${QANAVADIR}/src +QT += xml + ============================================================ --- guitone/res/forms/main_window.ui afaa9b341aa26361f28b3c8d857fa07473987959 +++ guitone/res/forms/main_window.ui cfd66fbddffb6167fcf8240eb95c7a3e93042c87 @@ -117,7 +117,7 @@ You can switch back to the workspace mod 0 0 926 - 24 + 22 @@ -153,13 +153,6 @@ You can switch back to the workspace mod - - - Database - - - - Workspace @@ -192,6 +185,14 @@ You can switch back to the workspace mod + + + Database + + + + + @@ -417,6 +418,14 @@ You can switch back to the workspace mod Ctrl+B + + + Ancestry Graph + + + Ctrl+A + + ============================================================ --- guitone/res/forms/preferences.ui 50dcda14fc2c4f5147800b73b015bf988efd52f4 +++ guitone/res/forms/preferences.ui 5c8f89400f2da1fc0833738eb97b81ef5210caa2 @@ -5,8 +5,8 @@ 0 0 - 372 - 248 + 428 + 278 @@ -17,18 +17,6 @@ 0 - - - 372 - 248 - - - - - 16777215 - 248 - - Qt::NoContextMenu ============================================================ --- guitone/res/i18n/guitone_de.ts 8a9f18c91d4da422143541bbe523025eb7a280d3 +++ guitone/res/i18n/guitone_de.ts afd926dcc7d88b42891b983cdeeff9f3d0f7bc8c @@ -27,6 +27,19 @@ + AncestryGraph + + + Ancestry Graph + + + + + Please wait while the graph is being created... + + + + Attributes @@ -562,7 +575,7 @@ MainWindow - + View Ansicht @@ -572,7 +585,7 @@ Hilfe - + Workspace Arbeitsbereich @@ -587,182 +600,182 @@ Vorherige Arbeitsbereiche - + Open Workspace Arbeitsbereich öffnen - + Ctrl+O Strg+O - + No previous workspaces available. Keine vorherigen Arbeitsbereiche verfügbar. - + Preferences.... Einstellungen... - + Ctrl+P Strg+P - + Quit Beenden - + Ctrl+Q Strg+Q - + Hide ignored files Ignorierte Dateien verstecken - + Ctrl+H Strg+H - + All files Alle Dateien - + A A - + All changed files Alle geänderten Dateien - + C G - + Patched files Inhaltlich geänderte Dateien - + P P - + Added files Hinzugefügte Dateien - + N H - + Removed files Entfernte Dateien - + D E - + Renamed files Umbenannte Dateien - + R U - + Missing files Fehlende Dateien - + M F - + Unknown files Unbekannte Dateien - + U K - + Ignored files Ignorierte Dateien - + I I - + Expand tree Baum aufklappen - + Ctrl+T Strg+T - + Switch revision Revision wechseln - + Ctrl+R Strg+R - + Key management Schlüsselverwaltung - + Ctrl+K Strg+K - + About Qt Über Qt - + About guitone Über guitone - + Show Zeige @@ -772,67 +785,67 @@ guitone - ein Frontend für monotone - + Error Fehler - + Critical Monotone Error Kritischer monotone-Fehler - + Select your workspace... Wählen Sie Ihren Arbeitsbereich aus... - + Loading aborted Laden abgebrochen - + Invalid workspace Ungültiger Arbeitsbereich - + The chosen directory is no monotone workspace! Das gewählte Verzeichnis ist kein monotone-Arbeitsverzeichnis! - + Unable to execute command Konnte Kommando nicht ausführen - + Unable to execute '%1' - maybe another command is still running? Konnte '%1' nicht ausführen - eventuell läuft noch ein anderes Kommando? - + Loading workspace... Lade Arbeitsbereich... - + Show ignored files Zeige ignorierte Dateien - + Collapse tree Baum zuklappen - + &%1 %2 &%1 %2 - + The path to the monotone binary is either invalid or points to an older version of monotone. Guitone requires monotone version %1 or a monotone with interface version %2 or later. Der Pfad zur ausführbaren Datei von monotone ist entweder ungültig oder zeigt auf eine ältere Version von monotone. Guitone benötigt monotone Version %1 oder ein monotone mit einer Interface-Version %2 oder neuer. @@ -842,52 +855,52 @@ Vorherige Datenbanken - + Open Database Datenbank öffnen - + No previous databases available. Keine vorherigen geöffneten Datenbanken verfügbar. - + Database Datenbank - + Ctrl+Shift+O Strg+Shift+O - + Loaded database: %1 Geladene Datenbank: %1 - + Select your database... Wählen Sie eine Datenbank aus... - + monotone Databases (*.mtn *.db) monotone-Datenbanken (*.mtn *.db) - + No database loaded Keine Datenbank geladen - + Ctrl+B Strg+B - + Changeset browser Änderungen-Browser @@ -898,6 +911,16 @@ Sie können zum Arbeitsbereich-Modus jed guitone befindet sich im Datenbank-Modus. Das bedeutet, dass nur Teile der Funktionalität verfügbar sind. Sie können zum Arbeitsbereich-Modus jederzeit zurückkehren, indem Sie einen Arbeitsbereich laden. + + + Ancestry Graph + + + + + Ctrl+A + + Monotone @@ -980,47 +1003,47 @@ monotone gab zurück: PreferencesDialog - + Preferences Einstellungen - + Path to monotone executable Pfad zur ausführbaren Datei von monotone - + Browse Durchsuchen - + OK OK - + Cancel Abbrechen - + Logging Protokollierung - + enable console logging Protokollierung auf Konsole aktivieren - + enable file logging (%1) Protokollierung in Datei aktivieren (%1) - + Log level Protokoll-Level @@ -1028,22 +1051,22 @@ monotone gab zurück: QShortcut - + Ctrl Strg - + Shift Umschalt - + Alt Alt - + Meta Meta ============================================================ --- guitone/src/view/MainWindow.cpp 7c815e194238287d2253ae81d76627724e6fa733 +++ guitone/src/view/MainWindow.cpp f0a536dfb4a7be366b0b1896dae37dacdad378eb @@ -33,6 +33,7 @@ #include "About.h" #include "Settings.h" #include "DatabaseView.h" +#include "AncestryGraph.h" #include #include @@ -472,12 +473,18 @@ void MainWindow::on_actionKey_management dialog.exec(); } -void MainWindow:: on_actionChangeset_browser_triggered() +void MainWindow::on_actionChangeset_browser_triggered() { DatabaseView dialog(this); dialog.exec(); } +void MainWindow::on_actionAncestry_Graph_triggered() +{ + AncestryGraph dialog(this); + dialog.exec(); +} + void MainWindow::on_actionAbout_guitone_triggered() { About dialog(this); ============================================================ --- guitone/src/view/MainWindow.h bf1f12efde46c96a0194070d00a9677c8f29e0cd +++ guitone/src/view/MainWindow.h 777cac1855851bd2b95dfbc1ecab9c13e4ce6a82 @@ -57,6 +57,7 @@ private slots: void on_actionAbout_guitone_triggered(); void on_actionAbout_Qt_triggered(); void on_actionChangeset_browser_triggered(); + void on_actionAncestry_Graph_triggered(); void openRecentWorkspace(); void openRecentDatabase(); ============================================================ --- guitone.pro fa381aa2095c52bb3876fb5ffff635ce8c30ab32 +++ guitone.pro 2297c37664f54796350e838adac98828c250f58a @@ -1,3 +1,3 @@ TEMPLATE = subdirs TEMPLATE = subdirs -SUBDIRS = guitone +SUBDIRS = libs guitone