• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdeedu API Reference
  • KDE Home
  • Contact Us
 

rocs/RocsCore

  • sources
  • kde-4.12
  • kdeedu
  • rocs
  • RocsCore
  • DataStructures
  • Graph
GraphStructure.cpp
Go to the documentation of this file.
1 /*
2  This file is part of Rocs.
3  Copyright 2011 Tomaz Canabrava <tomaz.canabrava@gmail.com>
4  Copyright 2011 Wagner Reck <wagner.reck@gmail.com>
5  Copyright 2011-2012 Andreas Cord-Landwehr <cola@uni-paderborn.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #undef QT_STRICT_ITERATORS // boost property map can't work with iterators being classes
22 
23 #include "GraphStructure.h"
24 #include "KDebug"
25 #include "Data.h"
26 #include "Pointer.h"
27 #include "Document.h"
28 #include "DataStructure.h"
29 #include <KMessageBox>
30 #include "GraphNode.h"
31 #include "QtScriptBackend.h"
32 
33 #include <boost/foreach.hpp>
34 #include <boost/graph/graph_traits.hpp>
35 #include <boost/graph/adjacency_list.hpp>
36 #include <boost/graph/dijkstra_shortest_paths.hpp>
37 #include <boost/graph/graph_concepts.hpp>
38 
39 #include <KLocale>
40 #include <QString>
41 
42 #include <cmath>
43 
44 DataStructurePtr Rocs::GraphStructure::create(Document *parent)
45 {
46  return DataStructure::create<GraphStructure>(parent);
47 }
48 
49 DataStructurePtr Rocs::GraphStructure::create(DataStructurePtr other, Document *parent)
50 {
51  boost::shared_ptr<GraphStructure> ds = boost::static_pointer_cast<GraphStructure>(Rocs::GraphStructure::create(parent));
52 
53  ds->importStructure(other);
54  return ds;
55 }
56 
57 Rocs::GraphStructure::GraphStructure(Document* parent) :
58  DataStructure(parent)
59 {
60  _type = Graph;
61 }
62 
63 void Rocs::GraphStructure::importStructure(DataStructurePtr other)
64 {
65  //FIXME this import does not correctly import different types
66  setGraphType(Graph);
67  QHash <Data*, DataPtr> dataTodata;
68  //FIXME only default data type considered
69  foreach(DataPtr n, other->dataList(0)) {
70  DataPtr newdata = createData("", 0); //n->name());
71  newdata->setColor(n->color());
72  newdata->setProperty("value", n->property("value").toString());
73  newdata->setX(n->x());
74  newdata->setY(n->y());
75  newdata->setWidth(n->width());
76  dataTodata.insert(n.get(), newdata);
77  }
78  //FIXME only default pointer type considered
79  foreach(PointerPtr e, other->pointers(0)) {
80  DataPtr from = dataTodata.value(e->from().get());
81  DataPtr to = dataTodata.value(e->to().get());
82 
83  PointerPtr newPointer = createPointer(from, to, 0);
84  if (newPointer.get()){
85  newPointer->setColor(e->color());
86  newPointer->setProperty("value", e->property("value").toString());
87  }
88  }
89 }
90 
91 Rocs::GraphStructure::~GraphStructure()
92 {
93 }
94 
95 QScriptValue Rocs::GraphStructure::nodes()
96 {
97  QScriptValue array = engine()->newArray();
98  foreach(int type, document()->dataTypeList()) {
99  foreach(DataPtr n, dataList(type)) {
100  array.property("push").call(array, QScriptValueList() << n->scriptValue());
101  }
102  }
103  return array;
104 }
105 
106 QScriptValue Rocs::GraphStructure::nodes(int type)
107 {
108  QScriptValue array = engine()->newArray();
109  foreach(DataPtr n, dataList(type)) {
110  array.property("push").call(array, QScriptValueList() << n->scriptValue());
111  }
112  return array;
113 }
114 
115 QScriptValue Rocs::GraphStructure::edges()
116 {
117  QScriptValue array = engine()->newArray();
118  foreach(int type, document()->pointerTypeList()) {
119  foreach(PointerPtr n, pointers(type)) {
120  array.property("push").call(array, QScriptValueList() << n->scriptValue());
121  }
122  }
123  return array;
124 }
125 
126 QScriptValue Rocs::GraphStructure::edges(int type)
127 {
128  QScriptValue array = engine()->newArray();
129  foreach(PointerPtr n, pointers(type)) {
130  array.property("push").call(array, QScriptValueList() << n->scriptValue());
131  }
132  return array;
133 }
134 
135 QScriptValue Rocs::GraphStructure::createNode()
136 {
137  return createNode(0);
138 }
139 
140 QScriptValue Rocs::GraphStructure::createNode(int type)
141 {
142  DataPtr n = createData("", type);
143  n->setEngine(engine());
144  return n->scriptValue();
145 }
146 
147 QScriptValue Rocs::GraphStructure::createEdge(Data* fromRaw, Data* toRaw)
148 {
149  return createEdge(fromRaw, toRaw, 0);
150 }
151 
152 QScriptValue Rocs::GraphStructure::createEdge(Data* fromRaw, Data* toRaw, int type)
153 {
154  if (fromRaw == 0 || toRaw == 0) {
155  kError() << "No edge added: data does not exist";
156  emit scriptError(i18n("Cannot create edge: nodes are not defined."));
157  return QScriptValue();
158  }
159  if (!document()->pointerTypeList().contains(type)) {
160  emit scriptError(i18n("Cannot create edge: pointer type %1 not defined", type));
161  return QScriptValue();
162  }
163 
164  DataPtr from = fromRaw->getData();
165  DataPtr to = toRaw->getData();
166 
167  PointerPtr edge = createPointer(from, to, type);
168  if (edge) {
169  edge->setEngine(engine());
170  return edge->scriptValue();
171  }
172  kError() << "Could not add pointer to data structure";
173 
174  return QScriptValue();
175 }
176 
177 QScriptValue Rocs::GraphStructure::overlay_edges(int overlay)
178 {
179  emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.",
180  QString("overlay_edges(int type)"),
181  QString("edges(int type)")));
182  return edges(overlay);
183 }
184 
185 QScriptValue Rocs::GraphStructure::list_nodes()
186 {
187  emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.",
188  QString("list_nodes()"),
189  QString("nodes()")));
190  return nodes();
191 }
192 
193 QScriptValue Rocs::GraphStructure::list_nodes(int type)
194 {
195  emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.",
196  QString("list_nodes(int type)"),
197  QString("nodes(int type)")));
198  return nodes(type);
199 }
200 
201 QScriptValue Rocs::GraphStructure::list_edges()
202 {
203  emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.",
204  QString("list_edges()"),
205  QString("edges()")));
206  return edges();
207 }
208 
209 QScriptValue Rocs::GraphStructure::list_edges(int type)
210 {
211  emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.",
212  QString("list_edges(int type)"),
213  QString("edges(int type)")));
214  return edges(type);
215 }
216 
217 QScriptValue Rocs::GraphStructure::add_node(const QString& name)
218 {
219  emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.",
220  QString("add_node(string name)"),
221  QString("createNode()")));
222  DataPtr n = createData(name, 0);
223  n->setEngine(engine());
224  return n->scriptValue();
225 }
226 
227 QScriptValue Rocs::GraphStructure::add_edge(Data* fromRaw, Data* toRaw)
228 {
229  emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.",
230  QString("add_edge(from, to)"),
231  QString("createEdge(node from, node to)")));
232  return add_overlay_edge(fromRaw, toRaw, 0);
233 }
234 
235 QScriptValue Rocs::GraphStructure::add_overlay_edge(Data* fromRaw, Data* toRaw, int overlay)
236 {
237  emit scriptError(i18n("The global method \"%1\" is deprecated, please use \"%2\" instead.",
238  QString("add_overlay_edge(from, to, overlay)"),
239  QString("createEdge(node from, node to, int type)")));
240  return createEdge(fromRaw, toRaw, overlay);
241 }
242 
243 QScriptValue Rocs::GraphStructure::dijkstra_shortest_path(Data* fromRaw, Data* toRaw)
244 {
245  if (fromRaw == 0 || toRaw == 0) {
246  return QScriptValue();
247  }
248  DataPtr from = fromRaw->getData();
249  DataPtr to = toRaw->getData();
250 
251  QMap<DataPtr,PointerList> shortestPaths = dijkstraShortestPaths(from);
252  QScriptValue pathEdges = engine()->newArray();
253  foreach (PointerPtr edge, shortestPaths[to]) {
254  pathEdges.property("push").call(
255  pathEdges,
256  QScriptValueList() << edge->scriptValue()
257  );
258  }
259  return pathEdges;
260 }
261 
262 QScriptValue Rocs::GraphStructure::distances(Data* fromRaw)
263 {
264  if (fromRaw == 0) {
265  return QScriptValue();
266  }
267  DataPtr from = fromRaw->getData();
268 
269  QMap<DataPtr,PointerList> shortestPaths = dijkstraShortestPaths(from);
270  QScriptValue distances = engine()->newArray();
271  foreach (DataPtr target, dataListAll()) {
272  qreal length = 0;
273 
274  if (shortestPaths[target].isEmpty() && from != target) {
275  length = INFINITY;
276  } else {
277  foreach (PointerPtr edge, shortestPaths[target]) {
278  if (!edge->property("value").toString().isEmpty()) {
279  length += edge->property("value").toDouble();
280  } else {
281  length += 1;
282  }
283  }
284  }
285 
286  distances.property("push").call(
287  distances,
288  QScriptValueList() << length
289  );
290  }
291  return distances;
292 }
293 
294 QMap<DataPtr,PointerList> Rocs::GraphStructure::dijkstraShortestPaths(DataPtr from)
295 {
296  // use copies of these lists to be safe agains changes of
297  // data/pointer lists while computing shortest paths
298  DataList dataListAll = this->dataListAll();
299  PointerList pointerListAll = this->pointerListAll();
300 
301  if (!from) {
302  return QMap<DataPtr,PointerList>();
303  }
304 
305  typedef boost::adjacency_list < boost::listS, boost::vecS, boost::directedS,
306  boost::no_property, boost::property <boost::edge_weight_t, qreal> > graph_t;
307  typedef boost::graph_traits <graph_t>::vertex_descriptor vertex_descriptor;
308  typedef boost::graph_traits <graph_t>::edge_descriptor edge_descriptor;
309  typedef std::pair<int, int> Edge;
310 
311  // create IDs for all nodes
312  QMap<int, int> node_mapping;
313  QMap<std::pair<int, int>, PointerPtr> edge_mapping; // to map all edges back afterwards
314  int counter = 0;
315  foreach(DataPtr data, dataListAll) {
316  node_mapping[data->identifier()] = counter++;
317  }
318 
319  // use doubled size for case of undirected edges
320  QVector<Edge> edges(pointerListAll.count() * 2);
321  QVector<qreal> weights(pointerListAll.count() * 2);
322 
323  counter = 0;
324  foreach(PointerPtr p, pointerListAll) {
325  edges[counter] = Edge(node_mapping[p->from()->identifier()], node_mapping[p->to()->identifier()]);
326  edge_mapping[std::make_pair < int, int > (node_mapping[p->from()->identifier()], node_mapping[p->to()->identifier()])] = p;
327  if (!p->property("value").toString().isEmpty()) {
328  weights[counter] = p->property("value").toDouble();
329  } else {
330  weights[counter] = 1;
331  }
332  counter++;
333  // if graph is directed, also add back-edges
334  if (p->direction() == PointerType::Bidirectional) {
335  edges[counter] = Edge(node_mapping[p->to()->identifier()], node_mapping[p->from()->identifier()]);
336  edge_mapping[std::make_pair< int, int >(node_mapping[p->to()->identifier()], node_mapping[p->from()->identifier()])] = p;
337  if (!p->property("value").toString().isEmpty()) {
338  weights[counter] = p->property("value").toDouble();
339  } else {
340  weights[counter] = 1;
341  }
342  counter++;
343  }
344  }
345 
346  // setup the graph
347  graph_t g(edges.begin(),
348  edges.end(),
349  weights.begin(),
350  dataListAll.count()
351  );
352 
353  // compute Dijkstra
354  vertex_descriptor source = boost::vertex(node_mapping[from->identifier()], g);
355  QVector<vertex_descriptor> p(boost::num_vertices(g));
356  QVector<int> dist(boost::num_vertices(g));
357  boost::dijkstra_shortest_paths(g,
358  source,
359  boost::predecessor_map(p.begin()).distance_map(dist.begin())
360  );
361 
362  // walk search tree and setup solution
363  QMap<DataPtr,PointerList> shortestPaths = QMap<DataPtr,PointerList>();
364 
365  DataList::iterator toIter = dataListAll.begin();
366  while (toIter != dataListAll.end()){
367  PointerList path = PointerList();
368  vertex_descriptor target = boost::vertex(node_mapping[(*toIter)->identifier()], g);
369  vertex_descriptor predecessor = target;
370  do {
371  if (edge_mapping.contains(std::make_pair<int, int>(p[predecessor], predecessor))) {
372  path.append(edge_mapping[std::make_pair < int, int > (p[predecessor], predecessor)]);
373  }
374  predecessor = p[predecessor];
375  } while (p[predecessor] != predecessor);
376 
377  shortestPaths.insert((*toIter), path);
378  ++toIter;
379  }
380  return shortestPaths;
381 }
382 
383 void Rocs::GraphStructure::setGraphType(int type)
384 {
385  if (_type == type) {
386  return;
387  }
388 
389  if (_type == Multigraph && type != _type) {
390  if (KMessageBox::warningContinueCancel(0, i18n("This action will probably remove some edges. Do you want to continue?")) != KMessageBox::Continue) {
391  return;
392  }
393  } else { // for switch to multigraph nothing has to be done
394  _type = GRAPH_TYPE(type);
395  return;
396  }
397 
398  // need to convert multigraph to graph
399  //FIXME only default data type considered
400  foreach(DataPtr data, dataList(0)) {
401  // Clear the rest. there should be only one edge between two nodes.
402  foreach(DataPtr neighbor, data->adjacentDataList()) {
403  if (data == neighbor) {
404  continue;
405  }
406  while (data->pointerList(neighbor).count() > 1) {
407  data->pointerList(neighbor).last()->remove();
408  }
409  }
410  }
411 }
412 
413 Rocs::GraphStructure::GRAPH_TYPE Rocs::GraphStructure::graphType() const
414 {
415  return _type;
416 }
417 
418 bool Rocs::GraphStructure::multigraph() const
419 {
420  return (_type == Multigraph);
421 }
422 
423 PointerPtr Rocs::GraphStructure::createPointer(DataPtr from, DataPtr to, int pointerType)
424 {
425  bool directed = document()->pointerType(pointerType)->direction() == PointerType::Unidirectional;
426  if (!directed && !multigraph()) {
427  // do not add back-edges if graph is undirected
428  foreach(PointerPtr pointer, from->pointerList(to)) {
429  if (pointer->pointerType() == pointerType) {
430  emit scriptError(i18n("Could not add back-edge (%1->%2) to undirected graph.", from->identifier(), to->identifier()));
431  return PointerPtr();
432  }
433  }
434  }
435 
436  if (!multigraph()) { // do not add double edges
437  PointerList list = from->outPointerList();
438  foreach(PointerPtr tmp, list) {
439  if (tmp->to() == to && tmp->pointerType() == pointerType) {
440  emit scriptError(
441  i18n("Could not add existing edge (%1->%2): this graph is no multigraph.", from->identifier(), to->identifier()));
442  return PointerPtr();
443  }
444  }
445  }
446 
447  return DataStructure::createPointer(from, to, pointerType);
448 }
449 
450 DataPtr Rocs::GraphStructure::createData(const QString& name, int dataType)
451 {
452  if (readOnly()) {
453  return DataPtr();
454  }
455  boost::shared_ptr<GraphNode> n = boost::static_pointer_cast<GraphNode>(
456  GraphNode::create(getDataStructure(), generateUniqueIdentifier(), dataType)
457  );
458  n->setProperty("name", name);
459  return addData(n);
460 }
461 
462 QMap<QString, QString> Rocs::GraphStructure::pluginProperties() const
463 {
464  QMap<QString,QString> properties = QMap<QString,QString>();
465  properties.insert("type", QString("%1").arg(_type));
466  return properties;
467 }
468 
469 void Rocs::GraphStructure::setPluginProperty(const QString& identifier, const QString& property)
470 {
471  if (identifier.startsWith(QLatin1String("type"))) {
472  setGraphType(property.toInt());
473  }
474  else {
475  kDebug() << "Skipping unknown graph structure property: " << identifier << " / " << property;
476  }
477 }
Rocs::GraphStructure::edges
Q_INVOKABLE QScriptValue edges()
Returns a list of all edges of the graph.
Definition: GraphStructure.cpp:115
Graph
boost::adjacency_list< boost::listS, boost::vecS, boost::undirectedS, boost::property< boost::vertex_name_t, std::string > > Graph
Definition: Topology.cpp:45
Rocs::GraphStructure::createPointer
PointerPtr createPointer(DataPtr from, DataPtr to, int pointerType)
Internal method to create new graph edge.
Definition: GraphStructure.cpp:423
Rocs::GraphStructure
Definition: GraphStructure.h:29
PointerType::Bidirectional
Definition: PointerType.h:48
DataStructure
Definition: DataStructure.h:43
Rocs::GraphStructure::createData
DataPtr createData(const QString &name, int dataType)
Internal method to create new graph node.
Definition: GraphStructure.cpp:450
Rocs::GraphStructure::GRAPH_TYPE
GRAPH_TYPE
Definition: GraphStructure.h:35
GraphNode.h
DataStructurePtr
boost::shared_ptr< DataStructure > DataStructurePtr
Definition: CoreTypes.h:38
GmlParser::document
Document * document
Definition: GmlGrammar.cpp:40
Rocs::GraphStructure::add_edge
QScriptValue add_edge(Data *fromRaw, Data *toRaw)
Creates a new edge from.
Definition: GraphStructure.cpp:227
DotParser::createData
void createData(const std::string &str)
Definition: DotGrammar.cpp:372
Rocs::GraphStructure::pluginProperties
QMap< QString, QString > pluginProperties() const
Gives a map with plugin specific properties of the data structure.
Definition: GraphStructure.cpp:462
Rocs::GraphStructure::graphType
GRAPH_TYPE graphType() const
Returns type of the graph given by enum.
Definition: GraphStructure.cpp:413
Rocs::GraphStructure::multigraph
bool multigraph() const
Definition: GraphStructure.cpp:418
Rocs::GraphStructure::list_nodes
QScriptValue list_nodes()
Returns a list of all nodes of the graph.
Definition: GraphStructure.cpp:185
Rocs::GraphStructure::~GraphStructure
~GraphStructure()
Definition: GraphStructure.cpp:91
DataList
QList< boost::shared_ptr< Data > > DataList
Definition: CoreTypes.h:30
Edge
QPair< int, int > Edge
Definition: Topology.cpp:53
QtScriptBackend.h
Rocs::GraphStructure::distances
QScriptValue distances(Data *fromRaw)
Computes the Dijkstra's shortest path algorithm to compute all shortest path distances from from...
Definition: GraphStructure.cpp:262
Data.h
Document.h
Rocs::GraphStructure::createNode
Q_INVOKABLE QScriptValue createNode()
Creates a new data element and return it.
Definition: GraphStructure.cpp:135
PointerType::Unidirectional
Definition: PointerType.h:47
Rocs::GraphStructure::add_overlay_edge
QScriptValue add_overlay_edge(Data *fromRaw, Data *toRaw, int overlay)
Creates a new overlay edge from.
Definition: GraphStructure.cpp:235
GraphStructure.h
Rocs::GraphStructure::dijkstraShortestPaths
QMap< DataPtr, PointerList > dijkstraShortestPaths(DataPtr from)
Computes the Dijkstra's shortest path algorithm to compute all shortest path distances from from...
Definition: GraphStructure.cpp:294
Rocs::GraphStructure::dijkstra_shortest_path
QScriptValue dijkstra_shortest_path(Data *fromRaw, Data *toRaw)
Computes the Dijkstra's shortest path algorithm to compute the shortes path from. ...
Definition: GraphStructure.cpp:243
PointerPtr
boost::shared_ptr< Pointer > PointerPtr
Definition: CoreTypes.h:35
GraphNode::create
static DataPtr create(DataStructurePtr parent, int uniqueIdentifier, int dataType)
Definition: GraphNode.cpp:26
Rocs::GraphStructure::overlay_edges
QScriptValue overlay_edges(int overlay)
Gives array of edges of specified overlay.
Definition: GraphStructure.cpp:177
DataStructure.h
Rocs::GraphStructure::setGraphType
void setGraphType(int type)
Setter for graph type.
Definition: GraphStructure.cpp:383
Rocs::GraphStructure::add_node
QScriptValue add_node(const QString &name)
Creates a new node with specified.
Definition: GraphStructure.cpp:217
Rocs::GraphStructure::setPluginProperty
void setPluginProperty(const QString &identifier, const QString &property)
Set plugin specific properties of data structure.
Definition: GraphStructure.cpp:469
DataStructure::createPointer
virtual PointerPtr createPointer(DataPtr from, DataPtr to, int pointerType)
Creates new pointer from data element "from" to data element "to" of given type "pointerType".
Definition: DataStructure.cpp:393
Document
Definition: Document.h:41
Data::getData
virtual DataPtr getData() const
Definition: Data.cpp:125
Document::pointerType
PointerTypePtr pointerType(int pointerType) const
Definition: Document.cpp:207
Rocs::GraphStructure::create
static DataStructurePtr create(Document *parent)
Definition: GraphStructure.cpp:44
Pointer.h
DataPtr
boost::shared_ptr< Data > DataPtr
Definition: CoreTypes.h:34
Rocs::GraphStructure::importStructure
void importStructure(DataStructurePtr other)
overwrites the current DataStructure with all values (Data and Pointer) from the given datastructure ...
Definition: GraphStructure.cpp:63
Data
Definition: Data.h:40
Rocs::GraphStructure::GraphStructure
GraphStructure(Document *parent=0)
Definition: GraphStructure.cpp:57
GraphNode
Definition: GraphNode.h:24
Rocs::GraphStructure::list_edges
QScriptValue list_edges()
Returns a list of all edges of the graph.
Definition: GraphStructure.cpp:201
Rocs::GraphStructure::nodes
Q_INVOKABLE QScriptValue nodes()
Returns a list of all nodes of the graph.
Definition: GraphStructure.cpp:95
Rocs::GraphStructure::Graph
Definition: GraphStructure.h:36
Rocs::GraphStructure::createEdge
Q_INVOKABLE QScriptValue createEdge(Data *fromRaw, Data *toRaw, int type)
Creates a new edge edge from.
Definition: GraphStructure.cpp:152
PointerList
QList< boost::shared_ptr< Pointer > > PointerList
Definition: CoreTypes.h:33
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:42:26 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

rocs/RocsCore

Skip menu "rocs/RocsCore"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdeedu API Reference

Skip menu "kdeedu API Reference"
  • Analitza
  •     lib
  • kalgebra
  • kalzium
  •   libscience
  • kanagram
  • kig
  •   lib
  • klettres
  • kstars
  • libkdeedu
  •   keduvocdocument
  • marble
  • parley
  • rocs
  •   App
  •   RocsCore
  •   VisualEditor
  •   stepcore

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal