From 50d0358c0cb71da757a6bbb067c102cc52b2b49d Mon Sep 17 00:00:00 2001
From: Walter Van Herck <w.van.herck@fz-juelich.de>
Date: Mon, 31 Mar 2014 10:47:47 +0200
Subject: [PATCH] Smarter drag and drop in treeview (GUI indicates permitted
 drop operations)

---
 GUI/coregui/Models/SessionModel.cpp           | 24 +++++----
 GUI/coregui/Models/SessionModel.h             |  8 +++
 .../SampleDesigner/ItemTreeView.cpp           | 51 +++++++++++++++++++
 .../Components/SampleDesigner/ItemTreeView.h  | 20 ++++++++
 GUI/coregui/Views/SampleViewComponents.cpp    |  4 +-
 GUI/coregui/Views/SampleViewComponents.h      |  4 +-
 GUI/coregui/utils/GUIHelpers.h                |  2 +-
 7 files changed, 99 insertions(+), 14 deletions(-)
 create mode 100644 GUI/coregui/Views/Components/SampleDesigner/ItemTreeView.cpp
 create mode 100644 GUI/coregui/Views/Components/SampleDesigner/ItemTreeView.h

diff --git a/GUI/coregui/Models/SessionModel.cpp b/GUI/coregui/Models/SessionModel.cpp
index bf99e2b03dd..e7594f63917 100644
--- a/GUI/coregui/Models/SessionModel.cpp
+++ b/GUI/coregui/Models/SessionModel.cpp
@@ -27,7 +27,6 @@ enum Column {
     ModelType,
     MaxColumns
 };
-const QString MimeType = "application/org.bornagainproject.xml.item.z";
 }
 
 SessionModel::SessionModel(QObject *parent)
@@ -46,8 +45,14 @@ Qt::ItemFlags SessionModel::flags(const QModelIndex &index) const
     Qt::ItemFlags result_flags = QAbstractItemModel::flags(index);
     if (index.isValid()) {
         result_flags |= Qt::ItemIsSelectable|Qt::ItemIsEnabled
-                     |Qt::ItemIsEditable|Qt::ItemIsDragEnabled
-                     |Qt::ItemIsDropEnabled;
+                     |Qt::ItemIsEditable|Qt::ItemIsDragEnabled;
+        QList<QString> acceptable_child_items = getAcceptableChildItems(index);
+        if (acceptable_child_items.contains(m_dragged_item_type)) {
+            result_flags |= Qt::ItemIsDropEnabled;
+        }
+    }
+    else {
+        result_flags |= Qt::ItemIsDropEnabled;
     }
     return result_flags;
 }
@@ -154,7 +159,7 @@ bool SessionModel::removeRows(int row, int count, const QModelIndex &parent)
 
 QStringList SessionModel::mimeTypes() const
 {
-    return QStringList() << MimeType;
+    return QStringList() << SessionXML::MimeType;
 }
 
 QMimeData *SessionModel::mimeData(const QModelIndexList &indices) const
@@ -165,7 +170,8 @@ QMimeData *SessionModel::mimeData(const QModelIndexList &indices) const
         QByteArray xml_data;
         QXmlStreamWriter writer(&xml_data);
         writeItemAndChildItems(&writer, item);
-        mime_data->setData(MimeType, qCompress(xml_data, MaxCompression));
+        mime_data->setData(SessionXML::MimeType,
+                           qCompress(xml_data, MaxCompression));
         return mime_data;
     }
     return 0;
@@ -178,10 +184,10 @@ bool SessionModel::canDropMimeData(const QMimeData *data, Qt::DropAction action,
     (void)row;
     if (action == Qt::IgnoreAction) return true;
     if (action != Qt::MoveAction || column > 0 || !data
-            || !data->hasFormat(MimeType)) return false;
+            || !data->hasFormat(SessionXML::MimeType)) return false;
     if (!parent.isValid()) return true;
     QList<QString> acceptable_child_items = getAcceptableChildItems(parent);
