diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 598b23fd05..06f42da202 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -7,6 +7,9 @@ set(LAUNCHER graphicspage.cpp naturalsort.cpp + pluginsmodel.cpp + pluginsview.cpp + datafilespage.hpp lineedit.hpp maindialog.hpp @@ -14,6 +17,9 @@ set(LAUNCHER combobox.hpp graphicspage.hpp naturalsort.hpp + + pluginsmodel.hpp + pluginsview.hpp ) set(MOC_HDRS @@ -24,6 +30,8 @@ set(MOC_HDRS combobox.hpp graphicspage.hpp + pluginsmodel.hpp + pluginsview.hpp ) find_package(Qt4 REQUIRED) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index d644761697..8fd99c3cc1 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -10,6 +10,8 @@ #include "datafilespage.hpp" #include "lineedit.hpp" #include "naturalsort.hpp" +#include "pluginsmodel.hpp" +#include "pluginsview.hpp" using namespace ESM; using namespace std; @@ -17,7 +19,7 @@ using namespace std; DataFilesPage::DataFilesPage(QWidget *parent) : QWidget(parent) { mDataFilesModel = new QStandardItemModel(); // Contains all plugins with masters - mPluginsModel = new QStandardItemModel(); // Contains selectable plugins + mPluginsModel = new PluginsModel(); // Contains selectable plugins mPluginsProxyModel = new QSortFilterProxyModel(); mPluginsProxyModel->setDynamicSortFilter(true); @@ -34,8 +36,6 @@ DataFilesPage::DataFilesPage(QWidget *parent) : QWidget(parent) topLayout->addWidget(filterLineEdit); mMastersWidget = new QTableWidget(this); // Contains the available masters - mPluginsTable = new QTableView(this); - mMastersWidget->setObjectName("MastersWidget"); mMastersWidget->setSelectionBehavior(QAbstractItemView::SelectRows); mMastersWidget->setSelectionMode(QAbstractItemView::MultiSelection); @@ -46,11 +46,9 @@ DataFilesPage::DataFilesPage(QWidget *parent) : QWidget(parent) mMastersWidget->verticalHeader()->hide(); mMastersWidget->insertColumn(0); + mPluginsTable = new PluginsView(this); mPluginsTable->setModel(mPluginsProxyModel); - mPluginsTable->setSelectionBehavior(QAbstractItemView::SelectRows); - mPluginsTable->setSelectionMode(QAbstractItemView::ExtendedSelection); - mPluginsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); - mPluginsTable->setAlternatingRowColors(true); + mPluginsTable->horizontalHeader()->setStretchLastSection(true); mPluginsTable->horizontalHeader()->hide(); @@ -60,14 +58,6 @@ DataFilesPage::DataFilesPage(QWidget *parent) : QWidget(parent) mPluginsTable->verticalHeader()->setDefaultSectionSize(height); - mPluginsTable->setDragEnabled(true); - mPluginsTable->setDragDropMode(QAbstractItemView::InternalMove); - mPluginsTable->setDropIndicatorShown(true); - mPluginsTable->setDragDropOverwriteMode(false); - mPluginsTable->viewport()->setAcceptDrops(true); - - mPluginsTable->setContextMenuPolicy(Qt::CustomContextMenu); - // Add both tables to a splitter QSplitter *splitter = new QSplitter(this); splitter->setOrientation(Qt::Horizontal); diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index da784f9a9e..f9fb8528e7 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -6,8 +6,6 @@ #include "combobox.hpp" class QTableWidget; -class QTableView; -class ComboBox; class QStandardItemModel; class QItemSelection; class QItemSelectionModel; @@ -17,6 +15,9 @@ class QSettings; class QAction; class QToolBar; class QMenu; +class PluginsModel; +class PluginsView; +class ComboBox; class DataFilesPage : public QWidget { @@ -58,10 +59,10 @@ public slots: private: QTableWidget *mMastersWidget; - QTableView *mPluginsTable; + PluginsView *mPluginsTable; QStandardItemModel *mDataFilesModel; - QStandardItemModel *mPluginsModel; + PluginsModel *mPluginsModel; QSortFilterProxyModel *mPluginsProxyModel; QItemSelectionModel *mPluginsSelectModel; diff --git a/apps/launcher/pluginsmodel.cpp b/apps/launcher/pluginsmodel.cpp new file mode 100644 index 0000000000..24072839e4 --- /dev/null +++ b/apps/launcher/pluginsmodel.cpp @@ -0,0 +1,148 @@ +#include +#include +#include + +#include "pluginsmodel.hpp" + +PluginsModel::PluginsModel(QObject *parent) : QStandardItemModel(parent) +{ + +} + +void decodeDataRecursive(QDataStream &stream, QStandardItem *item) +{ + int colCount, childCount; + stream >> *item; + stream >> colCount >> childCount; + item->setColumnCount(colCount); + + int childPos = childCount; + + while(childPos > 0) { + childPos--; + QStandardItem *child = new QStandardItem(); + decodeDataRecursive(stream, child); + item->setChild( childPos / colCount, childPos % colCount, child); + } +} + +bool PluginsModel::dropMimeData(const QMimeData *data, Qt::DropAction action, + int row, int column, const QModelIndex &parent) +{ + // Code largely based on QStandardItemModel::dropMimeData + + // check if the action is supported + if (!data || !(action == Qt::CopyAction || action == Qt::MoveAction)) + return false; + + // check if the format is supported + QString format = QLatin1String("application/x-qstandarditemmodeldatalist"); + if (!data->hasFormat(format)) + return QAbstractItemModel::dropMimeData(data, action, row, column, parent); + + if (row > rowCount(parent)) + row = rowCount(parent); + if (row == -1) + row = rowCount(parent); + if (column == -1) + column = 0; + + // decode and insert + QByteArray encoded = data->data(format); + QDataStream stream(&encoded, QIODevice::ReadOnly); + + + //code based on QAbstractItemModel::decodeData + // adapted to work with QStandardItem + int top = INT_MAX; + int left = INT_MAX; + int bottom = 0; + int right = 0; + QVector rows, columns; + QVector items; + + while (!stream.atEnd()) { + int r, c; + QStandardItem *item = new QStandardItem(); + stream >> r >> c; + decodeDataRecursive(stream, item); + + rows.append(r); + columns.append(c); + items.append(item); + top = qMin(r, top); + left = qMin(c, left); + bottom = qMax(r, bottom); + right = qMax(c, right); + } + + // insert the dragged items into the table, use a bit array to avoid overwriting items, + // since items from different tables can have the same row and column + int dragRowCount = 0; + int dragColumnCount = right - left + 1; + + // Compute the number of continuous rows upon insertion and modify the rows to match + QVector rowsToInsert(bottom + 1); + for (int i = 0; i < rows.count(); ++i) + rowsToInsert[rows.at(i)] = 1; + for (int i = 0; i < rowsToInsert.count(); ++i) { + if (rowsToInsert[i] == 1){ + rowsToInsert[i] = dragRowCount; + ++dragRowCount; + } + } + for (int i = 0; i < rows.count(); ++i) + rows[i] = top + rowsToInsert[rows[i]]; + + QBitArray isWrittenTo(dragRowCount * dragColumnCount); + + // make space in the table for the dropped data + int colCount = columnCount(parent); + if (colCount < dragColumnCount + column) { + insertColumns(colCount, dragColumnCount + column - colCount, parent); + colCount = columnCount(parent); + } + insertRows(row, dragRowCount, parent); + + row = qMax(0, row); + column = qMax(0, column); + + QStandardItem *parentItem = itemFromIndex (parent); + if (!parentItem) + parentItem = invisibleRootItem(); + + QVector newIndexes(items.size()); + // set the data in the table + for (int j = 0; j < items.size(); ++j) { + int relativeRow = rows.at(j) - top; + int relativeColumn = columns.at(j) - left; + int destinationRow = relativeRow + row; + int destinationColumn = relativeColumn + column; + int flat = (relativeRow * dragColumnCount) + relativeColumn; + // if the item was already written to, or we just can't fit it in the table, create a new row + if (destinationColumn >= colCount || isWrittenTo.testBit(flat)) { + destinationColumn = qBound(column, destinationColumn, colCount - 1); + destinationRow = row + dragRowCount; + insertRows(row + dragRowCount, 1, parent); + flat = (dragRowCount * dragColumnCount) + relativeColumn; + isWrittenTo.resize(++dragRowCount * dragColumnCount); + } + if (!isWrittenTo.testBit(flat)) { + newIndexes[j] = index(destinationRow, destinationColumn, parentItem->index()); + isWrittenTo.setBit(flat); + } + } + + for(int k = 0; k < newIndexes.size(); k++) { + if (newIndexes.at(k).isValid()) { + parentItem->setChild(newIndexes.at(k).row(), newIndexes.at(k).column(), items.at(k)); + } else { + delete items.at(k); + } + } + + // The important part, tell the view what is dropped + emit indexesDropped(newIndexes); + + return true; +} \ No newline at end of file diff --git a/apps/launcher/pluginsmodel.hpp b/apps/launcher/pluginsmodel.hpp new file mode 100644 index 0000000000..41df499b53 --- /dev/null +++ b/apps/launcher/pluginsmodel.hpp @@ -0,0 +1,21 @@ +#ifndef PLUGINSMODEL_H +#define PLUGINSMODEL_H + +#include + +class PluginsModel : public QStandardItemModel +{ + Q_OBJECT + +public: + PluginsModel(QObject *parent = 0); + ~PluginsModel() {}; + + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + +signals: + void indexesDropped(QVector indexes); + +}; + +#endif \ No newline at end of file diff --git a/apps/launcher/pluginsview.cpp b/apps/launcher/pluginsview.cpp new file mode 100644 index 0000000000..5787439acd --- /dev/null +++ b/apps/launcher/pluginsview.cpp @@ -0,0 +1,56 @@ +#include "pluginsview.hpp" + +#include + +#include + +PluginsView::PluginsView(QWidget *parent) : QTableView(parent) +{ + setSelectionBehavior(QAbstractItemView::SelectRows); + setSelectionMode(QAbstractItemView::ExtendedSelection); + setEditTriggers(QAbstractItemView::NoEditTriggers); + setAlternatingRowColors(true); + setDragEnabled(true); + setDragDropMode(QAbstractItemView::InternalMove); + setDropIndicatorShown(true); + setDragDropOverwriteMode(false); + //viewport()->setAcceptDrops(true); + + setContextMenuPolicy(Qt::CustomContextMenu); + + +} + +void PluginsView::startDrag(Qt::DropActions supportedActions) +{ + selectionModel()->select( selectionModel()->selection(), + QItemSelectionModel::Select | QItemSelectionModel::Rows ); + QAbstractItemView::startDrag( supportedActions ); +} + +void PluginsView::setModel(PluginsModel *model) +{ + /*QTableView::setModel(model); + + qRegisterMetaType< QVector >(); + + connect(model, SIGNAL(indexesDropped(QVector)), + this, SLOT(selectIndexes(QVector)), Qt::QueuedConnection);*/ +} + +void PluginsView::setModel(QSortFilterProxyModel *model) +{ + QTableView::setModel(model); + + qRegisterMetaType< QVector >(); + + connect(model->sourceModel(), SIGNAL(indexesDropped(QVector)), + this, SLOT(selectIndexes(QVector)), Qt::QueuedConnection); +} + +void PluginsView::selectIndexes( QVector aIndexes ) +{ + selectionModel()->clearSelection(); + foreach( QPersistentModelIndex pIndex, aIndexes ) + selectionModel()->select( pIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows ); +} diff --git a/apps/launcher/pluginsview.hpp b/apps/launcher/pluginsview.hpp new file mode 100644 index 0000000000..36a6119f34 --- /dev/null +++ b/apps/launcher/pluginsview.hpp @@ -0,0 +1,30 @@ +#ifndef PLUGINSVIEW_H +#define PLUGINSVIEW_H + +#include + +#include "pluginsmodel.hpp" + +class QSortFilterProxyModel; + +class PluginsView : public QTableView +{ + Q_OBJECT +public: + PluginsView(QWidget *parent = 0); + + PluginsModel* model() const + { return qobject_cast(QAbstractItemView::model()); } + + void startDrag(Qt::DropActions supportedActions); + void setModel(PluginsModel *model); + void setModel(QSortFilterProxyModel *model); + +public slots: + void selectIndexes(QVector aIndexes); + +}; + +Q_DECLARE_METATYPE(QVector); + +#endif \ No newline at end of file