BornAgain  1.19.79
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/Model/Model/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 
22 #include "GUI/Util/Error.h"
23 #include "GUI/Util/Path.h"
24 #include <QColor>
25 #include <QFile>
26 #include <QMimeData>
27 #include <QXmlStreamWriter>
28 #include <utility>
29 
30 namespace {
31 
32 const int MaxCompression = 9;
33 
34 //! Returns tooltip for given item.
35 QVariant toolTipRole(const SessionItem& item, int ncol)
36 {
37  QString result = item.toolTip();
38  if (result.isEmpty()) {
39  result = item.displayName();
40  if (ncol == 1 && item.value().canConvert<QString>())
41  result = item.value().toString();
42  }
43 
44  return QVariant(result);
45 }
46 
47 } // namespace
48 
49 SessionModel::SessionModel(QString model_tag, QObject* parent)
50  : QAbstractItemModel(parent)
51  , m_model_tag(std::move(model_tag))
52 {
54 }
55 
57 {
58  m_root_item = new SessionItem("ROOT_ITEM");
59  m_root_item->setModel(this);
60  m_root_item->registerTag("rootTag");
62 }
63 
65 {
66  delete m_root_item;
67 }
68 
69 // TODO make it relying on SessionItem::flags
70 
71 Qt::ItemFlags SessionModel::flags(const QModelIndex& index) const
72 {
73  Qt::ItemFlags result_flags = QAbstractItemModel::flags(index);
74  if (index.isValid()) {
75  result_flags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled;
77  if (index.column() == SessionFlags::ITEM_VALUE && item->value().isValid()
78  && item->isEditable() && item->isEnabled())
79  result_flags |= Qt::ItemIsEditable;
80  QVector<QString> acceptable_child_items = acceptableDefaultItemTypes(index);
81  if (acceptable_child_items.contains(m_dragged_item_type))
82  result_flags |= Qt::ItemIsDropEnabled;
83  } else
84  result_flags |= Qt::ItemIsDropEnabled;
85  return result_flags;
86 }
87 
88 QVariant SessionModel::data(const QModelIndex& index, int role) const
89 {
90  ASSERT(m_root_item);
91  if (!index.isValid() || index.column() < 0 || index.column() >= columnCount(QModelIndex()))
92  return {};
93  if (SessionItem* item = itemForIndex(index)) {
94  if (role == Qt::DisplayRole || role == Qt::EditRole) {
95  if (index.column() == SessionFlags::ITEM_VALUE)
96  return item->value();
97  if (index.column() == SessionFlags::ITEM_NAME)
98  return item->itemName();
99  } else if (role == Qt::ToolTipRole)
100  return toolTipRole(*item, index.column());
101  else if (role == Qt::ForegroundRole)
102  return item->isEnabled() ? QVariant() : QColor(Qt::gray);
103  else if (role == Qt::CheckStateRole && index.column() == SessionFlags::ITEM_VALUE) {
104  if (item->value().type() == QVariant::Bool)
105  return item->value().toBool() ? Qt::Checked : Qt::Unchecked;
106  } else
107  return item->roleProperty(role);
108  }
109  return {};
110 }
111 
112 QVariant SessionModel::headerData(int section, Qt::Orientation orientation, int role) const
113 {
114  if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
115  return {};
116  switch (section) {
118  return "Name";
120  return "Value";
121  default:
122  return {};
123  }
124 }
125 
126 int SessionModel::rowCount(const QModelIndex& parent) const
127 {
128  if (parent.isValid() && parent.column() != 0)
129  return 0;
130  SessionItem* parent_item = itemForIndex(parent);
131  return parent_item ? parent_item->numberOfChildren() : 0;
132 }
133 
134 int SessionModel::columnCount(const QModelIndex& parent) const
135 {
136  if (parent.isValid() && parent.column() != 0)
137  return 0;
139 }
140 
141 QModelIndex SessionModel::index(int row, int column, const QModelIndex& parent) const
142 {
143  ASSERT(m_root_item);
144  if (row < 0 || column < 0 || column >= columnCount(QModelIndex())
145  || (parent.isValid() && parent.column() != 0))
146  return QModelIndex();
147 
148  SessionItem* parent_item = itemForIndex(parent);
149 
150  if (SessionItem* item = parent_item->childAt(row))
151  return createIndex(row, column, item);
152  return QModelIndex();
153 }
154 
155 QModelIndex SessionModel::parent(const QModelIndex& child) const
156 {
157  if (!child.isValid())
158  return QModelIndex();
159 
160  if (SessionItem* child_item = itemForIndex(child)) {
161  if (SessionItem* parent_item = child_item->parentItem()) {
162  if (parent_item == m_root_item)
163  return QModelIndex();
164  return createIndex(parent_item->parentRow(), 0, parent_item);
165  }
166  }
167  return QModelIndex();
168 }
169 
170 bool SessionModel::setData(const QModelIndex& index, const QVariant& value, int role)
171 {
172  if (!index.isValid())
173  return false;
174 
175  QModelIndex dataIndex = index;
176  if (SessionItem* item = itemForIndex(dataIndex))
177  if (item->setRoleProperty(role, value))
178  return true;
179 
180  return false;
181 }
182 
183 bool SessionModel::removeRows(int row, int count, const QModelIndex& parent)
184 {
185  ASSERT(m_root_item);
186  SessionItem* item = parent.isValid() ? itemForIndex(parent) : m_root_item;
187  for (int i = 0; i < count; ++i)
188  delete item->takeRow(row);
189  return true;
190 }
191 
192 QStringList SessionModel::mimeTypes() const
193 {
194  return QStringList() << GUI::Session::XML::ItemMimeType;
195 }
196 
197 QMimeData* SessionModel::mimeData(const QModelIndexList& indices) const
198 {
199  if (indices.count() != 2)
200  return nullptr;
201 
202  if (SessionItem* item = itemForIndex(indices.at(0))) {
203  auto* mime_data = new QMimeData;
204  QByteArray xml_data;
205  QXmlStreamWriter writer(&xml_data);
207  mime_data->setData(GUI::Session::XML::ItemMimeType, qCompress(xml_data, MaxCompression));
208  return mime_data;
209  }
210  return nullptr;
211 }
212 
213 bool SessionModel::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row,
214  int column, const QModelIndex& parent) const
215 {
216  Q_UNUSED(row);
217 
218  if (action == Qt::IgnoreAction)
219  return true;
220  if (action != Qt::MoveAction || column > 0 || !data
221  || !data->hasFormat(GUI::Session::XML::ItemMimeType))
222  return false;
223  if (!parent.isValid())
224  return true;
225  QVector<QString> acceptable_child_items = acceptableDefaultItemTypes(parent);
226  QByteArray xml_data = qUncompress(data->data(GUI::Session::XML::ItemMimeType));
227  QXmlStreamReader reader(xml_data);
228  while (!reader.atEnd()) {
229  reader.readNext();
230  if (reader.isStartElement()) {
231  if (reader.name() == GUI::Session::XML::ItemTag) {
232  const QString model_type =
233  reader.attributes().value(GUI::Session::XML::ModelTypeAttribute).toString();
234  return acceptable_child_items.contains(model_type);
235  }
236  }
237  }
238  return false;
239 }
240 
241 bool SessionModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column,
242  const QModelIndex& parent)
243 {
244  if (action == Qt::IgnoreAction)
245  return true;
246  if (action != Qt::MoveAction || column > 0 || !data
247  || !data->hasFormat(GUI::Session::XML::ItemMimeType))
248  return false;
249  if (!canDropMimeData(data, action, row, column, parent))
250  return false;
251  if (SessionItem* item = itemForIndex(parent)) {
252  QByteArray xml_data = qUncompress(data->data(GUI::Session::XML::ItemMimeType));
253  QXmlStreamReader reader(xml_data);
254  if (row == -1)
255  row = item->numberOfChildren();
256  beginInsertRows(parent, row, row);
257  // this code block is currently not in use. The row parameter of the reader is removed
258  // SessionReader::readItems(&reader, item, row);
259  endInsertRows();
260  return true;
261  }
262  return false;
263 }
264 
265 QModelIndex SessionModel::indexOfItem(SessionItem* item) const
266 {
267  if (!item || item == m_root_item || !item->parentItem())
268  return QModelIndex();
269  SessionItem* parent_item = item->parentItem();
270  int row = parent_item->rowOfChild(item);
271  return createIndex(row, 0, item);
272 }
273 
274 SessionItem* SessionModel::insertNewItem(QString model_type, SessionItem* parent_item, int row,
275  QString tag)
276 {
277  ASSERT(m_root_item);
278  if (!parent_item)
279  parent_item = m_root_item;
280  if (row > parent_item->numberOfChildren())
281  return nullptr;
282  if (parent_item != m_root_item) {
283  if (tag.isEmpty())
284  tag = parent_item->defaultTag();
285  if (!parent_item->sessionItemTags()->isValid(tag, model_type))
286  return nullptr;
287  }
288 
289  SessionItem* new_item = GUI::Model::ItemFactory::CreateItem(model_type);
290  ASSERT(new_item);
291 
292  parent_item->insertChild(row, new_item, tag);
293 
294  return new_item;
295 }
296 
297 SessionItem* SessionModel::insertNewItem(QString model_type, const QModelIndex& parent_item,
298  int row, QString tag)
299 {
300  return insertNewItem(model_type, itemForIndex(parent_item), row, tag);
301 }
302 
304 {
305  QModelIndex index = indexOfItem(item);
306  if (index.isValid())
307  removeRows(index.row(), 1, index.parent());
308 }
309 
310 QVector<QString> SessionModel::acceptableDefaultItemTypes(const QModelIndex& parent) const
311 {
312  QVector<QString> result;
313  if (SessionItem* parent_item = itemForIndex(parent))
314  result = parent_item->acceptableDefaultItemTypes();
315 
316  return result;
317 }
318 
320 {
321  beginResetModel();
322  delete m_root_item;
323  createRootItem();
324  endResetModel();
325 }
326 
327 SessionItem* SessionModel::itemForIndex(const QModelIndex& index) const
328 {
329  if (index.isValid())
330  if (auto* item = static_cast<SessionItem*>(index.internalPointer()))
331  return item;
332 
333  return m_root_item;
334 }
335 
336 void SessionModel::readFrom(QXmlStreamReader* reader, MessageService* messageService)
337 {
338  ASSERT(reader);
339 
340  if (reader->name() != m_model_tag)
341  throw Error("SessionModel::readFrom() -> Format error in p1");
342 
343  beginResetModel();
344  clear();
345 
346  GUI::Session::XML::readItems(reader, m_root_item, QString(), messageService);
347  if (reader->hasError())
348  throw Error(reader->errorString());
349  endResetModel();
350 }
351 
352 void SessionModel::writeTo(QXmlStreamWriter* writer)
353 {
355 }
356 
357 //! Move given parameterized item to the new_parent at given row. If new_parent is not defined,
358 //! use root_item as a new parent.
359 
361  const QString& tag)
362 {
363  if (!new_parent)
364  new_parent = m_root_item;
365 
366  const QString tagName = tag.isEmpty() ? new_parent->defaultTag() : tag;
367 
368  if (!new_parent->sessionItemTags()->isValid(tagName, item->modelType()))
369  return nullptr;
370 
371  if (item->parentItem() == new_parent) {
372  // take care of indexes when moving item within same parent
373  int previousIndex = item->parentItem()->getItems(tagName).indexOf(item);
374  if (row == previousIndex)
375  return item;
376  if (previousIndex >= 0 && row > previousIndex)
377  row--;
378  }
379 
380  if (new_parent->sessionItemTags()->maximumReached(tagName)) {
381  SessionItem* prev = new_parent->takeItem(0, tagName);
382  m_root_item->insertChild(-1, prev);
383  }
384 
385  // removing item from previous parent and inserting to the new one
386  item->parentItem()->takeRow(item->parentItem()->rowOfChild(item));
387  new_parent->insertChild(row, item, tagName);
388 
389  return item;
390 }
391 
392 //! Copy given item to the new_parent at given row. Item intended for copying can belong to
393 //! another model and it will remain intact. Returns pointer to the new child.
394 
395 SessionItem* SessionModel::copy(const SessionItem* item_to_copy, SessionItem* new_parent,
396  const QString& tag)
397 {
398  if (!new_parent)
399  new_parent = m_root_item;
400 
401  const QString tagName = tag.isEmpty() ? new_parent->defaultTag() : tag;
402 
403  QByteArray xml_data;
404  QXmlStreamWriter writer(&xml_data);
405  GUI::Session::XML::writeItemAndChildItems(&writer, item_to_copy);
406 
407  QXmlStreamReader reader(xml_data);
408  GUI::Session::XML::readItems(&reader, new_parent, tagName);
409 
410  return new_parent->getItems(tagName).back();
411 }
412 
414 {
415  QByteArray byte_array;
416  QXmlStreamWriter writer(&byte_array);
417  writer.setAutoFormatting(true);
418 
419  model->writeTo(&writer);
420 
421  QXmlStreamReader reader(byte_array);
422 
423  while (!reader.atEnd()) {
424  reader.readNext();
425  if (reader.isStartElement())
426  readFrom(&reader);
427  }
428 }
429 
431 {
432  ASSERT(m_root_item);
433  return m_root_item;
434 }
435 
436 QVector<SessionItem*> SessionModel::nonXMLItems() const
437 {
438  return QVector<SessionItem*>();
439 }
Defines error class.
Defines namespace GUI::Model::ItemFactory.
Defines class MaterialItem.
Defines class Helpers functions.
Defines class SessionFlags.
Defines class SessionItemTags.
Defines class SessionItem.
Defines class SessionModel.
Defines reader and writer classes for 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. If modelType is not empty,...
bool maximumReached(const QString &tagName) const
Base class for a GUI data item.
Definition: SessionItem.h:204
QString itemName() const
Get item name, return display name if no name is set.
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. max = -1 -> unlimited,...
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:88
void insertChild(int row, SessionItem *item, const QString &tag="")
Insert item into given tag into given row.
QString displayName() const
Get display name of item, append index if ambigue.
QVariant value() const
Get value.
bool isEnabled() const
int rowOfChild(SessionItem *child) const
Returns row index of given child.
const SessionItemTags * sessionItemTags() const
bool isEditable() const
QString defaultTag() const
Get default tag.
void setDefaultTag(const QString &tag)
Set default tag.
QString toolTip() const
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 * parentItem() const
Returns parent of this item.
Definition: SessionItem.cpp:67
QVariant roleProperty(int role) const
Returns corresponding variant under given role, invalid variant when role is not present.
SessionItem * childAt(int row) const
Returns the child at the given row.
QString modelType() const
Get model type.
bool setRoleProperty(int role, const QVariant &value)
Set the contained role property to the given value. See also setTranslatorForRolePropertySetter.
void setModel(SessionModel *model)
Base class for a GUI data collection. A collection is e.g. all real data (RealDataModel)....
Definition: SessionModel.h:42
QVariant data(const QModelIndex &index, int role) const override
QModelIndex parent(const QModelIndex &child) const override
virtual void clear()
bool setData(const QModelIndex &index, const QVariant &value, int role) override
SessionItem * moveItem(SessionItem *item, SessionItem *new_parent=nullptr, int row=-1, const QString &tag="")
Move given parameterized item to the new_parent at given row. If new_parent is not defined,...
SessionModel(QString model_tag, QObject *parent=nullptr)
QMimeData * mimeData(const QModelIndexList &indices) const override
SessionItem * itemForIndex(const QModelIndex &index) const
Qt::ItemFlags flags(const QModelIndex &index) const override
virtual QVector< SessionItem * > nonXMLItems() const
QString m_dragged_item_type
Definition: SessionModel.h:132
QModelIndex index(int row, int column, const QModelIndex &parent) const override
QStringList mimeTypes() const override
int columnCount(const QModelIndex &parent) const override
friend class SessionItem
Definition: SessionModel.h:44
virtual void readFrom(QXmlStreamReader *reader, MessageService *messageService=nullptr)
void createRootItem()
void removeItem(SessionItem *item)
virtual void writeTo(QXmlStreamWriter *writer)
SessionItem * insertNewItem(QString model_type, SessionItem *parent_item=nullptr, int row=-1, QString tag="")
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override
virtual void initFrom(SessionModel *model, SessionItem *parent)
QString m_model_tag
Definition: SessionModel.h:133
SessionItem * rootItem() const
QVector< QString > acceptableDefaultItemTypes(const QModelIndex &parent) const
bool removeRows(int row, int count, const QModelIndex &parent) override
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override
QVariant headerData(int section, Qt::Orientation orientation, int role) const override
int rowCount(const QModelIndex &parent) const override
SessionItem * m_root_item
Definition: SessionModel.h:131
QModelIndex indexOfItem(SessionItem *item) const
SessionItem * copy(const SessionItem *item_to_copy, SessionItem *new_parent=nullptr, const QString &tag="")
Copy given item to the new_parent at given row. Item intended for copying can belong to another model...
~SessionModel() override
SessionItem * CreateItem(const QString &model_name, SessionItem *parent=nullptr)
create SessionItem of specific type and parent
Definition: ItemFactory.cpp:19
void writeItemAndChildItems(QXmlStreamWriter *writer, const SessionItem *item)
Definition: SessionXML.cpp:70
void readItems(QXmlStreamReader *reader, SessionItem *parent, QString topTag="", MessageService *messageService=nullptr)
Definition: SessionXML.cpp:113
constexpr auto ItemMimeType
Definition: SessionXML.h:30
void writeModel(QXmlStreamWriter *writer, SessionItem *modelRootItem)
Definition: SessionXML.cpp:59
constexpr auto ModelTypeAttribute("ModelType")
constexpr auto ItemTag("Item")