VPTissue Reference Manual
PTreeModel.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2011-2016 Universiteit Antwerpen
3  *
4  * Licensed under the EUPL, Version 1.1 or as soon they will be approved by
5  * the European Commission - subsequent versions of the EUPL (the "Licence");
6  * You may not use this work except in compliance with the Licence.
7  * You may obtain a copy of the Licence at: http://ec.europa.eu/idabc/eupl5
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the Licence is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the Licence for the specific language governing
13  * permissions and limitations under the Licence.
14  */
20 #include "PTreeModel.h"
21 
22 #include "PTreeUndoCommands.h"
23 
24 #include <QMimeData>
25 #include <QStringList>
26 #include <QUndoStack>
27 #include <boost/property_tree/ptree.hpp>
28 #include <boost/property_tree/xml_parser.hpp>
29 #include <sstream>
30 
31 namespace SimShell {
32 namespace Gui {
33 
34 using boost::property_tree::ptree;
35 
36 PTreeModel::PTreeModel(ptree const& tree, QObject* parent)
37  : QAbstractItemModel(parent)
38 {
39  root = new Item;
40  root->Load(tree);
41  undo_stack = 0;
42  only_edit_data = false;
43 }
44 
45 PTreeModel & PTreeModel::operator=(PTreeModel const& other)
46 {
47  if (this != &other) {
48  // copy entire tree structure
49  root = new Item(*other.root);
50  undo_stack = 0; // two models shouldn't use the same undo stack!
51  }
52  return *this;
53 }
54 
56 {
57 }
58 
59 int PTreeModel::columnCount(QModelIndex const& ) const
60 {
61  return 2;
62 }
63 
64 QVariant PTreeModel::data(QModelIndex const& index, int role) const
65 {
66  if (!index.isValid())
67  return QVariant();
68 
69  Item* item = static_cast<Item*>(index.internalPointer());
70 
71  if (role == Qt::DisplayRole || role==Qt::EditRole) {
72  switch (index.column()) {
73  case 0:
74  return item->key;
75  case 1:
76  return item->data;
77  default:
78  return QVariant();
79  }
80  } else if (role == Qt::BackgroundRole) {
81  return item->background;
82  } else {
83  return QVariant();
84  }
85 }
86 
87 bool PTreeModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column,
88  const QModelIndex & parent)
89 {
90  if (only_edit_data) {
91  return false;
92  }
93  if (action == Qt::IgnoreAction) {
94  return true;
95  }
96  if (action == Qt::LinkAction) {
97  return false;
98  }
99  if (!data->hasFormat("text/plain")) {
100  return false;
101  }
102  if (column > 1) {
103  return false;
104  }
105 
106  // always act as if data was dropped in first column
107  column = 0;
108 
109  if (row == -1) {
110  row = rowCount(parent);
111  }
112 
113  QString text = data->text();
114 
115  Item* parent_item;
116  if (parent.isValid()) {
117  parent_item = static_cast<Item*>(parent.internalPointer());
118  } else {
119  parent_item = root;
120  }
121 
122  std::stringstream sstream(text.toStdString());
123  ptree pt;
124  try {
125  boost::property_tree::xml_parser::read_xml(sstream, pt, boost::property_tree::xml_parser::trim_whitespace);
126  }
127  catch (boost::property_tree::xml_parser::xml_parser_error& e) {
128  return false;
129  }
130  undo_stack->push(new InsertRowsCommand(this, parent_item, parent, row, pt));
131  return true;
132 }
133 
134 Qt::ItemFlags PTreeModel::flags(QModelIndex const& index) const
135 {
136  if (!index.isValid()) {
137  return Qt::ItemIsDropEnabled;
138  }
139 
140  // only 2nd column items that have no children can be edited
141  if (index.column() == 0) {
142  if (only_edit_data) {
143  return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
144  } else {
145  return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable
146  | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
147  }
148  } else {
149  if (static_cast<Item*>(index.internalPointer())->GetChildrenCount() == 0) {
150  return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled;
151  } else {
152  return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled;
153  }
154  }
155 }
156 
157 QVariant PTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
158 {
159  if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
160  if (section == 0) {
161  return QVariant(QString("Key"));
162  } else if (section == 1) {
163  return QVariant(QString("Data"));
164  }
165  }
166  return QVariant();
167 }
168 
169 QModelIndex PTreeModel::index(int row, int column, QModelIndex const& parent) const
170 {
171  if (!hasIndex(row, column, parent)) {
172  return QModelIndex();
173  }
174 
175  Item* parent_tree;
176  if (!parent.isValid()) {
177  parent_tree = root;
178  } else {
179  parent_tree = static_cast<Item*>(parent.internalPointer());
180  }
181 
182  Item* child = parent_tree->GetChild(row);
183 
184  if (child) {
185  return createIndex(row, column, child);
186  } else {
187  return QModelIndex();
188  }
189 }
190 
191 bool PTreeModel::insertRow(int row, QModelIndex const& parent)
192 {
193  return insertRows(row, 1, parent);
194 }
195 
196 bool PTreeModel::insertRows(int row, int count, QModelIndex const& parent)
197 {
198  if (only_edit_data) {
199  return false;
200  }
201 
202  Item* parent_item;
203  if (parent.isValid()) {
204  parent_item = static_cast<Item*>(parent.internalPointer());
205  } else {
206  parent_item = root;
207  }
208 
209  if (row < 0) {
210  return false;
211  }
212 
213  if (row > parent_item->GetChildrenCount()) {
214  return false;
215  }
216 
217  undo_stack->push(new InsertRowsCommand(this, parent_item, parent, row, count));
218  return true;
219 }
220 
222 {
223  return only_edit_data;
224 }
225 
226 QMimeData* PTreeModel::mimeData(const QModelIndexList& indexes) const
227 {
228  QMimeData* mime = new QMimeData();
229  QString text;
230 
231  for (auto const& index : indexes) {
232  if (index.column() == 0) {
233  Item* item = static_cast<Item*>(index.internalPointer());
234  std::stringstream sstream;
235  ptree pt;
236  pt.add_child(item->key.toString().toStdString(), item->Store());
237  boost::property_tree::xml_parser::write_xml(sstream, pt);
238  text = sstream.str().c_str();
239  }
240  }
241  mime->setText(text);
242  return mime;
243 }
244 
245 bool PTreeModel::MoveRow(int old_row, const QModelIndex & old_parent, int new_row, const QModelIndex & new_parent)
246 {
247  return MoveRows(old_row, old_parent, new_row, new_parent, 1);
248 }
249 
250 bool PTreeModel::MoveRows(int old_row, QModelIndex const& old_parent, int new_row, QModelIndex const& new_parent,
251  int count)
252 {
253  if (only_edit_data) {
254  return false;
255  }
256 
257  Item* old_parent_item;
258  if (old_parent.isValid()) {
259  old_parent_item = static_cast<Item*>(old_parent.internalPointer());
260  } else {
261  old_parent_item = root;
262  }
263 
264  Item* new_parent_item;
265  if (new_parent.isValid()) {
266  new_parent_item = static_cast<Item*>(new_parent.internalPointer());
267  } else {
268  new_parent_item = root;
269  }
270 
271  if (old_row < 0) {
272  return false;
273  }
274  if (old_row + count > old_parent_item->GetChildrenCount()) {
275  return false;
276  }
277  if (new_row < 0) {
278  return false;
279  }
280  if (new_row + count > new_parent_item->GetChildrenCount()) {
281  return false;
282  }
283  undo_stack->push(new MoveRowsCommand(this, old_parent_item, old_parent, old_row,
284  new_parent_item, new_parent, new_row, count));
285  return true;
286 }
287 
288 QStringList PTreeModel::mimeTypes() const
289 {
290  QStringList types;
291  types << "text/plain";
292  return types;
293 }
294 
295 QModelIndex PTreeModel::parent(QModelIndex const& index) const
296 {
297  if (!index.isValid()) {
298  return QModelIndex();
299  }
300 
301  Item* child = static_cast<Item*>(index.internalPointer());
302  Item* parent = child->GetParent();
303  if (parent == root) {
304  return QModelIndex();
305  }
306 
307  return createIndex(parent->row, 0, parent);
308 }
309 
310 bool PTreeModel::removeRow(int row, QModelIndex const& parent)
311 {
312  return removeRows(row, 1, parent);
313 }
314 
315 bool PTreeModel::removeRows(int row, int count, QModelIndex const& parent)
316 {
317  if (only_edit_data) {
318  return false;
319  }
320 
321  Item* parent_item;
322  if (parent.isValid()) {
323  parent_item = static_cast<Item*>(parent.internalPointer());
324  } else {
325  parent_item = root;
326  }
327 
328  if (row < 0) {
329  return false;
330  }
331 
332  if (row + count > parent_item->GetChildrenCount()) {
333  return false;
334  }
335 
336  undo_stack->push(new RemoveRowsCommand(this, parent_item, parent, row, count));
337  return true;
338 }
339 
340 int PTreeModel::rowCount(QModelIndex const& parent) const
341 {
342  Item* item;
343  if (parent.column() > 0) {
344  return 0;
345  }
346 
347  if (!parent.isValid()) {
348  item = root;
349  } else {
350  item = static_cast<Item*>(parent.internalPointer());
351  }
352  return item->GetChildrenCount();
353 }
354 
355 bool PTreeModel::setData(QModelIndex const& index, const QVariant& value, int role)
356 {
357  if (!index.isValid()) {
358  return false;
359  }
360 
361  Item* item = static_cast<Item*>(index.internalPointer());
362  if (role == Qt::EditRole) {
363  if (index.column() == 0) {
364  if (only_edit_data) {
365  return false;
366  }
367 
368  if (value.toString().isEmpty()) {
369  return false; // changing key to empty string is probably a user mistake and illegal!
370  }
371  undo_stack->push(new EditKeyCommand(this, item, index, item->key, value));
372  } else if (index.column() == 1) {
373  undo_stack->push(new EditDataCommand(this, item, index, item->data, value));
374  }
375  // no need to emit dataChanged() signal, undo command will do this for us
376  return true;
377  } else if (role == Qt::BackgroundRole) {
378  item->background = value;
379  // emit dataChanged() for entire row
380  QModelIndex key_index = index.sibling(index.row(), 0);
381  QModelIndex data_index = index.sibling(index.row(), 1);
382  emit dataChanged(key_index, data_index);
383  return true;
384  } else {
385  return false;
386  }
387 }
388 
390 {
391  only_edit_data = b;
392 }
393 
394 void PTreeModel::SetUndoStack(QUndoStack* s)
395 {
396  undo_stack = s;
397 }
398 
399 ptree PTreeModel::Store() const
400 {
401  return root->Store();
402 }
403 
404 Qt::DropActions PTreeModel::supportedDragActions() const
405 {
406  if (only_edit_data) {
407  return Qt::CopyAction;
408  } else {
409  return Qt::CopyAction | Qt::MoveAction;
410  }
411 }
412 
413 Qt::DropActions PTreeModel::supportedDropActions() const
414 {
415  if (only_edit_data) {
416  return Qt::CopyAction;
417  }
418  else {
419  return Qt::CopyAction | Qt::MoveAction;
420  }
421 }
422 
423 // --- PTreeModel::Item implementation ---
424 
425 PTreeModel::Item::Item(Item* p, QVariant const& k, int r) : key(k), row(r), parent(p) {}
426 
427 PTreeModel::Item::Item(Item const& other)
428  : key(other.key), data(other.data), background(other.background),
429  row(other.row), parent(other.parent)
430 {
431  for (auto const& child : other.children) {
432  children.push_back(new Item(*child));
433  }
434 }
435 
436 PTreeModel::Item& PTreeModel::Item::operator=(Item const& other)
437 {
438  if (this != &other) {
439  // forget about our own children
440  for (auto const& child : children) {
441  delete child;
442  }
443 
444  parent = other.parent;
445  key = other.key;
446  data = other.data;
447  background = other.background;
448  row = other.row;
449 
450  for (auto const& child : other.children) {
451  children.push_back(new Item(*child));
452  }
453  }
454  return *this;
455 }
456 
457 PTreeModel::Item::~Item()
458 {
459  for (auto const& child : children) {
460  delete child;
461  }
462 }
463 
464 PTreeModel::Item* PTreeModel::Item::GetChild(unsigned int i)
465 {
466  if (i < children.size()) {
467  return children[i];
468  } else {
469  return 0;
470  }
471 }
472 
473 PTreeModel::Item const* PTreeModel::Item::GetChild(unsigned int i) const
474 {
475  if (i < children.size()) {
476  return children[i];
477  } else {
478  return 0;
479  }
480 }
481 
482 int PTreeModel::Item::GetChildrenCount() const
483 {
484  return children.size();
485 }
486 
487 PTreeModel::Item* PTreeModel::Item::GetParent()
488 {
489  return parent;
490 }
491 
492 PTreeModel::Item const* PTreeModel::Item::GetParent() const
493 {
494  return parent;
495 }
496 
497 void PTreeModel::Item::InsertChild(unsigned int row, Item* item)
498 {
499  children.insert(children.begin() + row, item);
500 
501  // correct row numbers of subsequent items
502  for (auto it = children.begin() + row; it != children.end(); it++) {
503  (*it)->row = it - children.begin();
504  }
505 }
506 
507 void PTreeModel::Item::Load(ptree const& tree)
508 {
509  for (auto const& child : children) {
510  delete child;
511  }
512  children.clear();
513 
514  // load tree contents
515  data = QVariant(QString(tree.data().c_str()));
516  children.reserve(tree.size());
517  for (ptree::value_type const & v : tree) {
518  Item* item = new Item(this, QVariant(QString(v.first.c_str())), children.size());
519  item->Load(v.second);
520  children.push_back(item);
521  }
522 }
523 
524 void PTreeModel::Item::RemoveChild(unsigned int row)
525 {
526  children.erase(children.begin() + row);
527 
528  // correct row numbers of subsequent items
529  for (auto it = children.begin() + row; it != children.end(); it++) {
530  (*it)->row = it - children.begin();
531  }
532 }
533 
534 ptree PTreeModel::Item::Store() const
535 {
536  ptree t;
537 
538  if (data.type() == QVariant::String) {
539  t.data() = ptree::data_type(data.toString().toStdString());
540  }
541  for (Item* v : children) {
542  ptree child_tree = v->Store();
543  t.push_back(ptree::value_type(ptree::key_type(v->key.toString().toStdString()), child_tree));
544  }
545  return ptree(t);
546 }
547 
548 } // end of namespace Gui
549 } // end of namespace SimShell
Interface for PTree undo commands.
bool MoveRows(int old_row, QModelIndex const &old_parent, int new_row, QModelIndex const &new_parent, int count)
Moves a block of contagious rows from one place to another.
Definition: PTreeModel.cpp:250
see the online Qt documentation
boost::property_tree::ptree Store() const
Creates a new property tree from the current model state.
Definition: PTreeModel.cpp:399
PTreeModel(boost::property_tree::ptree const &tree, QObject *parent=0)
Constructs an empty PTreeModel.
bool MoveRow(int old_row, QModelIndex const &old_parent, int new_row, QModelIndex const &new_parent)
Moves a single row from one place to another.
Definition: PTreeModel.cpp:245
Undo command that represents a single move operation of rows inside a PTreeModel. ...
virtual ~PTreeModel()
Virtual destructor.
Definition: PTreeModel.cpp:55
bool IsOnlyEditData() const
Test whether the "only edit data" option set.
Definition: PTreeModel.cpp:221
void SetOnlyEditData(bool)
Set the "only edit values" option.
Definition: PTreeModel.cpp:389
Interface for PTreeModel.
see the online Qt documentation
Namespace for generic graphical shell for simulators.
Definition: SimSession.h:32
void SetUndoStack(QUndoStack *)
Set this model to use a given undo stack.
Definition: PTreeModel.cpp:394