BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
SessionModel.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/coregui/Models/SessionModel.cpp
6 //! @brief Implements class SessionModel
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
20 #include <QFile>
21 #include <QMimeData>
22 #include <QtCore/QXmlStreamWriter>
23 
25 
26 namespace {
27 const int MaxCompression = 9;
28 }
29 
30 SessionModel::SessionModel(QString model_tag, QObject* parent)
31  : QAbstractItemModel(parent), m_root_item(0), m_name("DefaultName"), m_model_tag(model_tag)
32 {
34 }
35 
37 {
39  m_root_item->setModel(this);
40  m_root_item->registerTag("rootTag");
41  m_root_item->setDefaultTag("rootTag");
42 }
43 
45 {
46  delete m_root_item;
47 }
48 
49 // TODO make it relying on SessionItem::flags
50 
51 Qt::ItemFlags SessionModel::flags(const QModelIndex& index) const
52 {
53  Qt::ItemFlags result_flags = QAbstractItemModel::flags(index);
54  if (index.isValid()) {
55  result_flags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled;
57  if (index.column() == SessionFlags::ITEM_VALUE && item->value().isValid()
58  && item->isEditable() && item->isEnabled())
59  result_flags |= Qt::ItemIsEditable;
60  QVector<QString> acceptable_child_items = acceptableDefaultItemTypes(index);
61  if (acceptable_child_items.contains(m_dragged_item_type)) {
62  result_flags |= Qt::ItemIsDropEnabled;
63  }
64  } else {
65  result_flags |= Qt::ItemIsDropEnabled;
66  }
67  return result_flags;
68 }
69 
70 QVariant SessionModel::data(const QModelIndex& index, int role) const
71 {
72  if (!m_root_item || !index.isValid() || index.column() < 0
73  || index.column() >= columnCount(QModelIndex())) {
74  return QVariant();
75  }
76  if (SessionItem* item = itemForIndex(index)) {
77  if (role == Qt::DisplayRole || role == Qt::EditRole) {
78  if (index.column() == SessionFlags::ITEM_VALUE)
79  return item->value();
80  if (index.column() == SessionFlags::ITEM_NAME)
81  return item->itemName();
82  } else if (role == Qt::ToolTipRole) {
83  return SessionItemUtils::ToolTipRole(*item, index.column());
84 
85  } else if (role == Qt::ForegroundRole) {
87  } else if (role == Qt::DecorationRole && index.column() == SessionFlags::ITEM_VALUE) {
89  } else if (role == Qt::CheckStateRole && index.column() == SessionFlags::ITEM_VALUE) {
91  } else {
92  return item->roleProperty(role);
93  }
94  }
95  return QVariant();
96 }
97 
98 QVariant SessionModel::headerData(int section, Qt::Orientation orientation, int role) const
99 {
100  if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
101  switch (section) {
103  return "Name";
105  return "Value";
106  }
107  }
108  return QVariant();
109 }
110 
111 int SessionModel::rowCount(const QModelIndex& parent) const
112 {
113  if (parent.isValid() && parent.column() != 0)
114  return 0;
115  SessionItem* parent_item = itemForIndex(parent);
116  return parent_item ? parent_item->numberOfChildren() : 0;
117 }
118 
119 int SessionModel::columnCount(const QModelIndex& parent) const
120 {
121  if (parent.isValid() && parent.column() != 0)
122  return 0;
124 }
125 
126 QModelIndex SessionModel::index(int row, int column, const QModelIndex& parent) const
127 {
128  if (!m_root_item || row < 0 || column < 0 || column >= columnCount(QModelIndex())
129  || (parent.isValid() && parent.column() != 0))
130  return QModelIndex();
131 
132  SessionItem* parent_item = itemForIndex(parent);
133 
134  if (SessionItem* item = parent_item->childAt(row))
135  return createIndex(row, column, item);
136  return QModelIndex();
137 }
138 
139 QModelIndex SessionModel::parent(const QModelIndex& child) const
140 {
141  if (!child.isValid())
142  return QModelIndex();
143 
144  if (SessionItem* child_item = itemForIndex(child)) {
145  if (SessionItem* parent_item = child_item->parent()) {
146  if (parent_item == m_root_item)
147  return QModelIndex();
148 
149  return createIndex(ParentRow(*parent_item), 0, parent_item);
150  }
151  }
152  return QModelIndex();
153 }
154 
155 bool SessionModel::setData(const QModelIndex& index, const QVariant& value, int role)
156 {
157  if (!index.isValid())
158  return false;
159 
160  QModelIndex dataIndex = index;
161  if (SessionItem* item = itemForIndex(dataIndex))
162  if (item->setRoleProperty(role, value))
163  return true;
164 
165  return false;
166 }
167 
168 bool SessionModel::removeRows(int row, int count, const QModelIndex& parent)
169 {
170  if (!m_root_item)
171  return false;
172  SessionItem* item = parent.isValid() ? itemForIndex(parent) : m_root_item;
173  for (int i = 0; i < count; ++i)
174  delete item->takeRow(row);
175  return true;
176 }
177 
178 QStringList SessionModel::mimeTypes() const
179 {
180  return QStringList() << SessionXML::ItemMimeType;
181 }
182 
183 QMimeData* SessionModel::mimeData(const QModelIndexList& indices) const
184 {
185  if (indices.count() != 2)
186  return 0;
187 
188  if (SessionItem* item = itemForIndex(indices.at(0))) {
189  QMimeData* mime_data = new QMimeData;
190  QByteArray xml_data;
191  QXmlStreamWriter writer(&xml_data);
192  SessionXML::writeItemAndChildItems(&writer, item);
193  mime_data->setData(SessionXML::ItemMimeType, qCompress(xml_data, MaxCompression));
194  return mime_data;
195  }
196  return 0;
197 }
198 
199 bool SessionModel::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row,
200  int column, const QModelIndex& parent) const
201 {
202  Q_UNUSED(row);
203 
204  if (action == Qt::IgnoreAction)
205  return true;
206  if (action != Qt::MoveAction || column > 0 || !data
207  || !data->hasFormat(SessionXML::ItemMimeType))
208  return false;
209  if (!parent.isValid())
210  return true;
211  QVector<QString> acceptable_child_items = acceptableDefaultItemTypes(parent);
212  QByteArray xml_data = qUncompress(data->data(SessionXML::ItemMimeType));
213  QXmlStreamReader reader(xml_data);
214  while (!reader.atEnd()) {
215  reader.readNext();
216  if (reader.isStartElement()) {
217  if (reader.name() == SessionXML::ItemTag) {
218  const QString model_type =
219  reader.attributes().value(SessionXML::ModelTypeAttribute).toString();
220  return acceptable_child_items.contains(model_type);
221  }
222  }
223  }
224  return false;
225 }
226 
227 bool SessionModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column,
228  const QModelIndex& parent)
229 {
230  if (action == Qt::IgnoreAction)
231  return true;
232  if (action != Qt::MoveAction || column > 0 || !data
233  || !data->hasFormat(SessionXML::ItemMimeType))
234  return false;
235  if (!canDropMimeData(data, action, row, column, parent))
236  return false;
237  if (SessionItem* item = itemForIndex(parent)) {
238  QByteArray xml_data = qUncompress(data->data(SessionXML::ItemMimeType));
239  QXmlStreamReader reader(xml_data);
240  if (row == -1)
241  row = item->numberOfChildren();
242  beginInsertRows(parent, row, row);
243  // this code block is currently not in use. The row parameter of the reader is removed
244  // SessionReader::readItems(&reader, item, row);
245  endInsertRows();
246  return true;
247  }
248  return false;
249 }
250 
251 QModelIndex SessionModel::indexOfItem(SessionItem* item) const
252 {
253  if (!item || item == m_root_item || !item->parent())
254  return QModelIndex();
255  SessionItem* parent_item = item->parent();
256  int row = parent_item->rowOfChild(item);
257  return createIndex(row, 0, item);
258 }
259 
261  QString tag)
262 {
263  if (!parent_item)
264  parent_item = m_root_item;
265  if (row > parent_item->numberOfChildren())
266  return nullptr;
267  if (parent_item != m_root_item) {
268  if (tag.isEmpty())
269  tag = parent_item->defaultTag();
270 
271  if (!parent_item->sessionItemTags()->isValid(tag, model_type))
272  return nullptr;
273  }
274 
276 
277  if (!new_item)
278  throw GUIHelpers::Error("SessionModel::insertNewItem() -> Wrong model type " + model_type);
279 
280  if (!parent_item->insertItem(row, new_item, tag))
281  throw GUIHelpers::Error("SessionModel::insertNewItem -> Error. Can't insert item");
282 
283  return new_item;
284 }
285 
286 SessionItem* SessionModel::insertNewItem(QString model_type, const QModelIndex& parent_item,
287  int row, QString tag)
288 {
289  return insertNewItem(model_type, itemForIndex(parent_item), row, tag);
290 }
291 
293 {
294  QModelIndex index = indexOfItem(item);
295  if (index.isValid())
296  removeRows(index.row(), 1, QModelIndex());
297 }
298 
299 QVector<QString> SessionModel::acceptableDefaultItemTypes(const QModelIndex& parent) const
300 {
301  QVector<QString> result;
302  if (SessionItem* parent_item = itemForIndex(parent))
303  result = parent_item->acceptableDefaultItemTypes();
304 
305  return result;
306 }
307 
309 {
310  beginResetModel();
311  delete m_root_item;
312  createRootItem();
313  endResetModel();
314 }
315 
316 void SessionModel::load(const QString& filename)
317 {
318  beginResetModel();
319  QFile file(filename);
320  if (!file.open(QIODevice::ReadOnly))
321  throw GUIHelpers::Error(file.errorString());
322  clear();
324  QXmlStreamReader reader(&file);
326  if (reader.hasError())
327  throw GUIHelpers::Error(reader.errorString());
328  endResetModel();
329 }
330 
331 void SessionModel::save(const QString& filename)
332 {
333  QFile file(filename);
334  if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
335  throw GUIHelpers::Error(file.errorString());
336 
337  QXmlStreamWriter writer(&file);
338  writer.setAutoFormatting(true);
339  writer.writeStartDocument();
340  writer.writeStartElement("BornAgain");
341  writer.writeAttribute("Version", GUIHelpers::getBornAgainVersionString());
343  writer.writeEndElement(); // BornAgain
344  writer.writeEndDocument();
345 }
346 
347 SessionItem* SessionModel::itemForIndex(const QModelIndex& index) const
348 {
349  if (index.isValid())
350  if (SessionItem* item = static_cast<SessionItem*>(index.internalPointer()))
351  return item;
352 
353  return m_root_item;
354 }
355 
356 void SessionModel::readFrom(QXmlStreamReader* reader, MessageService* messageService)
357 {
358  ASSERT(reader);
359 
360  if (reader->name() != m_model_tag)
361  throw GUIHelpers::Error("SessionModel::readFrom() -> Format error in p1");
362 
363  beginResetModel();
364  clear();
365 
366  m_name = reader->attributes().value(SessionXML::ModelNameAttribute).toString();
367 
368  SessionXML::readItems(reader, m_root_item, QString(), messageService);
369  if (reader->hasError())
370  throw GUIHelpers::Error(reader->errorString());
371  endResetModel();
372 }
373 
374 void SessionModel::writeTo(QXmlStreamWriter* writer, SessionItem* parent)
375 {
376  if (!parent)
378  SessionXML::writeTo(writer, parent);
379 }
380 
381 //! Move given parameterized item to the new_parent at given row. If new_parent is not defined,
382 //! use root_item as a new parent.
383 
385  const QString& tag)
386 {
387  if (!new_parent)
388  new_parent = m_root_item;
389 
390  const QString tagName = tag.isEmpty() ? new_parent->defaultTag() : tag;
391 
392  if (!new_parent->sessionItemTags()->isValid(tagName, item->modelType()))
393  return 0;
394 
395  if (item->parent() == new_parent) {
396  // take care of indexes when moving item within same parent
397  int previousIndex = item->parent()->getItems(tagName).indexOf(item);
398  if (row == previousIndex)
399  return item;
400  else if (previousIndex >= 0 && row > previousIndex)
401  row--;
402  }
403 
404  if (new_parent->sessionItemTags()->maximumReached(tagName)) {
405  SessionItem* prev = new_parent->takeItem(0, tagName);
406  m_root_item->insertItem(-1, prev);
407  }
408 
409  // removing item from previous parent and inserting to the new one
410  item->parent()->takeRow(item->parent()->rowOfChild(item));
411  new_parent->insertItem(row, item, tagName);
412 
413  return item;
414 }
415 
416 //! Copy given item to the new_parent at given row. Item intended for copying can belong to
417 //! another model and it will remain intact. Returns pointer to the new child.
418 
419 SessionItem* SessionModel::copy(const SessionItem* item_to_copy, SessionItem* new_parent,
420  const QString& tag)
421 {
422  if (!new_parent)
423  new_parent = m_root_item;
424 
425  const QString tagName = tag.isEmpty() ? new_parent->defaultTag() : tag;
426 
427  QByteArray xml_data;
428  QXmlStreamWriter writer(&xml_data);
429  SessionXML::writeItemAndChildItems(&writer, item_to_copy);
430 
431  QXmlStreamReader reader(xml_data);
432  SessionXML::readItems(&reader, new_parent, tagName);
433 
434  return new_parent->getItems(tagName).back();
435 }
436 
438 {
439  Q_UNUSED(parent);
440  throw GUIHelpers::Error("SessionModel::createCopy() -> Error. Not implemented.");
441 }
442 
444 {
445  QByteArray byte_array;
446  QXmlStreamWriter writer(&byte_array);
447  writer.setAutoFormatting(true);
448 
449  model->writeTo(&writer);
450 
451  QXmlStreamReader reader(byte_array);
452 
453  while (!reader.atEnd()) {
454  reader.readNext();
455  if (reader.isStartElement())
456  readFrom(&reader);
457  }
458  modelLoaded();
459 }
460 
462 {
463  return m_root_item;
464 }
465 
466 QVector<SessionItem*> SessionModel::nonXMLItems() const
467 {
468  return QVector<SessionItem*>();
469 }
#define ASSERT(condition)
Definition: Assert.h:31
Defines class GUIHelpers functions.
Defines class ItemFactory.
Defines class SessionItemTags.
Defines namespace SessionItemUtils.
Defines class SessionModel.
The service to collect messages from different senders.
bool isValid(const QString &tagName, const QString &modelType="") const
Returns true if there is a registered tag with such name.
bool maximumReached(const QString &tagName) const
SessionItemTags * sessionItemTags()
bool insertItem(int row, SessionItem *item, const QString &tag="")
Insert item into given tag into given row.
bool registerTag(const QString &name, int min=0, int max=-1, QStringList modelTypes={})
Add new tag to this item with given name, min, max and types.
QVector< SessionItem * > getItems(const QString &tag="") const
Returns vector of all items of given tag.
int numberOfChildren() const
Returns total number of children.
Definition: SessionItem.cpp:94
QVariant value() const
Get value.
bool isEnabled() const
int rowOfChild(SessionItem *child) const
Returns row index of given child.
bool isEditable() const
QString defaultTag() const
Get default tag.
SessionItem * parent() const
Returns parent of this item.
Definition: SessionItem.cpp:73
void setDefaultTag(const QString &tag)
Set default tag.
SessionItem * takeRow(int row)
Removes row from item and returns the item.
SessionItem * takeItem(int row, const QString &tag)
Remove item from given row from given tag.
SessionItem * childAt(int row) const
Returns the child at the given row.
QString modelType() const
Get model type.
void setModel(SessionModel *model)
virtual QModelIndex parent(const QModelIndex &child) const
virtual void clear()
void modelLoaded()
SessionItem * copy(const SessionItem *item_to_copy, SessionItem *new_parent=0, const QString &tag="")
Copy given item to the new_parent at given row.
QString m_name
model name
Definition: SessionModel.h:121
virtual void writeTo(QXmlStreamWriter *writer, SessionItem *parent=0)
virtual Qt::ItemFlags flags(const QModelIndex &index) const
SessionItem * itemForIndex(const QModelIndex &index) const
virtual ~SessionModel()
virtual bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
virtual QVector< SessionItem * > nonXMLItems() const
QString m_dragged_item_type
Definition: SessionModel.h:120
virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const
virtual bool setData(const QModelIndex &index, const QVariant &value, int role)
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const
void createRootItem()
virtual int columnCount(const QModelIndex &parent) const
virtual int rowCount(const QModelIndex &parent) const
virtual bool removeRows(int row, int count, const QModelIndex &parent)
void load(const QString &filename="")
SessionModel(QString model_tag, QObject *parent=0)
void removeItem(SessionItem *item)
SessionItem * insertNewItem(QString model_type, SessionItem *parent_item=nullptr, int row=-1, QString tag="")
virtual QStringList mimeTypes() const
void save(const QString &filename="")
virtual void initFrom(SessionModel *model, SessionItem *parent)
SessionItem * moveItem(SessionItem *item, SessionItem *new_parent=0, int row=-1, const QString &tag="")
Move given parameterized item to the new_parent at given row.
QString m_model_tag
model tag (SampleModel, InstrumentModel)
Definition: SessionModel.h:122
SessionItem * rootItem() const
virtual QVariant data(const QModelIndex &index, int role) const
QVector< QString > acceptableDefaultItemTypes(const QModelIndex &parent) const
virtual QMimeData * mimeData(const QModelIndexList &indexes) const
virtual void readFrom(QXmlStreamReader *reader, MessageService *messageService=0)
SessionItem * m_root_item
Definition: SessionModel.h:119
QModelIndex indexOfItem(SessionItem *item) const
virtual SessionModel * createCopy(SessionItem *parent=0)
std::string filename(const std::string &path)
Returns path without directory part ("Foo/Bar/Doz.int.gz" -> "Doz.int.gz")
QString getBornAgainVersionString()
Definition: GUIHelpers.cpp:130
SessionItem * CreateEmptyItem()
create empty SessionItem that serves as a root item
Definition: ItemFactory.cpp:39
SessionItem * CreateItem(const QString &model_name, SessionItem *parent=nullptr)
create SessionItem of specific type and parent
Definition: ItemFactory.cpp:30
std::string model_type
Definition: types.h:23
QVariant DecorationRole(const SessionItem &item)
Returns tooltip for given item.
QVariant CheckStateRole(const SessionItem &item)
Returns check state for given item.
QVariant ToolTipRole(const SessionItem &item, int ncol=0)
Returns tooltip for given item.
QVariant ForegroundRole(const SessionItem &item)
Returns text color for given item.
int ParentRow(const SessionItem &item)
Returns the index of the given item within its parent. Returns -1 when no parent is set.
const QString ModelTypeAttribute("ModelType")
void writeItemAndChildItems(QXmlStreamWriter *writer, const SessionItem *item)
Definition: SessionXML.cpp:49
void readItems(QXmlStreamReader *reader, SessionItem *parent, QString topTag="", MessageService *messageService=nullptr)
Definition: SessionXML.cpp:122
void writeTo(QXmlStreamWriter *writer, SessionItem *parent)
Definition: SessionXML.cpp:39
const QString ModelNameAttribute("Name")
const QString ItemTag("Item")
const QString ItemMimeType
Definition: SessionXML.h:26