From a6e7cf9a8c795ffebd6c45e72f80b4650f1bbd7b Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sat, 7 Sep 2013 15:57:40 -0500 Subject: [PATCH] Implementing drag and drop --- apps/opencs/view/doc/filedialog.cpp | 2 +- components/CMakeLists.txt | 3 +- components/esxselector/model/contentmodel.cpp | 428 ++++++++++++++++++ components/esxselector/model/contentmodel.hpp | 64 +++ .../esxselector/model/datafilesmodel.cpp | 216 ++++++--- .../esxselector/model/datafilesmodel.hpp | 25 +- components/esxselector/model/esmfile.cpp | 18 +- components/esxselector/model/esmfile.hpp | 5 + .../esxselector/model/masterproxymodel.cpp | 34 +- .../esxselector/model/masterproxymodel.hpp | 6 +- components/esxselector/model/modelitem.cpp | 16 +- components/esxselector/model/modelitem.hpp | 10 +- .../esxselector/model/pluginsproxymodel.cpp | 19 +- .../esxselector/model/pluginsproxymodel.hpp | 6 +- components/esxselector/model/sourcemodel.cpp | 6 + components/esxselector/model/sourcemodel.h | 18 + .../esxselector/view/contentselector.cpp | 58 ++- .../esxselector/view/contentselector.hpp | 3 + files/ui/datafilespage.ui | 63 ++- 19 files changed, 887 insertions(+), 113 deletions(-) create mode 100644 components/esxselector/model/contentmodel.cpp create mode 100644 components/esxselector/model/contentmodel.hpp create mode 100644 components/esxselector/model/sourcemodel.cpp create mode 100644 components/esxselector/model/sourcemodel.h diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 561f7f5d7..fb031fe5f 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -27,7 +27,7 @@ CSVDoc::FileDialog::FileDialog(QWidget *parent) : resize(400, 400); - connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList))); + // connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList))); connect(projectNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateCreateButton(QString))); connect(projectCreateButton, SIGNAL(clicked()), this, SIGNAL(createNewFile())); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 8b07a4e00..0f7c5017b 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -1,5 +1,5 @@ project (Components) - +set (CMAKE_BUILD_TYPE DEBUG) # source files add_component_dir (settings @@ -74,6 +74,7 @@ if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) add_component_qt_dir (esxselector model/masterproxymodel model/modelitem model/datafilesmodel model/pluginsproxymodel model/esmfile model/naturalsort + model/contentmodel view/profilescombobox view/comboboxlineedit view/lineedit view/contentselector ) diff --git a/components/esxselector/model/contentmodel.cpp b/components/esxselector/model/contentmodel.cpp new file mode 100644 index 000000000..673665775 --- /dev/null +++ b/components/esxselector/model/contentmodel.cpp @@ -0,0 +1,428 @@ +#include "contentmodel.hpp" +#include "esmfile.hpp" +#include +#include +#include +#include + +EsxModel::ContentModel::ContentModel(QObject *parent) : + QAbstractTableModel(parent), mEncoding("win1252") +{} + +void EsxModel::ContentModel::setEncoding(const QString &encoding) +{ + mEncoding = encoding; +} + +int EsxModel::ContentModel::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return 1; +} +/* +QModelIndex EsxModel::ContentModel::parent(const QModelIndex &child) const +{ + if(!child.isValid()) + return 0; + + return child.parent(); +} + +QModelIndex EsxModel::ContentModel::index(int row, int column, const QModelIndex &parent) const +{ + +} +*/ +int EsxModel::ContentModel::rowCount(const QModelIndex &parent) const +{ + if(parent.isValid()) + return 0; + + return mFiles.size(); +} + +QVariant EsxModel::ContentModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= mFiles.size()) + return QVariant(); + + EsmFile *file = item(index.row()); + + if (!file) + return QVariant(); + + const int column = index.column(); + + switch (role) + { + case Qt::EditRole: + case Qt::DisplayRole: + { + switch (column) + { + case 0: + return file->fileName(); + case 1: + return file->author(); + case 2: + return QString("%1 kB").arg(int((file->size() + 1023) / 1024)); + case 3: + return file->modified().toString(Qt::ISODate); + case 4: + return file->accessed().toString(Qt::TextDate); + case 5: + return file->version(); + case 6: + return file->path(); + case 7: + return file->masters().join(", "); + case 8: + return file->description(); + } + + return QVariant(); + } + + case Qt::TextAlignmentRole: + { + switch (column) + { + case 0: + case 1: + return Qt::AlignLeft + Qt::AlignVCenter; + case 2: + case 3: + case 4: + case 5: + return Qt::AlignRight + Qt::AlignVCenter; + default: + return Qt::AlignLeft + Qt::AlignVCenter; + } + return QVariant(); + } + + case Qt::ToolTipRole: + { + if (column != 0) + return QVariant(); + + if (file->version() == 0.0f) + return QVariant(); // Data not set + + return QString("Author: %1
\ + Version: %2
\ +
Description:
%3
\ +
Dependencies: %4
") + .arg(file->author()) + .arg(QString::number(file->version())) + .arg(file->description()) + .arg(file->masters().join(", ")); + } + + case Qt::UserRole: + { + if (file->masters().size() == 0) + return "game"; + else + return "addon"; + } + + default: + return QVariant(); + } +} + +Qt::ItemFlags EsxModel::ContentModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + EsmFile *file = item(index.row()); + + if (!file) + return Qt::NoItemFlags; + + Qt::ItemFlags dragDropFlags = Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + Qt::ItemFlags checkFlags = Qt::ItemIsUserCheckable; + Qt::ItemFlags defaultFlags = Qt::ItemIsDropEnabled | Qt::ItemIsSelectable; + + if (canBeChecked(file)) + return Qt::ItemIsEnabled | dragDropFlags | checkFlags | defaultFlags; + else + return defaultFlags; +} + +bool EsxModel::ContentModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.isValid() && role == Qt::EditRole) + { + QString fname = value.value(); + mFiles.replace(index.row(), findItem(fname)); + emit dataChanged(index, index); + return true; + } + + return false; +} + +bool EsxModel::ContentModel::insertRows(int position, int rows, const QModelIndex &parent) +{ + beginInsertRows(parent, position, position+rows-1); + + for (int row = 0; row < rows; ++row) + mFiles.insert(position, new EsmFile); + + endInsertRows(); + return true; +} + +bool EsxModel::ContentModel::removeRows(int position, int rows, const QModelIndex &parent) +{ + beginRemoveRows(parent, position, position+rows-1); + + for (int row = 0; row < rows; ++row) + mFiles.removeAt(position); + + endRemoveRows(); + emit dataChanged(index(0,0,parent), index(rowCount()-1, 0, parent)); + return true; +} + +Qt::DropActions EsxModel::ContentModel::supportedDropActions() const +{ + return Qt::CopyAction | Qt::MoveAction; +} + +QStringList EsxModel::ContentModel::mimeTypes() const +{ + QStringList types; + types << "application/omwcontent"; + return types; +} + +QMimeData *EsxModel::ContentModel::mimeData(const QModelIndexList &indexes) const +{ + QMimeData *mimeData = new QMimeData(); + QByteArray encodedData; + + QDataStream stream(&encodedData, QIODevice::WriteOnly); + + foreach (const QModelIndex &index, indexes) + { + if (index.isValid()) + { + QString text = data(index, Qt::DisplayRole).toString(); + stream << text; + } + } + + mimeData->setData("application/omwcontent", encodedData); + return mimeData; +} + +bool EsxModel::ContentModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) +{ + if (action == Qt::IgnoreAction) + return true; + + if (!data->hasFormat("application/omwcontent")) + return false; + + if (column > 0) + return false; + + int beginRow; + + if (row != -1) + beginRow = row; + else if (parent.isValid()) + beginRow = parent.row(); + else + beginRow = rowCount(); + + QByteArray encodedData = data->data("application/omwcontent"); + QDataStream stream(&encodedData, QIODevice::ReadOnly); + QStringList newItems; + int rows = 0; + + while (!stream.atEnd()) + { + QString text; + stream >> text; + newItems << text; + ++rows; + } + + insertRows(beginRow, rows, QModelIndex()); + + foreach (const QString &text, newItems) + { + QModelIndex idx = index(beginRow, 0, QModelIndex()); + setData(idx, text); + beginRow++; + } + + return true; +} + +void EsxModel::ContentModel::addFile(EsmFile *file) +{ + emit beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); + mFiles.append(file); + emit endInsertRows(); +} + +void EsxModel::ContentModel::addFiles(const QString &path) +{ + QDir dir(path); + QStringList filters; + filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon"; + dir.setNameFilters(filters); + + // Create a decoder for non-latin characters in esx metadata + QTextCodec *codec; + + if (mEncoding == QLatin1String("win1252")) { + codec = QTextCodec::codecForName("windows-1252"); + } else if (mEncoding == QLatin1String("win1251")) { + codec = QTextCodec::codecForName("windows-1251"); + } else if (mEncoding == QLatin1String("win1250")) { + codec = QTextCodec::codecForName("windows-1250"); + } else { + return; // This should never happen; + } + + QTextDecoder *decoder = codec->makeDecoder(); + + foreach (const QString &path, dir.entryList()) { + QFileInfo info(dir.absoluteFilePath(path)); + EsmFile *file = new EsmFile(path); + + try { + ESM::ESMReader fileReader; + ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(mEncoding.toStdString())); + fileReader.setEncoder(&encoder); + fileReader.open(dir.absoluteFilePath(path).toStdString()); + + std::vector mlist = fileReader.getMasters(); + + QStringList masters; + + for (unsigned int i = 0; i < mlist.size(); ++i) { + QString master = QString::fromStdString(mlist[i].name); + masters.append(master); + } + + file->setAuthor(decoder->toUnicode(fileReader.getAuthor().c_str())); + file->setSize(info.size()); + file->setDates(info.lastModified(), info.lastRead()); + file->setVersion(fileReader.getFVer()); + file->setPath(info.absoluteFilePath()); + file->setMasters(masters); + file->setDescription(decoder->toUnicode(fileReader.getDesc().c_str())); + + + // Put the file in the table + if (findItem(path) == 0) + addFile(file); + + } catch(std::runtime_error &e) { + // An error occurred while reading the .esp + qWarning() << "Error reading addon file: " << e.what(); + continue; + } + + } + + delete decoder; +} + +EsxModel::EsmFile* EsxModel::ContentModel::findItem(const QString &name) +{ + for (int i = 0; i < mFiles.size(); ++i) + { + if (name == item(i)->fileName()) + return item(i); + } + + // Not found + return 0; +} + +EsxModel::EsmFile* EsxModel::ContentModel::item(int row) const +{ + if (row >= 0 && row < mFiles.count()) + return mFiles.at(row); + + return 0; +} + +QModelIndex EsxModel::ContentModel::indexFromItem(EsmFile *item) const +{ + if (item) + //return createIndex(mFiles.indexOf(item), 0); + return index(mFiles.indexOf(item),0); + + return QModelIndex(); +} + +Qt::CheckState EsxModel::ContentModel::checkState(const QModelIndex &index) +{ + return mCheckStates[item(index.row())->fileName()]; +} + +void EsxModel::ContentModel::setCheckState(const QModelIndex &index, Qt::CheckState state) +{ + if (!index.isValid()) + return; + + QString name = item(index.row())->fileName(); + mCheckStates[name] = state; + + // Force a redraw of the view since unchecking one item can affect another + QModelIndex firstIndex = indexFromItem(mFiles.first()); + QModelIndex lastIndex = indexFromItem(mFiles.last()); + + emit dataChanged(firstIndex, lastIndex); + //emit checkedItemsChanged(checkedItems()); + +} + +bool EsxModel::ContentModel::canBeChecked(const EsmFile *file) const +{ + //element can be checked if all its dependencies are + foreach (const QString &master, file->masters()) + { + if (!mCheckStates.contains(master) || mCheckStates[master] != Qt::Checked) + return false; + } + return true; +} + +EsxModel::ContentFileList EsxModel::ContentModel::checkedItems() const +{ + ContentFileList list; + + for (int i = 0; i < mFiles.size(); ++i) + { + EsmFile *file = item(i); + + // Only add the items that are in the checked list and available + if (mCheckStates[file->fileName()] == Qt::Checked && canBeChecked(file)) + list << file; + } + + return list; +} + +void EsxModel::ContentModel::uncheckAll() +{ + emit layoutAboutToBeChanged(); + mCheckStates.clear(); + emit layoutChanged(); +} diff --git a/components/esxselector/model/contentmodel.hpp b/components/esxselector/model/contentmodel.hpp new file mode 100644 index 000000000..a585ab63b --- /dev/null +++ b/components/esxselector/model/contentmodel.hpp @@ -0,0 +1,64 @@ +#ifndef CONTENTMODEL_HPP +#define CONTENTMODEL_HPP + +#include + +namespace EsxModel +{ + class EsmFile; + + typedef QList ContentFileList; + + class ContentModel : public QAbstractTableModel + { + Q_OBJECT + public: + explicit ContentModel(QObject *parent = 0); + + void setEncoding(const QString &encoding); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + QVariant data(const QModelIndex &index, int role) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()); + bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()); + + Qt::DropActions supportedDropActions() const; + QStringList mimeTypes() const; + QMimeData *mimeData(const QModelIndexList &indexes) const; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + + void addFiles(const QString &path); + + QModelIndex indexFromItem(EsmFile *item) const; + EsxModel::EsmFile *findItem(const QString &name); + + Qt::CheckState checkState(const QModelIndex &index); + void setCheckState(const QModelIndex &index, Qt::CheckState state); + ContentFileList checkedItems() const; + void uncheckAll(); +/* + QModelIndex index(int row, int column, const QModelIndex &parent) const; + QModelIndex parent(const QModelIndex &child) const; +*/ + private: + + void addFile(EsmFile *file); + EsmFile* item(int row) const; + bool canBeChecked(const EsmFile *file) const; + + ContentFileList mFiles; + QHash mCheckStates; + QString mEncoding; + + signals: + + public slots: + + }; +} +#endif // CONTENTMODEL_HPP diff --git a/components/esxselector/model/datafilesmodel.cpp b/components/esxselector/model/datafilesmodel.cpp index 49d0d6132..ee940bb27 100644 --- a/components/esxselector/model/datafilesmodel.cpp +++ b/components/esxselector/model/datafilesmodel.cpp @@ -48,13 +48,12 @@ void EsxModel::DataFilesModel::setCheckState(const QModelIndex &index, Qt::Check Qt::CheckState EsxModel::DataFilesModel::checkState(const QModelIndex &index) { - EsmFile *file = item(index.row()); - return mCheckStates[file->fileName()]; + return mCheckStates[item(index.row())->fileName()]; } int EsxModel::DataFilesModel::columnCount(const QModelIndex &parent) const { - return parent.isValid() ? 0 : 9; + return parent.isValid() ? 0 : 1; } int EsxModel::DataFilesModel::rowCount(const QModelIndex &parent) const @@ -82,7 +81,7 @@ QVariant EsxModel::DataFilesModel::data(const QModelIndex &index, int role) cons if (!index.isValid()) return QVariant(); - EsmFile *file = item(index.row()); + const EsmFile *file = item(index.row()); if (!file) return QVariant(); @@ -90,6 +89,7 @@ QVariant EsxModel::DataFilesModel::data(const QModelIndex &index, int role) cons const int column = index.column(); switch (role) { + case Qt::EditRole: case Qt::DisplayRole: { switch (column) { @@ -172,25 +172,26 @@ Qt::ItemFlags EsxModel::DataFilesModel::flags(const QModelIndex &index) const if (!index.isValid()) return Qt::NoItemFlags; - EsmFile *file = item(index.row()); + const EsmFile *file = item(index.row()); + + Qt::ItemFlags dragDropFlags = Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + Qt::ItemFlags checkFlags = Qt::ItemIsUserCheckable | Qt::ItemIsSelectable; if (!file) return Qt::NoItemFlags; - if (canBeChecked(file)) { - if (index.column() == 0) { - return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; - } else { - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; - } - } else { - if (index.column() == 0) { - return Qt::ItemIsUserCheckable | Qt::ItemIsSelectable; - } else { - return Qt::ItemIsSelectable; - } + if (canBeChecked(file)) + { + if (index.column() == 0) + return dragDropFlags | checkFlags | Qt::ItemIsEnabled; + else + return Qt::ItemIsDropEnabled | Qt::ItemIsEnabled | Qt::ItemIsSelectable; } + if (index.column() == 0) + return dragDropFlags | checkFlags; + + return Qt::ItemIsDropEnabled | Qt::ItemIsSelectable; } QVariant EsxModel::DataFilesModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -210,22 +211,27 @@ QVariant EsxModel::DataFilesModel::headerData(int section, Qt::Orientation orien case 7: return tr("Masters"); case 8: return tr("Description"); } - } /* else { - // Show row numbers - return ++section; } -*/ return QVariant(); } bool EsxModel::DataFilesModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) - return false; + return false; + + if (role == Qt::EditRole) + { + qDebug() << "replacing: " << mFiles.at(index.row())->fileName(); +// mFiles.replace(index.row(), value.value()); + qDebug() << "with: " << mFiles.at(index.row())->fileName(); + emit dataChanged(index, index); + return true; + } return false; } - +//!!!!!!!!!!!!!!!!!!!!!!! bool lessThanEsmFile(const EsxModel::EsmFile *e1, const EsxModel::EsmFile *e2) { //Masters first then alphabetically @@ -236,16 +242,15 @@ bool lessThanEsmFile(const EsxModel::EsmFile *e1, const EsxModel::EsmFile *e2) return e1->fileName().toLower() < e2->fileName().toLower(); } - +//!!!!!!!!!!!!!!!!!!!!!!! bool lessThanDate(const EsxModel::EsmFile *e1, const EsxModel::EsmFile *e2) { - if (e1->modified().toString(Qt::ISODate) < e2->modified().toString(Qt::ISODate)) { + if (e1->modified().toString(Qt::ISODate) < e2->modified().toString(Qt::ISODate)) return true; - } else { + else return false; - } } - +//!!!!!!!!!!!!!!!!!!!!!!! void EsxModel::DataFilesModel::sort(int column, Qt::SortOrder order) { emit layoutAboutToBeChanged(); @@ -259,7 +264,7 @@ void EsxModel::DataFilesModel::sort(int column, Qt::SortOrder order) emit layoutChanged(); } -void EsxModel::DataFilesModel::addFile(EsmFile *file) +void EsxModel::DataFilesModel::addFile(const EsmFile *file) { emit beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); mFiles.append(file); @@ -331,22 +336,23 @@ void EsxModel::DataFilesModel::addFiles(const QString &path) delete decoder; } -QModelIndex EsxModel::DataFilesModel::indexFromItem(EsmFile *item) const +QModelIndex EsxModel::DataFilesModel::indexFromItem(const EsmFile *item) const { if (item) - return createIndex(mFiles.indexOf(item), 0); + //return createIndex(mFiles.indexOf(item), 0); + return index(mFiles.indexOf(item),0); return QModelIndex(); } -EsxModel::EsmFile* EsxModel::DataFilesModel::findItem(const QString &name) +const EsxModel::EsmFile* EsxModel::DataFilesModel::findItem(const QString &name) { - QList::ConstIterator it; - QList::ConstIterator itEnd = mFiles.constEnd(); + EsmFileList::ConstIterator it; + EsmFileList::ConstIterator itEnd = mFiles.constEnd(); int i = 0; for (it = mFiles.constBegin(); it != itEnd; ++it) { - EsmFile *file = item(i); + const EsmFile *file = item(i); ++i; if (name == file->fileName()) @@ -357,12 +363,12 @@ EsxModel::EsmFile* EsxModel::DataFilesModel::findItem(const QString &name) return 0; } -EsxModel::EsmFile* EsxModel::DataFilesModel::item(int row) const +const EsxModel::EsmFile* EsxModel::DataFilesModel::item(int row) const { if (row >= 0 && row < mFiles.count()) return mFiles.at(row); - else - return 0; + + return 0; } EsxModel::EsmFileList EsxModel::DataFilesModel::checkedItems() @@ -372,14 +378,11 @@ EsxModel::EsmFileList EsxModel::DataFilesModel::checkedItems() EsmFileList::ConstIterator it; EsmFileList::ConstIterator itEnd = mFiles.constEnd(); - int i = 0; for (it = mFiles.constBegin(); it != itEnd; ++it) { - EsmFile *file = *it; - // Only add the items that are in the checked list and available - if (mCheckStates[file->fileName()] == Qt::Checked && canBeChecked(file)) - list << file; + if (mCheckStates[(*it)->fileName()] == Qt::Checked && canBeChecked(*it)) + list << (*it); } return list; @@ -389,12 +392,12 @@ QStringList EsxModel::DataFilesModel::checkedItemsPaths() { QStringList list; - QList::ConstIterator it; - QList::ConstIterator itEnd = mFiles.constEnd(); + EsmFileList::ConstIterator it; + EsmFileList::ConstIterator itEnd = mFiles.constEnd(); int i = 0; for (it = mFiles.constBegin(); it != itEnd; ++it) { - EsmFile *file = item(i); + const EsmFile *file = item(i); ++i; if (mCheckStates[file->fileName()] == Qt::Checked && canBeChecked(file)) @@ -403,7 +406,6 @@ QStringList EsxModel::DataFilesModel::checkedItemsPaths() return list; } - void EsxModel::DataFilesModel::uncheckAll() { emit layoutAboutToBeChanged(); @@ -411,18 +413,17 @@ void EsxModel::DataFilesModel::uncheckAll() emit layoutChanged(); } +/* EsxModel::EsmFileList EsxModel::DataFilesModel::uncheckedItems() { EsmFileList list; EsmFileList checked = checkedItems(); EsmFileList::ConstIterator it; - EsmFileList::ConstIterator itEnd = mFiles.constEnd(); - int i = 0; - for (it = mFiles.constBegin(); it != itEnd; ++it) { - EsmFile *file = item(i); - ++i; + for (it = mFiles.constBegin(); it != mFiles.constEnd(); ++it) + { + const EsmFile *file = *it; // Add the items that are not in the checked list if (!checked.contains(file)) @@ -431,8 +432,8 @@ EsxModel::EsmFileList EsxModel::DataFilesModel::uncheckedItems() return list; } - -bool EsxModel::DataFilesModel::canBeChecked(EsmFile *file) const +*/ +bool EsxModel::DataFilesModel::canBeChecked(const EsmFile *file) const { //element can be checked if all its dependencies are foreach (const QString &master, file->masters()) @@ -442,3 +443,110 @@ bool EsxModel::DataFilesModel::canBeChecked(EsmFile *file) const } return true; } + +Qt::DropActions EsxModel::DataFilesModel::supportedDropActions() const +{ + return Qt::CopyAction | Qt::MoveAction; +} + +QStringList EsxModel::DataFilesModel::mimeTypes() const +{ + QStringList types; + types << "application/omwcontent"; + return types; +} + +QMimeData *EsxModel::DataFilesModel::mimeData(const QModelIndexList &indexes) const +{ +// if (indexes.at(0).isValid()) +// return new EsmFile(*item(indexes.at(0).row())); + + return 0; +} + +bool EsxModel::DataFilesModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) +{ + if (action == Qt::IgnoreAction) + return true; + + if (action != Qt::MoveAction) + return false; + + if (!data->hasFormat("application/omwcontent")) + return false; + + int dropRow = row; + + if (dropRow == -1) + { + if (parent.isValid()) + dropRow = parent.row(); + else + dropRow = rowCount(QModelIndex()); + } + + if (parent.isValid()) + qDebug() << "parent: " << parent.data().toString(); + qDebug() << "dragged file: " << (qobject_cast(data))->fileName(); +// qDebug() << "inserting file: " << droppedfile->fileName() << " ahead of " << file->fileName(); + insertRows (dropRow, 1, QModelIndex()); + + + const EsmFile *draggedFile = qobject_cast(data); + + int dragRow = -1; + + for (int i = 0; i < mFiles.size(); ++i) + if (draggedFile->fileName() == mFiles.at(i)->fileName()) + { + dragRow = i; + break; + } + + for (int i = 0; i < mFiles.count(); ++i) + { + qDebug() << "index: " << i << "file: " << item(i)->fileName(); + qDebug() << mFiles.at(i)->fileName(); + } + + qDebug() << "drop row: " << dropRow << "; drag row: " << dragRow; +// const EsmFile *file = qobject_cast(data); + // int index = mFiles.indexOf(file); + //qDebug() << "file name: " << file->fileName() << "; index: " << index; + mFiles.swap(dropRow, dragRow); + //setData(index(startRow, 0), varFile); + emit dataChanged(index(0,0), index(rowCount(),0)); + return true; +} + +bool EsxModel::DataFilesModel::insertRows(int row, int count, const QModelIndex &parent) +{ + qDebug() << "inserting row: " << row << " count: " << count; + beginInsertRows(QModelIndex(),row, row+count-1); + + EsmFile *file = new EsmFile(); + + for (int i = 0; i < count; ++i) + mFiles.insert(row + i, file); + + endInsertRows(); + return true; +} + +bool EsxModel::DataFilesModel::removeRows(int row, int count, const QModelIndex &parent) +{ + qDebug() << "removing row: " << row << " count: " << count; + beginRemoveRows(QModelIndex(), row, row+count-1); + + for (int i = 0; i < count; ++i) + { + mFiles.removeAt(i); + } + + endRemoveRows(); + qDebug() <<"remove success"; + + emit dataChanged(parent, index(rowCount()-1, 0, parent)); + return true; +} + diff --git a/components/esxselector/model/datafilesmodel.hpp b/components/esxselector/model/datafilesmodel.hpp index 24b36aa88..4f23cd530 100644 --- a/components/esxselector/model/datafilesmodel.hpp +++ b/components/esxselector/model/datafilesmodel.hpp @@ -10,7 +10,7 @@ namespace EsxModel { class EsmFile; - typedef QList EsmFileList; + typedef QList EsmFileList; class DataFilesModel : public QAbstractTableModel { @@ -21,6 +21,8 @@ namespace EsxModel virtual ~DataFilesModel(); virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + bool removeRows(int row, int count, const QModelIndex &parent); + bool insertRows(int row, int count, const QModelIndex &parent); bool moveRow(int oldrow, int row, const QModelIndex &parent = QModelIndex()); @@ -33,7 +35,10 @@ namespace EsxModel void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); inline QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const - { return QAbstractTableModel::index(row, column, parent); } + { + QModelIndex idx = QAbstractTableModel::index(row, 0, parent); + return idx; + } void setEncoding(const QString &encoding); @@ -41,6 +46,11 @@ namespace EsxModel void uncheckAll(); + Qt::DropActions supportedDropActions() const; + QStringList mimeTypes() const; + QMimeData *mimeData(const QModelIndexList &indexes) const; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + EsmFileList checkedItems(); EsmFileList uncheckedItems(); QStringList checkedItemsPaths(); @@ -48,16 +58,17 @@ namespace EsxModel Qt::CheckState checkState(const QModelIndex &index); void setCheckState(const QModelIndex &index, Qt::CheckState state); - QModelIndex indexFromItem(EsmFile *item) const; - EsmFile* findItem(const QString &name); - EsmFile* item(int row) const; + QModelIndex indexFromItem(const EsmFile *item) const; + const EsmFile* findItem(const QString &name); + const EsmFile* item(int row) const; signals: void checkedItemsChanged(const EsmFileList &items); private: - bool canBeChecked(EsmFile *file) const; - void addFile(EsmFile *file); + + bool canBeChecked(const EsmFile *file) const; + void addFile(const EsmFile *file); EsmFileList mFiles; QHash mCheckStates; diff --git a/components/esxselector/model/esmfile.cpp b/components/esxselector/model/esmfile.cpp index 96b90e44e..95cf70312 100644 --- a/components/esxselector/model/esmfile.cpp +++ b/components/esxselector/model/esmfile.cpp @@ -1,13 +1,17 @@ #include "esmfile.hpp" EsxModel::EsmFile::EsmFile(QString fileName, ModelItem *parent) - : ModelItem(parent) -{ - mFileName = fileName; - mSize = 0; - mVersion = 0.0f; -} - + : ModelItem(parent), mFileName(fileName), mSize(0), mVersion(0.0f) +{} +/* +EsxModel::EsmFile::EsmFile(const EsmFile &file) + : ModelItem(file.parent()), mFileName(file.mFileName), mSize(file.mSize), + mVersion(file.mVersion), mAuthor(file.mAuthor), mModified(file.mModified), + mAccessed(file.mAccessed), mPath(file.mPath), mMasters(file.mMasters), + mDescription(file.mDescription) +{} + +*/ void EsxModel::EsmFile::setFileName(const QString &fileName) { mFileName = fileName; diff --git a/components/esxselector/model/esmfile.hpp b/components/esxselector/model/esmfile.hpp index 6a3e36b53..0cda018b3 100644 --- a/components/esxselector/model/esmfile.hpp +++ b/components/esxselector/model/esmfile.hpp @@ -14,7 +14,9 @@ namespace EsxModel Q_PROPERTY(QString filename READ fileName) public: + EsmFile(QString fileName = QString(), ModelItem *parent = 0); + // EsmFile(const EsmFile &); ~EsmFile() {} @@ -38,6 +40,7 @@ namespace EsxModel inline QStringList masters() const { return mMasters; } inline QString description() const { return mDescription; } + //inline ModelItem *parent() const { return ModelItem::parent(); this->} private: QString mFileName; @@ -53,4 +56,6 @@ namespace EsxModel }; } +Q_DECLARE_METATYPE (EsxModel::EsmFile *) + #endif diff --git a/components/esxselector/model/masterproxymodel.cpp b/components/esxselector/model/masterproxymodel.cpp index 011e5ebd5..46d68ca51 100644 --- a/components/esxselector/model/masterproxymodel.cpp +++ b/components/esxselector/model/masterproxymodel.cpp @@ -1,4 +1,6 @@ #include "masterproxymodel.hpp" +#include +#include EsxModel::MasterProxyModel::MasterProxyModel(QObject *parent, QAbstractTableModel* model) : QSortFilterProxyModel(parent) @@ -7,10 +9,36 @@ EsxModel::MasterProxyModel::MasterProxyModel(QObject *parent, QAbstractTableMode setFilterRole (Qt::UserRole); if (model) - setSourceModel (model); + setSourceModel (model); + //connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(slotSourceModelChanged(QModelIndex, QModelIndex))); } - +/* QVariant EsxModel::MasterProxyModel::data(const QModelIndex &index, int role) const { - return QSortFilterProxyModel::data (index, role); + if (index.isValid()) + return QSortFilterProxyModel::data (index, role); + + return 0; +} +*/ +void EsxModel::MasterProxyModel::slotSourceModelChanged(QModelIndex topLeft, QModelIndex botRight) +{ + qDebug() << "source data changed.. updating master proxy"; + emit dataChanged(index(0,0), index(rowCount()-1,0)); + + int curRow = -1; +/* + for (int i = 0; i < rowCount() - 1; ++i) + { + if (index(i,0).data(Qt::CheckState) == Qt::Checked) + { + curRow = i; + break; + } + } + + reset(); +*/ + if (curRow != -1); + // index(curRow, 0).setDataQt::CheckState) } diff --git a/components/esxselector/model/masterproxymodel.hpp b/components/esxselector/model/masterproxymodel.hpp index fed01bdb1..8a5c73032 100644 --- a/components/esxselector/model/masterproxymodel.hpp +++ b/components/esxselector/model/masterproxymodel.hpp @@ -2,6 +2,8 @@ #define MASTERPROXYMODEL_HPP #include +#include +#include class QAbstractTableModel; @@ -12,12 +14,12 @@ namespace EsxModel Q_OBJECT public: explicit MasterProxyModel(QObject *parent = 0, QAbstractTableModel *model = 0); - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + // virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; signals: public slots: - + void slotSourceModelChanged(QModelIndex topLeft, QModelIndex botRight); }; } #endif // MASTERPROXYMODEL_HPP diff --git a/components/esxselector/model/modelitem.cpp b/components/esxselector/model/modelitem.cpp index 8c1e83695..03b19f691 100644 --- a/components/esxselector/model/modelitem.cpp +++ b/components/esxselector/model/modelitem.cpp @@ -2,9 +2,14 @@ EsxModel::ModelItem::ModelItem(ModelItem *parent) : mParentItem(parent) - , QObject(parent) { } +/* +EsxModel::ModelItem::ModelItem(const ModelItem *parent) + // : mParentItem(parent) +{ +} +*/ EsxModel::ModelItem::~ModelItem() { @@ -12,11 +17,18 @@ EsxModel::ModelItem::~ModelItem() } -EsxModel::ModelItem *EsxModel::ModelItem::parent() +EsxModel::ModelItem *EsxModel::ModelItem::parent() const { return mParentItem; } +bool EsxModel::ModelItem::hasFormat(const QString &mimetype) const +{ + if (mimetype == "application/omwcontent") + return true; + + return QMimeData::hasFormat(mimetype); +} int EsxModel::ModelItem::row() const { if (mParentItem) diff --git a/components/esxselector/model/modelitem.hpp b/components/esxselector/model/modelitem.hpp index 64596302c..5ee5e417e 100644 --- a/components/esxselector/model/modelitem.hpp +++ b/components/esxselector/model/modelitem.hpp @@ -1,20 +1,22 @@ #ifndef MODELITEM_HPP #define MODELITEM_HPP -#include +#include #include namespace EsxModel { - class ModelItem : public QObject + class ModelItem : public QMimeData { Q_OBJECT public: ModelItem(ModelItem *parent = 0); + //ModelItem(const ModelItem *parent = 0); + ~ModelItem(); - ModelItem *parent(); + ModelItem *parent() const; int row() const; int childCount() const; @@ -24,6 +26,8 @@ namespace EsxModel void appendChild(ModelItem *child); void removeChild(int row); + bool hasFormat(const QString &mimetype) const; + //virtual bool acceptChild(ModelItem *child); protected: diff --git a/components/esxselector/model/pluginsproxymodel.cpp b/components/esxselector/model/pluginsproxymodel.cpp index 4fde11f47..412367b64 100644 --- a/components/esxselector/model/pluginsproxymodel.cpp +++ b/components/esxselector/model/pluginsproxymodel.cpp @@ -1,7 +1,8 @@ #include "pluginsproxymodel.hpp" -#include "datafilesmodel.hpp" +#include "contentmodel.hpp" +#include -EsxModel::PluginsProxyModel::PluginsProxyModel(QObject *parent, DataFilesModel *model) : +EsxModel::PluginsProxyModel::PluginsProxyModel(QObject *parent, ContentModel *model) : QSortFilterProxyModel(parent) { setFilterRegExp (QString("addon")); @@ -22,12 +23,18 @@ QVariant EsxModel::PluginsProxyModel::data(const QModelIndex &index, int role) c { case Qt::CheckStateRole: { - if (index.column() != 0) + if (index.column() != 0) return QVariant(); - return static_cast(sourceModel())->checkState(mapToSource(index)); + return static_cast(sourceModel())->checkState(mapToSource(index)); } - }; + } + return QSortFilterProxyModel::data(index, role); +} + +bool EsxModel::PluginsProxyModel::removeRows(int position, int rows, const QModelIndex &parent) +{ + bool success = QSortFilterProxyModel::removeRows(position, rows, parent); - return QSortFilterProxyModel::data (index, role); + return success; } diff --git a/components/esxselector/model/pluginsproxymodel.hpp b/components/esxselector/model/pluginsproxymodel.hpp index 04c18c2e2..4415df716 100644 --- a/components/esxselector/model/pluginsproxymodel.hpp +++ b/components/esxselector/model/pluginsproxymodel.hpp @@ -8,7 +8,7 @@ class QAbstractTableModel; namespace EsxModel { - class DataFilesModel; + class ContentModel; class PluginsProxyModel : public QSortFilterProxyModel { @@ -16,10 +16,12 @@ namespace EsxModel public: - explicit PluginsProxyModel(QObject *parent = 0, DataFilesModel *model = 0); + explicit PluginsProxyModel(QObject *parent = 0, ContentModel *model = 0); ~PluginsProxyModel(); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + bool removeRows(int row, int count, const QModelIndex &parent); }; } diff --git a/components/esxselector/model/sourcemodel.cpp b/components/esxselector/model/sourcemodel.cpp new file mode 100644 index 000000000..7b54adba9 --- /dev/null +++ b/components/esxselector/model/sourcemodel.cpp @@ -0,0 +1,6 @@ +#include "sourcemodel.h" + +SourceModel::SourceModel(QObject *parent) : + QAbstractTableClass(parent) +{ +} diff --git a/components/esxselector/model/sourcemodel.h b/components/esxselector/model/sourcemodel.h new file mode 100644 index 000000000..cf5145675 --- /dev/null +++ b/components/esxselector/model/sourcemodel.h @@ -0,0 +1,18 @@ +#ifndef SOURCEMODEL_H +#define SOURCEMODEL_H + +#include + +class SourceModel : public QAbstractTableClass +{ + Q_OBJECT +public: + explicit SourceModel(QObject *parent = 0); + +signals: + +public slots: + +}; + +#endif // SOURCEMODEL_H diff --git a/components/esxselector/view/contentselector.cpp b/components/esxselector/view/contentselector.cpp index 6cba643d2..bc7cc2b8b 100644 --- a/components/esxselector/view/contentselector.cpp +++ b/components/esxselector/view/contentselector.cpp @@ -3,6 +3,8 @@ #include "../model/datafilesmodel.hpp" #include "../model/masterproxymodel.hpp" #include "../model/pluginsproxymodel.hpp" +#include "../model/contentmodel.hpp" +#include "../model/esmfile.hpp" #include @@ -14,7 +16,33 @@ EsxView::ContentSelector::ContentSelector(QWidget *parent) : QDialog(parent) { setupUi(this); - buildModelsAndViews(); + // buildModelsAndViews(); + buildDragDropModelView(); +} +void EsxView::ContentSelector::buildDragDropModelView() +{ + mContentModel = new EsxModel::ContentModel(); + + //mContentModel->addFiles("/home/joel/Projects/OpenMW/Data_Files"); + mMasterProxyModel = new EsxModel::MasterProxyModel(this, mContentModel); + mPluginsProxyModel = new EsxModel::PluginsProxyModel(this, mContentModel); + + tableView->setModel (mPluginsProxyModel); + + masterView->setPlaceholderText(QString("Select a game file...")); + masterView->setModel(mMasterProxyModel); + pluginView->setModel(mPluginsProxyModel); + + profilesComboBox->setPlaceholderText(QString("Select a profile...")); + + updateViews(); + connect(pluginView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotPluginTableItemClicked(const QModelIndex &))); + connect(masterView, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentMasterIndexChanged(int))); + connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); + + + connect(mContentModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); + connect(tableView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotPluginTableItemClicked(const QModelIndex &))); } void EsxView::ContentSelector::buildModelsAndViews() @@ -22,15 +50,15 @@ void EsxView::ContentSelector::buildModelsAndViews() // Models mDataFilesModel = new EsxModel::DataFilesModel (this); - mMasterProxyModel = new EsxModel::MasterProxyModel (this, mDataFilesModel); - mPluginsProxyModel = new EsxModel::PluginsProxyModel (this, mDataFilesModel); + // mMasterProxyModel = new EsxModel::MasterProxyModel (this, mDataFilesModel); + // mPluginsProxyModel = new EsxModel::PluginsProxyModel (this, mDataFilesModel); masterView->setPlaceholderText(QString("Select a game file...")); masterView->setModel(mMasterProxyModel); pluginView->setModel(mPluginsProxyModel); profilesComboBox->setPlaceholderText(QString("Select a profile...")); - + updateViews(); connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); connect(pluginView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotPluginTableItemClicked(const QModelIndex &))); connect(masterView, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentMasterIndexChanged(int))); @@ -39,15 +67,15 @@ void EsxView::ContentSelector::buildModelsAndViews() void EsxView::ContentSelector::addFiles(const QString &path) { - mDataFilesModel->addFiles(path); - mDataFilesModel->sort(3); // Sort by date accessed + mContentModel->addFiles(path); + mContentModel->sort(3); // Sort by date accessed masterView->setCurrentIndex(-1); - mDataFilesModel->uncheckAll(); + mContentModel->uncheckAll(); } void EsxView::ContentSelector::setEncoding(const QString &encoding) { - mDataFilesModel->setEncoding(encoding); + mContentModel->setEncoding(encoding); } void EsxView::ContentSelector::setCheckState(QModelIndex index, QSortFilterProxyModel *model) @@ -62,15 +90,20 @@ void EsxView::ContentSelector::setCheckState(QModelIndex index, QSortFilterProxy if (sourceIndex.isValid()) { - (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) - ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) - : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); + (mContentModel->checkState(sourceIndex) == Qt::Checked) + ? mContentModel->setCheckState(sourceIndex, Qt::Unchecked) + : mContentModel->setCheckState(sourceIndex, Qt::Checked); } } QStringList EsxView::ContentSelector::checkedItemsPaths() { - return mDataFilesModel->checkedItemsPaths(); + QStringList itemPaths; + + foreach( const EsxModel::EsmFile *file, mContentModel->checkedItems()) + itemPaths << file->path(); + + return itemPaths; } void EsxView::ContentSelector::updateViews() @@ -106,5 +139,6 @@ void EsxView::ContentSelector::slotCurrentMasterIndexChanged(int index) void EsxView::ContentSelector::slotPluginTableItemClicked(const QModelIndex &index) { + qDebug() << "setting checkstate in plugin..."; setCheckState(index, mPluginsProxyModel); } diff --git a/components/esxselector/view/contentselector.hpp b/components/esxselector/view/contentselector.hpp index 658e0176c..06cc8f3f0 100644 --- a/components/esxselector/view/contentselector.hpp +++ b/components/esxselector/view/contentselector.hpp @@ -7,6 +7,7 @@ namespace EsxModel { + class ContentModel; class DataFilesModel; class PluginsProxyModel; class MasterProxyModel; @@ -23,6 +24,7 @@ namespace EsxView protected: EsxModel::DataFilesModel *mDataFilesModel; + EsxModel::ContentModel *mContentModel; EsxModel::MasterProxyModel *mMasterProxyModel; EsxModel::PluginsProxyModel *mPluginsProxyModel; @@ -37,6 +39,7 @@ namespace EsxView void setCheckState(QModelIndex index, QSortFilterProxyModel *model); QStringList checkedItemsPaths(); void on_checkAction_triggered(); + void buildDragDropModelView(); signals: void profileChanged(int index); diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 60e8b8bf5..76689627b 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -7,28 +7,19 @@ 0 0 518 - 304 + 310 Qt::DefaultContextMenu - + Content - - - 3 - - - 6 - - - 6 - + @@ -41,7 +32,7 @@ - + @@ -56,6 +47,18 @@ QAbstractItemView::NoEditTriggers + + true + + + false + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + true @@ -82,6 +85,40 @@ + + + + QAbstractItemView::NoEditTriggers + + + true + + + false + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + +