From 9e8cf82ee0a287f50059a9b34488f3d6b8887354 Mon Sep 17 00:00:00 2001
From: Walter Van Herck <w.van.herck@fz-juelich.de>
Date: Fri, 21 Feb 2014 13:15:54 +0100
Subject: [PATCH] Changed to QAbstractItemModel and implemented drag and drop
 in QTreeView

---
 GUI/coregui/Models/ItemFactory.cpp            |   9 +-
 GUI/coregui/Models/ItemFactory.h              |   3 +-
 GUI/coregui/Models/LayerItem.cpp              |   4 +-
 GUI/coregui/Models/LayerItem.h                |   2 +-
 GUI/coregui/Models/MultiLayerItem.cpp         |   4 +-
 GUI/coregui/Models/MultiLayerItem.h           |   2 +-
 GUI/coregui/Models/ParameterizedItem.cpp      |  27 +-
 GUI/coregui/Models/ParameterizedItem.h        |  58 ++-
 GUI/coregui/Models/ParticleDecorationItem.cpp |   4 +-
 GUI/coregui/Models/ParticleDecorationItem.h   |   2 +-
 GUI/coregui/Models/SessionModel.cpp           | 344 ++++++++++++++++--
 GUI/coregui/Models/SessionModel.h             |  68 +++-
 GUI/coregui/Views/SampleView.cpp              |  48 ++-
 GUI/coregui/Views/SampleView.h                |   1 +
 GUI/coregui/Views/SampleViewComponents.cpp    |   1 +
 GUI/coregui/utils/GUIHelpers.h                |  15 +
 16 files changed, 508 insertions(+), 84 deletions(-)

diff --git a/GUI/coregui/Models/ItemFactory.cpp b/GUI/coregui/Models/ItemFactory.cpp
index 211fd6c4718..a43f0a3ad00 100644
--- a/GUI/coregui/Models/ItemFactory.cpp
+++ b/GUI/coregui/Models/ItemFactory.cpp
@@ -23,19 +23,20 @@ QList<QString> ItemFactory::m_all_item_names = QList<QString>()
         << QString("Layer")
         << QString("ParticleDecoration");
 
-ParameterizedItem *ItemFactory::createItem(const QString &model_name)
+ParameterizedItem *ItemFactory::createItem(const QString &model_name,
+                                           ParameterizedItem *parent)
 {
     if (!m_all_item_names.contains(model_name)) {
         return 0;
     }
     if (model_name==QString("MultiLayer")) {
-        return new MultiLayerItem();
+        return new MultiLayerItem(parent);
     }
     if (model_name==QString("Layer")) {
-        return new LayerItem();
+        return new LayerItem(parent);
     }
     if (model_name==QString("ParticleDecoration")) {
-        return new ParticleDecorationItem();
+        return new ParticleDecorationItem(parent);
     }
     return 0;
 }