-    QByteArray xml_data = qUncompress(data->data(MimeType));
+    QByteArray xml_data = qUncompress(data->data(SessionXML::MimeType));
     QXmlStreamReader reader(xml_data);
     while (!reader.atEnd()) {
         reader.readNext();
@@ -201,10 +207,10 @@ bool SessionModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
 {
     if (action == Qt::IgnoreAction) return true;
     if (action != Qt::MoveAction || column > 0 || !data
-            || !data->hasFormat(MimeType)) return false;
+            || !data->hasFormat(SessionXML::MimeType)) return false;
     if (!canDropMimeData(data, action, row, column, parent)) return false;
     if (ParameterizedItem *item = itemForIndex(parent)) {
-        QByteArray xml_data = qUncompress(data->data(MimeType));
+        QByteArray xml_data = qUncompress(data->data(SessionXML::MimeType));
         QXmlStreamReader reader(xml_data);
         if (row == -1) row = item->childItemCount();
         beginInsertRows(parent, row, row);
diff --git a/GUI/coregui/Models/SessionModel.h b/GUI/coregui/Models/SessionModel.h
index c9fa1d7d6d2..408fb39d252 100644
--- a/GUI/coregui/Models/SessionModel.h
+++ b/GUI/coregui/Models/SessionModel.h
@@ -23,6 +23,8 @@
 #include "ParameterizedItem.h"
 
 namespace SessionXML {
+const QString MimeType = "application/org.bornagainproject.xml.item.z";
+
 const QString ItemTag("Item");
 const QString ModelTypeAttribute("ModelType");
 const QString ItemNameAttribute("ItemName");
@@ -82,6 +84,11 @@ public:
     void load(const QString &filename=QString());
     void save(const QString &filename=QString());
 
+    // Sets mimedata pointer of item being dragged
+    void setDraggedItemType(const QString& type) {
+        m_dragged_item_type = type;
+    }
+
 private:
     ParameterizedItem *insertNewItem(QString model_type,
                                      ParameterizedItem *parent,
@@ -96,6 +103,7 @@ private:
                        const char *property_name) const;
     QString m_filename;
     ParameterizedItem *m_root_item;
+    QString m_dragged_item_type;
 };
 
 #endif // SESSIONMODEL_H
diff --git a/GUI/coregui/Views/Components/SampleDesigner/ItemTreeView.cpp b/GUI/coregui/Views/Components/SampleDesigner/ItemTreeView.cpp
new file mode 100644
index 00000000000..ddec416d054
--- /dev/null
+++ b/GUI/coregui/Views/Components/SampleDesigner/ItemTreeView.cpp
@@ -0,0 +1,51 @@
+// ************************************************************************** //
+//
+//  BornAgain: simulate and fit scattering at grazing incidence
+//
+//! @file      Views/SampleDesigner/ItemTreeView.cpp
+//! @brief     Implements class ItemTreeView.
+//!
+//! @homepage  http://apps.jcns.fz-juelich.de/BornAgain
+//! @license   GNU General Public License v3 or higher (see COPYING)
+//! @copyright Forschungszentrum Jülich GmbH 2013
+//! @authors   Scientific Computing Group at MLZ Garching
+//! @authors   C. Durniak, G. Pospelov, W. Van Herck, J. Wuttke
+//
+// ************************************************************************** //
+
+#include "ItemTreeView.h"
+#include "SessionModel.h"
+#include "GUIHelpers.h"
+
+#include <QDragMoveEvent>
+#include <QMimeData>
+
+ItemTreeView::ItemTreeView(QWidget *parent)
+    : QTreeView(parent)
+{
+}
+
+ItemTreeView::~ItemTreeView()
+{
+}
+
+void ItemTreeView::dragMoveEvent(QDragMoveEvent *event)
+{
+    QTreeView::dragMoveEvent(event);
+    SessionModel *model = static_cast<SessionModel *>(this->model());
+    model->setDraggedItemType(QString());
+    QByteArray xml_data = qUncompress(
+                event->mimeData()->data(SessionXML::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();
+                model->setDraggedItemType(model_type);
+                break;
+            }
+        }
+    }
+}
diff --git a/GUI/coregui/Views/Components/SampleDesigner/ItemTreeView.h b/GUI/coregui/Views/Components/SampleDesigner/ItemTreeView.h
new file mode 100644
index 00000000000..216b50aaddd
--- /dev/null
+++ b/GUI/coregui/Views/Components/SampleDesigner/ItemTreeView.h
@@ -0,0 +1,20 @@
+#ifndef ITEMTREEVIEW_H
+#define ITEMTREEVIEW_H
+
+#include <QTreeView>
+
+class ItemTreeView : public QTreeView
+{
+    Q_OBJECT
+public:
+    explicit ItemTreeView(QWidget *parent=0);
+    ~ItemTreeView();
+
+protected:
+#ifndef QT_NO_DRAGANDDROP
+    void dragMoveEvent(QDragMoveEvent *event);
+#endif
+};
+
+
+#endif // ITEMTREEVIEW_H
diff --git a/GUI/coregui/Views/SampleViewComponents.cpp b/GUI/coregui/Views/SampleViewComponents.cpp
index 9bc098aba65..dfbbf816e2d 100644
--- a/GUI/coregui/Views/SampleViewComponents.cpp
+++ b/GUI/coregui/Views/SampleViewComponents.cpp
@@ -41,10 +41,10 @@ SamplePropertyEditor *SampleViewComponents::createPropertyEditor(
     return new SamplePropertyEditor(selection_model, parent);
 }
 
-QTreeView *SampleViewComponents::createTreeView(
+ItemTreeView *SampleViewComponents::createTreeView(
         SessionModel *session_model, QWidget *parent)
 {
-    QTreeView *tree_view = new QTreeView(parent);
+    ItemTreeView *tree_view = new ItemTreeView(parent);
     tree_view->setModel(session_model);
     tree_view->setAllColumnsShowFocus(true);
     tree_view->setWindowTitle(QString("Object Tree View"));
diff --git a/GUI/coregui/Views/SampleViewComponents.h b/GUI/coregui/Views/SampleViewComponents.h
index b9c34a37304..dfaf98a5913 100644
--- a/GUI/coregui/Views/SampleViewComponents.h
+++ b/GUI/coregui/Views/SampleViewComponents.h
@@ -2,7 +2,7 @@
 #define SAMPLEVIEWCOMPONENTS_H
 
 #include <QWidget>
-#include <QTreeView>
+#include "ItemTreeView.h"
 #include "widgetbox.h"
 
 #include "SamplePropertyEditor.h"
@@ -45,7 +45,7 @@ public:
             SampleDesignerInterface *core, QWidget *parent);
     static SamplePropertyEditor *createPropertyEditor(
             QItemSelectionModel *selection_model, QWidget *parent);
-    static QTreeView *createTreeView(
+    static ItemTreeView *createTreeView(
             SessionModel *session_model, QWidget *parent);
     static SampleInfoStreamInterface *createInfoStream(QWidget *parent);
 };
diff --git a/GUI/coregui/utils/GUIHelpers.h b/GUI/coregui/utils/GUIHelpers.h
index 4222f44d14c..af9d116013e 100644
--- a/GUI/coregui/utils/GUIHelpers.h
+++ b/GUI/coregui/utils/GUIHelpers.h
@@ -46,7 +46,7 @@ bool question(QWidget *parent, const QString &title,
         const QString &noText=QObject::tr("&No"));
 bool okToDelete(QWidget *parent, const QString &title,
         const QString &text, const QString &detailedText=QString());
-};
+}
 
 #endif // GUIHELPERS_H
 
-- 
GitLab