diff --git a/GUI/coregui/Models/ItemFactory.h b/GUI/coregui/Models/ItemFactory.h
index ce4316bbac7..3cf4672fc6c 100644
--- a/GUI/coregui/Models/ItemFactory.h
+++ b/GUI/coregui/Models/ItemFactory.h
@@ -21,7 +21,8 @@
 class ItemFactory
 {
 public:
-    static ParameterizedItem *createItem(const QString &model_name);
+    static ParameterizedItem *createItem(const QString &model_name,
+                                         ParameterizedItem *parent=0);
     static QList<QString> getAllItemNames() {
         return m_all_item_names;
     }
diff --git a/GUI/coregui/Models/LayerItem.cpp b/GUI/coregui/Models/LayerItem.cpp
index 373fe206f04..48f689a924d 100644
--- a/GUI/coregui/Models/LayerItem.cpp
+++ b/GUI/coregui/Models/LayerItem.cpp
@@ -15,8 +15,8 @@
 
 #include "LayerItem.h"
 
-LayerItem::LayerItem()
-    : ParameterizedItem(QString("Layer"))
+LayerItem::LayerItem(ParameterizedItem *parent)
+    : ParameterizedItem(QString("Layer"), parent)
 {
     m_parameters[QString("Thickness")] = 0.0;
     m_valid_children.append(QString("ParticleDecoration"));
diff --git a/GUI/coregui/Models/LayerItem.h b/GUI/coregui/Models/LayerItem.h
index 0d5f67ac864..a384181506c 100644
--- a/GUI/coregui/Models/LayerItem.h
+++ b/GUI/coregui/Models/LayerItem.h
@@ -21,7 +21,7 @@
 class LayerItem : public ParameterizedItem
 {
 public:
-    explicit LayerItem();
+    explicit LayerItem(ParameterizedItem *parent=0);
     ~LayerItem();
 };
 
diff --git a/GUI/coregui/Models/MultiLayerItem.cpp b/GUI/coregui/Models/MultiLayerItem.cpp
index 4963c953a70..99695d96048 100644
--- a/GUI/coregui/Models/MultiLayerItem.cpp
+++ b/GUI/coregui/Models/MultiLayerItem.cpp
@@ -16,8 +16,8 @@
 #include "MultiLayerItem.h"
 
 
-MultiLayerItem::MultiLayerItem()
-    : ParameterizedItem(QString("MultiLayer"))
+MultiLayerItem::MultiLayerItem(ParameterizedItem *parent)
+    : ParameterizedItem(QString("MultiLayer"), parent)
 {
     m_parameters[QString("Cross Correlation Length")] = 0.0;
     m_valid_children.append(QString("Layer"));
diff --git a/GUI/coregui/Models/MultiLayerItem.h b/GUI/coregui/Models/MultiLayerItem.h
index a13e8c137d0..4251d0eeeef 100644
--- a/GUI/coregui/Models/MultiLayerItem.h
+++ b/GUI/coregui/Models/MultiLayerItem.h
@@ -21,7 +21,7 @@
 class MultiLayerItem : public ParameterizedItem
 {
 public:
-    explicit MultiLayerItem();
+    explicit MultiLayerItem(ParameterizedItem *parent=0);
     ~MultiLayerItem();
 };
 
diff --git a/GUI/coregui/Models/ParameterizedItem.cpp b/GUI/coregui/Models/ParameterizedItem.cpp
index fd71ec61d02..44691a3406a 100644
--- a/GUI/coregui/Models/ParameterizedItem.cpp
+++ b/GUI/coregui/Models/ParameterizedItem.cpp
@@ -18,20 +18,34 @@
 
 
 
-ParameterizedItem::ParameterizedItem(const QString &name)
-    : QStandardItem(name)
+ParameterizedItem::ParameterizedItem(const QString &model_type,
+                                     ParameterizedItem *parent)
+    : m_model_type(model_type)
+    , m_parent(parent)
 {
+    if (m_parent) {
+        m_parent->addChildItem(this);
+    }
 }
 
 ParameterizedItem::~ParameterizedItem()
 {
+    qDeleteAll(m_children);
+}
+
+ParameterizedItem *ParameterizedItem::takeChildItem(int row)
+{
+    ParameterizedItem *item = m_children.takeAt(row);
+    item->m_parent = 0;
+    return item;
 }
 
 double ParameterizedItem::getParameterValue(const QString &name) const
 {
     if (!m_parameters.contains(name)) {
-        throw Exceptions::RuntimeErrorException("ParameterizedItem::getParameterValue: "
-                                                "parameter does not exist");
+        throw Exceptions::RuntimeErrorException(
+                    "ParameterizedItem::getParameterValue: "
+                    "parameter does not exist");
     }
     return m_parameters[name];
 }
@@ -39,8 +53,9 @@ double ParameterizedItem::getParameterValue(const QString &name) const
 void ParameterizedItem::setParameter(const QString &name, double value)
 {
     if (!m_parameters.contains(name)) {
-        throw Exceptions::RuntimeErrorException("ParameterizedItem::getParameterValue: "
-                                                "parameter does not exist");
+        throw Exceptions::RuntimeErrorException(
+                    "ParameterizedItem::getParameterValue: "
+                    "parameter does not exist");
     }
     m_parameters[name] = value;
 }
diff --git a/GUI/coregui/Models/ParameterizedItem.h b/GUI/coregui/Models/ParameterizedItem.h
index 563be4172bb..06ad91acab0 100644
--- a/GUI/coregui/Models/ParameterizedItem.h
+++ b/GUI/coregui/Models/ParameterizedItem.h
@@ -20,12 +20,53 @@
 #include <QList>
 #include <QMap>
 
-class ParameterizedItem : public QStandardItem
+class ParameterizedItem
 {
 public:
-    explicit ParameterizedItem(const QString &name);
+    explicit ParameterizedItem(const QString &model_type=QString(),
+                               ParameterizedItem *parent=0);
     ~ParameterizedItem();
 
+    //! retrieves the model type
+    QString modelType() const { return m_model_type; }
+
+    //! retrieve parent item
+    ParameterizedItem *getParent() const { return m_parent; }
+
+    //! retrieve child item in given row
+    ParameterizedItem *getChildAt(int row) const {
+        return m_children.value(row);
+    }
+
+    //! get row number of child
+    int rowOfChild(ParameterizedItem *child) const {
+        return m_children.indexOf(child);
+    }
+
+    //! get number of child items
+    int childItemCount() const { return m_children.count(); }
+
+    //! indicates if item has child items
+    bool hasChildItems() const { return !m_children.isEmpty(); }
+
+    //! returns the a list of child items
+    QList<ParameterizedItem *> childItems() const { return m_children; }
+
+    //! inserts a child item at specified row
+    void insertChildItem(int row, ParameterizedItem *item)
+        { item->m_parent = this; m_children.insert(row, item); }
+
+    //! append child item
+    void addChildItem(ParameterizedItem *item)
+        { item->m_parent = this; m_children << item; }
+
+    //! swap two child items
+    void swapChildItems(int row_1, int row_2)
+        { m_children.swap(row_1, row_2); }
+
+    //! take child item (this removes it from the current item)
+    ParameterizedItem *takeChildItem(int row);
+
     //! retrieves the parameter's value
     double getParameterValue(const QString &name) const;
 
@@ -33,22 +74,23 @@ public:
     void setParameter(const QString &name, double value);
 
     //! retrieves the whole list of paramaters
-    QMap<QString, double> &getParameters() {
-        return m_parameters;
-    }
+    QMap<QString, double> &getParameters() { return m_parameters; }
 
     //! indicates if the passed item can be set as
     //! a child item
     bool acceptsAsChild(const QString &child_name) const;
 
     //! get list of acceptable child object names
-    QList<QString> getAcceptableChildren() const {
-        return m_valid_children;
-    }
+    QList<QString> getAcceptableChildren() const { return m_valid_children; }
+
 protected:
     QList<QString> m_valid_children;
     QMap<QString, double> m_parameters;
+
 private:
+    QString m_model_type;
+    ParameterizedItem *m_parent;
+    QList<ParameterizedItem *> m_children;
 };
 
 
diff --git a/GUI/coregui/Models/ParticleDecorationItem.cpp b/GUI/coregui/Models/ParticleDecorationItem.cpp
index 387d7a6d406..59a3144a770 100644
--- a/GUI/coregui/Models/ParticleDecorationItem.cpp
+++ b/GUI/coregui/Models/ParticleDecorationItem.cpp
@@ -15,8 +15,8 @@
 
 #include "ParticleDecorationItem.h"
 
-ParticleDecorationItem::ParticleDecorationItem()
-    : ParameterizedItem(QString("ParticleDecoration"))
+ParticleDecorationItem::ParticleDecorationItem(ParameterizedItem *parent)
+    : ParameterizedItem(QString("ParticleDecoration"), parent)
 {
 //    m_valid_children.append(QString(""));
 }
diff --git a/GUI/coregui/Models/ParticleDecorationItem.h b/GUI/coregui/Models/ParticleDecorationItem.h
index 2fd010b2b3a..c496ca0e8ca 100644
--- a/GUI/coregui/Models/ParticleDecorationItem.h
+++ b/GUI/coregui/Models/ParticleDecorationItem.h
@@ -21,7 +21,7 @@
 class ParticleDecorationItem : public ParameterizedItem
 {
 public:
-    ParticleDecorationItem();
+    explicit ParticleDecorationItem(ParameterizedItem *parent=0);
     ~ParticleDecorationItem();
 };
 
diff --git a/GUI/coregui/Models/SessionModel.cpp b/GUI/coregui/Models/SessionModel.cpp
index b3b109bc923..98bfbb7d31e 100644
--- a/GUI/coregui/Models/SessionModel.cpp
+++ b/GUI/coregui/Models/SessionModel.cpp
@@ -15,53 +15,353 @@
 
 #include "SessionModel.h"
 #include "ItemFactory.h"
+#include "GUIHelpers.h"
 
+#include <QFile>
+#include <QMimeData>
+
+namespace {
+const int MaxCompression = 9;
+enum Column {
+    ModelType,
+    MaxColumns
+};
+const QString MimeType = "application/org.bornagainproject.xml.item.z";
+}
 
 SessionModel::SessionModel(QObject *parent)
-    : QStandardItemModel(parent)
+    : QAbstractItemModel(parent)
+    , m_root_item(0)
 {
-    initialize();
 }
 
 SessionModel::~SessionModel()
 {
+    delete m_root_item;
 }
 
-QStandardItem *SessionModel::insertNewItem(QString model_type, const QModelIndex &index)
+Qt::ItemFlags SessionModel::flags(const QModelIndex &index) const
 {
-    QStandardItem *parent;
+    Qt::ItemFlags result_flags = QAbstractItemModel::flags(index);
     if (index.isValid()) {
-        parent = itemFromIndex(index);
-        if (!parent) {
-            return 0;
+        result_flags |= Qt::ItemIsSelectable|Qt::ItemIsEnabled
+                     |Qt::ItemIsEditable|Qt::ItemIsDragEnabled
+                     |Qt::ItemIsDropEnabled;
+    }
+    return result_flags;
+}
+
+QVariant SessionModel::data(const QModelIndex &index, int role) const
+{
+    if (!m_root_item || !index.isValid() || index.column() < 0
+            || index.column() >= MaxColumns) {
+        return QVariant();
+    }
+    if (ParameterizedItem *item = itemForIndex(index)) {
+        if (role == Qt::DisplayRole || role == Qt::EditRole) {
+            switch (index.column()) {
+            case ModelType: return item->modelType();
+            default: return QVariant();
+            }
         }
     }
-    else {
-        parent = invisibleRootItem();
+    return QVariant();
+}
+
+QVariant SessionModel::headerData(int section, Qt::Orientation orientation, int role) const
+{
+    if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
+        switch (section) {
+        case ModelType: return tr("Model Type");
+        }
     }
-    return createNewItem(parent, model_type);
+    return QVariant();
 }
 
-void SessionModel::initialize()
+int SessionModel::rowCount(const QModelIndex &parent) const
 {
+    if (parent.isValid() && parent.column() != 0) return 0;
+    ParameterizedItem *parent_item = itemForIndex(parent);
+    return parent_item ? parent_item->childItemCount() : 0;
 }
 
-QStandardItem *SessionModel::createNewItem(QStandardItem *parent, QString model_type)
+int SessionModel::columnCount(const QModelIndex &parent) const
 {
-    // Check if child is allowed to be added to parent
-    if (parent!=invisibleRootItem())
-    {
-        ParameterizedItem *p_par_parent = dynamic_cast<ParameterizedItem *>(parent);
-        if (!p_par_parent || !p_par_parent->acceptsAsChild(model_type)) {
-            return 0;
+    if (parent.isValid() && parent.column() != 0) return 0;
+    return MaxColumns;
+}
+
+QModelIndex SessionModel::index(int row, int column, const QModelIndex &parent) const
+{
+    if (!m_root_item || row < 0 || column < 0 || column >= MaxColumns
+            || (parent.isValid() && parent.column() != 0))
+        return QModelIndex();
+    ParameterizedItem *parent_item = itemForIndex(parent);
+    if (ParameterizedItem *item = parent_item->getChildAt(row)) {
+        return createIndex(row, column, item);
+    }
+    return QModelIndex();
+}
+
+QModelIndex SessionModel::parent(const QModelIndex &child) const
+{
+    if (!child.isValid()) return QModelIndex();
+    if (ParameterizedItem *child_item = itemForIndex(child)) {
+        if (ParameterizedItem *parent_item = child_item->getParent()) {
+            if (parent_item == m_root_item) return QModelIndex();
+            if (ParameterizedItem *grandparent_item = parent_item->getParent()) {
+                int row = grandparent_item->rowOfChild(parent_item);
+                return createIndex(row, 0, parent_item);
+            }
         }
     }
+    return QModelIndex();
+}
 
-    ParameterizedItem *new_item = ItemFactory::createItem(model_type);
-    if (!new_item) {
-        return 0;
+bool SessionModel::setData(const QModelIndex &index,
+                           const QVariant &value, int role)
+{
+    (void)index;
+    (void)value;
+    (void)role;
+    return false;
+}
+
+bool SessionModel::removeRows(int row, int count, const QModelIndex &parent)
+{
+    if (!m_root_item) return false;
+    ParameterizedItem *item = parent.isValid() ? itemForIndex(parent)
+                                               : m_root_item;
+    beginRemoveRows(parent, row, row + count - 1);
+    for (int i=0; i<count; ++i) {
+        delete item->takeChildItem(row);
+    }
+    endRemoveRows();
+    return true;
+}
+
+QStringList SessionModel::mimeTypes() const
+{
+    return QStringList() << MimeType;
+}
+
+QMimeData *SessionModel::mimeData(const QModelIndexList &indices) const
+{
+    if (indices.count() != 1) return 0;
+    if (ParameterizedItem *item = itemForIndex(indices.at(0))) {
+        QMimeData *mime_data = new QMimeData;
+        QByteArray xml_data;
+        QXmlStreamWriter writer(&xml_data);
+        writeItemAndChildItems(&writer, item);
+        mime_data->setData(MimeType, qCompress(xml_data, MaxCompression));
+        return mime_data;
+    }
+    return 0;
+}
+
+bool SessionModel::canDropMimeData(const QMimeData *data, Qt::DropAction action,
+                                   int row, int column,
+                                   const QModelIndex &parent) const
+{
+    (void)row;
+    if (action == Qt::IgnoreAction) return true;
+    if (action != Qt::MoveAction || column > 0 || !data
+            || !data->hasFormat(MimeType)) return false;
+    if (!parent.isValid()) return true;
+    QList<QString> acceptable_child_items = getAcceptableChildItems(parent);
+    QByteArray xml_data = qUncompress(data->data(MimeType));
+    QXmlStreamReader reader(xml_data);
+    while (!reader.atEnd()) {
+        reader.readNext();
+        if (reader.isStartElement()) {
+            if (reader.name() == SessionXML::ItemTag) {
+                const QString model_type = reader.attributes()
+                        .value(SessionXML::ModelTypeAttribute).toString();
+                return acceptable_child_items.contains(model_type);
+            }
+        }
+    }
+    return false;
+}
+
+bool SessionModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
+                                int row, int column, const QModelIndex &parent)
+{
+    if (action == Qt::IgnoreAction) return true;
+    if (action != Qt::MoveAction || column > 0 || !data
+            || !data->hasFormat(MimeType)) return false;
+    if (!canDropMimeData(data, action, row, column, parent)) return false;
+    if (ParameterizedItem *item = itemForIndex(parent)) {
+        QByteArray xml_data = qUncompress(data->data(MimeType));
+        QXmlStreamReader reader(xml_data);
+        if (row == -1) row = item->childItemCount();
+        beginInsertRows(parent, row, row);
+        readItems(&reader, item, row);
+        endInsertRows();
+        return true;
+    }
+    return false;
+}
+
+QModelIndex SessionModel::indexOfItem(ParameterizedItem *item) const
+{
+    if (!item || item == m_root_item) return QModelIndex();
+    ParameterizedItem *parent_item = item->getParent();
+    int row = parent_item->rowOfChild(item);
+    return createIndex(row, 0, item);
+}
+
+ParameterizedItem *SessionModel::insertNewItem(QString model_type,
+                                               const QModelIndex &parent,
+                                               int row)
+{
+    if (!m_root_item) {
+        m_root_item = new ParameterizedItem;
     }
+    ParameterizedItem *parent_item = itemForIndex(parent);
+    if (row==-1) row = parent_item->childItemCount();
+    beginInsertRows(parent, row, row);
+    ParameterizedItem *new_item = insertNewItem(model_type, parent_item, row);
+    endInsertRows();
+    return new_item;
+}
+
+QList<QString> SessionModel::getAcceptableChildItems(
+        const QModelIndex &parent) const
+{
+    QList<QString> result;
+    if (ParameterizedItem *parent_item = itemForIndex(parent)) {
+        result = parent_item->getAcceptableChildren();
+    }
+    return result;
+}
 
-    parent->appendRow(new_item);
+void SessionModel::clear()
+{
+    beginResetModel();
+    delete m_root_item;
+    m_root_item = 0;
+    endResetModel();
+}
+
+void SessionModel::load(const QString &filename)
+{
+    beginResetModel();
+    if (!filename.isEmpty())
+        m_filename = filename;
+    if (m_filename.isEmpty())
+        throw GUIHelpers::Error(tr("no filename specified"));
+    QFile file(m_filename);
+    if (!file.open(QIODevice::ReadOnly))
+        throw GUIHelpers::Error(file.errorString());
+    clear();
+    m_root_item = new ParameterizedItem;
+    QXmlStreamReader reader(&file);
+    readItems(&reader, m_root_item);
+    if (reader.hasError())
+        throw GUIHelpers::Error(reader.errorString());
+    endResetModel();
+}
+
+void SessionModel::save(const QString &filename)
+{
+    if (!filename.isEmpty())
+        m_filename = filename;
+    if (m_filename.isEmpty())
+        throw GUIHelpers::Error(tr("no filename specified"));
+    QFile file(m_filename);
+    if (!file.open(QIODevice::WriteOnly|QIODevice::Text))
+        throw GUIHelpers::Error(file.errorString());
+
+    QXmlStreamWriter writer(&file);
+    writer.setAutoFormatting(true);
+    writer.writeStartDocument();
+    writer.writeStartElement("BornAgain");
+    writer.writeAttribute("Version", "1.9");
+    writeItemAndChildItems(&writer, m_root_item);
+    writer.writeEndElement(); // BornAgain
+    writer.writeEndDocument();
+}
+
+ParameterizedItem *SessionModel::insertNewItem(QString model_type,
+                                               ParameterizedItem *parent,
+                                               int row)
+{
+    if (!m_root_item) {
+        m_root_item = new ParameterizedItem;
+    }
+    if (!parent) parent = m_root_item;
+    if (row == -1) row = parent->childItemCount();
+    if (row < 0 || row > parent->childItemCount()) return 0;
+    if (parent != m_root_item) {
+        if (!parent->acceptsAsChild(model_type))
+            return 0;
+    }
+    ParameterizedItem *new_item = ItemFactory::createItem(model_type);
+    parent->insertChildItem(row, new_item);
     return new_item;
 }
+
+ParameterizedItem *SessionModel::itemForIndex(const QModelIndex &index) const
+{
+    if (index.isValid()) {
+        if (ParameterizedItem *item = static_cast<ParameterizedItem *>(
+                    index.internalPointer()))
+            return item;
+    }
+    return m_root_item;
+}
+
+void SessionModel::readItems(QXmlStreamReader *reader, ParameterizedItem *item,
+                             int row)
+{
+    while (!reader->atEnd()) {
+        reader->readNext();
+        if (reader->isStartElement()) {
+            if (reader->name() == SessionXML::ItemTag) {
+                const QString model_type = reader->attributes()
+                        .value(SessionXML::ModelTypeAttribute).toString();
+                item = insertNewItem(model_type, item, row);
+                row = -1; // all but the first item should be appended
+            }
+            else if (reader->name() == SessionXML::ParameterTag) {
+                const QString parameter_name = reader->attributes()
+                        .value(SessionXML::ParameterNameAttribute)
+                        .toString();
+                double parameter_value = reader->attributes()
+                        .value(SessionXML::ParameterValueAttribute)
+                        .toDouble();
+                item->setParameter(parameter_name, parameter_value);
+            }
+        }
+        else if (reader->isEndElement()) {
+            if (reader->name() == SessionXML::ItemTag) {
+                item = item->getParent();
+            }
+        }
+    }
+}
+
+void SessionModel::writeItemAndChildItems(QXmlStreamWriter *writer, ParameterizedItem *item) const
+{
+    if (item != m_root_item) {
+        writer->writeStartElement(SessionXML::ItemTag);
+        writer->writeAttribute(SessionXML::ModelTypeAttribute, item->modelType());
+        QMapIterator<QString, double> it(item->getParameters());
+        while (it.hasNext()) {
+            it.next();
+            writer->writeStartElement(SessionXML::ParameterTag);
+            writer->writeAttribute(SessionXML::ParameterNameAttribute,
+                                   it.key());
+            writer->writeAttribute(SessionXML::ParameterValueAttribute,
+                                   QString::number(it.value(), 'g', 12) );
+            writer->writeEndElement(); // ParameterTag
+        }
+    }
+    foreach (ParameterizedItem *child_item, item->childItems()) {
+        writeItemAndChildItems(writer, child_item);
+    }
+    if (item != m_root_item) {
+        writer->writeEndElement(); // ItemTag
+    }
+}
diff --git a/GUI/coregui/Models/SessionModel.h b/GUI/coregui/Models/SessionModel.h
index 78d97fcabd4..0d4d2736663 100644
--- a/GUI/coregui/Models/SessionModel.h
+++ b/GUI/coregui/Models/SessionModel.h
@@ -16,31 +16,81 @@
 #ifndef SESSIONMODEL_H
 #define SESSIONMODEL_H
 
-#include <QStandardItemModel>
+#include <QAbstractItemModel>
+#include <QtCore/QXmlStreamReader>
+#include <QtCore/QXmlStreamWriter>
+
 #include "ParameterizedItem.h"
 
-class SessionModel : public QStandardItemModel
+namespace SessionXML {
+const QString ItemTag("Item");
+const QString ModelTypeAttribute("ModelType");
+const QString ParameterTag("Parameter");
+const QString ParameterNameAttribute("Name");
+const QString ParameterValueAttribute("Value");
+}
+
+class SessionModel : public QAbstractItemModel
 {
     Q_OBJECT
+
 public:
     explicit SessionModel(QObject *parent=0);
     ~SessionModel();
 
+    // Begin overriden methods from QAbstractItemModel
+    Qt::ItemFlags flags(const QModelIndex &index) const;
+    QVariant data(const QModelIndex &index, int role) const;
+    QVariant headerData(int section, Qt::Orientation orientation,
+                        int role) const;
+    int rowCount(const QModelIndex &parent) const;
+    int columnCount(const QModelIndex &parent) const;
+    QModelIndex index(int row, int column, const QModelIndex &parent) const;
+    QModelIndex parent(const QModelIndex &child) const;
+
+    bool setHeaderData(int, Qt::Orientation, const QVariant&,
+                       int=Qt::EditRole) { return false; }
+    bool setData(const QModelIndex &index, const QVariant &value, int role);
+    bool removeRows(int row, int count, const QModelIndex &parent);
+
+    Qt::DropActions supportedDragActions() const
+        { return Qt::MoveAction; }
+    Qt::DropActions supportedDropActions() const
+        { return Qt::MoveAction; }
+    QStringList mimeTypes() const;
+    QMimeData *mimeData(const QModelIndexList &indexes) const;
+    bool canDropMimeData(const QMimeData *data, Qt::DropAction action,
+                         int row, int column, const QModelIndex &parent) const;
+    bool dropMimeData(const QMimeData *data, Qt::DropAction action,
+                      int row, int column, const QModelIndex &parent);
+    // End overriden methods from QAbstractItemModel
+
+    QModelIndex indexOfItem(ParameterizedItem *item) const;
+    ParameterizedItem *insertNewItem(QString model_type,
+                                     const QModelIndex &parent=QModelIndex(),
+                                     int row=-1);
     QString filename() const { return m_filename; }
     void setFilename(const QString &filename) {
         m_filename = filename;
     }
-    QStandardItem *insertNewItem(QString model_type,
-                                 const QModelIndex &index);
 
-//    void load(const QString &filename=QString());
-//    void save(const QString &filename=QString());
+    QList<QString> getAcceptableChildItems(const QModelIndex &parent) const;
+
+    void clear();
+    void load(const QString &filename=QString());
+    void save(const QString &filename=QString());
 
 private:
-    void initialize();
-    QStandardItem *createNewItem(QStandardItem *parent,
-                                 QString model_type);
+    ParameterizedItem *insertNewItem(QString model_type,
+                                     ParameterizedItem *parent,
+                                     int row=-1);
+    ParameterizedItem *itemForIndex(const QModelIndex &index) const;
+    void readItems(QXmlStreamReader *reader, ParameterizedItem *item,
+                   int row=-1);
+    void writeItemAndChildItems(QXmlStreamWriter *writer,
+                                ParameterizedItem *item) const;
     QString m_filename;
+    ParameterizedItem *m_root_item;
 };
 
 #endif // SESSIONMODEL_H
diff --git a/GUI/coregui/Views/SampleView.cpp b/GUI/coregui/Views/SampleView.cpp
index 4c216f7c34d..d702b030098 100644
--- a/GUI/coregui/Views/SampleView.cpp
+++ b/GUI/coregui/Views/SampleView.cpp
@@ -82,10 +82,12 @@ void SampleView::initSubWindows()
 
     m_session = new SessionModel();
     // Temporary for testing
-    QStandardItem *multilayer = m_session->insertNewItem("MultiLayer", QModelIndex());
-    QStandardItem *layer = m_session->insertNewItem("Layer", multilayer->index());
-    m_session->insertNewItem("ParticleDecoration", layer->index());
-    m_session->insertNewItem("Layer", QModelIndex());
+    ParameterizedItem *multilayer = m_session->insertNewItem("MultiLayer");
+    ParameterizedItem *layer = m_session->insertNewItem("Layer",
+                   m_session->indexOfItem(multilayer));
+    m_session->insertNewItem("ParticleDecoration",
+                   m_session->indexOfItem(layer));
+    m_session->insertNewItem("Layer");
     // END temporary
     m_tree_view = SampleViewComponents::createTreeView(m_session, this);
     m_subWindows[SampleTreeView] = m_tree_view;
@@ -135,30 +137,21 @@ void SampleView::resetToDefaultLayout()
 void SampleView::addItem(const QString &item_name)
 {
     QModelIndex currentIndex = getTreeView()->currentIndex();
-    getSessionModel()->insertNewItem(item_name, currentIndex);
+    ParameterizedItem * new_item = getSessionModel()->insertNewItem(
+                item_name, currentIndex);
+    if (new_item) setCurrentIndex(getSessionModel()->indexOfItem(new_item));
     setDirty();
     updateUi();
 }
 
-
 void SampleView::deleteItem()
 {
     QModelIndex currentIndex = getTreeView()->currentIndex();
+    if (!currentIndex.isValid()) return;
+    QModelIndex parent_index = getSessionModel()->parent(currentIndex);
+    int row = currentIndex.row();
     if (currentIndex.isValid()) {
-        QStandardItem *item = getSessionModel()->itemFromIndex(currentIndex);
-        QString name = item->text();
-        int rows = item->rowCount();
-        QString message;
-        if (rows == 0)
-            message = tr("<p>Delete '%1'").arg(name);
-        else if (rows == 1)
-            message = tr("<p>Delete '%1' and its child (and "
-                         "grandchildren etc.)").arg(name);
-        else if (rows > 1)
-            message = tr("<p>Delete '%1' and its %2 children (and "
-                         "grandchildren etc.)").arg(name).arg(rows);
-        if (!GUIHelpers::okToDelete(this, tr("Delete"), message)) return;
-        getSessionModel()->removeRow(currentIndex.row(), currentIndex.parent());
+        getSessionModel()->removeRows(row, 1, parent_index);
         setDirty();
         updateUi();
     }
@@ -174,9 +167,7 @@ void SampleView::showContextMenu(const QPoint &pnt)
     if (!parent_index.isValid()) {
         addItemNames = ItemFactory::getAllItemNames();
     } else {
-        QStandardItem *parent = getSessionModel()->itemFromIndex(parent_index);
-        ParameterizedItem *parent_par = static_cast<ParameterizedItem *>(parent);
-        addItemNames = parent_par->getAcceptableChildren();
+        addItemNames = getSessionModel()->getAcceptableChildItems(parent_index);
     }
     if (addItemNames.size() > 0) {
         foreach (QString item_name, addItemNames) {
@@ -219,13 +210,20 @@ void SampleView::connectSignals()
     connect(m_toolBar, SIGNAL(sceneToISample()), m_sampleDesigner, SLOT(sceneToISample()));
 
     // connect context menu for tree view
-    QTreeView *tree_view = static_cast<QTreeView *>(m_subWindows[SampleTreeView]);
-    connect(tree_view, SIGNAL(customContextMenuRequested(const QPoint &)),
+    connect(m_tree_view, SIGNAL(customContextMenuRequested(const QPoint &)),
             this, SLOT(showContextMenu(const QPoint &)));
 
     addToolBar(m_toolBar);
 }
 
+void SampleView::setCurrentIndex(const QModelIndex &index)
+{
+    if (index.isValid()) {
+        m_tree_view->scrollTo(index);
+        m_tree_view->setCurrentIndex(index);
+    }
+}
+
 SessionModel *SampleView::getSessionModel()
 {
     return m_session;
diff --git a/GUI/coregui/Views/SampleView.h b/GUI/coregui/Views/SampleView.h
index ba2c7b2d5d6..f29f6f7147a 100644
--- a/GUI/coregui/Views/SampleView.h
+++ b/GUI/coregui/Views/SampleView.h
@@ -52,6 +52,7 @@ private:
     void createActions();
     void connectSignals();
     void clearSignalMapper();
+    void setCurrentIndex(const QModelIndex &index);
 
     SessionModel *getSessionModel();
     QTreeView *getTreeView();
diff --git a/GUI/coregui/Views/SampleViewComponents.cpp b/GUI/coregui/Views/SampleViewComponents.cpp
index 0c7e0481e1e..12d69f765ea 100644
--- a/GUI/coregui/Views/SampleViewComponents.cpp
+++ b/GUI/coregui/Views/SampleViewComponents.cpp
@@ -47,6 +47,7 @@ QTreeView *SampleViewComponents::createTreeView(SessionModel *session_model, QWi
     tree_view->setWindowTitle(QString("Object Tree View"));
     tree_view->setObjectName(QString("ObjectTree"));
     tree_view->setContextMenuPolicy(Qt::CustomContextMenu);
+    tree_view->setDragDropMode(QAbstractItemView::InternalMove);
     return tree_view;
 }
 
diff --git a/GUI/coregui/utils/GUIHelpers.h b/GUI/coregui/utils/GUIHelpers.h
index 2ed035c90df..4222f44d14c 100644
--- a/GUI/coregui/utils/GUIHelpers.h
+++ b/GUI/coregui/utils/GUIHelpers.h
@@ -19,8 +19,23 @@
 #include <QWidget>
 #include <QString>
 
+#include <exception>
+
 namespace GUIHelpers
 {
+class Error : public std::exception
+{
+public:
+    explicit Error(const QString &message) throw()
+        : message(message.toUtf8()) {}
+    ~Error() throw() {}
+
+    const char *what() const throw() { return message; }
+
+private:
+    const char *message;
+};
+
 void information(QWidget *parent, const QString &title,
         const QString &text, const QString &detailedText=QString());
 void warning(QWidget *parent, const QString &title,
-- 
GitLab