From bfb71f23c9f2e1c3b9f6525ef1ed8b1185425c6d Mon Sep 17 00:00:00 2001 From: graffy76 Date: Fri, 16 Aug 2013 17:26:23 -0500 Subject: [PATCH 001/148] Changed filter mechanism for game / addons Filters by number of master references, regardless of extension --- apps/opencs/view/doc/filedialog.cpp | 8 ++++---- components/fileorderlist/model/datafilesmodel.cpp | 9 +++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index f956317a71..02421a7883 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -25,13 +25,13 @@ FileDialog::FileDialog(QWidget *parent) : mDataFilesModel = new DataFilesModel(this); mMastersProxyModel = new QSortFilterProxyModel(); - mMastersProxyModel->setFilterRegExp(QString("^.*\\.esm")); - mMastersProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + mMastersProxyModel->setFilterRegExp("game"); //QString("^.*\\.esm")); + mMastersProxyModel->setFilterRole (Qt::UserRole); mMastersProxyModel->setSourceModel(mDataFilesModel); mPluginsProxyModel = new PluginsProxyModel(); - mPluginsProxyModel->setFilterRegExp(QString("^.*\\.esp")); - mPluginsProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + mPluginsProxyModel->setFilterRegExp("addon"); //QString("^.*\\.esp")); + mPluginsProxyModel->setFilterRole (Qt::UserRole); mPluginsProxyModel->setSourceModel(mDataFilesModel); mFilterProxyModel = new QSortFilterProxyModel(); diff --git a/components/fileorderlist/model/datafilesmodel.cpp b/components/fileorderlist/model/datafilesmodel.cpp index 02a6766b02..cf1fa1b0a5 100644 --- a/components/fileorderlist/model/datafilesmodel.cpp +++ b/components/fileorderlist/model/datafilesmodel.cpp @@ -144,6 +144,15 @@ QVariant DataFilesModel::data(const QModelIndex &index, int role) const return tooltip; } + + case Qt::UserRole: + { + if (file->masters().size() == 0) + return "game"; + else + return "addon"; + } + default: return QVariant(); } From 84e5c2610ab0347127645e277f08dd83e11383fd Mon Sep 17 00:00:00 2001 From: graffy76 Date: Fri, 16 Aug 2013 18:00:23 -0500 Subject: [PATCH 002/148] Implemented combobox for game file selection --- apps/launcher/datafilespage.cpp | 2 +- apps/opencs/view/doc/filedialog.cpp | 20 +++++++------- apps/opencs/view/doc/filedialog.hpp | 2 +- .../fileorderlist/utils/profilescombobox.cpp | 16 +++++++++++ .../fileorderlist/utils/profilescombobox.hpp | 5 +++- files/ui/datafilespage.ui | 27 +++---------------- 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index add3dea40e..1fafd59226 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -101,7 +101,7 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); - connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); + //connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); connect(splitter, SIGNAL(splitterMoved(int,int)), this, SLOT(updateSplitter())); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 02421a7883..b06c970085 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -41,6 +41,8 @@ FileDialog::FileDialog(QWidget *parent) : QCheckBox checkBox; unsigned int height = checkBox.sizeHint().height() + 4; + masterView->setModel(mMastersProxyModel); + mastersTable->setModel(mMastersProxyModel); mastersTable->setObjectName("MastersTable"); mastersTable->setContextMenuPolicy(Qt::CustomContextMenu); @@ -83,12 +85,12 @@ FileDialog::FileDialog(QWidget *parent) : mNameLabel = new QLabel(tr("File Name:"), this); QRegExpValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9\\s]*$")); - mNameLineEdit = new LineEdit(this); - mNameLineEdit->setValidator(validator); + //mNameLineEdit = new LineEdit(this); + //mNameLineEdit->setValidator(validator); nameLayout->addSpacerItem(spacer); nameLayout->addWidget(mNameLabel); - nameLayout->addWidget(mNameLineEdit); + //nameLayout->addWidget(mNameLineEdit); mButtonBox = new QDialogButtonBox(this); @@ -109,9 +111,9 @@ FileDialog::FileDialog(QWidget *parent) : connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList))); - connect(mNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateCreateButton(QString))); + //connect(mNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateCreateButton(QString))); - connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); + //connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); connect(pluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); @@ -223,7 +225,7 @@ QStringList FileDialog::checkedItemsPaths() QString FileDialog::fileName() { - return mNameLineEdit->text(); + //return mNameLineEdit->text(); } void FileDialog::openFile() @@ -231,7 +233,7 @@ void FileDialog::openFile() setWindowTitle(tr("Open")); mNameLabel->hide(); - mNameLineEdit->hide(); + //mNameLineEdit->hide(); mCreateButton->hide(); mButtonBox->removeButton(mCreateButton); @@ -249,8 +251,8 @@ void FileDialog::newFile() setWindowTitle(tr("New")); mNameLabel->show(); - mNameLineEdit->clear(); - mNameLineEdit->show(); + //mNameLineEdit->clear(); + //mNameLineEdit->show(); mCreateButton->show(); mButtonBox->setStandardButtons(QDialogButtonBox::Cancel); diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index b21618d5de..4c3fe9ffab 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -51,7 +51,7 @@ private slots: private: QLabel *mNameLabel; - LineEdit *mNameLineEdit; + //LineEdit *mNameLineEdit; QPushButton *mCreateButton; QDialogButtonBox *mButtonBox; diff --git a/components/fileorderlist/utils/profilescombobox.cpp b/components/fileorderlist/utils/profilescombobox.cpp index c3ff953ae0..9346276dae 100644 --- a/components/fileorderlist/utils/profilescombobox.cpp +++ b/components/fileorderlist/utils/profilescombobox.cpp @@ -90,3 +90,19 @@ void ProfilesComboBox::slotIndexChanged(int index) emit(profileChanged(mOldProfile, currentText())); mOldProfile = itemText(index); } + +void ProfilesComboBox::paintEvent(QPaintEvent *) +{ + QStylePainter painter(this); + painter.setPen(palette().color(QPalette::Text)); + + // draw the combobox frame, focusrect and selected etc. + QStyleOptionComboBox opt; + initStyleOption(&opt); + painter.drawComplexControl(QStyle::CC_ComboBox, opt); + + // draw the icon and text + if (!opt.editable && currentIndex() == -1) // <<< we adjust the text displayed when nothing is selected + opt.currentText = tr("Select a game file..."); + painter.drawControl(QStyle::CE_ComboBoxLabel, opt); +} diff --git a/components/fileorderlist/utils/profilescombobox.hpp b/components/fileorderlist/utils/profilescombobox.hpp index 08ead9a7ab..55913d7fec 100644 --- a/components/fileorderlist/utils/profilescombobox.hpp +++ b/components/fileorderlist/utils/profilescombobox.hpp @@ -2,7 +2,7 @@ #define PROFILESCOMBOBOX_HPP #include - +#include class QString; class QRegExpValidator; @@ -25,6 +25,9 @@ private slots: private: QString mOldProfile; QRegExpValidator *mValidator; + +protected: + void paintEvent(QPaintEvent *); }; #endif // PROFILESCOMBOBOX_HPP diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 041a9576d0..342e4d9e90 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -14,28 +14,12 @@ - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Filter: + + + false - - - @@ -151,11 +135,6 @@ - - LineEdit - QLineEdit -
components/fileorderlist/utils/lineedit.hpp
-
ProfilesComboBox QComboBox From 49c4e1bf9eccd1efd91c371a22e0810a5c8cb815 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Fri, 16 Aug 2013 18:23:02 -0500 Subject: [PATCH 003/148] Removed master table widget --- apps/launcher/datafilespage.cpp | 34 +++++++++++++++-------------- apps/opencs/view/doc/filedialog.cpp | 9 ++++---- files/ui/datafilespage.ui | 1 - 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 1fafd59226..077f3c292f 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -47,7 +47,7 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam QCheckBox checkBox; unsigned int height = checkBox.sizeHint().height() + 4; - +/* mastersTable->setModel(mMastersProxyModel); mastersTable->setObjectName("MastersTable"); mastersTable->setContextMenuPolicy(Qt::CustomContextMenu); @@ -63,7 +63,7 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam mastersTable->verticalHeader()->setDefaultSectionSize(height); mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); mastersTable->verticalHeader()->hide(); - +*/ pluginsTable->setModel(mFilterProxyModel); pluginsTable->setObjectName("PluginsTable"); pluginsTable->setContextMenuPolicy(Qt::CustomContextMenu); @@ -94,10 +94,10 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam connect(mNewProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString))); connect(pluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); - connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); + //connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); connect(pluginsTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); - connect(mastersTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); + //connect(mastersTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); @@ -255,6 +255,7 @@ void DataFilesPage::updateSplitter() void DataFilesPage::updateViews() { // Ensure the columns are hidden because sort() re-enables them + /* mastersTable->setColumnHidden(1, true); mastersTable->setColumnHidden(2, true); mastersTable->setColumnHidden(3, true); @@ -263,7 +264,7 @@ void DataFilesPage::updateViews() mastersTable->setColumnHidden(6, true); mastersTable->setColumnHidden(7, true); mastersTable->setColumnHidden(8, true); - +*/ pluginsTable->setColumnHidden(1, true); pluginsTable->setColumnHidden(2, true); pluginsTable->setColumnHidden(3, true); @@ -335,8 +336,8 @@ void DataFilesPage::on_checkAction_triggered() if (pluginsTable->hasFocus()) setPluginsCheckstates(Qt::Checked); - if (mastersTable->hasFocus()) - setMastersCheckstates(Qt::Checked); + //if (mastersTable->hasFocus()) + // setMastersCheckstates(Qt::Checked); } @@ -345,17 +346,17 @@ void DataFilesPage::on_uncheckAction_triggered() if (pluginsTable->hasFocus()) setPluginsCheckstates(Qt::Unchecked); - if (mastersTable->hasFocus()) - setMastersCheckstates(Qt::Unchecked); + //if (mastersTable->hasFocus()) + // setMastersCheckstates(Qt::Unchecked); } void DataFilesPage::setMastersCheckstates(Qt::CheckState state) -{ - if (!mastersTable->selectionModel()->hasSelection()) { - return; - } +{/* + //if (!mastersTable->selectionModel()->hasSelection()) { + // return; + //} - QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes(); + //QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes(); foreach (const QModelIndex &index, indexes) { @@ -368,7 +369,7 @@ void DataFilesPage::setMastersCheckstates(Qt::CheckState state) return; mDataFilesModel->setCheckState(sourceIndex, state); - } + }*/ } void DataFilesPage::setPluginsCheckstates(Qt::CheckState state) @@ -519,7 +520,7 @@ void DataFilesPage::showContextMenu(const QPoint &point) // Show menu mContextMenu->exec(globalPos); } - +/* if (object->objectName() == QLatin1String("MastersTable")) { if (!mastersTable->selectionModel()->hasSelection()) return; @@ -548,4 +549,5 @@ void DataFilesPage::showContextMenu(const QPoint &point) mContextMenu->exec(globalPos); } + */ } diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index b06c970085..a07b854df6 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -42,7 +42,7 @@ FileDialog::FileDialog(QWidget *parent) : unsigned int height = checkBox.sizeHint().height() + 4; masterView->setModel(mMastersProxyModel); - +/* mastersTable->setModel(mMastersProxyModel); mastersTable->setObjectName("MastersTable"); mastersTable->setContextMenuPolicy(Qt::CustomContextMenu); @@ -57,7 +57,7 @@ FileDialog::FileDialog(QWidget *parent) : mastersTable->verticalHeader()->setDefaultSectionSize(height); mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); mastersTable->verticalHeader()->hide(); - +*/ pluginsTable->setModel(mFilterProxyModel); pluginsTable->setObjectName("PluginsTable"); pluginsTable->setContextMenuPolicy(Qt::CustomContextMenu); @@ -116,7 +116,7 @@ FileDialog::FileDialog(QWidget *parent) : //connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); connect(pluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); - connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); + //connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); connect(mCreateButton, SIGNAL(clicked()), this, SLOT(createButtonClicked())); @@ -127,6 +127,7 @@ FileDialog::FileDialog(QWidget *parent) : void FileDialog::updateViews() { // Ensure the columns are hidden because sort() re-enables them + /* mastersTable->setColumnHidden(1, true); mastersTable->setColumnHidden(3, true); mastersTable->setColumnHidden(4, true); @@ -135,7 +136,7 @@ void FileDialog::updateViews() mastersTable->setColumnHidden(7, true); mastersTable->setColumnHidden(8, true); mastersTable->resizeColumnsToContents(); - +*/ pluginsTable->setColumnHidden(1, true); pluginsTable->setColumnHidden(3, true); pluginsTable->setColumnHidden(4, true); diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 342e4d9e90..816d288a65 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -36,7 +36,6 @@ false -
From b850fe02895dca508685d1ebbabb647f1e71c048 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Fri, 16 Aug 2013 18:59:01 -0500 Subject: [PATCH 004/148] Removed vertical headers from plugin view --- apps/launcher/datafilespage.cpp | 6 ++++-- components/fileorderlist/model/datafilesmodel.cpp | 4 ++-- components/fileorderlist/model/pluginsproxymodel.cpp | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 077f3c292f..fe2fe82556 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -32,12 +32,14 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam mDataFilesModel = new DataFilesModel(this); mMastersProxyModel = new QSortFilterProxyModel(); - mMastersProxyModel->setFilterRegExp(QString("^.*\\.esm")); + mMastersProxyModel->setFilterRegExp(QString("game")); //QString("^.*\\.esm")); + mMastersProxyModel->setFilterRole (Qt::UserRole); mMastersProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); mMastersProxyModel->setSourceModel(mDataFilesModel); mPluginsProxyModel = new PluginsProxyModel(); - mPluginsProxyModel->setFilterRegExp(QString("^.*\\.esp")); + mPluginsProxyModel->setFilterRegExp(QString("addon")); //^.*\\.esp")); + mPluginsProxyModel->setFilterRole (Qt::UserRole); mPluginsProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); mPluginsProxyModel->setSourceModel(mDataFilesModel); diff --git a/components/fileorderlist/model/datafilesmodel.cpp b/components/fileorderlist/model/datafilesmodel.cpp index cf1fa1b0a5..99c1aaebf4 100644 --- a/components/fileorderlist/model/datafilesmodel.cpp +++ b/components/fileorderlist/model/datafilesmodel.cpp @@ -202,11 +202,11 @@ QVariant DataFilesModel::headerData(int section, Qt::Orientation orientation, in case 7: return tr("Masters"); case 8: return tr("Description"); } - } else { + } /* else { // Show row numbers return ++section; } - +*/ return QVariant(); } diff --git a/components/fileorderlist/model/pluginsproxymodel.cpp b/components/fileorderlist/model/pluginsproxymodel.cpp index 6be152b555..726a3f158b 100644 --- a/components/fileorderlist/model/pluginsproxymodel.cpp +++ b/components/fileorderlist/model/pluginsproxymodel.cpp @@ -11,7 +11,7 @@ PluginsProxyModel::~PluginsProxyModel() QVariant PluginsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (orientation != Qt::Vertical || role != Qt::DisplayRole) + //if (orientation != Qt::Vertical || role != Qt::DisplayRole) return QSortFilterProxyModel::headerData(section, orientation, role); - return section + 1; + // return section + 1; } From 2bc56d0b5c092a2e230281d55da9bdcecbc7e81c Mon Sep 17 00:00:00 2001 From: graffy76 Date: Fri, 16 Aug 2013 20:59:58 -0500 Subject: [PATCH 005/148] Fixed missing item list in launcher combobox --- apps/launcher/datafilespage.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index fe2fe82556..ddeb43ab7a 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -47,6 +47,8 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam mFilterProxyModel->setDynamicSortFilter(true); mFilterProxyModel->setSourceModel(mPluginsProxyModel); + masterView->setModel (mMastersProxyModel); + QCheckBox checkBox; unsigned int height = checkBox.sizeHint().height() + 4; /* From 7389507eb52d2dadda0eba4f52624eb2d6f63360 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Fri, 16 Aug 2013 21:12:30 -0500 Subject: [PATCH 006/148] Created masterproxylist class --- apps/launcher/datafilespage.cpp | 7 ++++--- apps/opencs/view/doc/filedialog.cpp | 4 +++- components/CMakeLists.txt | 1 + components/fileorderlist/masterproxymodel.cpp | 11 +++++++++++ components/fileorderlist/masterproxymodel.hpp | 19 +++++++++++++++++++ .../fileorderlist/model/pluginsproxymodel.cpp | 6 ++---- .../fileorderlist/model/pluginsproxymodel.hpp | 2 +- 7 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 components/fileorderlist/masterproxymodel.cpp create mode 100644 components/fileorderlist/masterproxymodel.hpp diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index ddeb43ab7a..f674e9dc79 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -48,9 +48,10 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam mFilterProxyModel->setSourceModel(mPluginsProxyModel); masterView->setModel (mMastersProxyModel); - +/* QCheckBox checkBox; unsigned int height = checkBox.sizeHint().height() + 4; + */ /* mastersTable->setModel(mMastersProxyModel); mastersTable->setObjectName("MastersTable"); @@ -80,8 +81,8 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam pluginsTable->horizontalHeader()->setStretchLastSection(true); pluginsTable->horizontalHeader()->hide(); - pluginsTable->verticalHeader()->setDefaultSectionSize(height); - pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); + //pluginsTable->verticalHeader()->setDefaultSectionSize(height); + //pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); // Adjust the tableview widths inside the splitter QList sizeList; diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index a07b854df6..d49906949c 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -16,6 +16,8 @@ #include +#include "components/fileorderlist/masterproxymodel.hpp" + FileDialog::FileDialog(QWidget *parent) : QDialog(parent) { @@ -24,7 +26,7 @@ FileDialog::FileDialog(QWidget *parent) : // Models mDataFilesModel = new DataFilesModel(this); - mMastersProxyModel = new QSortFilterProxyModel(); + mMastersProxyModel = new MasterProxyModel(); mMastersProxyModel->setFilterRegExp("game"); //QString("^.*\\.esm")); mMastersProxyModel->setFilterRole (Qt::UserRole); mMastersProxyModel->setSourceModel(mDataFilesModel); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 529891b4cb..bbaca48054 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -70,6 +70,7 @@ find_package(Qt4 COMPONENTS QtCore QtGui) if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) add_component_qt_dir (fileorderlist + masterproxymodel model/modelitem model/datafilesmodel model/pluginsproxymodel model/esm/esmfile utils/profilescombobox utils/comboboxlineedit utils/lineedit utils/naturalsort ) diff --git a/components/fileorderlist/masterproxymodel.cpp b/components/fileorderlist/masterproxymodel.cpp new file mode 100644 index 0000000000..ce874318d9 --- /dev/null +++ b/components/fileorderlist/masterproxymodel.cpp @@ -0,0 +1,11 @@ +#include "masterproxymodel.hpp" + +MasterProxyModel::MasterProxyModel(QObject *parent) : + QSortFilterProxyModel(parent) +{ +} + +QVariant MasterProxyModel::data(const QModelIndex &index, int role) const +{ + return QSortFilterProxyModel::data (index, role); +} diff --git a/components/fileorderlist/masterproxymodel.hpp b/components/fileorderlist/masterproxymodel.hpp new file mode 100644 index 0000000000..d0d2888730 --- /dev/null +++ b/components/fileorderlist/masterproxymodel.hpp @@ -0,0 +1,19 @@ +#ifndef MASTERPROXYMODEL_HPP +#define MASTERPROXYMODEL_HPP + +#include + +class MasterProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + explicit MasterProxyModel(QObject *parent = 0); + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + +signals: + +public slots: + +}; + +#endif // MASTERPROXYMODEL_HPP diff --git a/components/fileorderlist/model/pluginsproxymodel.cpp b/components/fileorderlist/model/pluginsproxymodel.cpp index 726a3f158b..4648d28330 100644 --- a/components/fileorderlist/model/pluginsproxymodel.cpp +++ b/components/fileorderlist/model/pluginsproxymodel.cpp @@ -9,9 +9,7 @@ PluginsProxyModel::~PluginsProxyModel() { } -QVariant PluginsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const +QVariant PluginsProxyModel::data(const QModelIndex &index, int role) const { - //if (orientation != Qt::Vertical || role != Qt::DisplayRole) - return QSortFilterProxyModel::headerData(section, orientation, role); - // return section + 1; + return QSortFilterProxyModel::data (index, role); } diff --git a/components/fileorderlist/model/pluginsproxymodel.hpp b/components/fileorderlist/model/pluginsproxymodel.hpp index 8fde732361..08baa2338c 100644 --- a/components/fileorderlist/model/pluginsproxymodel.hpp +++ b/components/fileorderlist/model/pluginsproxymodel.hpp @@ -12,7 +12,7 @@ public: explicit PluginsProxyModel(QObject *parent = 0); ~PluginsProxyModel(); - QVariant headerData(int section, Qt::Orientation orientation, int role) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; }; #endif // PLUGINSPROXYMODEL_HPP From 4c8c6d697119c84d6ac27233cb480483074d6f66 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Fri, 16 Aug 2013 21:20:48 -0500 Subject: [PATCH 007/148] Moved init code to master / plugin proxy classes --- apps/launcher/datafilespage.cpp | 15 ++++---------- apps/opencs/view/doc/filedialog.cpp | 20 +++++++------------ components/fileorderlist/masterproxymodel.cpp | 7 ++++++- components/fileorderlist/masterproxymodel.hpp | 4 +++- .../fileorderlist/model/pluginsproxymodel.cpp | 7 ++++++- .../fileorderlist/model/pluginsproxymodel.hpp | 3 ++- 6 files changed, 28 insertions(+), 28 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index f674e9dc79..42e10c76b1 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -15,6 +15,7 @@ #include #include +#include "a.out.h" #include "settings/gamesettings.hpp" #include "settings/launchersettings.hpp" @@ -29,19 +30,11 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam setupUi(this); // Models - mDataFilesModel = new DataFilesModel(this); + mDataFilesModel = new DataFilesModel (this); - mMastersProxyModel = new QSortFilterProxyModel(); - mMastersProxyModel->setFilterRegExp(QString("game")); //QString("^.*\\.esm")); - mMastersProxyModel->setFilterRole (Qt::UserRole); - mMastersProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); - mMastersProxyModel->setSourceModel(mDataFilesModel); + mMastersProxyModel = new MasterProxyModel (this, mDataFilesModel); - mPluginsProxyModel = new PluginsProxyModel(); - mPluginsProxyModel->setFilterRegExp(QString("addon")); //^.*\\.esp")); - mPluginsProxyModel->setFilterRole (Qt::UserRole); - mPluginsProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); - mPluginsProxyModel->setSourceModel(mDataFilesModel); + mPluginsProxyModel = new PluginsProxyModel (this, mDataFilesModel); mFilterProxyModel = new QSortFilterProxyModel(); mFilterProxyModel->setDynamicSortFilter(true); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index d49906949c..dd94b05717 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -24,25 +24,19 @@ FileDialog::FileDialog(QWidget *parent) : setupUi(this); // Models - mDataFilesModel = new DataFilesModel(this); + mDataFilesModel = new DataFilesModel (this); - mMastersProxyModel = new MasterProxyModel(); - mMastersProxyModel->setFilterRegExp("game"); //QString("^.*\\.esm")); - mMastersProxyModel->setFilterRole (Qt::UserRole); - mMastersProxyModel->setSourceModel(mDataFilesModel); + mMastersProxyModel = new MasterProxyModel (this, mDataFilesModel); + mPluginsProxyModel = new PluginsProxyModel (this, mDataFilesModel); - mPluginsProxyModel = new PluginsProxyModel(); - mPluginsProxyModel->setFilterRegExp("addon"); //QString("^.*\\.esp")); - mPluginsProxyModel->setFilterRole (Qt::UserRole); - mPluginsProxyModel->setSourceModel(mDataFilesModel); mFilterProxyModel = new QSortFilterProxyModel(); mFilterProxyModel->setDynamicSortFilter(true); mFilterProxyModel->setSourceModel(mPluginsProxyModel); - +/* QCheckBox checkBox; unsigned int height = checkBox.sizeHint().height() + 4; - +*/ masterView->setModel(mMastersProxyModel); /* mastersTable->setModel(mMastersProxyModel); @@ -70,10 +64,10 @@ FileDialog::FileDialog(QWidget *parent) : pluginsTable->setAlternatingRowColors(true); pluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem); pluginsTable->horizontalHeader()->setStretchLastSection(true); - +/* pluginsTable->verticalHeader()->setDefaultSectionSize(height); pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); - +*/ // Hide the profile elements profileLabel->hide(); profilesComboBox->hide(); diff --git a/components/fileorderlist/masterproxymodel.cpp b/components/fileorderlist/masterproxymodel.cpp index ce874318d9..dc702e9457 100644 --- a/components/fileorderlist/masterproxymodel.cpp +++ b/components/fileorderlist/masterproxymodel.cpp @@ -1,8 +1,13 @@ #include "masterproxymodel.hpp" -MasterProxyModel::MasterProxyModel(QObject *parent) : +MasterProxyModel::MasterProxyModel(QObject *parent, QAbstractTableModel* model) : QSortFilterProxyModel(parent) { + setFilterRegExp(QString("game")); + setFilterRole (Qt::UserRole); + + if (model) + setSourceModel (model); } QVariant MasterProxyModel::data(const QModelIndex &index, int role) const diff --git a/components/fileorderlist/masterproxymodel.hpp b/components/fileorderlist/masterproxymodel.hpp index d0d2888730..d9e12dada9 100644 --- a/components/fileorderlist/masterproxymodel.hpp +++ b/components/fileorderlist/masterproxymodel.hpp @@ -3,11 +3,13 @@ #include +class QAbstractTableModel; + class MasterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: - explicit MasterProxyModel(QObject *parent = 0); + explicit MasterProxyModel(QObject *parent = 0, QAbstractTableModel *model = 0); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; signals: diff --git a/components/fileorderlist/model/pluginsproxymodel.cpp b/components/fileorderlist/model/pluginsproxymodel.cpp index 4648d28330..61ffb88946 100644 --- a/components/fileorderlist/model/pluginsproxymodel.cpp +++ b/components/fileorderlist/model/pluginsproxymodel.cpp @@ -1,8 +1,13 @@ #include "pluginsproxymodel.hpp" -PluginsProxyModel::PluginsProxyModel(QObject *parent) : +PluginsProxyModel::PluginsProxyModel(QObject *parent, QAbstractTableModel *model) : QSortFilterProxyModel(parent) { + setFilterRegExp(QString("addon")); + setFilterRole (Qt::UserRole); + + if (model) + setSourceModel (model); } PluginsProxyModel::~PluginsProxyModel() diff --git a/components/fileorderlist/model/pluginsproxymodel.hpp b/components/fileorderlist/model/pluginsproxymodel.hpp index 08baa2338c..238d2aac77 100644 --- a/components/fileorderlist/model/pluginsproxymodel.hpp +++ b/components/fileorderlist/model/pluginsproxymodel.hpp @@ -4,12 +4,13 @@ #include class QVariant; +class QAbstractTableModel; class PluginsProxyModel : public QSortFilterProxyModel { Q_OBJECT public: - explicit PluginsProxyModel(QObject *parent = 0); + explicit PluginsProxyModel(QObject *parent = 0, QAbstractTableModel *model = 0); ~PluginsProxyModel(); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; From 61602789e1092eb80bdcb677f03ce7a02fce6501 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Fri, 16 Aug 2013 22:23:21 -0500 Subject: [PATCH 008/148] Began migrating code to ContentSelector base --- apps/launcher/datafilespage.cpp | 21 +-- apps/launcher/datafilespage.hpp | 4 +- apps/opencs/view/doc/filedialog.cpp | 117 +++------------- apps/opencs/view/doc/filedialog.hpp | 12 +- components/CMakeLists.txt | 9 +- components/fileorderlist/contentselector.cpp | 132 ++++++++++++++++++ components/fileorderlist/contentselector.hpp | 40 ++++++ components/fileorderlist/masterproxymodel.cpp | 4 +- components/fileorderlist/masterproxymodel.hpp | 22 +-- 9 files changed, 232 insertions(+), 129 deletions(-) create mode 100644 components/fileorderlist/contentselector.cpp create mode 100644 components/fileorderlist/contentselector.hpp diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 42e10c76b1..7a48900518 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -15,7 +15,7 @@ #include #include -#include "a.out.h" +#include "components/fileorderlist/masterproxymodel.hpp" #include "settings/gamesettings.hpp" #include "settings/launchersettings.hpp" @@ -25,10 +25,11 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam : mCfgMgr(cfg) , mGameSettings(gameSettings) , mLauncherSettings(launcherSettings) - , QWidget(parent) + , ContentSelector(parent) { setupUi(this); - + buildModelsAndViews(); + /* // Models mDataFilesModel = new DataFilesModel (this); @@ -41,11 +42,11 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam mFilterProxyModel->setSourceModel(mPluginsProxyModel); masterView->setModel (mMastersProxyModel); -/* - QCheckBox checkBox; - unsigned int height = checkBox.sizeHint().height() + 4; - */ -/* + + //QCheckBox checkBox; + // unsigned int height = checkBox.sizeHint().height() + 4; + + mastersTable->setModel(mMastersProxyModel); mastersTable->setObjectName("MastersTable"); mastersTable->setContextMenuPolicy(Qt::CustomContextMenu); @@ -61,7 +62,7 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam mastersTable->verticalHeader()->setDefaultSectionSize(height); mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); mastersTable->verticalHeader()->hide(); -*/ + pluginsTable->setModel(mFilterProxyModel); pluginsTable->setObjectName("PluginsTable"); pluginsTable->setContextMenuPolicy(Qt::CustomContextMenu); @@ -76,7 +77,7 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam //pluginsTable->verticalHeader()->setDefaultSectionSize(height); //pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); - +*/ // Adjust the tableview widths inside the splitter QList sizeList; sizeList << mLauncherSettings.value(QString("General/MastersTable/width"), QString("200")).toInt(); diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index a0b0293309..99aa24ff3c 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -5,6 +5,7 @@ #include #include "ui_datafilespage.h" +#include "components/fileorderlist/contentselector.hpp" class QSortFilterProxyModel; class QAbstractItemModel; @@ -19,10 +20,9 @@ class PluginsProxyModel; namespace Files { struct ConfigurationManager; } -class DataFilesPage : public QWidget, private Ui::DataFilesPage +class DataFilesPage : public FileOrderList::ContentSelector { Q_OBJECT - public: DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent = 0); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index dd94b05717..f770e80a1e 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -19,10 +19,11 @@ #include "components/fileorderlist/masterproxymodel.hpp" FileDialog::FileDialog(QWidget *parent) : - QDialog(parent) + ContentSelector(parent) { setupUi(this); - + buildModelsAndViews(); + /* // Models mDataFilesModel = new DataFilesModel (this); @@ -33,12 +34,12 @@ FileDialog::FileDialog(QWidget *parent) : mFilterProxyModel = new QSortFilterProxyModel(); mFilterProxyModel->setDynamicSortFilter(true); mFilterProxyModel->setSourceModel(mPluginsProxyModel); -/* - QCheckBox checkBox; - unsigned int height = checkBox.sizeHint().height() + 4; -*/ + +// QCheckBox checkBox; +// unsigned int height = checkBox.sizeHint().height() + 4; + masterView->setModel(mMastersProxyModel); -/* + mastersTable->setModel(mMastersProxyModel); mastersTable->setObjectName("MastersTable"); mastersTable->setContextMenuPolicy(Qt::CustomContextMenu); @@ -53,7 +54,7 @@ FileDialog::FileDialog(QWidget *parent) : mastersTable->verticalHeader()->setDefaultSectionSize(height); mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); mastersTable->verticalHeader()->hide(); -*/ + pluginsTable->setModel(mFilterProxyModel); pluginsTable->setObjectName("PluginsTable"); pluginsTable->setContextMenuPolicy(Qt::CustomContextMenu); @@ -64,10 +65,11 @@ FileDialog::FileDialog(QWidget *parent) : pluginsTable->setAlternatingRowColors(true); pluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem); pluginsTable->horizontalHeader()->setStretchLastSection(true); -/* - pluginsTable->verticalHeader()->setDefaultSectionSize(height); - pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); -*/ + +// pluginsTable->verticalHeader()->setDefaultSectionSize(height); +// pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); + + */ // Hide the profile elements profileLabel->hide(); profilesComboBox->hide(); @@ -105,43 +107,19 @@ FileDialog::FileDialog(QWidget *parent) : resize(600, 400); - connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); - connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList))); + // + // connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList))); //connect(mNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateCreateButton(QString))); //connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); - connect(pluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); + // connect(pluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); //connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); - connect(mCreateButton, SIGNAL(clicked()), this, SLOT(createButtonClicked())); - - connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept())); - connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject())); -} - -void FileDialog::updateViews() -{ - // Ensure the columns are hidden because sort() re-enables them - /* - mastersTable->setColumnHidden(1, true); - mastersTable->setColumnHidden(3, true); - mastersTable->setColumnHidden(4, true); - mastersTable->setColumnHidden(5, true); - mastersTable->setColumnHidden(6, true); - mastersTable->setColumnHidden(7, true); - mastersTable->setColumnHidden(8, true); - mastersTable->resizeColumnsToContents(); -*/ - pluginsTable->setColumnHidden(1, true); - pluginsTable->setColumnHidden(3, true); - pluginsTable->setColumnHidden(4, true); - pluginsTable->setColumnHidden(5, true); - pluginsTable->setColumnHidden(6, true); - pluginsTable->setColumnHidden(7, true); - pluginsTable->setColumnHidden(8, true); - pluginsTable->resizeColumnsToContents(); + // connect(mCreateButton, SIGNAL(clicked()), this, SLOT(createButtonClicked())); + // connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept())); + // connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject())); } void FileDialog::updateOpenButton(const QStringList &items) @@ -161,64 +139,13 @@ void FileDialog::updateCreateButton(const QString &name) mCreateButton->setEnabled(!name.isEmpty()); } - +/* void FileDialog::filterChanged(const QString &filter) { QRegExp filterRe(filter, Qt::CaseInsensitive, QRegExp::FixedString); mFilterProxyModel->setFilterRegExp(filterRe); } - -void FileDialog::addFiles(const QString &path) -{ - mDataFilesModel->addFiles(path); - mDataFilesModel->sort(3); // Sort by date accessed -} - -void FileDialog::setEncoding(const QString &encoding) -{ - mDataFilesModel->setEncoding(encoding); -} - -void FileDialog::setCheckState(QModelIndex index) -{ - if (!index.isValid()) - return; - - QObject *object = QObject::sender(); - - // Not a signal-slot call - if (!object) - return; - - - if (object->objectName() == QLatin1String("PluginsTable")) { - QModelIndex sourceIndex = mPluginsProxyModel->mapToSource( - mFilterProxyModel->mapToSource(index)); - - if (sourceIndex.isValid()) { - (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) - ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) - : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); - } - } - - if (object->objectName() == QLatin1String("MastersTable")) { - QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index); - - if (sourceIndex.isValid()) { - (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) - ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) - : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); - } - } - - return; -} - -QStringList FileDialog::checkedItemsPaths() -{ - return mDataFilesModel->checkedItemsPaths(); -} +*/ QString FileDialog::fileName() { diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 4c3fe9ffab..2f4d4e381d 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -4,6 +4,7 @@ #include #include +#include "components/fileorderlist/contentselector.hpp" #include "ui_datafilespage.h" class QDialogButtonBox; @@ -17,19 +18,16 @@ class QMenu; class DataFilesModel; class PluginsProxyModel; -class FileDialog : public QDialog, private Ui::DataFilesPage +class FileDialog : public FileOrderList::ContentSelector { Q_OBJECT public: explicit FileDialog(QWidget *parent = 0); - void addFiles(const QString &path); - void setEncoding(const QString &encoding); void openFile(); void newFile(); void accepted(); - QStringList checkedItemsPaths(); QString fileName(); signals: @@ -40,12 +38,12 @@ public slots: void accept(); private slots: - void updateViews(); + //void updateViews(); void updateOpenButton(const QStringList &items); void updateCreateButton(const QString &name); - void setCheckState(QModelIndex index); - void filterChanged(const QString &filter); + + //void filterChanged(const QString &filter); void createButtonClicked(); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index bbaca48054..19af6c3b21 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -66,22 +66,25 @@ add_component_dir (translation translation ) +set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui + ) find_package(Qt4 COMPONENTS QtCore QtGui) if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) add_component_qt_dir (fileorderlist - masterproxymodel + masterproxymodel contentselector model/modelitem model/datafilesmodel model/pluginsproxymodel model/esm/esmfile utils/profilescombobox utils/comboboxlineedit utils/lineedit utils/naturalsort ) include(${QT_USE_FILE}) + QT4_WRAP_UI(ESM_UI_HDR ${ESM_UI}) QT4_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES}) endif(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) -include_directories(${BULLET_INCLUDE_DIRS}) +include_directories(${BULLET_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) -add_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS}) +add_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS} ${ESM_UI_HDR}) target_link_libraries(components ${Boost_LIBRARIES} ${OGRE_LIBRARIES}) diff --git a/components/fileorderlist/contentselector.cpp b/components/fileorderlist/contentselector.cpp new file mode 100644 index 0000000000..16e0718910 --- /dev/null +++ b/components/fileorderlist/contentselector.cpp @@ -0,0 +1,132 @@ +#include "contentselector.hpp" + +#include "model/datafilesmodel.hpp" +#include "masterproxymodel.hpp" +#include "model/pluginsproxymodel.hpp" + +#include + +FileOrderList::ContentSelector::ContentSelector(QWidget *parent) : + QWidget(parent) +{ +} + +void FileOrderList::ContentSelector::buildModelsAndViews() +{ + // Models + mDataFilesModel = new DataFilesModel (this); + + mMasterProxyModel = new FileOrderList::MasterProxyModel (this, mDataFilesModel); + mPluginsProxyModel = new PluginsProxyModel (this, mDataFilesModel); + + + mFilterProxyModel = new QSortFilterProxyModel(); + mFilterProxyModel->setDynamicSortFilter(true); + mFilterProxyModel->setSourceModel(mPluginsProxyModel); + + masterView->setModel(mMasterProxyModel); +/* + mastersTable->setModel(mMastersProxyModel); + mastersTable->setObjectName("MastersTable"); + mastersTable->setContextMenuPolicy(Qt::CustomContextMenu); + mastersTable->setSortingEnabled(false); + mastersTable->setSelectionBehavior(QAbstractItemView::SelectRows); + mastersTable->setSelectionMode(QAbstractItemView::ExtendedSelection); + mastersTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + mastersTable->setAlternatingRowColors(true); + mastersTable->horizontalHeader()->setStretchLastSection(true); + + // Set the row height to the size of the checkboxes + mastersTable->verticalHeader()->setDefaultSectionSize(height); + mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); + mastersTable->verticalHeader()->hide(); +*/ + pluginsTable->setModel(mFilterProxyModel); + pluginsTable->setObjectName("PluginsTable"); + pluginsTable->setContextMenuPolicy(Qt::CustomContextMenu); + pluginsTable->setSortingEnabled(false); + pluginsTable->setSelectionBehavior(QAbstractItemView::SelectRows); + pluginsTable->setSelectionMode(QAbstractItemView::ExtendedSelection); + pluginsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + pluginsTable->setAlternatingRowColors(true); + pluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem); + pluginsTable->horizontalHeader()->setStretchLastSection(true); + + connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); +} + +void FileOrderList::ContentSelector::addFiles(const QString &path) +{ + mDataFilesModel->addFiles(path); + mDataFilesModel->sort(3); // Sort by date accessed +} + +void FileOrderList::ContentSelector::setEncoding(const QString &encoding) +{ + mDataFilesModel->setEncoding(encoding); +} + +void FileOrderList::ContentSelector::setCheckState(QModelIndex index) +{ + if (!index.isValid()) + return; + + QObject *object = QObject::sender(); + + // Not a signal-slot call + if (!object) + return; + + + if (object->objectName() == QLatin1String("PluginsTable")) { + QModelIndex sourceIndex = mPluginsProxyModel->mapToSource( + mFilterProxyModel->mapToSource(index)); + + if (sourceIndex.isValid()) { + (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) + ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) + : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); + } + } +/* + if (object->objectName() == QLatin1String("MastersTable")) { + QModelIndex sourceIndex = mMasterProxyModel->mapToSource(index); + + if (sourceIndex.isValid()) { + (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) + ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) + : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); + } + } +*/ + return; +} + +QStringList FileOrderList::ContentSelector::checkedItemsPaths() +{ + return mDataFilesModel->checkedItemsPaths(); +} + +void FileOrderList::ContentSelector::updateViews() +{ + // Ensure the columns are hidden because sort() re-enables them + /* + mastersTable->setColumnHidden(1, true); + mastersTable->setColumnHidden(3, true); + mastersTable->setColumnHidden(4, true); + mastersTable->setColumnHidden(5, true); + mastersTable->setColumnHidden(6, true); + mastersTable->setColumnHidden(7, true); + mastersTable->setColumnHidden(8, true); + mastersTable->resizeColumnsToContents(); +*/ + pluginsTable->setColumnHidden(1, true); + pluginsTable->setColumnHidden(3, true); + pluginsTable->setColumnHidden(4, true); + pluginsTable->setColumnHidden(5, true); + pluginsTable->setColumnHidden(6, true); + pluginsTable->setColumnHidden(7, true); + pluginsTable->setColumnHidden(8, true); + pluginsTable->resizeColumnsToContents(); + +} diff --git a/components/fileorderlist/contentselector.hpp b/components/fileorderlist/contentselector.hpp new file mode 100644 index 0000000000..f17e3a9090 --- /dev/null +++ b/components/fileorderlist/contentselector.hpp @@ -0,0 +1,40 @@ +#ifndef CONTENTSELECTOR_HPP +#define CONTENTSELECTOR_HPP + +#include + +#include "ui_datafilespage.h" + +class DataFilesModel; +class PluginsProxyModel; +class QSortFilterProxyModel; + +namespace FileOrderList +{ + class MasterProxyModel; + + class ContentSelector : public QWidget, protected Ui::DataFilesPage + { + Q_OBJECT + + DataFilesModel *mDataFilesModel; + MasterProxyModel *mMasterProxyModel; + PluginsProxyModel *mPluginsProxyModel; + QSortFilterProxyModel *mFilterProxyModel; + + public: + explicit ContentSelector(QWidget *parent = 0); + + void buildModelsAndViews(); + + void addFiles(const QString &path); + void setEncoding(const QString &encoding); + void setCheckState(QModelIndex index); + QStringList checkedItemsPaths(); + + private slots: + void updateViews(); + }; +} + +#endif // CONTENTSELECTOR_HPP diff --git a/components/fileorderlist/masterproxymodel.cpp b/components/fileorderlist/masterproxymodel.cpp index dc702e9457..7701d4f17a 100644 --- a/components/fileorderlist/masterproxymodel.cpp +++ b/components/fileorderlist/masterproxymodel.cpp @@ -1,6 +1,6 @@ #include "masterproxymodel.hpp" -MasterProxyModel::MasterProxyModel(QObject *parent, QAbstractTableModel* model) : +FileOrderList::MasterProxyModel::MasterProxyModel(QObject *parent, QAbstractTableModel* model) : QSortFilterProxyModel(parent) { setFilterRegExp(QString("game")); @@ -10,7 +10,7 @@ MasterProxyModel::MasterProxyModel(QObject *parent, QAbstractTableModel* model) setSourceModel (model); } -QVariant MasterProxyModel::data(const QModelIndex &index, int role) const +QVariant FileOrderList::MasterProxyModel::data(const QModelIndex &index, int role) const { return QSortFilterProxyModel::data (index, role); } diff --git a/components/fileorderlist/masterproxymodel.hpp b/components/fileorderlist/masterproxymodel.hpp index d9e12dada9..49ca369deb 100644 --- a/components/fileorderlist/masterproxymodel.hpp +++ b/components/fileorderlist/masterproxymodel.hpp @@ -5,17 +5,19 @@ class QAbstractTableModel; -class MasterProxyModel : public QSortFilterProxyModel +namespace FileOrderList { - Q_OBJECT -public: - explicit MasterProxyModel(QObject *parent = 0, QAbstractTableModel *model = 0); - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + class MasterProxyModel : public QSortFilterProxyModel + { + Q_OBJECT + public: + explicit MasterProxyModel(QObject *parent = 0, QAbstractTableModel *model = 0); + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; -signals: - -public slots: - -}; + signals: + public slots: + + }; +} #endif // MASTERPROXYMODEL_HPP From 0087b0d67c4b36cfb9e9e9ce5af5d282673d9419 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sat, 17 Aug 2013 05:37:23 -0500 Subject: [PATCH 009/148] Removed checkboxes from master list Moved checkbox code from datafilesmodel to pluginsproxymodel --- .../fileorderlist/model/datafilesmodel.cpp | 5 ----- .../fileorderlist/model/pluginsproxymodel.cpp | 16 ++++++++++++++-- .../fileorderlist/model/pluginsproxymodel.hpp | 7 ++++++- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/components/fileorderlist/model/datafilesmodel.cpp b/components/fileorderlist/model/datafilesmodel.cpp index 99c1aaebf4..3f63e73cce 100644 --- a/components/fileorderlist/model/datafilesmodel.cpp +++ b/components/fileorderlist/model/datafilesmodel.cpp @@ -117,11 +117,6 @@ QVariant DataFilesModel::data(const QModelIndex &index, int role) const } } - case Qt::CheckStateRole: { - if (column != 0) - return QVariant(); - return mCheckStates[file->fileName()]; - } case Qt::ToolTipRole: { if (column != 0) diff --git a/components/fileorderlist/model/pluginsproxymodel.cpp b/components/fileorderlist/model/pluginsproxymodel.cpp index 61ffb88946..f6864d85cd 100644 --- a/components/fileorderlist/model/pluginsproxymodel.cpp +++ b/components/fileorderlist/model/pluginsproxymodel.cpp @@ -1,7 +1,8 @@ #include "pluginsproxymodel.hpp" +#include "datafilesmodel.hpp" -PluginsProxyModel::PluginsProxyModel(QObject *parent, QAbstractTableModel *model) : - QSortFilterProxyModel(parent) +PluginsProxyModel::PluginsProxyModel(QObject *parent, DataFilesModel *model) : + QSortFilterProxyModel(parent), mSourceModel (model) { setFilterRegExp(QString("addon")); setFilterRole (Qt::UserRole); @@ -16,5 +17,16 @@ PluginsProxyModel::~PluginsProxyModel() QVariant PluginsProxyModel::data(const QModelIndex &index, int role) const { + switch (role) + { + case Qt::CheckStateRole: + { + if (index.column() != 0) + return QVariant(); + + return mSourceModel->checkState(index); + } + }; + return QSortFilterProxyModel::data (index, role); } diff --git a/components/fileorderlist/model/pluginsproxymodel.hpp b/components/fileorderlist/model/pluginsproxymodel.hpp index 238d2aac77..e148ea3b16 100644 --- a/components/fileorderlist/model/pluginsproxymodel.hpp +++ b/components/fileorderlist/model/pluginsproxymodel.hpp @@ -5,12 +5,17 @@ class QVariant; class QAbstractTableModel; +class DataFilesModel; class PluginsProxyModel : public QSortFilterProxyModel { Q_OBJECT + + DataFilesModel *mSourceModel; + public: - explicit PluginsProxyModel(QObject *parent = 0, QAbstractTableModel *model = 0); + + explicit PluginsProxyModel(QObject *parent = 0, DataFilesModel *model = 0); ~PluginsProxyModel(); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; From b24dd5c6acf9985b34aa9af45c24d817cc211a0c Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sat, 17 Aug 2013 05:55:43 -0500 Subject: [PATCH 010/148] Continued migration of code to ContentSelector --- apps/launcher/datafilespage.cpp | 106 ++---------------- apps/launcher/datafilespage.hpp | 8 -- apps/opencs/view/doc/filedialog.cpp | 50 --------- apps/opencs/view/doc/filedialog.hpp | 6 - components/fileorderlist/contentselector.cpp | 26 +---- components/fileorderlist/contentselector.hpp | 3 +- .../fileorderlist/model/pluginsproxymodel.cpp | 3 +- 7 files changed, 17 insertions(+), 185 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 7a48900518..d077c3e0e9 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -21,63 +21,14 @@ #include "utils/textinputdialog.hpp" +#include + DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent) : mCfgMgr(cfg) , mGameSettings(gameSettings) , mLauncherSettings(launcherSettings) , ContentSelector(parent) { - setupUi(this); - buildModelsAndViews(); - /* - // Models - mDataFilesModel = new DataFilesModel (this); - - mMastersProxyModel = new MasterProxyModel (this, mDataFilesModel); - - mPluginsProxyModel = new PluginsProxyModel (this, mDataFilesModel); - - mFilterProxyModel = new QSortFilterProxyModel(); - mFilterProxyModel->setDynamicSortFilter(true); - mFilterProxyModel->setSourceModel(mPluginsProxyModel); - - masterView->setModel (mMastersProxyModel); - - //QCheckBox checkBox; - // unsigned int height = checkBox.sizeHint().height() + 4; - - - mastersTable->setModel(mMastersProxyModel); - mastersTable->setObjectName("MastersTable"); - mastersTable->setContextMenuPolicy(Qt::CustomContextMenu); - mastersTable->setSortingEnabled(false); - mastersTable->setSelectionBehavior(QAbstractItemView::SelectRows); - mastersTable->setSelectionMode(QAbstractItemView::ExtendedSelection); - mastersTable->setEditTriggers(QAbstractItemView::NoEditTriggers); - mastersTable->setAlternatingRowColors(true); - mastersTable->horizontalHeader()->setStretchLastSection(true); - mastersTable->horizontalHeader()->hide(); - - // Set the row height to the size of the checkboxes - mastersTable->verticalHeader()->setDefaultSectionSize(height); - mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); - mastersTable->verticalHeader()->hide(); - - pluginsTable->setModel(mFilterProxyModel); - pluginsTable->setObjectName("PluginsTable"); - pluginsTable->setContextMenuPolicy(Qt::CustomContextMenu); - pluginsTable->setSortingEnabled(false); - pluginsTable->setSelectionBehavior(QAbstractItemView::SelectRows); - pluginsTable->setSelectionMode(QAbstractItemView::ExtendedSelection); - pluginsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); - pluginsTable->setAlternatingRowColors(true); - pluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem); - pluginsTable->horizontalHeader()->setStretchLastSection(true); - pluginsTable->horizontalHeader()->hide(); - - //pluginsTable->verticalHeader()->setDefaultSectionSize(height); - //pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); -*/ // Adjust the tableview widths inside the splitter QList sizeList; sizeList << mLauncherSettings.value(QString("General/MastersTable/width"), QString("200")).toInt(); @@ -98,8 +49,6 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam connect(pluginsTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); //connect(mastersTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); - connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); - //connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); connect(splitter, SIGNAL(splitterMoved(int,int)), this, SLOT(updateSplitter())); @@ -123,6 +72,9 @@ void DataFilesPage::createActions() void DataFilesPage::setupDataFiles() { + if (!mDataFilesModel) + qDebug() << "data files model undefined"; + // Set the encoding to the one found in openmw.cfg or the default mDataFilesModel->setEncoding(mGameSettings.value(QString("encoding"), QString("win1252"))); @@ -384,8 +336,7 @@ void DataFilesPage::setPluginsCheckstates(Qt::CheckState state) if (!index.isValid()) return; - QModelIndex sourceIndex = mPluginsProxyModel->mapToSource( - mFilterProxyModel->mapToSource(index)); + QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(index); if (!sourceIndex.isValid()) return; @@ -394,48 +345,6 @@ void DataFilesPage::setPluginsCheckstates(Qt::CheckState state) } } -void DataFilesPage::setCheckState(QModelIndex index) -{ - if (!index.isValid()) - return; - - QObject *object = QObject::sender(); - - // Not a signal-slot call - if (!object) - return; - - - if (object->objectName() == QLatin1String("PluginsTable")) { - QModelIndex sourceIndex = mPluginsProxyModel->mapToSource( - mFilterProxyModel->mapToSource(index)); - - if (sourceIndex.isValid()) { - (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) - ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) - : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); - } - } - - if (object->objectName() == QLatin1String("MastersTable")) { - QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index); - - if (sourceIndex.isValid()) { - (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) - ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) - : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); - } - } - - return; -} - -void DataFilesPage::filterChanged(const QString filter) -{ - QRegExp regExp(filter, Qt::CaseInsensitive, QRegExp::FixedString); - mFilterProxyModel->setFilterRegExp(regExp); -} - void DataFilesPage::profileChanged(const QString &previous, const QString ¤t) { // Prevent the deletion of the default profile @@ -505,8 +414,7 @@ void DataFilesPage::showContextMenu(const QPoint &point) if (!index.isValid()) return; - QModelIndex sourceIndex = mPluginsProxyModel->mapToSource( - mFilterProxyModel->mapToSource(index)); + QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(index); if (!sourceIndex.isValid()) return; diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 99aa24ff3c..1c528ab26e 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -36,10 +36,8 @@ signals: void profileChanged(int index); public slots: - void setCheckState(QModelIndex index); void setProfilesComboBoxIndex(int index); - void filterChanged(const QString filter); void showContextMenu(const QPoint &point); void profileChanged(const QString &previous, const QString ¤t); void profileRenamed(const QString &previous, const QString ¤t); @@ -57,12 +55,6 @@ private slots: void slotCurrentIndexChanged(int index); private: - DataFilesModel *mDataFilesModel; - - PluginsProxyModel *mPluginsProxyModel; - QSortFilterProxyModel *mMastersProxyModel; - - QSortFilterProxyModel *mFilterProxyModel; QMenu *mContextMenu; diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index f770e80a1e..2f3a6dc219 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -21,55 +21,6 @@ FileDialog::FileDialog(QWidget *parent) : ContentSelector(parent) { - setupUi(this); - buildModelsAndViews(); - /* - // Models - mDataFilesModel = new DataFilesModel (this); - - mMastersProxyModel = new MasterProxyModel (this, mDataFilesModel); - mPluginsProxyModel = new PluginsProxyModel (this, mDataFilesModel); - - - mFilterProxyModel = new QSortFilterProxyModel(); - mFilterProxyModel->setDynamicSortFilter(true); - mFilterProxyModel->setSourceModel(mPluginsProxyModel); - -// QCheckBox checkBox; -// unsigned int height = checkBox.sizeHint().height() + 4; - - masterView->setModel(mMastersProxyModel); - - mastersTable->setModel(mMastersProxyModel); - mastersTable->setObjectName("MastersTable"); - mastersTable->setContextMenuPolicy(Qt::CustomContextMenu); - mastersTable->setSortingEnabled(false); - mastersTable->setSelectionBehavior(QAbstractItemView::SelectRows); - mastersTable->setSelectionMode(QAbstractItemView::ExtendedSelection); - mastersTable->setEditTriggers(QAbstractItemView::NoEditTriggers); - mastersTable->setAlternatingRowColors(true); - mastersTable->horizontalHeader()->setStretchLastSection(true); - - // Set the row height to the size of the checkboxes - mastersTable->verticalHeader()->setDefaultSectionSize(height); - mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); - mastersTable->verticalHeader()->hide(); - - pluginsTable->setModel(mFilterProxyModel); - pluginsTable->setObjectName("PluginsTable"); - pluginsTable->setContextMenuPolicy(Qt::CustomContextMenu); - pluginsTable->setSortingEnabled(false); - pluginsTable->setSelectionBehavior(QAbstractItemView::SelectRows); - pluginsTable->setSelectionMode(QAbstractItemView::ExtendedSelection); - pluginsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); - pluginsTable->setAlternatingRowColors(true); - pluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem); - pluginsTable->horizontalHeader()->setStretchLastSection(true); - -// pluginsTable->verticalHeader()->setDefaultSectionSize(height); -// pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); - - */ // Hide the profile elements profileLabel->hide(); profilesComboBox->hide(); @@ -107,7 +58,6 @@ FileDialog::FileDialog(QWidget *parent) : resize(600, 400); - // // connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList))); //connect(mNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateCreateButton(QString))); diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 2f4d4e381d..7944b7fb3d 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -53,12 +53,6 @@ private: QPushButton *mCreateButton; QDialogButtonBox *mButtonBox; - - DataFilesModel *mDataFilesModel; - - PluginsProxyModel *mPluginsProxyModel; - QSortFilterProxyModel *mMastersProxyModel; - QSortFilterProxyModel *mFilterProxyModel; }; #endif // FILEDIALOG_HPP diff --git a/components/fileorderlist/contentselector.cpp b/components/fileorderlist/contentselector.cpp index 16e0718910..6f8a86a490 100644 --- a/components/fileorderlist/contentselector.cpp +++ b/components/fileorderlist/contentselector.cpp @@ -9,6 +9,8 @@ FileOrderList::ContentSelector::ContentSelector(QWidget *parent) : QWidget(parent) { + setupUi(this); + buildModelsAndViews(); } void FileOrderList::ContentSelector::buildModelsAndViews() @@ -19,11 +21,6 @@ void FileOrderList::ContentSelector::buildModelsAndViews() mMasterProxyModel = new FileOrderList::MasterProxyModel (this, mDataFilesModel); mPluginsProxyModel = new PluginsProxyModel (this, mDataFilesModel); - - mFilterProxyModel = new QSortFilterProxyModel(); - mFilterProxyModel->setDynamicSortFilter(true); - mFilterProxyModel->setSourceModel(mPluginsProxyModel); - masterView->setModel(mMasterProxyModel); /* mastersTable->setModel(mMastersProxyModel); @@ -41,7 +38,7 @@ void FileOrderList::ContentSelector::buildModelsAndViews() mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); mastersTable->verticalHeader()->hide(); */ - pluginsTable->setModel(mFilterProxyModel); + pluginsTable->setModel(mPluginsProxyModel); pluginsTable->setObjectName("PluginsTable"); pluginsTable->setContextMenuPolicy(Qt::CustomContextMenu); pluginsTable->setSortingEnabled(false); @@ -79,8 +76,7 @@ void FileOrderList::ContentSelector::setCheckState(QModelIndex index) if (object->objectName() == QLatin1String("PluginsTable")) { - QModelIndex sourceIndex = mPluginsProxyModel->mapToSource( - mFilterProxyModel->mapToSource(index)); + QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(index); if (sourceIndex.isValid()) { (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) @@ -88,7 +84,7 @@ void FileOrderList::ContentSelector::setCheckState(QModelIndex index) : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); } } -/* + if (object->objectName() == QLatin1String("MastersTable")) { QModelIndex sourceIndex = mMasterProxyModel->mapToSource(index); @@ -98,7 +94,7 @@ void FileOrderList::ContentSelector::setCheckState(QModelIndex index) : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); } } -*/ + return; } @@ -110,16 +106,6 @@ QStringList FileOrderList::ContentSelector::checkedItemsPaths() void FileOrderList::ContentSelector::updateViews() { // Ensure the columns are hidden because sort() re-enables them - /* - mastersTable->setColumnHidden(1, true); - mastersTable->setColumnHidden(3, true); - mastersTable->setColumnHidden(4, true); - mastersTable->setColumnHidden(5, true); - mastersTable->setColumnHidden(6, true); - mastersTable->setColumnHidden(7, true); - mastersTable->setColumnHidden(8, true); - mastersTable->resizeColumnsToContents(); -*/ pluginsTable->setColumnHidden(1, true); pluginsTable->setColumnHidden(3, true); pluginsTable->setColumnHidden(4, true); diff --git a/components/fileorderlist/contentselector.hpp b/components/fileorderlist/contentselector.hpp index f17e3a9090..914e8bacb7 100644 --- a/components/fileorderlist/contentselector.hpp +++ b/components/fileorderlist/contentselector.hpp @@ -17,10 +17,11 @@ namespace FileOrderList { Q_OBJECT + protected: + DataFilesModel *mDataFilesModel; MasterProxyModel *mMasterProxyModel; PluginsProxyModel *mPluginsProxyModel; - QSortFilterProxyModel *mFilterProxyModel; public: explicit ContentSelector(QWidget *parent = 0); diff --git a/components/fileorderlist/model/pluginsproxymodel.cpp b/components/fileorderlist/model/pluginsproxymodel.cpp index f6864d85cd..9e3cdd7309 100644 --- a/components/fileorderlist/model/pluginsproxymodel.cpp +++ b/components/fileorderlist/model/pluginsproxymodel.cpp @@ -4,8 +4,9 @@ PluginsProxyModel::PluginsProxyModel(QObject *parent, DataFilesModel *model) : QSortFilterProxyModel(parent), mSourceModel (model) { - setFilterRegExp(QString("addon")); + setFilterRegExp (QString("addon")); setFilterRole (Qt::UserRole); + setDynamicSortFilter (true); if (model) setSourceModel (model); From 2878f51cd324c0dcc605b54f00b927a3dafac31c Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sat, 17 Aug 2013 19:40:28 -0500 Subject: [PATCH 011/148] Reimplemented dependency selection feature Moved more code to ContentSelector Added support for omwgame and omwaddon files --- apps/launcher/datafilespage.cpp | 125 +++--------------- apps/launcher/datafilespage.hpp | 8 +- apps/opencs/view/doc/filedialog.cpp | 19 --- apps/opencs/view/doc/filedialog.hpp | 3 - components/fileorderlist/contentselector.cpp | 99 ++++++-------- components/fileorderlist/contentselector.hpp | 7 +- .../fileorderlist/model/datafilesmodel.cpp | 15 +-- .../fileorderlist/model/pluginsproxymodel.cpp | 4 +- .../fileorderlist/model/pluginsproxymodel.hpp | 2 - files/ui/datafilespage.ui | 44 +++--- 10 files changed, 99 insertions(+), 227 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index d077c3e0e9..5cbcae5459 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -29,29 +29,14 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam , mLauncherSettings(launcherSettings) , ContentSelector(parent) { - // Adjust the tableview widths inside the splitter - QList sizeList; - sizeList << mLauncherSettings.value(QString("General/MastersTable/width"), QString("200")).toInt(); - sizeList << mLauncherSettings.value(QString("General/PluginTable/width"), QString("340")).toInt(); - - splitter->setSizes(sizeList); // Create a dialog for the new profile name input mNewProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this); - connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentIndexChanged(int))); - connect(mNewProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString))); - connect(pluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); - //connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); - - connect(pluginsTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); - //connect(mastersTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); - - //connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); - - connect(splitter, SIGNAL(splitterMoved(int,int)), this, SLOT(updateSplitter())); + //connect(pluginView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); + //connect(masterView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); createActions(); setupDataFiles(); @@ -193,49 +178,11 @@ void DataFilesPage::updateOkButton(const QString &text) : mNewProfileDialog->setOkButtonEnabled(false); } -void DataFilesPage::updateSplitter() -{ - // Sigh, update the saved splitter size in settings only when moved - // Since getting mSplitter->sizes() if page is hidden returns invalid values - QList sizes = splitter->sizes(); - - mLauncherSettings.setValue(QString("General/MastersTable/width"), QString::number(sizes.at(0))); - mLauncherSettings.setValue(QString("General/PluginsTable/width"), QString::number(sizes.at(1))); -} - -void DataFilesPage::updateViews() -{ - // Ensure the columns are hidden because sort() re-enables them - /* - mastersTable->setColumnHidden(1, true); - mastersTable->setColumnHidden(2, true); - mastersTable->setColumnHidden(3, true); - mastersTable->setColumnHidden(4, true); - mastersTable->setColumnHidden(5, true); - mastersTable->setColumnHidden(6, true); - mastersTable->setColumnHidden(7, true); - mastersTable->setColumnHidden(8, true); -*/ - pluginsTable->setColumnHidden(1, true); - pluginsTable->setColumnHidden(2, true); - pluginsTable->setColumnHidden(3, true); - pluginsTable->setColumnHidden(4, true); - pluginsTable->setColumnHidden(5, true); - pluginsTable->setColumnHidden(6, true); - pluginsTable->setColumnHidden(7, true); - pluginsTable->setColumnHidden(8, true); -} - void DataFilesPage::setProfilesComboBoxIndex(int index) { profilesComboBox->setCurrentIndex(index); } -void DataFilesPage::slotCurrentIndexChanged(int index) -{ - emit profileChanged(index); -} - QAbstractItemModel* DataFilesPage::profilesComboBoxModel() { return profilesComboBox->model(); @@ -282,54 +229,13 @@ void DataFilesPage::on_deleteProfileAction_triggered() } } -void DataFilesPage::on_checkAction_triggered() -{ - if (pluginsTable->hasFocus()) - setPluginsCheckstates(Qt::Checked); - - //if (mastersTable->hasFocus()) - // setMastersCheckstates(Qt::Checked); - -} - -void DataFilesPage::on_uncheckAction_triggered() -{ - if (pluginsTable->hasFocus()) - setPluginsCheckstates(Qt::Unchecked); - - //if (mastersTable->hasFocus()) - // setMastersCheckstates(Qt::Unchecked); -} - -void DataFilesPage::setMastersCheckstates(Qt::CheckState state) -{/* - //if (!mastersTable->selectionModel()->hasSelection()) { - // return; - //} - - //QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes(); - - foreach (const QModelIndex &index, indexes) - { - if (!index.isValid()) - return; - - QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index); - - if (!sourceIndex.isValid()) - return; - - mDataFilesModel->setCheckState(sourceIndex, state); - }*/ -} - void DataFilesPage::setPluginsCheckstates(Qt::CheckState state) { - if (!pluginsTable->selectionModel()->hasSelection()) { + if (!pluginView->selectionModel()->hasSelection()) { return; } - QModelIndexList indexes = pluginsTable->selectionModel()->selectedIndexes(); + QModelIndexList indexes = pluginView->selectionModel()->selectedIndexes(); foreach (const QModelIndex &index, indexes) { @@ -389,7 +295,7 @@ void DataFilesPage::profileRenamed(const QString &previous, const QString &curre loadSettings(); } - +/* void DataFilesPage::showContextMenu(const QPoint &point) { QObject *object = QObject::sender(); @@ -398,12 +304,12 @@ void DataFilesPage::showContextMenu(const QPoint &point) if (!object) return; - if (object->objectName() == QLatin1String("PluginsTable")) { - if (!pluginsTable->selectionModel()->hasSelection()) + if (object->objectName() == QLatin1String("PluginView")) { + if (!pluginView->selectionModel()->hasSelection()) return; - QPoint globalPos = pluginsTable->mapToGlobal(point); - QModelIndexList indexes = pluginsTable->selectionModel()->selectedIndexes(); + QPoint globalPos = pluginView->mapToGlobal(point); + QModelIndexList indexes = pluginView->selectionModel()->selectedIndexes(); // Show the check/uncheck actions depending on the state of the selected items uncheckAction->setEnabled(false); @@ -427,13 +333,13 @@ void DataFilesPage::showContextMenu(const QPoint &point) // Show menu mContextMenu->exec(globalPos); } -/* - if (object->objectName() == QLatin1String("MastersTable")) { - if (!mastersTable->selectionModel()->hasSelection()) + + if (object->objectName() == QLatin1String("MasterView")) { + if (!masterView->selectionModel()->hasSelection()) return; - QPoint globalPos = mastersTable->mapToGlobal(point); - QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes(); + QPoint globalPos = masterView->mapToGlobal(point); + QModelIndexList indexes = masterView->selectionModel()->selectedIndexes(); // Show the check/uncheck actions depending on the state of the selected items uncheckAction->setEnabled(false); @@ -456,5 +362,6 @@ void DataFilesPage::showContextMenu(const QPoint &point) mContextMenu->exec(globalPos); } - */ + } +*/ diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 1c528ab26e..db391519f4 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -38,21 +38,16 @@ signals: public slots: void setProfilesComboBoxIndex(int index); - void showContextMenu(const QPoint &point); + //void showContextMenu(const QPoint &point); void profileChanged(const QString &previous, const QString ¤t); void profileRenamed(const QString &previous, const QString ¤t); void updateOkButton(const QString &text); - void updateSplitter(); - void updateViews(); // Action slots void on_newProfileAction_triggered(); void on_deleteProfileAction_triggered(); - void on_checkAction_triggered(); - void on_uncheckAction_triggered(); private slots: - void slotCurrentIndexChanged(int index); private: @@ -65,7 +60,6 @@ private: TextInputDialog *mNewProfileDialog; - void setMastersCheckstates(Qt::CheckState state); void setPluginsCheckstates(Qt::CheckState state); void createActions(); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 2f3a6dc219..9f1c72068e 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -49,23 +49,11 @@ FileDialog::FileDialog(QWidget *parent) : verticalLayout->addLayout(nameLayout); verticalLayout->addWidget(mButtonBox); - // Set sizes - QList sizeList; - sizeList << 175; - sizeList << 200; - - splitter->setSizes(sizeList); - resize(600, 400); // connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList))); //connect(mNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateCreateButton(QString))); - //connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); - - // connect(pluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); - //connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); - // connect(mCreateButton, SIGNAL(clicked()), this, SLOT(createButtonClicked())); // connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept())); @@ -89,13 +77,6 @@ void FileDialog::updateCreateButton(const QString &name) mCreateButton->setEnabled(!name.isEmpty()); } -/* -void FileDialog::filterChanged(const QString &filter) -{ - QRegExp filterRe(filter, Qt::CaseInsensitive, QRegExp::FixedString); - mFilterProxyModel->setFilterRegExp(filterRe); -} -*/ QString FileDialog::fileName() { diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 7944b7fb3d..232f250beb 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -42,9 +42,6 @@ private slots: void updateOpenButton(const QStringList &items); void updateCreateButton(const QString &name); - - //void filterChanged(const QString &filter); - void createButtonClicked(); private: diff --git a/components/fileorderlist/contentselector.cpp b/components/fileorderlist/contentselector.cpp index 6f8a86a490..e7ab9b0cfc 100644 --- a/components/fileorderlist/contentselector.cpp +++ b/components/fileorderlist/contentselector.cpp @@ -6,6 +6,7 @@ #include +#include FileOrderList::ContentSelector::ContentSelector(QWidget *parent) : QWidget(parent) { @@ -22,40 +23,20 @@ void FileOrderList::ContentSelector::buildModelsAndViews() mPluginsProxyModel = new PluginsProxyModel (this, mDataFilesModel); masterView->setModel(mMasterProxyModel); -/* - mastersTable->setModel(mMastersProxyModel); - mastersTable->setObjectName("MastersTable"); - mastersTable->setContextMenuPolicy(Qt::CustomContextMenu); - mastersTable->setSortingEnabled(false); - mastersTable->setSelectionBehavior(QAbstractItemView::SelectRows); - mastersTable->setSelectionMode(QAbstractItemView::ExtendedSelection); - mastersTable->setEditTriggers(QAbstractItemView::NoEditTriggers); - mastersTable->setAlternatingRowColors(true); - mastersTable->horizontalHeader()->setStretchLastSection(true); - - // Set the row height to the size of the checkboxes - mastersTable->verticalHeader()->setDefaultSectionSize(height); - mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); - mastersTable->verticalHeader()->hide(); -*/ - pluginsTable->setModel(mPluginsProxyModel); - pluginsTable->setObjectName("PluginsTable"); - pluginsTable->setContextMenuPolicy(Qt::CustomContextMenu); - pluginsTable->setSortingEnabled(false); - pluginsTable->setSelectionBehavior(QAbstractItemView::SelectRows); - pluginsTable->setSelectionMode(QAbstractItemView::ExtendedSelection); - pluginsTable->setEditTriggers(QAbstractItemView::NoEditTriggers); - pluginsTable->setAlternatingRowColors(true); - pluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem); - pluginsTable->horizontalHeader()->setStretchLastSection(true); + pluginView->setModel(mPluginsProxyModel); connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); + connect(masterView, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentMasterIndexChanged(int))); + + connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); } void FileOrderList::ContentSelector::addFiles(const QString &path) { mDataFilesModel->addFiles(path); mDataFilesModel->sort(3); // Sort by date accessed + masterView->setCurrentIndex(-1); + mDataFilesModel->uncheckAll(); } void FileOrderList::ContentSelector::setEncoding(const QString &encoding) @@ -63,39 +44,22 @@ void FileOrderList::ContentSelector::setEncoding(const QString &encoding) mDataFilesModel->setEncoding(encoding); } -void FileOrderList::ContentSelector::setCheckState(QModelIndex index) +void FileOrderList::ContentSelector::setCheckState(QModelIndex index, QSortFilterProxyModel *model) { if (!index.isValid()) return; - QObject *object = QObject::sender(); - - // Not a signal-slot call - if (!object) + if (!model) return; + QModelIndex sourceIndex = model->mapToSource(index); - if (object->objectName() == QLatin1String("PluginsTable")) { - QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(index); - - if (sourceIndex.isValid()) { - (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) - ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) - : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); - } + if (sourceIndex.isValid()) + { + (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) + ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) + : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); } - - if (object->objectName() == QLatin1String("MastersTable")) { - QModelIndex sourceIndex = mMasterProxyModel->mapToSource(index); - - if (sourceIndex.isValid()) { - (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) - ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) - : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); - } - } - - return; } QStringList FileOrderList::ContentSelector::checkedItemsPaths() @@ -106,13 +70,30 @@ QStringList FileOrderList::ContentSelector::checkedItemsPaths() void FileOrderList::ContentSelector::updateViews() { // Ensure the columns are hidden because sort() re-enables them - pluginsTable->setColumnHidden(1, true); - pluginsTable->setColumnHidden(3, true); - pluginsTable->setColumnHidden(4, true); - pluginsTable->setColumnHidden(5, true); - pluginsTable->setColumnHidden(6, true); - pluginsTable->setColumnHidden(7, true); - pluginsTable->setColumnHidden(8, true); - pluginsTable->resizeColumnsToContents(); + pluginView->setColumnHidden(1, true); + pluginView->setColumnHidden(3, true); + pluginView->setColumnHidden(4, true); + pluginView->setColumnHidden(5, true); + pluginView->setColumnHidden(6, true); + pluginView->setColumnHidden(7, true); + pluginView->setColumnHidden(8, true); + pluginView->resizeColumnsToContents(); } + +void FileOrderList::ContentSelector::slotCurrentProfileIndexChanged(int index) +{ + emit profileChanged(index); +} + +void FileOrderList::ContentSelector::slotCurrentMasterIndexChanged(int index) +{ + qDebug() << "index Changed: " << index; + QObject *object = QObject::sender(); + + // Not a signal-slot call + if (!object) + return; + + setCheckState(mMasterProxyModel->index(index, 0), mMasterProxyModel); +} diff --git a/components/fileorderlist/contentselector.hpp b/components/fileorderlist/contentselector.hpp index 914e8bacb7..138ffc6298 100644 --- a/components/fileorderlist/contentselector.hpp +++ b/components/fileorderlist/contentselector.hpp @@ -30,11 +30,16 @@ namespace FileOrderList void addFiles(const QString &path); void setEncoding(const QString &encoding); - void setCheckState(QModelIndex index); + void setCheckState(QModelIndex index, QSortFilterProxyModel *model); QStringList checkedItemsPaths(); + signals: + void profileChanged(int index); + private slots: void updateViews(); + void slotCurrentProfileIndexChanged(int index); + void slotCurrentMasterIndexChanged(int index); }; } diff --git a/components/fileorderlist/model/datafilesmodel.cpp b/components/fileorderlist/model/datafilesmodel.cpp index 3f63e73cce..8e27f1f751 100644 --- a/components/fileorderlist/model/datafilesmodel.cpp +++ b/components/fileorderlist/model/datafilesmodel.cpp @@ -174,7 +174,7 @@ Qt::ItemFlags DataFilesModel::flags(const QModelIndex &index) const if (index.column() == 0) { return Qt::ItemIsUserCheckable | Qt::ItemIsSelectable; } else { - return Qt::NoItemFlags | Qt::ItemIsSelectable; + return Qt::ItemIsSelectable; } } @@ -270,7 +270,7 @@ void DataFilesModel::addFiles(const QString &path) { QDir dir(path); QStringList filters; - filters << "*.esp" << "*.esm"; + filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon"; dir.setNameFilters(filters); // Create a decoder for non-latin characters in esx metadata @@ -319,9 +319,10 @@ void DataFilesModel::addFiles(const QString &path) // 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 esp: " << e.what(); + qWarning() << "Error reading addon file: " << e.what(); continue; } @@ -436,14 +437,10 @@ QStringList DataFilesModel::uncheckedItems() bool DataFilesModel::canBeChecked(EsmFile *file) const { //element can be checked if all its dependencies are - bool canBeChecked = true; foreach (const QString &master, file->masters()) { if (!mCheckStates.contains(master) || mCheckStates[master] != Qt::Checked) - { - canBeChecked = false; - break; - } + return false; } - return canBeChecked; + return true; } diff --git a/components/fileorderlist/model/pluginsproxymodel.cpp b/components/fileorderlist/model/pluginsproxymodel.cpp index 9e3cdd7309..18aebc6b68 100644 --- a/components/fileorderlist/model/pluginsproxymodel.cpp +++ b/components/fileorderlist/model/pluginsproxymodel.cpp @@ -2,7 +2,7 @@ #include "datafilesmodel.hpp" PluginsProxyModel::PluginsProxyModel(QObject *parent, DataFilesModel *model) : - QSortFilterProxyModel(parent), mSourceModel (model) + QSortFilterProxyModel(parent) { setFilterRegExp (QString("addon")); setFilterRole (Qt::UserRole); @@ -25,7 +25,7 @@ QVariant PluginsProxyModel::data(const QModelIndex &index, int role) const if (index.column() != 0) return QVariant(); - return mSourceModel->checkState(index); + return static_cast(sourceModel())->checkState(mapToSource(index)); } }; diff --git a/components/fileorderlist/model/pluginsproxymodel.hpp b/components/fileorderlist/model/pluginsproxymodel.hpp index e148ea3b16..cfade092eb 100644 --- a/components/fileorderlist/model/pluginsproxymodel.hpp +++ b/components/fileorderlist/model/pluginsproxymodel.hpp @@ -11,8 +11,6 @@ class PluginsProxyModel : public QSortFilterProxyModel { Q_OBJECT - DataFilesModel *mSourceModel; - public: explicit PluginsProxyModel(QObject *parent = 0, DataFilesModel *model = 0); diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 816d288a65..87213c5cc8 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -10,7 +10,7 @@ 304 - + @@ -23,21 +23,33 @@ - - - - 0 - 0 - - - - Qt::Horizontal - - - false - - - + + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectItems + + + Qt::ElideLeft + + + false + + + false + + + false + + + + From a9db983233dde491c0b90b53ca427e4427d0188f Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 18 Aug 2013 07:29:48 -0500 Subject: [PATCH 012/148] Fixing row-click selection --- components/fileorderlist/contentselector.cpp | 2 +- files/ui/datafilespage.ui | 24 ++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/components/fileorderlist/contentselector.cpp b/components/fileorderlist/contentselector.cpp index e7ab9b0cfc..6254601dc6 100644 --- a/components/fileorderlist/contentselector.cpp +++ b/components/fileorderlist/contentselector.cpp @@ -26,8 +26,8 @@ void FileOrderList::ContentSelector::buildModelsAndViews() pluginView->setModel(mPluginsProxyModel); connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); + //connect(pluginView, SIGNAL()) connect(masterView, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentMasterIndexChanged(int))); - connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); } diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 87213c5cc8..cf6f30b998 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -29,11 +29,14 @@ QAbstractItemView::NoEditTriggers + + true + QAbstractItemView::SingleSelection - QAbstractItemView::SelectItems + QAbstractItemView::SelectRows Qt::ElideLeft @@ -153,5 +156,22 @@ - + + + pluginView + clicked(QModelIndex) + checkAction + toggle() + + + 258 + 151 + + + -1 + -1 + + + + From 66e50343adba0bfca059da804dee0b865eba84d9 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 18 Aug 2013 08:54:51 -0500 Subject: [PATCH 013/148] Fixed row-selection/check feature --- components/fileorderlist/contentselector.cpp | 11 ++++++-- components/fileorderlist/contentselector.hpp | 3 ++ .../fileorderlist/model/datafilesmodel.cpp | 27 +++++++++--------- files/ui/datafilespage.ui | 28 +++++++------------ 4 files changed, 35 insertions(+), 34 deletions(-) diff --git a/components/fileorderlist/contentselector.cpp b/components/fileorderlist/contentselector.cpp index 6254601dc6..b2a5c5ba59 100644 --- a/components/fileorderlist/contentselector.cpp +++ b/components/fileorderlist/contentselector.cpp @@ -7,6 +7,9 @@ #include #include +#include +#include + FileOrderList::ContentSelector::ContentSelector(QWidget *parent) : QWidget(parent) { @@ -26,7 +29,7 @@ void FileOrderList::ContentSelector::buildModelsAndViews() pluginView->setModel(mPluginsProxyModel); connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); - //connect(pluginView, SIGNAL()) + 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))); } @@ -88,7 +91,6 @@ void FileOrderList::ContentSelector::slotCurrentProfileIndexChanged(int index) void FileOrderList::ContentSelector::slotCurrentMasterIndexChanged(int index) { - qDebug() << "index Changed: " << index; QObject *object = QObject::sender(); // Not a signal-slot call @@ -97,3 +99,8 @@ void FileOrderList::ContentSelector::slotCurrentMasterIndexChanged(int index) setCheckState(mMasterProxyModel->index(index, 0), mMasterProxyModel); } + +void FileOrderList::ContentSelector::slotPluginTableItemClicked(const QModelIndex &index) +{ + setCheckState(index, mPluginsProxyModel); +} diff --git a/components/fileorderlist/contentselector.hpp b/components/fileorderlist/contentselector.hpp index 138ffc6298..f1d8f6927a 100644 --- a/components/fileorderlist/contentselector.hpp +++ b/components/fileorderlist/contentselector.hpp @@ -30,8 +30,10 @@ namespace FileOrderList void addFiles(const QString &path); void setEncoding(const QString &encoding); + void setPluginCheckState(); void setCheckState(QModelIndex index, QSortFilterProxyModel *model); QStringList checkedItemsPaths(); + void on_checkAction_triggered(); signals: void profileChanged(int index); @@ -40,6 +42,7 @@ namespace FileOrderList void updateViews(); void slotCurrentProfileIndexChanged(int index); void slotCurrentMasterIndexChanged(int index); + void slotPluginTableItemClicked(const QModelIndex &index); }; } diff --git a/components/fileorderlist/model/datafilesmodel.cpp b/components/fileorderlist/model/datafilesmodel.cpp index 8e27f1f751..ae842381e7 100644 --- a/components/fileorderlist/model/datafilesmodel.cpp +++ b/components/fileorderlist/model/datafilesmodel.cpp @@ -30,7 +30,19 @@ void DataFilesModel::setEncoding(const QString &encoding) void DataFilesModel::setCheckState(const QModelIndex &index, Qt::CheckState state) { - setData(index, state, Qt::CheckStateRole); + 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()); + } Qt::CheckState DataFilesModel::checkState(const QModelIndex &index) @@ -210,19 +222,6 @@ bool DataFilesModel::setData(const QModelIndex &index, const QVariant &value, in if (!index.isValid()) return false; - if (role == Qt::CheckStateRole) { - QString name = item(index.row())->fileName(); - mCheckStates[name] = static_cast(value.toInt()); - - // 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()); - return true; - } - return false; } diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index cf6f30b998..a6c1768b5d 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -10,6 +10,9 @@ 304 + + Qt::DefaultContextMenu + @@ -26,6 +29,9 @@ + + Qt::DefaultContextMenu + QAbstractItemView::NoEditTriggers @@ -138,6 +144,9 @@ + + true + Check Selection @@ -156,22 +165,5 @@ - - - pluginView - clicked(QModelIndex) - checkAction - toggle() - - - 258 - 151 - - - -1 - -1 - - - - + From 45277c00825c759bb826a25e0e73cf940968086f Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 18 Aug 2013 09:34:33 -0500 Subject: [PATCH 014/148] Minor changes Stretched table columns to fit widget width Reduced width of opencs file dialog Hid the file size column for launcher Added alternating row colors in table view --- apps/launcher/datafilespage.cpp | 1 + apps/opencs/view/doc/filedialog.cpp | 2 +- components/fileorderlist/contentselector.cpp | 1 + files/ui/datafilespage.ui | 9 +++++++++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 5cbcae5459..44f92d7fe0 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -30,6 +30,7 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam , ContentSelector(parent) { + pluginView->hideColumn(2); // Create a dialog for the new profile name input mNewProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 9f1c72068e..252c760faa 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -49,7 +49,7 @@ FileDialog::FileDialog(QWidget *parent) : verticalLayout->addLayout(nameLayout); verticalLayout->addWidget(mButtonBox); - resize(600, 400); + resize(400, 400); // connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList))); //connect(mNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateCreateButton(QString))); diff --git a/components/fileorderlist/contentselector.cpp b/components/fileorderlist/contentselector.cpp index b2a5c5ba59..27be996fc8 100644 --- a/components/fileorderlist/contentselector.cpp +++ b/components/fileorderlist/contentselector.cpp @@ -27,6 +27,7 @@ void FileOrderList::ContentSelector::buildModelsAndViews() masterView->setModel(mMasterProxyModel); pluginView->setModel(mPluginsProxyModel); + pluginView-> connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); connect(pluginView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotPluginTableItemClicked(const QModelIndex &))); diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index a6c1768b5d..07ad9d3ba6 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -29,6 +29,12 @@ + + + 0 + 0 + + Qt::DefaultContextMenu @@ -53,6 +59,9 @@ false + + true + false From d0363b037cc9716ceb5f7d03594a1a836e8724c1 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 18 Aug 2013 09:41:02 -0500 Subject: [PATCH 015/148] Renamed components/fileorderlist to components/esxselector --- components/CMakeLists.txt | 2 +- components/{fileorderlist => esxselector}/contentselector.cpp | 0 components/{fileorderlist => esxselector}/contentselector.hpp | 0 components/{fileorderlist => esxselector}/masterproxymodel.cpp | 0 components/{fileorderlist => esxselector}/masterproxymodel.hpp | 0 .../{fileorderlist => esxselector}/model/datafilesmodel.cpp | 0 .../{fileorderlist => esxselector}/model/datafilesmodel.hpp | 0 components/{fileorderlist => esxselector}/model/esm/esmfile.cpp | 0 components/{fileorderlist => esxselector}/model/esm/esmfile.hpp | 0 components/{fileorderlist => esxselector}/model/modelitem.cpp | 0 components/{fileorderlist => esxselector}/model/modelitem.hpp | 0 .../{fileorderlist => esxselector}/model/pluginsproxymodel.cpp | 0 .../{fileorderlist => esxselector}/model/pluginsproxymodel.hpp | 0 .../{fileorderlist => esxselector}/utils/comboboxlineedit.cpp | 0 .../{fileorderlist => esxselector}/utils/comboboxlineedit.hpp | 0 components/{fileorderlist => esxselector}/utils/lineedit.cpp | 0 components/{fileorderlist => esxselector}/utils/lineedit.hpp | 0 components/{fileorderlist => esxselector}/utils/naturalsort.cpp | 0 components/{fileorderlist => esxselector}/utils/naturalsort.hpp | 0 .../{fileorderlist => esxselector}/utils/profilescombobox.cpp | 0 .../{fileorderlist => esxselector}/utils/profilescombobox.hpp | 0 21 files changed, 1 insertion(+), 1 deletion(-) rename components/{fileorderlist => esxselector}/contentselector.cpp (100%) rename components/{fileorderlist => esxselector}/contentselector.hpp (100%) rename components/{fileorderlist => esxselector}/masterproxymodel.cpp (100%) rename components/{fileorderlist => esxselector}/masterproxymodel.hpp (100%) rename components/{fileorderlist => esxselector}/model/datafilesmodel.cpp (100%) rename components/{fileorderlist => esxselector}/model/datafilesmodel.hpp (100%) rename components/{fileorderlist => esxselector}/model/esm/esmfile.cpp (100%) rename components/{fileorderlist => esxselector}/model/esm/esmfile.hpp (100%) rename components/{fileorderlist => esxselector}/model/modelitem.cpp (100%) rename components/{fileorderlist => esxselector}/model/modelitem.hpp (100%) rename components/{fileorderlist => esxselector}/model/pluginsproxymodel.cpp (100%) rename components/{fileorderlist => esxselector}/model/pluginsproxymodel.hpp (100%) rename components/{fileorderlist => esxselector}/utils/comboboxlineedit.cpp (100%) rename components/{fileorderlist => esxselector}/utils/comboboxlineedit.hpp (100%) rename components/{fileorderlist => esxselector}/utils/lineedit.cpp (100%) rename components/{fileorderlist => esxselector}/utils/lineedit.hpp (100%) rename components/{fileorderlist => esxselector}/utils/naturalsort.cpp (100%) rename components/{fileorderlist => esxselector}/utils/naturalsort.hpp (100%) rename components/{fileorderlist => esxselector}/utils/profilescombobox.cpp (100%) rename components/{fileorderlist => esxselector}/utils/profilescombobox.hpp (100%) diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 19af6c3b21..85f1a3508e 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -71,7 +71,7 @@ set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui find_package(Qt4 COMPONENTS QtCore QtGui) if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) - add_component_qt_dir (fileorderlist + add_component_qt_dir (esxselector masterproxymodel contentselector model/modelitem model/datafilesmodel model/pluginsproxymodel model/esm/esmfile utils/profilescombobox utils/comboboxlineedit utils/lineedit utils/naturalsort diff --git a/components/fileorderlist/contentselector.cpp b/components/esxselector/contentselector.cpp similarity index 100% rename from components/fileorderlist/contentselector.cpp rename to components/esxselector/contentselector.cpp diff --git a/components/fileorderlist/contentselector.hpp b/components/esxselector/contentselector.hpp similarity index 100% rename from components/fileorderlist/contentselector.hpp rename to components/esxselector/contentselector.hpp diff --git a/components/fileorderlist/masterproxymodel.cpp b/components/esxselector/masterproxymodel.cpp similarity index 100% rename from components/fileorderlist/masterproxymodel.cpp rename to components/esxselector/masterproxymodel.cpp diff --git a/components/fileorderlist/masterproxymodel.hpp b/components/esxselector/masterproxymodel.hpp similarity index 100% rename from components/fileorderlist/masterproxymodel.hpp rename to components/esxselector/masterproxymodel.hpp diff --git a/components/fileorderlist/model/datafilesmodel.cpp b/components/esxselector/model/datafilesmodel.cpp similarity index 100% rename from components/fileorderlist/model/datafilesmodel.cpp rename to components/esxselector/model/datafilesmodel.cpp diff --git a/components/fileorderlist/model/datafilesmodel.hpp b/components/esxselector/model/datafilesmodel.hpp similarity index 100% rename from components/fileorderlist/model/datafilesmodel.hpp rename to components/esxselector/model/datafilesmodel.hpp diff --git a/components/fileorderlist/model/esm/esmfile.cpp b/components/esxselector/model/esm/esmfile.cpp similarity index 100% rename from components/fileorderlist/model/esm/esmfile.cpp rename to components/esxselector/model/esm/esmfile.cpp diff --git a/components/fileorderlist/model/esm/esmfile.hpp b/components/esxselector/model/esm/esmfile.hpp similarity index 100% rename from components/fileorderlist/model/esm/esmfile.hpp rename to components/esxselector/model/esm/esmfile.hpp diff --git a/components/fileorderlist/model/modelitem.cpp b/components/esxselector/model/modelitem.cpp similarity index 100% rename from components/fileorderlist/model/modelitem.cpp rename to components/esxselector/model/modelitem.cpp diff --git a/components/fileorderlist/model/modelitem.hpp b/components/esxselector/model/modelitem.hpp similarity index 100% rename from components/fileorderlist/model/modelitem.hpp rename to components/esxselector/model/modelitem.hpp diff --git a/components/fileorderlist/model/pluginsproxymodel.cpp b/components/esxselector/model/pluginsproxymodel.cpp similarity index 100% rename from components/fileorderlist/model/pluginsproxymodel.cpp rename to components/esxselector/model/pluginsproxymodel.cpp diff --git a/components/fileorderlist/model/pluginsproxymodel.hpp b/components/esxselector/model/pluginsproxymodel.hpp similarity index 100% rename from components/fileorderlist/model/pluginsproxymodel.hpp rename to components/esxselector/model/pluginsproxymodel.hpp diff --git a/components/fileorderlist/utils/comboboxlineedit.cpp b/components/esxselector/utils/comboboxlineedit.cpp similarity index 100% rename from components/fileorderlist/utils/comboboxlineedit.cpp rename to components/esxselector/utils/comboboxlineedit.cpp diff --git a/components/fileorderlist/utils/comboboxlineedit.hpp b/components/esxselector/utils/comboboxlineedit.hpp similarity index 100% rename from components/fileorderlist/utils/comboboxlineedit.hpp rename to components/esxselector/utils/comboboxlineedit.hpp diff --git a/components/fileorderlist/utils/lineedit.cpp b/components/esxselector/utils/lineedit.cpp similarity index 100% rename from components/fileorderlist/utils/lineedit.cpp rename to components/esxselector/utils/lineedit.cpp diff --git a/components/fileorderlist/utils/lineedit.hpp b/components/esxselector/utils/lineedit.hpp similarity index 100% rename from components/fileorderlist/utils/lineedit.hpp rename to components/esxselector/utils/lineedit.hpp diff --git a/components/fileorderlist/utils/naturalsort.cpp b/components/esxselector/utils/naturalsort.cpp similarity index 100% rename from components/fileorderlist/utils/naturalsort.cpp rename to components/esxselector/utils/naturalsort.cpp diff --git a/components/fileorderlist/utils/naturalsort.hpp b/components/esxselector/utils/naturalsort.hpp similarity index 100% rename from components/fileorderlist/utils/naturalsort.hpp rename to components/esxselector/utils/naturalsort.hpp diff --git a/components/fileorderlist/utils/profilescombobox.cpp b/components/esxselector/utils/profilescombobox.cpp similarity index 100% rename from components/fileorderlist/utils/profilescombobox.cpp rename to components/esxselector/utils/profilescombobox.cpp diff --git a/components/fileorderlist/utils/profilescombobox.hpp b/components/esxselector/utils/profilescombobox.hpp similarity index 100% rename from components/fileorderlist/utils/profilescombobox.hpp rename to components/esxselector/utils/profilescombobox.hpp From a14e0b32d8daa1fad311d535a65bb14c5b123b91 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 18 Aug 2013 09:48:13 -0500 Subject: [PATCH 016/148] Restructured esxselector directory Added ./view Removed ./utils and ./model/esm Relocated code accordingly. --- apps/launcher/datafilespage.hpp | 2 +- apps/opencs/view/doc/filedialog.hpp | 2 +- components/CMakeLists.txt | 7 +-- components/esxselector/model/esm/esmfile.cpp | 50 ----------------- components/esxselector/model/esm/esmfile.hpp | 54 ------------------- .../{ => model}/masterproxymodel.cpp | 4 +- .../{ => model}/masterproxymodel.hpp | 2 +- .../{utils => model}/naturalsort.cpp | 0 .../{utils => model}/naturalsort.hpp | 0 .../{utils => view}/comboboxlineedit.cpp | 0 .../{utils => view}/comboboxlineedit.hpp | 0 .../{ => view}/contentselector.cpp | 22 ++++---- .../{ => view}/contentselector.hpp | 0 .../esxselector/{utils => view}/lineedit.cpp | 0 .../esxselector/{utils => view}/lineedit.hpp | 0 .../{utils => view}/profilescombobox.cpp | 0 .../{utils => view}/profilescombobox.hpp | 0 17 files changed, 20 insertions(+), 123 deletions(-) delete mode 100644 components/esxselector/model/esm/esmfile.cpp delete mode 100644 components/esxselector/model/esm/esmfile.hpp rename components/esxselector/{ => model}/masterproxymodel.cpp (57%) rename components/esxselector/{ => model}/masterproxymodel.hpp (95%) rename components/esxselector/{utils => model}/naturalsort.cpp (100%) rename components/esxselector/{utils => model}/naturalsort.hpp (100%) rename components/esxselector/{utils => view}/comboboxlineedit.cpp (100%) rename components/esxselector/{utils => view}/comboboxlineedit.hpp (100%) rename components/esxselector/{ => view}/contentselector.cpp (74%) rename components/esxselector/{ => view}/contentselector.hpp (100%) rename components/esxselector/{utils => view}/lineedit.cpp (100%) rename components/esxselector/{utils => view}/lineedit.hpp (100%) rename components/esxselector/{utils => view}/profilescombobox.cpp (100%) rename components/esxselector/{utils => view}/profilescombobox.hpp (100%) diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index db391519f4..356cb88d63 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -20,7 +20,7 @@ class PluginsProxyModel; namespace Files { struct ConfigurationManager; } -class DataFilesPage : public FileOrderList::ContentSelector +class DataFilesPage : public EsxSelector::ContentSelector { Q_OBJECT public: diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 232f250beb..1b4d09745c 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -18,7 +18,7 @@ class QMenu; class DataFilesModel; class PluginsProxyModel; -class FileDialog : public FileOrderList::ContentSelector +class FileDialog : public EsxSelector::ContentSelector { Q_OBJECT public: diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 85f1a3508e..4f14fa9d96 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -72,9 +72,10 @@ find_package(Qt4 COMPONENTS QtCore QtGui) if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) add_component_qt_dir (esxselector - masterproxymodel contentselector - model/modelitem model/datafilesmodel model/pluginsproxymodel model/esm/esmfile - utils/profilescombobox utils/comboboxlineedit utils/lineedit utils/naturalsort + model/masterproxymodel model/modelitem model/datafilesmodel + model/pluginsproxymodel model/esm/esmfile model/naturalsort + view/profilescombobox view/comboboxlineedit + view/lineedit view/contentselector ) include(${QT_USE_FILE}) diff --git a/components/esxselector/model/esm/esmfile.cpp b/components/esxselector/model/esm/esmfile.cpp deleted file mode 100644 index 93d83091e7..0000000000 --- a/components/esxselector/model/esm/esmfile.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "esmfile.hpp" - -EsmFile::EsmFile(QString fileName, ModelItem *parent) - : ModelItem(parent) -{ - mFileName = fileName; - mSize = 0; - mVersion = 0.0f; -} - -void EsmFile::setFileName(const QString &fileName) -{ - mFileName = fileName; -} - -void EsmFile::setAuthor(const QString &author) -{ - mAuthor = author; -} - -void EsmFile::setSize(const int size) -{ - mSize = size; -} - -void EsmFile::setDates(const QDateTime &modified, const QDateTime &accessed) -{ - mModified = modified; - mAccessed = accessed; -} - -void EsmFile::setVersion(float version) -{ - mVersion = version; -} - -void EsmFile::setPath(const QString &path) -{ - mPath = path; -} - -void EsmFile::setMasters(const QStringList &masters) -{ - mMasters = masters; -} - -void EsmFile::setDescription(const QString &description) -{ - mDescription = description; -} diff --git a/components/esxselector/model/esm/esmfile.hpp b/components/esxselector/model/esm/esmfile.hpp deleted file mode 100644 index 52b3fbd007..0000000000 --- a/components/esxselector/model/esm/esmfile.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef ESMFILE_HPP -#define ESMFILE_HPP - -#include -#include - -#include "../modelitem.hpp" - -class EsmFile : public ModelItem -{ - Q_OBJECT - Q_PROPERTY(QString filename READ fileName) - -public: - EsmFile(QString fileName = QString(), ModelItem *parent = 0); - - ~EsmFile() - {} - - void setFileName(const QString &fileName); - void setAuthor(const QString &author); - void setSize(const int size); - void setDates(const QDateTime &modified, const QDateTime &accessed); - void setVersion(const float version); - void setPath(const QString &path); - void setMasters(const QStringList &masters); - void setDescription(const QString &description); - - inline QString fileName() const { return mFileName; } - inline QString author() const { return mAuthor; } - inline int size() const { return mSize; } - inline QDateTime modified() const { return mModified; } - inline QDateTime accessed() const { return mAccessed; } - inline float version() const { return mVersion; } - inline QString path() const { return mPath; } - inline QStringList masters() const { return mMasters; } - inline QString description() const { return mDescription; } - - -private: - QString mFileName; - QString mAuthor; - int mSize; - QDateTime mModified; - QDateTime mAccessed; - float mVersion; - QString mPath; - QStringList mMasters; - QString mDescription; - -}; - - -#endif diff --git a/components/esxselector/masterproxymodel.cpp b/components/esxselector/model/masterproxymodel.cpp similarity index 57% rename from components/esxselector/masterproxymodel.cpp rename to components/esxselector/model/masterproxymodel.cpp index 7701d4f17a..04a7f0033f 100644 --- a/components/esxselector/masterproxymodel.cpp +++ b/components/esxselector/model/masterproxymodel.cpp @@ -1,6 +1,6 @@ #include "masterproxymodel.hpp" -FileOrderList::MasterProxyModel::MasterProxyModel(QObject *parent, QAbstractTableModel* model) : +EsxSelector::MasterProxyModel::MasterProxyModel(QObject *parent, QAbstractTableModel* model) : QSortFilterProxyModel(parent) { setFilterRegExp(QString("game")); @@ -10,7 +10,7 @@ FileOrderList::MasterProxyModel::MasterProxyModel(QObject *parent, QAbstractTabl setSourceModel (model); } -QVariant FileOrderList::MasterProxyModel::data(const QModelIndex &index, int role) const +QVariant EsxSelector::MasterProxyModel::data(const QModelIndex &index, int role) const { return QSortFilterProxyModel::data (index, role); } diff --git a/components/esxselector/masterproxymodel.hpp b/components/esxselector/model/masterproxymodel.hpp similarity index 95% rename from components/esxselector/masterproxymodel.hpp rename to components/esxselector/model/masterproxymodel.hpp index 49ca369deb..6fbdd31545 100644 --- a/components/esxselector/masterproxymodel.hpp +++ b/components/esxselector/model/masterproxymodel.hpp @@ -5,7 +5,7 @@ class QAbstractTableModel; -namespace FileOrderList +namespace EsxSelector { class MasterProxyModel : public QSortFilterProxyModel { diff --git a/components/esxselector/utils/naturalsort.cpp b/components/esxselector/model/naturalsort.cpp similarity index 100% rename from components/esxselector/utils/naturalsort.cpp rename to components/esxselector/model/naturalsort.cpp diff --git a/components/esxselector/utils/naturalsort.hpp b/components/esxselector/model/naturalsort.hpp similarity index 100% rename from components/esxselector/utils/naturalsort.hpp rename to components/esxselector/model/naturalsort.hpp diff --git a/components/esxselector/utils/comboboxlineedit.cpp b/components/esxselector/view/comboboxlineedit.cpp similarity index 100% rename from components/esxselector/utils/comboboxlineedit.cpp rename to components/esxselector/view/comboboxlineedit.cpp diff --git a/components/esxselector/utils/comboboxlineedit.hpp b/components/esxselector/view/comboboxlineedit.hpp similarity index 100% rename from components/esxselector/utils/comboboxlineedit.hpp rename to components/esxselector/view/comboboxlineedit.hpp diff --git a/components/esxselector/contentselector.cpp b/components/esxselector/view/contentselector.cpp similarity index 74% rename from components/esxselector/contentselector.cpp rename to components/esxselector/view/contentselector.cpp index 27be996fc8..16139d9e69 100644 --- a/components/esxselector/contentselector.cpp +++ b/components/esxselector/view/contentselector.cpp @@ -10,19 +10,19 @@ #include #include -FileOrderList::ContentSelector::ContentSelector(QWidget *parent) : +EsxSelector::ContentSelector::ContentSelector(QWidget *parent) : QWidget(parent) { setupUi(this); buildModelsAndViews(); } -void FileOrderList::ContentSelector::buildModelsAndViews() +void EsxSelector::ContentSelector::buildModelsAndViews() { // Models mDataFilesModel = new DataFilesModel (this); - mMasterProxyModel = new FileOrderList::MasterProxyModel (this, mDataFilesModel); + mMasterProxyModel = new EsxSelector::MasterProxyModel (this, mDataFilesModel); mPluginsProxyModel = new PluginsProxyModel (this, mDataFilesModel); masterView->setModel(mMasterProxyModel); @@ -35,7 +35,7 @@ void FileOrderList::ContentSelector::buildModelsAndViews() connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); } -void FileOrderList::ContentSelector::addFiles(const QString &path) +void EsxSelector::ContentSelector::addFiles(const QString &path) { mDataFilesModel->addFiles(path); mDataFilesModel->sort(3); // Sort by date accessed @@ -43,12 +43,12 @@ void FileOrderList::ContentSelector::addFiles(const QString &path) mDataFilesModel->uncheckAll(); } -void FileOrderList::ContentSelector::setEncoding(const QString &encoding) +void EsxSelector::ContentSelector::setEncoding(const QString &encoding) { mDataFilesModel->setEncoding(encoding); } -void FileOrderList::ContentSelector::setCheckState(QModelIndex index, QSortFilterProxyModel *model) +void EsxSelector::ContentSelector::setCheckState(QModelIndex index, QSortFilterProxyModel *model) { if (!index.isValid()) return; @@ -66,12 +66,12 @@ void FileOrderList::ContentSelector::setCheckState(QModelIndex index, QSortFilte } } -QStringList FileOrderList::ContentSelector::checkedItemsPaths() +QStringList EsxSelector::ContentSelector::checkedItemsPaths() { return mDataFilesModel->checkedItemsPaths(); } -void FileOrderList::ContentSelector::updateViews() +void EsxSelector::ContentSelector::updateViews() { // Ensure the columns are hidden because sort() re-enables them pluginView->setColumnHidden(1, true); @@ -85,12 +85,12 @@ void FileOrderList::ContentSelector::updateViews() } -void FileOrderList::ContentSelector::slotCurrentProfileIndexChanged(int index) +void EsxSelector::ContentSelector::slotCurrentProfileIndexChanged(int index) { emit profileChanged(index); } -void FileOrderList::ContentSelector::slotCurrentMasterIndexChanged(int index) +void EsxSelector::ContentSelector::slotCurrentMasterIndexChanged(int index) { QObject *object = QObject::sender(); @@ -101,7 +101,7 @@ void FileOrderList::ContentSelector::slotCurrentMasterIndexChanged(int index) setCheckState(mMasterProxyModel->index(index, 0), mMasterProxyModel); } -void FileOrderList::ContentSelector::slotPluginTableItemClicked(const QModelIndex &index) +void EsxSelector::ContentSelector::slotPluginTableItemClicked(const QModelIndex &index) { setCheckState(index, mPluginsProxyModel); } diff --git a/components/esxselector/contentselector.hpp b/components/esxselector/view/contentselector.hpp similarity index 100% rename from components/esxselector/contentselector.hpp rename to components/esxselector/view/contentselector.hpp diff --git a/components/esxselector/utils/lineedit.cpp b/components/esxselector/view/lineedit.cpp similarity index 100% rename from components/esxselector/utils/lineedit.cpp rename to components/esxselector/view/lineedit.cpp diff --git a/components/esxselector/utils/lineedit.hpp b/components/esxselector/view/lineedit.hpp similarity index 100% rename from components/esxselector/utils/lineedit.hpp rename to components/esxselector/view/lineedit.hpp diff --git a/components/esxselector/utils/profilescombobox.cpp b/components/esxselector/view/profilescombobox.cpp similarity index 100% rename from components/esxselector/utils/profilescombobox.cpp rename to components/esxselector/view/profilescombobox.cpp diff --git a/components/esxselector/utils/profilescombobox.hpp b/components/esxselector/view/profilescombobox.hpp similarity index 100% rename from components/esxselector/utils/profilescombobox.hpp rename to components/esxselector/view/profilescombobox.hpp From f6217f9c6ac160d6372236698f1abcf24b3c785c Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 18 Aug 2013 15:11:29 -0500 Subject: [PATCH 017/148] Moved esx selector classes out of global namespace --- apps/launcher/datafilespage.cpp | 14 ++-- apps/launcher/datafilespage.hpp | 4 +- apps/launcher/graphicspage.cpp | 2 +- apps/launcher/utils/textinputdialog.cpp | 4 +- apps/launcher/utils/textinputdialog.hpp | 10 ++- apps/opencs/editor.hpp | 2 +- apps/opencs/view/doc/filedialog.cpp | 26 +++--- apps/opencs/view/doc/filedialog.hpp | 54 +++++++------ components/CMakeLists.txt | 2 +- .../esxselector/model/datafilesmodel.cpp | 53 ++++++------ .../esxselector/model/datafilesmodel.hpp | 81 ++++++++++--------- components/esxselector/model/esmfile.cpp | 50 ++++++++++++ components/esxselector/model/esmfile.hpp | 56 +++++++++++++ .../esxselector/model/masterproxymodel.cpp | 4 +- .../esxselector/model/masterproxymodel.hpp | 2 +- components/esxselector/model/modelitem.cpp | 18 ++--- components/esxselector/model/modelitem.hpp | 37 +++++---- components/esxselector/model/naturalsort.hpp | 8 +- .../esxselector/model/pluginsproxymodel.cpp | 6 +- .../esxselector/model/pluginsproxymodel.hpp | 20 +++-- .../esxselector/view/comboboxlineedit.cpp | 6 +- .../esxselector/view/comboboxlineedit.hpp | 26 +++--- .../esxselector/view/contentselector.cpp | 32 ++++---- .../esxselector/view/contentselector.hpp | 19 +++-- components/esxselector/view/lineedit.cpp | 6 +- components/esxselector/view/lineedit.hpp | 26 +++--- .../esxselector/view/profilescombobox.cpp | 12 +-- .../esxselector/view/profilescombobox.hpp | 43 +++++----- files/ui/datafilespage.ui | 8 +- 29 files changed, 381 insertions(+), 250 deletions(-) create mode 100644 components/esxselector/model/esmfile.cpp create mode 100644 components/esxselector/model/esmfile.hpp diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 44f92d7fe0..2346a0b014 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -7,15 +7,15 @@ #include -#include -#include -#include +#include +#include +#include -#include -#include -#include +#include +#include +#include -#include "components/fileorderlist/masterproxymodel.hpp" +#include "components/esxselector/model/masterproxymodel.hpp" #include "settings/gamesettings.hpp" #include "settings/launchersettings.hpp" diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 356cb88d63..f3792b1f13 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -5,7 +5,7 @@ #include #include "ui_datafilespage.h" -#include "components/fileorderlist/contentselector.hpp" +#include "components/esxselector/view/contentselector.hpp" class QSortFilterProxyModel; class QAbstractItemModel; @@ -20,7 +20,7 @@ class PluginsProxyModel; namespace Files { struct ConfigurationManager; } -class DataFilesPage : public EsxSelector::ContentSelector +class DataFilesPage : public EsxView::ContentSelector { Q_OBJECT public: diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 1bbf7f8973..4d5975e589 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include "settings/graphicssettings.hpp" diff --git a/apps/launcher/utils/textinputdialog.cpp b/apps/launcher/utils/textinputdialog.cpp index a4b36b95ea..052fc58e40 100644 --- a/apps/launcher/utils/textinputdialog.cpp +++ b/apps/launcher/utils/textinputdialog.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) : QDialog(parent) @@ -19,7 +19,7 @@ TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWid // Line edit QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore - mLineEdit = new LineEdit(this); + mLineEdit = new EsxView::LineEdit(this); mLineEdit->setValidator(validator); mLineEdit->setCompleter(0); diff --git a/apps/launcher/utils/textinputdialog.hpp b/apps/launcher/utils/textinputdialog.hpp index cbb453ac83..2fb6e0f6b4 100644 --- a/apps/launcher/utils/textinputdialog.hpp +++ b/apps/launcher/utils/textinputdialog.hpp @@ -5,17 +5,21 @@ //#include "lineedit.hpp" class QDialogButtonBox; -class LineEdit; + +namespace EsxView { + class LineEdit; +} + class TextInputDialog : public QDialog { Q_OBJECT public: explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0); - inline LineEdit *lineEdit() { return mLineEdit; } + inline EsxView::LineEdit *lineEdit() { return mLineEdit; } void setOkButtonEnabled(bool enabled); - LineEdit *mLineEdit; + EsxView::LineEdit *mLineEdit; int exec(); diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 380e434c24..c88efcfb9b 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -22,7 +22,7 @@ namespace CS CSMDoc::DocumentManager mDocumentManager; CSVDoc::ViewManager mViewManager; CSVDoc::StartupDialogue mStartup; - FileDialog mFileDialog; + CSVDoc::FileDialog mFileDialog; Files::ConfigurationManager mCfgMgr; void setupDataFiles(); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 252c760faa..12849d6ee0 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -10,15 +10,15 @@ #include #include -#include -#include -#include +#include +#include +#include -#include +#include -#include "components/fileorderlist/masterproxymodel.hpp" +#include "components/esxselector/model/masterproxymodel.hpp" -FileDialog::FileDialog(QWidget *parent) : +CSVDoc::FileDialog::FileDialog(QWidget *parent) : ContentSelector(parent) { // Hide the profile elements @@ -60,7 +60,7 @@ FileDialog::FileDialog(QWidget *parent) : // connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject())); } -void FileDialog::updateOpenButton(const QStringList &items) +void CSVDoc::FileDialog::updateOpenButton(const QStringList &items) { QPushButton *openButton = mButtonBox->button(QDialogButtonBox::Open); @@ -70,7 +70,7 @@ void FileDialog::updateOpenButton(const QStringList &items) openButton->setEnabled(!items.isEmpty()); } -void FileDialog::updateCreateButton(const QString &name) +void CSVDoc::FileDialog::updateCreateButton(const QString &name) { if (!mCreateButton->isVisible()) return; @@ -78,12 +78,12 @@ void FileDialog::updateCreateButton(const QString &name) mCreateButton->setEnabled(!name.isEmpty()); } -QString FileDialog::fileName() +QString CSVDoc::FileDialog::fileName() { //return mNameLineEdit->text(); } -void FileDialog::openFile() +void CSVDoc::FileDialog::openFile() { setWindowTitle(tr("Open")); @@ -101,7 +101,7 @@ void FileDialog::openFile() activateWindow(); } -void FileDialog::newFile() +void CSVDoc::FileDialog::newFile() { setWindowTitle(tr("New")); @@ -118,12 +118,12 @@ void FileDialog::newFile() activateWindow(); } -void FileDialog::accept() +void CSVDoc::FileDialog::accept() { emit openFiles(); } -void FileDialog::createButtonClicked() +void CSVDoc::FileDialog::createButtonClicked() { emit createNewFile(); } diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 1b4d09745c..d016ad32d1 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -4,7 +4,7 @@ #include #include -#include "components/fileorderlist/contentselector.hpp" +#include "components/esxselector/view/contentselector.hpp" #include "ui_datafilespage.h" class QDialogButtonBox; @@ -18,38 +18,40 @@ class QMenu; class DataFilesModel; class PluginsProxyModel; -class FileDialog : public EsxSelector::ContentSelector +namespace CSVDoc { - Q_OBJECT -public: - explicit FileDialog(QWidget *parent = 0); + class FileDialog : public EsxView::ContentSelector + { + Q_OBJECT + public: + explicit FileDialog(QWidget *parent = 0); - void openFile(); - void newFile(); - void accepted(); + void openFile(); + void newFile(); + void accepted(); - QString fileName(); + QString fileName(); -signals: - void openFiles(); - void createNewFile(); - -public slots: - void accept(); + signals: + void openFiles(); + void createNewFile(); -private slots: - //void updateViews(); - void updateOpenButton(const QStringList &items); - void updateCreateButton(const QString &name); + public slots: + void accept(); - void createButtonClicked(); + private slots: + //void updateViews(); + void updateOpenButton(const QStringList &items); + void updateCreateButton(const QString &name); -private: - QLabel *mNameLabel; - //LineEdit *mNameLineEdit; + void createButtonClicked(); - QPushButton *mCreateButton; - QDialogButtonBox *mButtonBox; -}; + private: + QLabel *mNameLabel; + //LineEdit *mNameLineEdit; + QPushButton *mCreateButton; + QDialogButtonBox *mButtonBox; + }; +} #endif // FILEDIALOG_HPP diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 4f14fa9d96..8b07a4e00e 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -73,7 +73,7 @@ find_package(Qt4 COMPONENTS QtCore QtGui) if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) add_component_qt_dir (esxselector model/masterproxymodel model/modelitem model/datafilesmodel - model/pluginsproxymodel model/esm/esmfile model/naturalsort + model/pluginsproxymodel model/esmfile model/naturalsort view/profilescombobox view/comboboxlineedit view/lineedit view/contentselector ) diff --git a/components/esxselector/model/datafilesmodel.cpp b/components/esxselector/model/datafilesmodel.cpp index ae842381e7..2980313f0e 100644 --- a/components/esxselector/model/datafilesmodel.cpp +++ b/components/esxselector/model/datafilesmodel.cpp @@ -2,33 +2,34 @@ #include #include #include +#include #include #include -#include "esm/esmfile.hpp" +#include "esmfile.hpp" #include "datafilesmodel.hpp" #include -DataFilesModel::DataFilesModel(QObject *parent) : +EsxModel::DataFilesModel::DataFilesModel(QObject *parent) : QAbstractTableModel(parent) { mEncoding = QString("win1252"); } -DataFilesModel::~DataFilesModel() +EsxModel::DataFilesModel::~DataFilesModel() { } -void DataFilesModel::setEncoding(const QString &encoding) +void EsxModel::DataFilesModel::setEncoding(const QString &encoding) { mEncoding = encoding; } -void DataFilesModel::setCheckState(const QModelIndex &index, Qt::CheckState state) +void EsxModel::DataFilesModel::setCheckState(const QModelIndex &index, Qt::CheckState state) { if (!index.isValid()) return; @@ -45,24 +46,24 @@ void DataFilesModel::setCheckState(const QModelIndex &index, Qt::CheckState stat } -Qt::CheckState DataFilesModel::checkState(const QModelIndex &index) +Qt::CheckState EsxModel::DataFilesModel::checkState(const QModelIndex &index) { EsmFile *file = item(index.row()); return mCheckStates[file->fileName()]; } -int DataFilesModel::columnCount(const QModelIndex &parent) const +int EsxModel::DataFilesModel::columnCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : 9; } -int DataFilesModel::rowCount(const QModelIndex &parent) const +int EsxModel::DataFilesModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : mFiles.count(); } -bool DataFilesModel::moveRow(int oldrow, int row, const QModelIndex &parent) +bool EsxModel::DataFilesModel::moveRow(int oldrow, int row, const QModelIndex &parent) { if (oldrow < 0 || row < 0 || oldrow == row) return false; @@ -76,7 +77,7 @@ bool DataFilesModel::moveRow(int oldrow, int row, const QModelIndex &parent) return true; } -QVariant DataFilesModel::data(const QModelIndex &index, int role) const +QVariant EsxModel::DataFilesModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); @@ -166,7 +167,7 @@ QVariant DataFilesModel::data(const QModelIndex &index, int role) const } -Qt::ItemFlags DataFilesModel::flags(const QModelIndex &index) const +Qt::ItemFlags EsxModel::DataFilesModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; @@ -192,7 +193,7 @@ Qt::ItemFlags DataFilesModel::flags(const QModelIndex &index) const } -QVariant DataFilesModel::headerData(int section, Qt::Orientation orientation, int role) const +QVariant EsxModel::DataFilesModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role != Qt::DisplayRole) return QVariant(); @@ -217,7 +218,7 @@ QVariant DataFilesModel::headerData(int section, Qt::Orientation orientation, in return QVariant(); } -bool DataFilesModel::setData(const QModelIndex &index, const QVariant &value, int role) +bool EsxModel::DataFilesModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) return false; @@ -225,7 +226,7 @@ bool DataFilesModel::setData(const QModelIndex &index, const QVariant &value, in return false; } -bool lessThanEsmFile(const EsmFile *e1, const EsmFile *e2) +bool lessThanEsmFile(const EsxModel::EsmFile *e1, const EsxModel::EsmFile *e2) { //Masters first then alphabetically if (e1->fileName().endsWith(".esm") && !e2->fileName().endsWith(".esm")) @@ -236,7 +237,7 @@ bool lessThanEsmFile(const EsmFile *e1, const EsmFile *e2) return e1->fileName().toLower() < e2->fileName().toLower(); } -bool lessThanDate(const EsmFile *e1, const EsmFile *e2) +bool lessThanDate(const EsxModel::EsmFile *e1, const EsxModel::EsmFile *e2) { if (e1->modified().toString(Qt::ISODate) < e2->modified().toString(Qt::ISODate)) { return true; @@ -245,7 +246,7 @@ bool lessThanDate(const EsmFile *e1, const EsmFile *e2) } } -void DataFilesModel::sort(int column, Qt::SortOrder order) +void EsxModel::DataFilesModel::sort(int column, Qt::SortOrder order) { emit layoutAboutToBeChanged(); @@ -258,14 +259,14 @@ void DataFilesModel::sort(int column, Qt::SortOrder order) emit layoutChanged(); } -void DataFilesModel::addFile(EsmFile *file) +void EsxModel::DataFilesModel::addFile(EsmFile *file) { emit beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); mFiles.append(file); emit endInsertRows(); } -void DataFilesModel::addFiles(const QString &path) +void EsxModel::DataFilesModel::addFiles(const QString &path) { QDir dir(path); QStringList filters; @@ -330,7 +331,7 @@ void DataFilesModel::addFiles(const QString &path) delete decoder; } -QModelIndex DataFilesModel::indexFromItem(EsmFile *item) const +QModelIndex EsxModel::DataFilesModel::indexFromItem(EsmFile *item) const { if (item) return createIndex(mFiles.indexOf(item), 0); @@ -338,7 +339,7 @@ QModelIndex DataFilesModel::indexFromItem(EsmFile *item) const return QModelIndex(); } -EsmFile* DataFilesModel::findItem(const QString &name) +EsxModel::EsmFile* EsxModel::DataFilesModel::findItem(const QString &name) { QList::ConstIterator it; QList::ConstIterator itEnd = mFiles.constEnd(); @@ -356,7 +357,7 @@ EsmFile* DataFilesModel::findItem(const QString &name) return 0; } -EsmFile* DataFilesModel::item(int row) const +EsxModel::EsmFile* EsxModel::DataFilesModel::item(int row) const { if (row >= 0 && row < mFiles.count()) return mFiles.at(row); @@ -364,7 +365,7 @@ EsmFile* DataFilesModel::item(int row) const return 0; } -QStringList DataFilesModel::checkedItems() +QStringList EsxModel::DataFilesModel::checkedItems() { QStringList list; @@ -386,7 +387,7 @@ QStringList DataFilesModel::checkedItems() return list; } -QStringList DataFilesModel::checkedItemsPaths() +QStringList EsxModel::DataFilesModel::checkedItemsPaths() { QStringList list; @@ -405,14 +406,14 @@ QStringList DataFilesModel::checkedItemsPaths() return list; } -void DataFilesModel::uncheckAll() +void EsxModel::DataFilesModel::uncheckAll() { emit layoutAboutToBeChanged(); mCheckStates.clear(); emit layoutChanged(); } -QStringList DataFilesModel::uncheckedItems() +QStringList EsxModel::DataFilesModel::uncheckedItems() { QStringList list; QStringList checked = checkedItems(); @@ -433,7 +434,7 @@ QStringList DataFilesModel::uncheckedItems() return list; } -bool DataFilesModel::canBeChecked(EsmFile *file) const +bool EsxModel::DataFilesModel::canBeChecked(EsmFile *file) const { //element can be checked if all its dependencies are foreach (const QString &master, file->masters()) diff --git a/components/esxselector/model/datafilesmodel.hpp b/components/esxselector/model/datafilesmodel.hpp index 0a07a536f8..bc55bb6cff 100644 --- a/components/esxselector/model/datafilesmodel.hpp +++ b/components/esxselector/model/datafilesmodel.hpp @@ -6,61 +6,62 @@ #include #include - -class EsmFile; - -class DataFilesModel : public QAbstractTableModel +namespace EsxModel { - Q_OBJECT + class EsmFile; -public: - explicit DataFilesModel(QObject *parent = 0); - virtual ~DataFilesModel(); - virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + class DataFilesModel : public QAbstractTableModel + { + Q_OBJECT - bool moveRow(int oldrow, int row, const QModelIndex &parent = QModelIndex()); + public: + explicit DataFilesModel(QObject *parent = 0); + virtual ~DataFilesModel(); + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; - virtual Qt::ItemFlags flags(const QModelIndex &index) const; + bool moveRow(int oldrow, int row, const QModelIndex &parent = QModelIndex()); - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; - virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - inline QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const - { return QAbstractTableModel::index(row, column, parent); } + virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); - void setEncoding(const QString &encoding); + inline QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const + { return QAbstractTableModel::index(row, column, parent); } - void addFiles(const QString &path); + void setEncoding(const QString &encoding); - void uncheckAll(); + void addFiles(const QString &path); - QStringList checkedItems(); - QStringList uncheckedItems(); - QStringList checkedItemsPaths(); + void uncheckAll(); - Qt::CheckState checkState(const QModelIndex &index); - void setCheckState(const QModelIndex &index, Qt::CheckState state); + QStringList checkedItems(); + QStringList uncheckedItems(); + QStringList checkedItemsPaths(); - QModelIndex indexFromItem(EsmFile *item) const; - EsmFile* findItem(const QString &name); - EsmFile* item(int row) const; + Qt::CheckState checkState(const QModelIndex &index); + void setCheckState(const QModelIndex &index, Qt::CheckState state); -signals: - void checkedItemsChanged(const QStringList &items); - -private: - bool canBeChecked(EsmFile *file) const; - void addFile(EsmFile *file); - - QList mFiles; - QHash mCheckStates; + QModelIndex indexFromItem(EsmFile *item) const; + EsmFile* findItem(const QString &name); + EsmFile* item(int row) const; - QString mEncoding; + signals: + void checkedItemsChanged(const QStringList &items); -}; + private: + bool canBeChecked(EsmFile *file) const; + void addFile(EsmFile *file); + QList mFiles; + QHash mCheckStates; + + QString mEncoding; + + }; +} #endif // DATAFILESMODEL_HPP diff --git a/components/esxselector/model/esmfile.cpp b/components/esxselector/model/esmfile.cpp new file mode 100644 index 0000000000..96b90e44e2 --- /dev/null +++ b/components/esxselector/model/esmfile.cpp @@ -0,0 +1,50 @@ +#include "esmfile.hpp" + +EsxModel::EsmFile::EsmFile(QString fileName, ModelItem *parent) + : ModelItem(parent) +{ + mFileName = fileName; + mSize = 0; + mVersion = 0.0f; +} + +void EsxModel::EsmFile::setFileName(const QString &fileName) +{ + mFileName = fileName; +} + +void EsxModel::EsmFile::setAuthor(const QString &author) +{ + mAuthor = author; +} + +void EsxModel::EsmFile::setSize(const int size) +{ + mSize = size; +} + +void EsxModel::EsmFile::setDates(const QDateTime &modified, const QDateTime &accessed) +{ + mModified = modified; + mAccessed = accessed; +} + +void EsxModel::EsmFile::setVersion(float version) +{ + mVersion = version; +} + +void EsxModel::EsmFile::setPath(const QString &path) +{ + mPath = path; +} + +void EsxModel::EsmFile::setMasters(const QStringList &masters) +{ + mMasters = masters; +} + +void EsxModel::EsmFile::setDescription(const QString &description) +{ + mDescription = description; +} diff --git a/components/esxselector/model/esmfile.hpp b/components/esxselector/model/esmfile.hpp new file mode 100644 index 0000000000..6a3e36b538 --- /dev/null +++ b/components/esxselector/model/esmfile.hpp @@ -0,0 +1,56 @@ +#ifndef ESMFILE_HPP +#define ESMFILE_HPP + +#include +#include + +#include "modelitem.hpp" + +namespace EsxModel +{ + class EsmFile : public ModelItem + { + Q_OBJECT + Q_PROPERTY(QString filename READ fileName) + + public: + EsmFile(QString fileName = QString(), ModelItem *parent = 0); + + ~EsmFile() + {} + + void setFileName(const QString &fileName); + void setAuthor(const QString &author); + void setSize(const int size); + void setDates(const QDateTime &modified, const QDateTime &accessed); + void setVersion(const float version); + void setPath(const QString &path); + void setMasters(const QStringList &masters); + void setDescription(const QString &description); + + inline QString fileName() const { return mFileName; } + inline QString author() const { return mAuthor; } + inline int size() const { return mSize; } + inline QDateTime modified() const { return mModified; } + inline QDateTime accessed() const { return mAccessed; } + inline float version() const { return mVersion; } + inline QString path() const { return mPath; } + inline QStringList masters() const { return mMasters; } + inline QString description() const { return mDescription; } + + + private: + QString mFileName; + QString mAuthor; + int mSize; + QDateTime mModified; + QDateTime mAccessed; + float mVersion; + QString mPath; + QStringList mMasters; + QString mDescription; + + }; +} + +#endif diff --git a/components/esxselector/model/masterproxymodel.cpp b/components/esxselector/model/masterproxymodel.cpp index 04a7f0033f..011e5ebd54 100644 --- a/components/esxselector/model/masterproxymodel.cpp +++ b/components/esxselector/model/masterproxymodel.cpp @@ -1,6 +1,6 @@ #include "masterproxymodel.hpp" -EsxSelector::MasterProxyModel::MasterProxyModel(QObject *parent, QAbstractTableModel* model) : +EsxModel::MasterProxyModel::MasterProxyModel(QObject *parent, QAbstractTableModel* model) : QSortFilterProxyModel(parent) { setFilterRegExp(QString("game")); @@ -10,7 +10,7 @@ EsxSelector::MasterProxyModel::MasterProxyModel(QObject *parent, QAbstractTableM setSourceModel (model); } -QVariant EsxSelector::MasterProxyModel::data(const QModelIndex &index, int role) const +QVariant EsxModel::MasterProxyModel::data(const QModelIndex &index, int role) const { return QSortFilterProxyModel::data (index, role); } diff --git a/components/esxselector/model/masterproxymodel.hpp b/components/esxselector/model/masterproxymodel.hpp index 6fbdd31545..fed01bdb14 100644 --- a/components/esxselector/model/masterproxymodel.hpp +++ b/components/esxselector/model/masterproxymodel.hpp @@ -5,7 +5,7 @@ class QAbstractTableModel; -namespace EsxSelector +namespace EsxModel { class MasterProxyModel : public QSortFilterProxyModel { diff --git a/components/esxselector/model/modelitem.cpp b/components/esxselector/model/modelitem.cpp index 0ff7e45cb9..8c1e83695d 100644 --- a/components/esxselector/model/modelitem.cpp +++ b/components/esxselector/model/modelitem.cpp @@ -1,23 +1,23 @@ #include "modelitem.hpp" -ModelItem::ModelItem(ModelItem *parent) +EsxModel::ModelItem::ModelItem(ModelItem *parent) : mParentItem(parent) , QObject(parent) { } -ModelItem::~ModelItem() +EsxModel::ModelItem::~ModelItem() { qDeleteAll(mChildItems); } -ModelItem *ModelItem::parent() +EsxModel::ModelItem *EsxModel::ModelItem::parent() { return mParentItem; } -int ModelItem::row() const +int EsxModel::ModelItem::row() const { if (mParentItem) return 1; @@ -28,30 +28,30 @@ int ModelItem::row() const } -int ModelItem::childCount() const +int EsxModel::ModelItem::childCount() const { return mChildItems.count(); } -int ModelItem::childRow(ModelItem *child) const +int EsxModel::ModelItem::childRow(ModelItem *child) const { Q_ASSERT(child); return mChildItems.indexOf(child); } -ModelItem *ModelItem::child(int row) +EsxModel::ModelItem *EsxModel::ModelItem::child(int row) { return mChildItems.value(row); } -void ModelItem::appendChild(ModelItem *item) +void EsxModel::ModelItem::appendChild(ModelItem *item) { mChildItems.append(item); } -void ModelItem::removeChild(int row) +void EsxModel::ModelItem::removeChild(int row) { mChildItems.removeAt(row); } diff --git a/components/esxselector/model/modelitem.hpp b/components/esxselector/model/modelitem.hpp index f4cb4322ff..64596302c8 100644 --- a/components/esxselector/model/modelitem.hpp +++ b/components/esxselector/model/modelitem.hpp @@ -4,29 +4,32 @@ #include #include -class ModelItem : public QObject +namespace EsxModel { - Q_OBJECT + class ModelItem : public QObject + { + Q_OBJECT -public: - ModelItem(ModelItem *parent = 0); - ~ModelItem(); + public: + ModelItem(ModelItem *parent = 0); + ~ModelItem(); - ModelItem *parent(); - int row() const; + ModelItem *parent(); + int row() const; - int childCount() const; - int childRow(ModelItem *child) const; - ModelItem *child(int row); + int childCount() const; + int childRow(ModelItem *child) const; + ModelItem *child(int row); - void appendChild(ModelItem *child); - void removeChild(int row); + void appendChild(ModelItem *child); + void removeChild(int row); - //virtual bool acceptChild(ModelItem *child); + //virtual bool acceptChild(ModelItem *child); -protected: - ModelItem *mParentItem; - QList mChildItems; -}; + protected: + ModelItem *mParentItem; + QList mChildItems; + }; +} #endif diff --git a/components/esxselector/model/naturalsort.hpp b/components/esxselector/model/naturalsort.hpp index 59271547a5..8386e4e9f0 100644 --- a/components/esxselector/model/naturalsort.hpp +++ b/components/esxselector/model/naturalsort.hpp @@ -3,9 +3,9 @@ #include -bool naturalSortLessThanCS( const QString &left, const QString &right ); -bool naturalSortLessThanCI( const QString &left, const QString &right ); -bool naturalSortGreaterThanCS( const QString &left, const QString &right ); -bool naturalSortGreaterThanCI( const QString &left, const QString &right ); + bool naturalSortLessThanCS( const QString &left, const QString &right ); + bool naturalSortLessThanCI( const QString &left, const QString &right ); + bool naturalSortGreaterThanCS( const QString &left, const QString &right ); + bool naturalSortGreaterThanCI( const QString &left, const QString &right ); #endif diff --git a/components/esxselector/model/pluginsproxymodel.cpp b/components/esxselector/model/pluginsproxymodel.cpp index 18aebc6b68..4fde11f470 100644 --- a/components/esxselector/model/pluginsproxymodel.cpp +++ b/components/esxselector/model/pluginsproxymodel.cpp @@ -1,7 +1,7 @@ #include "pluginsproxymodel.hpp" #include "datafilesmodel.hpp" -PluginsProxyModel::PluginsProxyModel(QObject *parent, DataFilesModel *model) : +EsxModel::PluginsProxyModel::PluginsProxyModel(QObject *parent, DataFilesModel *model) : QSortFilterProxyModel(parent) { setFilterRegExp (QString("addon")); @@ -12,11 +12,11 @@ PluginsProxyModel::PluginsProxyModel(QObject *parent, DataFilesModel *model) : setSourceModel (model); } -PluginsProxyModel::~PluginsProxyModel() +EsxModel::PluginsProxyModel::~PluginsProxyModel() { } -QVariant PluginsProxyModel::data(const QModelIndex &index, int role) const +QVariant EsxModel::PluginsProxyModel::data(const QModelIndex &index, int role) const { switch (role) { diff --git a/components/esxselector/model/pluginsproxymodel.hpp b/components/esxselector/model/pluginsproxymodel.hpp index cfade092eb..04c18c2e22 100644 --- a/components/esxselector/model/pluginsproxymodel.hpp +++ b/components/esxselector/model/pluginsproxymodel.hpp @@ -5,18 +5,22 @@ class QVariant; class QAbstractTableModel; -class DataFilesModel; -class PluginsProxyModel : public QSortFilterProxyModel +namespace EsxModel { - Q_OBJECT + class DataFilesModel; -public: + class PluginsProxyModel : public QSortFilterProxyModel + { + Q_OBJECT - explicit PluginsProxyModel(QObject *parent = 0, DataFilesModel *model = 0); - ~PluginsProxyModel(); + public: - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; -}; + explicit PluginsProxyModel(QObject *parent = 0, DataFilesModel *model = 0); + ~PluginsProxyModel(); + + virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + }; +} #endif // PLUGINSPROXYMODEL_HPP diff --git a/components/esxselector/view/comboboxlineedit.cpp b/components/esxselector/view/comboboxlineedit.cpp index 4d62e1399a..815a1130b5 100644 --- a/components/esxselector/view/comboboxlineedit.cpp +++ b/components/esxselector/view/comboboxlineedit.cpp @@ -3,7 +3,7 @@ #include "comboboxlineedit.hpp" -ComboBoxLineEdit::ComboBoxLineEdit(QWidget *parent) +EsxView::ComboBoxLineEdit::ComboBoxLineEdit(QWidget *parent) : QLineEdit(parent) { mClearButton = new QToolButton(this); @@ -21,7 +21,7 @@ ComboBoxLineEdit::ComboBoxLineEdit(QWidget *parent) setStyleSheet(QString("ComboBoxLineEdit { background-color: transparent; padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1)); } -void ComboBoxLineEdit::resizeEvent(QResizeEvent *) +void EsxView::ComboBoxLineEdit::resizeEvent(QResizeEvent *) { QSize sz = mClearButton->sizeHint(); int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); @@ -29,7 +29,7 @@ void ComboBoxLineEdit::resizeEvent(QResizeEvent *) (rect().bottom() + 1 - sz.height())/2); } -void ComboBoxLineEdit::updateClearButton(const QString& text) +void EsxView::ComboBoxLineEdit::updateClearButton(const QString& text) { mClearButton->setVisible(!text.isEmpty()); } diff --git a/components/esxselector/view/comboboxlineedit.hpp b/components/esxselector/view/comboboxlineedit.hpp index ba10731ae3..f3b251955d 100644 --- a/components/esxselector/view/comboboxlineedit.hpp +++ b/components/esxselector/view/comboboxlineedit.hpp @@ -14,22 +14,24 @@ class QToolButton; -class ComboBoxLineEdit : public QLineEdit +namespace EsxView { - Q_OBJECT + class ComboBoxLineEdit : public QLineEdit + { + Q_OBJECT -public: - ComboBoxLineEdit(QWidget *parent = 0); + public: + ComboBoxLineEdit(QWidget *parent = 0); -protected: - void resizeEvent(QResizeEvent *); + protected: + void resizeEvent(QResizeEvent *); -private slots: - void updateClearButton(const QString &text); - -private: - QToolButton *mClearButton; -}; + private slots: + void updateClearButton(const QString &text); + private: + QToolButton *mClearButton; + }; +} #endif // LIENEDIT_H diff --git a/components/esxselector/view/contentselector.cpp b/components/esxselector/view/contentselector.cpp index 16139d9e69..2de68e5bf8 100644 --- a/components/esxselector/view/contentselector.cpp +++ b/components/esxselector/view/contentselector.cpp @@ -1,8 +1,8 @@ #include "contentselector.hpp" -#include "model/datafilesmodel.hpp" -#include "masterproxymodel.hpp" -#include "model/pluginsproxymodel.hpp" +#include "../model/datafilesmodel.hpp" +#include "../model/masterproxymodel.hpp" +#include "../model/pluginsproxymodel.hpp" #include @@ -10,20 +10,20 @@ #include #include -EsxSelector::ContentSelector::ContentSelector(QWidget *parent) : +EsxView::ContentSelector::ContentSelector(QWidget *parent) : QWidget(parent) { setupUi(this); buildModelsAndViews(); } -void EsxSelector::ContentSelector::buildModelsAndViews() +void EsxView::ContentSelector::buildModelsAndViews() { // Models - mDataFilesModel = new DataFilesModel (this); + mDataFilesModel = new EsxModel::DataFilesModel (this); - mMasterProxyModel = new EsxSelector::MasterProxyModel (this, mDataFilesModel); - mPluginsProxyModel = new PluginsProxyModel (this, mDataFilesModel); + mMasterProxyModel = new EsxModel::MasterProxyModel (this, mDataFilesModel); + mPluginsProxyModel = new EsxModel::PluginsProxyModel (this, mDataFilesModel); masterView->setModel(mMasterProxyModel); pluginView->setModel(mPluginsProxyModel); @@ -35,7 +35,7 @@ void EsxSelector::ContentSelector::buildModelsAndViews() connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); } -void EsxSelector::ContentSelector::addFiles(const QString &path) +void EsxView::ContentSelector::addFiles(const QString &path) { mDataFilesModel->addFiles(path); mDataFilesModel->sort(3); // Sort by date accessed @@ -43,12 +43,12 @@ void EsxSelector::ContentSelector::addFiles(const QString &path) mDataFilesModel->uncheckAll(); } -void EsxSelector::ContentSelector::setEncoding(const QString &encoding) +void EsxView::ContentSelector::setEncoding(const QString &encoding) { mDataFilesModel->setEncoding(encoding); } -void EsxSelector::ContentSelector::setCheckState(QModelIndex index, QSortFilterProxyModel *model) +void EsxView::ContentSelector::setCheckState(QModelIndex index, QSortFilterProxyModel *model) { if (!index.isValid()) return; @@ -66,12 +66,12 @@ void EsxSelector::ContentSelector::setCheckState(QModelIndex index, QSortFilterP } } -QStringList EsxSelector::ContentSelector::checkedItemsPaths() +QStringList EsxView::ContentSelector::checkedItemsPaths() { return mDataFilesModel->checkedItemsPaths(); } -void EsxSelector::ContentSelector::updateViews() +void EsxView::ContentSelector::updateViews() { // Ensure the columns are hidden because sort() re-enables them pluginView->setColumnHidden(1, true); @@ -85,12 +85,12 @@ void EsxSelector::ContentSelector::updateViews() } -void EsxSelector::ContentSelector::slotCurrentProfileIndexChanged(int index) +void EsxView::ContentSelector::slotCurrentProfileIndexChanged(int index) { emit profileChanged(index); } -void EsxSelector::ContentSelector::slotCurrentMasterIndexChanged(int index) +void EsxView::ContentSelector::slotCurrentMasterIndexChanged(int index) { QObject *object = QObject::sender(); @@ -101,7 +101,7 @@ void EsxSelector::ContentSelector::slotCurrentMasterIndexChanged(int index) setCheckState(mMasterProxyModel->index(index, 0), mMasterProxyModel); } -void EsxSelector::ContentSelector::slotPluginTableItemClicked(const QModelIndex &index) +void EsxView::ContentSelector::slotPluginTableItemClicked(const QModelIndex &index) { setCheckState(index, mPluginsProxyModel); } diff --git a/components/esxselector/view/contentselector.hpp b/components/esxselector/view/contentselector.hpp index f1d8f6927a..35ef3a07c8 100644 --- a/components/esxselector/view/contentselector.hpp +++ b/components/esxselector/view/contentselector.hpp @@ -5,23 +5,26 @@ #include "ui_datafilespage.h" -class DataFilesModel; -class PluginsProxyModel; +namespace EsxModel +{ + class DataFilesModel; + class PluginsProxyModel; + class MasterProxyModel; +} + class QSortFilterProxyModel; -namespace FileOrderList +namespace EsxView { - class MasterProxyModel; - class ContentSelector : public QWidget, protected Ui::DataFilesPage { Q_OBJECT protected: - DataFilesModel *mDataFilesModel; - MasterProxyModel *mMasterProxyModel; - PluginsProxyModel *mPluginsProxyModel; + EsxModel::DataFilesModel *mDataFilesModel; + EsxModel::MasterProxyModel *mMasterProxyModel; + EsxModel::PluginsProxyModel *mPluginsProxyModel; public: explicit ContentSelector(QWidget *parent = 0); diff --git a/components/esxselector/view/lineedit.cpp b/components/esxselector/view/lineedit.cpp index b0f3395897..8944251ae8 100644 --- a/components/esxselector/view/lineedit.cpp +++ b/components/esxselector/view/lineedit.cpp @@ -3,7 +3,7 @@ #include "lineedit.hpp" -LineEdit::LineEdit(QWidget *parent) +EsxView::LineEdit::LineEdit(QWidget *parent) : QLineEdit(parent) { mClearButton = new QToolButton(this); @@ -24,7 +24,7 @@ LineEdit::LineEdit(QWidget *parent) qMax(msz.height(), mClearButton->sizeHint().height() + frameWidth * 2 + 2)); } -void LineEdit::resizeEvent(QResizeEvent *) +void EsxView::LineEdit::resizeEvent(QResizeEvent *) { QSize sz = mClearButton->sizeHint(); int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); @@ -32,7 +32,7 @@ void LineEdit::resizeEvent(QResizeEvent *) (rect().bottom() + 1 - sz.height())/2); } -void LineEdit::updateClearButton(const QString& text) +void EsxView::LineEdit::updateClearButton(const QString& text) { mClearButton->setVisible(!text.isEmpty()); } diff --git a/components/esxselector/view/lineedit.hpp b/components/esxselector/view/lineedit.hpp index 14bd7b1b4c..e48392ba86 100644 --- a/components/esxselector/view/lineedit.hpp +++ b/components/esxselector/view/lineedit.hpp @@ -14,22 +14,24 @@ class QToolButton; -class LineEdit : public QLineEdit +namespace EsxView { - Q_OBJECT + class LineEdit : public QLineEdit + { + Q_OBJECT -public: - LineEdit(QWidget *parent = 0); + public: + LineEdit(QWidget *parent = 0); -protected: - void resizeEvent(QResizeEvent *); + protected: + void resizeEvent(QResizeEvent *); -private slots: - void updateClearButton(const QString &text); - -private: - QToolButton *mClearButton; -}; + private slots: + void updateClearButton(const QString &text); + private: + QToolButton *mClearButton; + }; +} #endif // LIENEDIT_H diff --git a/components/esxselector/view/profilescombobox.cpp b/components/esxselector/view/profilescombobox.cpp index 9346276dae..b765f87ca2 100644 --- a/components/esxselector/view/profilescombobox.cpp +++ b/components/esxselector/view/profilescombobox.cpp @@ -7,7 +7,7 @@ #include "profilescombobox.hpp" #include "comboboxlineedit.hpp" -ProfilesComboBox::ProfilesComboBox(QWidget *parent) : +EsxView::ProfilesComboBox::ProfilesComboBox(QWidget *parent) : QComboBox(parent) { mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore @@ -21,7 +21,7 @@ ProfilesComboBox::ProfilesComboBox(QWidget *parent) : setInsertPolicy(QComboBox::NoInsert); } -void ProfilesComboBox::setEditEnabled(bool editable) +void EsxView::ProfilesComboBox::setEditEnabled(bool editable) { if (isEditable() == editable) return; @@ -47,7 +47,7 @@ void ProfilesComboBox::setEditEnabled(bool editable) SLOT(slotTextChanged(QString))); } -void ProfilesComboBox::slotTextChanged(const QString &text) +void EsxView::ProfilesComboBox::slotTextChanged(const QString &text) { QPalette *palette = new QPalette(); palette->setColor(QPalette::Text,Qt::red); @@ -61,7 +61,7 @@ void ProfilesComboBox::slotTextChanged(const QString &text) } } -void ProfilesComboBox::slotEditingFinished() +void EsxView::ProfilesComboBox::slotEditingFinished() { QString current = currentText(); QString previous = itemText(currentIndex()); @@ -82,7 +82,7 @@ void ProfilesComboBox::slotEditingFinished() emit(profileRenamed(previous, current)); } -void ProfilesComboBox::slotIndexChanged(int index) +void EsxView::ProfilesComboBox::slotIndexChanged(int index) { if (index == -1) return; @@ -91,7 +91,7 @@ void ProfilesComboBox::slotIndexChanged(int index) mOldProfile = itemText(index); } -void ProfilesComboBox::paintEvent(QPaintEvent *) +void EsxView::ProfilesComboBox::paintEvent(QPaintEvent *) { QStylePainter painter(this); painter.setPen(palette().color(QPalette::Text)); diff --git a/components/esxselector/view/profilescombobox.hpp b/components/esxselector/view/profilescombobox.hpp index 55913d7fec..218948e7bc 100644 --- a/components/esxselector/view/profilescombobox.hpp +++ b/components/esxselector/view/profilescombobox.hpp @@ -6,28 +6,31 @@ class QString; class QRegExpValidator; -class ProfilesComboBox : public QComboBox +namespace EsxView { - Q_OBJECT -public: - explicit ProfilesComboBox(QWidget *parent = 0); - void setEditEnabled(bool editable); - -signals: - void profileChanged(const QString &previous, const QString ¤t); - void profileRenamed(const QString &oldName, const QString &newName); - -private slots: - void slotEditingFinished(); - void slotIndexChanged(int index); - void slotTextChanged(const QString &text); + class ProfilesComboBox : public QComboBox + { + Q_OBJECT + public: + explicit ProfilesComboBox(QWidget *parent = 0); + void setEditEnabled(bool editable); -private: - QString mOldProfile; - QRegExpValidator *mValidator; + signals: + void profileChanged(const QString &previous, const QString ¤t); + void profileRenamed(const QString &oldName, const QString &newName); -protected: - void paintEvent(QPaintEvent *); -}; + private slots: + void slotEditingFinished(); + void slotIndexChanged(int index); + void slotTextChanged(const QString &text); + + private: + QString mOldProfile; + QRegExpValidator *mValidator; + + protected: + void paintEvent(QPaintEvent *); + }; +} #endif // PROFILESCOMBOBOX_HPP diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 07ad9d3ba6..523ee69cca 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -17,7 +17,7 @@ - + false @@ -79,7 +79,7 @@ - + true @@ -168,9 +168,9 @@ - ProfilesComboBox + EsxView::ProfilesComboBox QComboBox -
components/fileorderlist/utils/profilescombobox.hpp
+
components/esxselector/view/profilescombobox.hpp
From e614ec335334d186dac0fba1147fa4c0e5bd6400 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 18 Aug 2013 17:11:23 -0500 Subject: [PATCH 018/148] Fixing profile code in progress... --- apps/opencs/view/doc/filedialog.cpp | 11 +- apps/opencs/view/doc/filedialog.hpp | 8 +- .../esxselector/view/contentselector.cpp | 4 +- .../esxselector/view/profilescombobox.cpp | 7 +- .../esxselector/view/profilescombobox.hpp | 2 + files/ui/datafilespage.ui | 257 +++++++++++------- 6 files changed, 175 insertions(+), 114 deletions(-) diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 12849d6ee0..4f4aef4f23 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -22,10 +22,7 @@ CSVDoc::FileDialog::FileDialog(QWidget *parent) : ContentSelector(parent) { // Hide the profile elements - profileLabel->hide(); - profilesComboBox->hide(); - newProfileButton->hide(); - deleteProfileButton->hide(); + profileGroupBox->hide(); // Add some extra widgets QHBoxLayout *nameLayout = new QHBoxLayout(); @@ -34,12 +31,12 @@ CSVDoc::FileDialog::FileDialog(QWidget *parent) : mNameLabel = new QLabel(tr("File Name:"), this); QRegExpValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9\\s]*$")); - //mNameLineEdit = new LineEdit(this); - //mNameLineEdit->setValidator(validator); + mNameLineEdit = new EsxView::LineEdit(this); + mNameLineEdit->setValidator(validator); nameLayout->addSpacerItem(spacer); nameLayout->addWidget(mNameLabel); - //nameLayout->addWidget(mNameLineEdit); + nameLayout->addWidget(mNameLineEdit); mButtonBox = new QDialogButtonBox(this); diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index d016ad32d1..0e2d8f32b8 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -14,10 +14,16 @@ class QPushButton; class QStringList; class QString; class QMenu; +class QLabel; class DataFilesModel; class PluginsProxyModel; +namespace EsxView +{ + class LineEdit; +} + namespace CSVDoc { class FileDialog : public EsxView::ContentSelector @@ -48,7 +54,7 @@ namespace CSVDoc private: QLabel *mNameLabel; - //LineEdit *mNameLineEdit; + EsxView::LineEdit *mNameLineEdit; QPushButton *mCreateButton; QDialogButtonBox *mButtonBox; diff --git a/components/esxselector/view/contentselector.cpp b/components/esxselector/view/contentselector.cpp index 2de68e5bf8..266fd76dcb 100644 --- a/components/esxselector/view/contentselector.cpp +++ b/components/esxselector/view/contentselector.cpp @@ -25,9 +25,11 @@ void EsxView::ContentSelector::buildModelsAndViews() 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); - pluginView-> + profilesComboBox->setPlaceholderText(QString("Select a profile...")); + connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); connect(pluginView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotPluginTableItemClicked(const QModelIndex &))); diff --git a/components/esxselector/view/profilescombobox.cpp b/components/esxselector/view/profilescombobox.cpp index b765f87ca2..0d709aa50c 100644 --- a/components/esxselector/view/profilescombobox.cpp +++ b/components/esxselector/view/profilescombobox.cpp @@ -103,6 +103,11 @@ void EsxView::ProfilesComboBox::paintEvent(QPaintEvent *) // draw the icon and text if (!opt.editable && currentIndex() == -1) // <<< we adjust the text displayed when nothing is selected - opt.currentText = tr("Select a game file..."); + opt.currentText = mPlaceholderText; painter.drawControl(QStyle::CE_ComboBoxLabel, opt); } + +void EsxView::ProfilesComboBox::setPlaceholderText(const QString &text) +{ + mPlaceholderText = text; +} diff --git a/components/esxselector/view/profilescombobox.hpp b/components/esxselector/view/profilescombobox.hpp index 218948e7bc..28740783ba 100644 --- a/components/esxselector/view/profilescombobox.hpp +++ b/components/esxselector/view/profilescombobox.hpp @@ -14,6 +14,7 @@ namespace EsxView public: explicit ProfilesComboBox(QWidget *parent = 0); void setEditEnabled(bool editable); + void setPlaceholderText (const QString &text); signals: void profileChanged(const QString &previous, const QString ¤t); @@ -26,6 +27,7 @@ namespace EsxView private: QString mOldProfile; + QString mPlaceholderText; QRegExpValidator *mValidator; protected: diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 523ee69cca..6235f31afd 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -14,113 +14,162 @@ Qt::DefaultContextMenu + + 6 + + + 6 + - - - - - false - - - - + + + Content + + + + 9 + + + 6 + + + 6 + + + + + + + false + + + + + + + + + + + + 0 + 0 + + + + Qt::DefaultContextMenu + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + Qt::ElideLeft + + + false + + + false + + + true + + + false + + + + + + + pluginView + masterView + pluginView + masterView + + + - - - - - - 0 - 0 - - - - Qt::DefaultContextMenu - - - QAbstractItemView::NoEditTriggers - - - true - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - Qt::ElideLeft - - - false - - - false - - - true - - - false - - - - - - - - - - - Current Profile: - - - - - - - true - - - - 0 - 0 - - - - - - - - New Profile - - - &New Profile - - - true - - - - - - - Delete Profile - - - Delete Profile - - - Ctrl+D - - - true - - - - + + + Qt::NoFocus + + + Profiles + + + false + + + + 6 + + + 9 + + + 9 + + + 0 + + + 6 + + + + + true + + + + 0 + 0 + + + + + + + + New Profile + + + &New Profile + + + true + + + + + + + Delete Profile + + + Delete Profile + + + Ctrl+D + + + true + + + + + From b52645bf2ac594b6762fabf2b79943fd3e42d8c7 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Tue, 20 Aug 2013 03:23:32 -0500 Subject: [PATCH 019/148] Fixes to accommodate master/plugin loading --- apps/launcher/datafilespage.cpp | 96 ++----------------- apps/opencs/view/doc/filedialog.cpp | 9 +- .../esxselector/model/datafilesmodel.cpp | 34 ++++--- .../esxselector/model/datafilesmodel.hpp | 10 +- .../esxselector/view/contentselector.cpp | 1 + files/ui/datafilespage.ui | 4 +- 6 files changed, 38 insertions(+), 116 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 2346a0b014..15f8d9ba27 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -29,16 +29,11 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam , mLauncherSettings(launcherSettings) , ContentSelector(parent) { - - pluginView->hideColumn(2); // Create a dialog for the new profile name input mNewProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this); connect(mNewProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString))); - //connect(pluginView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); - //connect(masterView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); - createActions(); setupDataFiles(); } @@ -49,11 +44,6 @@ void DataFilesPage::createActions() // Add the actions to the toolbuttons newProfileButton->setDefaultAction(newProfileAction); deleteProfileButton->setDefaultAction(deleteProfileAction); - - // Context menu actions - mContextMenu = new QMenu(this); - mContextMenu->addAction(checkAction); - mContextMenu->addAction(uncheckAction); } void DataFilesPage::setupDataFiles() @@ -150,17 +140,17 @@ void DataFilesPage::saveSettings() mGameSettings.remove(QString("master")); mGameSettings.remove(QString("plugin")); - QStringList items = mDataFilesModel->checkedItems(); + EsxModel::EsmFileList items = mDataFilesModel->checkedItems(); - foreach(const QString &item, items) { + foreach(const EsxModel::EsmFile *item, items) { - if (item.endsWith(QString(".esm"), Qt::CaseInsensitive)) { - mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/master"), item); - mGameSettings.setMultiValue(QString("master"), item); + if (item->masters().size() == 0) { + mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/master"), item->fileName()); + mGameSettings.setMultiValue(QString("master"), item->fileName()); - } else if (item.endsWith(QString(".esp"), Qt::CaseInsensitive)) { - mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/plugin"), item); - mGameSettings.setMultiValue(QString("plugin"), item); + } else { + mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/plugin"), item->fileName()); + mGameSettings.setMultiValue(QString("plugin"), item->fileName()); } } @@ -296,73 +286,3 @@ void DataFilesPage::profileRenamed(const QString &previous, const QString &curre loadSettings(); } -/* -void DataFilesPage::showContextMenu(const QPoint &point) -{ - QObject *object = QObject::sender(); - - // Not a signal-slot call - if (!object) - return; - - if (object->objectName() == QLatin1String("PluginView")) { - if (!pluginView->selectionModel()->hasSelection()) - return; - - QPoint globalPos = pluginView->mapToGlobal(point); - QModelIndexList indexes = pluginView->selectionModel()->selectedIndexes(); - - // Show the check/uncheck actions depending on the state of the selected items - uncheckAction->setEnabled(false); - checkAction->setEnabled(false); - - foreach (const QModelIndex &index, indexes) - { - if (!index.isValid()) - return; - - QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(index); - - if (!sourceIndex.isValid()) - return; - - (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) - ? uncheckAction->setEnabled(true) - : checkAction->setEnabled(true); - } - - // Show menu - mContextMenu->exec(globalPos); - } - - if (object->objectName() == QLatin1String("MasterView")) { - if (!masterView->selectionModel()->hasSelection()) - return; - - QPoint globalPos = masterView->mapToGlobal(point); - QModelIndexList indexes = masterView->selectionModel()->selectedIndexes(); - - // Show the check/uncheck actions depending on the state of the selected items - uncheckAction->setEnabled(false); - checkAction->setEnabled(false); - - foreach (const QModelIndex &index, indexes) - { - if (!index.isValid()) - return; - - QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index); - - if (!sourceIndex.isValid()) - return; - - (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) - ? uncheckAction->setEnabled(true) - : checkAction->setEnabled(true); - } - - mContextMenu->exec(globalPos); - } - -} -*/ diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 4f4aef4f23..69ed1c13a7 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -23,6 +23,7 @@ CSVDoc::FileDialog::FileDialog(QWidget *parent) : { // Hide the profile elements profileGroupBox->hide(); + pluginView->showColumn(2); // Add some extra widgets QHBoxLayout *nameLayout = new QHBoxLayout(); @@ -77,7 +78,7 @@ void CSVDoc::FileDialog::updateCreateButton(const QString &name) QString CSVDoc::FileDialog::fileName() { - //return mNameLineEdit->text(); + return mNameLineEdit->text(); } void CSVDoc::FileDialog::openFile() @@ -85,7 +86,7 @@ void CSVDoc::FileDialog::openFile() setWindowTitle(tr("Open")); mNameLabel->hide(); - //mNameLineEdit->hide(); + mNameLineEdit->hide(); mCreateButton->hide(); mButtonBox->removeButton(mCreateButton); @@ -103,8 +104,8 @@ void CSVDoc::FileDialog::newFile() setWindowTitle(tr("New")); mNameLabel->show(); - //mNameLineEdit->clear(); - //mNameLineEdit->show(); + mNameLineEdit->clear(); + mNameLineEdit->show(); mCreateButton->show(); mButtonBox->setStandardButtons(QDialogButtonBox::Cancel); diff --git a/components/esxselector/model/datafilesmodel.cpp b/components/esxselector/model/datafilesmodel.cpp index 2980313f0e..49d0d6132a 100644 --- a/components/esxselector/model/datafilesmodel.cpp +++ b/components/esxselector/model/datafilesmodel.cpp @@ -365,23 +365,21 @@ EsxModel::EsmFile* EsxModel::DataFilesModel::item(int row) const return 0; } -QStringList EsxModel::DataFilesModel::checkedItems() +EsxModel::EsmFileList EsxModel::DataFilesModel::checkedItems() { - QStringList list; + EsmFileList 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); - ++i; - - QString name = file->fileName(); + for (it = mFiles.constBegin(); it != itEnd; ++it) + { + EsmFile *file = *it; // Only add the items that are in the checked list and available - if (mCheckStates[name] == Qt::Checked && canBeChecked(file)) - list << name; + if (mCheckStates[file->fileName()] == Qt::Checked && canBeChecked(file)) + list << file; } return list; @@ -413,13 +411,13 @@ void EsxModel::DataFilesModel::uncheckAll() emit layoutChanged(); } -QStringList EsxModel::DataFilesModel::uncheckedItems() +EsxModel::EsmFileList EsxModel::DataFilesModel::uncheckedItems() { - QStringList list; - QStringList checked = checkedItems(); + EsmFileList list; + EsmFileList checked = checkedItems(); - 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) { @@ -427,8 +425,8 @@ QStringList EsxModel::DataFilesModel::uncheckedItems() ++i; // Add the items that are not in the checked list - if (!checked.contains(file->fileName())) - list << file->fileName(); + if (!checked.contains(file)) + list << file; } return list; diff --git a/components/esxselector/model/datafilesmodel.hpp b/components/esxselector/model/datafilesmodel.hpp index bc55bb6cff..24b36aa88b 100644 --- a/components/esxselector/model/datafilesmodel.hpp +++ b/components/esxselector/model/datafilesmodel.hpp @@ -10,6 +10,8 @@ namespace EsxModel { class EsmFile; + typedef QList EsmFileList; + class DataFilesModel : public QAbstractTableModel { Q_OBJECT @@ -39,8 +41,8 @@ namespace EsxModel void uncheckAll(); - QStringList checkedItems(); - QStringList uncheckedItems(); + EsmFileList checkedItems(); + EsmFileList uncheckedItems(); QStringList checkedItemsPaths(); Qt::CheckState checkState(const QModelIndex &index); @@ -51,13 +53,13 @@ namespace EsxModel EsmFile* item(int row) const; signals: - void checkedItemsChanged(const QStringList &items); + void checkedItemsChanged(const EsmFileList &items); private: bool canBeChecked(EsmFile *file) const; void addFile(EsmFile *file); - QList mFiles; + EsmFileList mFiles; QHash mCheckStates; QString mEncoding; diff --git a/components/esxselector/view/contentselector.cpp b/components/esxselector/view/contentselector.cpp index 266fd76dcb..0b47802416 100644 --- a/components/esxselector/view/contentselector.cpp +++ b/components/esxselector/view/contentselector.cpp @@ -77,6 +77,7 @@ void EsxView::ContentSelector::updateViews() { // Ensure the columns are hidden because sort() re-enables them pluginView->setColumnHidden(1, true); + pluginView->setColumnHidden(2, true); pluginView->setColumnHidden(3, true); pluginView->setColumnHidden(4, true); pluginView->setColumnHidden(5, true); diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 6235f31afd..ecc70dfcb3 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -27,7 +27,7 @@ - 9 + 3 6 @@ -115,7 +115,7 @@ 6 - 9 + 3 9 From 24e38846da6f96b79f722cdad989672dcbb99c21 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Tue, 20 Aug 2013 03:53:23 -0500 Subject: [PATCH 020/148] Fixed broken profile actions --- apps/launcher/datafilespage.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 15f8d9ba27..6f8d1dfac3 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -29,6 +29,8 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam , mLauncherSettings(launcherSettings) , ContentSelector(parent) { + QMetaObject::connectSlotsByName(this); + // Create a dialog for the new profile name input mNewProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this); @@ -40,10 +42,13 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam void DataFilesPage::createActions() { - + qDebug () << "adding actions..."; // Add the actions to the toolbuttons newProfileButton->setDefaultAction(newProfileAction); deleteProfileButton->setDefaultAction(deleteProfileAction); + + for (int i = 0; i < newProfileButton->actions().size(); i++) + qDebug() << newProfileButton->actions().at(i)->objectName(); } void DataFilesPage::setupDataFiles() @@ -186,6 +191,7 @@ int DataFilesPage::profilesComboBoxIndex() void DataFilesPage::on_newProfileAction_triggered() { + qDebug() << "new_profile_action_triggered"; if (mNewProfileDialog->exec() == QDialog::Accepted) { QString profile = mNewProfileDialog->lineEdit()->text(); profilesComboBox->addItem(profile); From 6898321676e0841cd2dbc70bf93b57748199d924 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Tue, 20 Aug 2013 08:16:56 -0500 Subject: [PATCH 021/148] Reenabling features Profile functions enabled New/load file functions partially enabled Layout reorganized --- apps/launcher/datafilespage.cpp | 4 +- apps/opencs/view/doc/filedialog.cpp | 67 +++++------------------- apps/opencs/view/doc/filedialog.hpp | 11 ---- components/esxselector/view/lineedit.cpp | 1 + components/esxselector/view/lineedit.hpp | 2 + files/ui/datafilespage.ui | 53 +++++++++++++++---- 6 files changed, 60 insertions(+), 78 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 6f8d1dfac3..d51952b113 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -31,6 +31,8 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam { QMetaObject::connectSlotsByName(this); + projectGroupBox->hide(); + // Create a dialog for the new profile name input mNewProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this); @@ -42,7 +44,6 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam void DataFilesPage::createActions() { - qDebug () << "adding actions..."; // Add the actions to the toolbuttons newProfileButton->setDefaultAction(newProfileAction); deleteProfileButton->setDefaultAction(deleteProfileAction); @@ -191,7 +192,6 @@ int DataFilesPage::profilesComboBoxIndex() void DataFilesPage::on_newProfileAction_triggered() { - qDebug() << "new_profile_action_triggered"; if (mNewProfileDialog->exec() == QDialog::Accepted) { QString profile = mNewProfileDialog->lineEdit()->text(); profilesComboBox->addItem(profile); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 69ed1c13a7..a0360cc5ea 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -25,42 +25,20 @@ CSVDoc::FileDialog::FileDialog(QWidget *parent) : profileGroupBox->hide(); pluginView->showColumn(2); - // Add some extra widgets - QHBoxLayout *nameLayout = new QHBoxLayout(); - QSpacerItem *spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - - mNameLabel = new QLabel(tr("File Name:"), this); - - QRegExpValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9\\s]*$")); - mNameLineEdit = new EsxView::LineEdit(this); - mNameLineEdit->setValidator(validator); - - nameLayout->addSpacerItem(spacer); - nameLayout->addWidget(mNameLabel); - nameLayout->addWidget(mNameLineEdit); - - mButtonBox = new QDialogButtonBox(this); - - mCreateButton = new QPushButton(tr("Create"), this); - mCreateButton->setEnabled(false); - - verticalLayout->addLayout(nameLayout); - verticalLayout->addWidget(mButtonBox); - resize(400, 400); // connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList))); //connect(mNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateCreateButton(QString))); - // connect(mCreateButton, SIGNAL(clicked()), this, SLOT(createButtonClicked())); + connect(projectCreateButton, SIGNAL(clicked()), this, SIGNAL(createNewFile())); - // connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept())); - // connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject())); + connect(mButtonBox, SIGNAL(accepted()), this, SIGNAL(openFiles()); + // connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject())); } void CSVDoc::FileDialog::updateOpenButton(const QStringList &items) { - QPushButton *openButton = mButtonBox->button(QDialogButtonBox::Open); + QPushButton *openButton = projectButtonBox->button(QDialogButtonBox::Open); if (!openButton) return; @@ -70,29 +48,25 @@ void CSVDoc::FileDialog::updateOpenButton(const QStringList &items) void CSVDoc::FileDialog::updateCreateButton(const QString &name) { - if (!mCreateButton->isVisible()) + if (!projectCreateButton->isVisible()) return; - mCreateButton->setEnabled(!name.isEmpty()); + projectCreateButton->setEnabled(!name.isEmpty()); } QString CSVDoc::FileDialog::fileName() { - return mNameLineEdit->text(); + return projectNameLineEdit->text(); } void CSVDoc::FileDialog::openFile() { setWindowTitle(tr("Open")); - mNameLabel->hide(); - mNameLineEdit->hide(); - mCreateButton->hide(); - - mButtonBox->removeButton(mCreateButton); - mButtonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Open); - QPushButton *openButton = mButtonBox->button(QDialogButtonBox::Open); - openButton->setEnabled(false); + projectNameLineEdit->hide(); + projectCreateButton->hide(); + projectGroupBox->setTitle(tr("")); + projectButtonBox->button(QDialogButtonBox::Open)->setEnabled(false); show(); raise(); @@ -103,25 +77,10 @@ void CSVDoc::FileDialog::newFile() { setWindowTitle(tr("New")); - mNameLabel->show(); - mNameLineEdit->clear(); - mNameLineEdit->show(); - mCreateButton->show(); - - mButtonBox->setStandardButtons(QDialogButtonBox::Cancel); - mButtonBox->addButton(mCreateButton, QDialogButtonBox::ActionRole); + projectButtonBox->setStandardButtons(QDialogButtonBox::Cancel); + projectButtonBox->addButton(projectCreateButton, QDialogButtonBox::ActionRole); show(); raise(); activateWindow(); } - -void CSVDoc::FileDialog::accept() -{ - emit openFiles(); -} - -void CSVDoc::FileDialog::createButtonClicked() -{ - emit createNewFile(); -} diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 0e2d8f32b8..c749099d48 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -34,7 +34,6 @@ namespace CSVDoc void openFile(); void newFile(); - void accepted(); QString fileName(); @@ -43,21 +42,11 @@ namespace CSVDoc void createNewFile(); public slots: - void accept(); private slots: //void updateViews(); void updateOpenButton(const QStringList &items); void updateCreateButton(const QString &name); - - void createButtonClicked(); - - private: - QLabel *mNameLabel; - EsxView::LineEdit *mNameLineEdit; - - QPushButton *mCreateButton; - QDialogButtonBox *mButtonBox; }; } #endif // FILEDIALOG_HPP diff --git a/components/esxselector/view/lineedit.cpp b/components/esxselector/view/lineedit.cpp index 8944251ae8..48be2f022f 100644 --- a/components/esxselector/view/lineedit.cpp +++ b/components/esxselector/view/lineedit.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "lineedit.hpp" diff --git a/components/esxselector/view/lineedit.hpp b/components/esxselector/view/lineedit.hpp index e48392ba86..4e0cbe3390 100644 --- a/components/esxselector/view/lineedit.hpp +++ b/components/esxselector/view/lineedit.hpp @@ -20,6 +20,8 @@ namespace EsxView { Q_OBJECT + QString mPlaceholderText; + public: LineEdit(QWidget *parent = 0); diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index ecc70dfcb3..60e8b8bf5d 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -14,12 +14,6 @@ Qt::DefaultContextMenu - - 6 - - - 6 - @@ -91,21 +85,53 @@
- pluginView - masterView - pluginView - masterView
+ + + + Project + + + + + + Enter project name... + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Open + + + + + + + false + + + Create + + + + + projectButtonBox + projectCreateButton + projectNameLineEdit + + Qt::NoFocus - Profiles + Profile false @@ -221,6 +247,11 @@ QComboBox
components/esxselector/view/profilescombobox.hpp
+ + EsxView::LineEdit + QLineEdit +
components/esxselector/view/lineedit.hpp
+
From e6fdc7e7fdce784b2e540022087cc90b35a0bbc4 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Tue, 20 Aug 2013 12:34:39 -0500 Subject: [PATCH 022/148] ... --- apps/launcher/datafilespage.cpp | 3 --- apps/opencs/view/doc/filedialog.cpp | 8 ++++---- components/esxselector/view/contentselector.cpp | 2 +- components/esxselector/view/contentselector.hpp | 4 ++-- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index d51952b113..070f455e4e 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -47,9 +47,6 @@ void DataFilesPage::createActions() // Add the actions to the toolbuttons newProfileButton->setDefaultAction(newProfileAction); deleteProfileButton->setDefaultAction(deleteProfileAction); - - for (int i = 0; i < newProfileButton->actions().size(); i++) - qDebug() << newProfileButton->actions().at(i)->objectName(); } void DataFilesPage::setupDataFiles() diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index a0360cc5ea..561f7f5d7d 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -27,13 +27,13 @@ CSVDoc::FileDialog::FileDialog(QWidget *parent) : resize(400, 400); - // connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList))); - //connect(mNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateCreateButton(QString))); + 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())); - connect(mButtonBox, SIGNAL(accepted()), this, SIGNAL(openFiles()); - // connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject())); + connect(projectButtonBox, SIGNAL(accepted()), this, SIGNAL(openFiles())); + connect(projectButtonBox, SIGNAL(rejected()), this, SLOT(reject())); } void CSVDoc::FileDialog::updateOpenButton(const QStringList &items) diff --git a/components/esxselector/view/contentselector.cpp b/components/esxselector/view/contentselector.cpp index 0b47802416..6cba643d21 100644 --- a/components/esxselector/view/contentselector.cpp +++ b/components/esxselector/view/contentselector.cpp @@ -11,7 +11,7 @@ #include EsxView::ContentSelector::ContentSelector(QWidget *parent) : - QWidget(parent) + QDialog(parent) { setupUi(this); buildModelsAndViews(); diff --git a/components/esxselector/view/contentselector.hpp b/components/esxselector/view/contentselector.hpp index 35ef3a07c8..658e0176c6 100644 --- a/components/esxselector/view/contentselector.hpp +++ b/components/esxselector/view/contentselector.hpp @@ -1,7 +1,7 @@ #ifndef CONTENTSELECTOR_HPP #define CONTENTSELECTOR_HPP -#include +#include #include "ui_datafilespage.h" @@ -16,7 +16,7 @@ class QSortFilterProxyModel; namespace EsxView { - class ContentSelector : public QWidget, protected Ui::DataFilesPage + class ContentSelector : public QDialog, protected Ui::DataFilesPage { Q_OBJECT From a6e7cf9a8c795ffebd6c45e72f80b4650f1bbd7b Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sat, 7 Sep 2013 15:57:40 -0500 Subject: [PATCH 023/148] 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 | 16 +- 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 | 21 +- .../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 561f7f5d7d..fb031fe5f5 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 8b07a4e00e..0f7c5017b7 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 0000000000..673665775a --- /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 0000000000..a585ab63b8 --- /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 49d0d6132a..ee940bb273 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 24b36aa88b..4f23cd5307 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 96b90e44e2..95cf703121 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 6a3e36b538..0cda018b3c 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 011e5ebd54..46d68ca513 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 fed01bdb14..8a5c730325 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 8c1e83695d..03b19f6910 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 64596302c8..5ee5e417ed 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 4fde11f470..412367b649 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); + } + return QSortFilterProxyModel::data(index, role); +} + +bool EsxModel::PluginsProxyModel::removeRows(int position, int rows, const QModelIndex &parent) +{ + bool success = QSortFilterProxyModel::removeRows(position, rows, parent); + + return success; } diff --git a/components/esxselector/model/pluginsproxymodel.hpp b/components/esxselector/model/pluginsproxymodel.hpp index 04c18c2e22..4415df716e 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 0000000000..7b54adba91 --- /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 0000000000..cf51456755 --- /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 6cba643d21..bc7cc2b8bb 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 658e0176c6..06cc8f3f0b 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 60e8b8bf5d..76689627b0 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 + + + From c961abce963d4d9681861510dad796c0fb65dd7a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 14 Sep 2013 14:00:07 +0200 Subject: [PATCH 024/148] added warning message to startup window --- apps/opencs/view/doc/startup.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apps/opencs/view/doc/startup.cpp b/apps/opencs/view/doc/startup.cpp index 4cc64f2dfa..5d59492c64 100644 --- a/apps/opencs/view/doc/startup.cpp +++ b/apps/opencs/view/doc/startup.cpp @@ -104,6 +104,17 @@ CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2) layout->addWidget (createButtons()); layout->addWidget (createTools()); + /// \todo remove this label once loading and saving are fully implemented + QLabel *warning = new QLabel ("WARNING:

OpenCS is in alpha stage.
The code for loading and saving is incomplete.
This version of OpenCS is only a preview.
Do NOT use it for real editing!
You will lose records both on loading and on saving.

Please note:
If you lose data and come to the OpenMW forum to complain,
we will mock you.
"); + + QFont font; + font.setPointSize (12); + font.setBold (true); + + warning->setFont (font); + + layout->addWidget (warning, 1); + setLayout (layout); QRect scr = QApplication::desktop()->screenGeometry(); From 077a157841a59bf8bb3ed7e4e7dbfd5bf745b9ff Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 14 Sep 2013 14:56:23 +0200 Subject: [PATCH 025/148] moved Operation and Stage from model/tools to model/doc --- apps/opencs/CMakeLists.txt | 8 ++++---- apps/opencs/model/{tools => doc}/operation.cpp | 17 ++++++++--------- apps/opencs/model/{tools => doc}/operation.hpp | 6 +++--- apps/opencs/model/doc/stage.cpp | 4 ++++ apps/opencs/model/{tools => doc}/stage.hpp | 8 ++++---- apps/opencs/model/tools/birthsigncheck.hpp | 4 ++-- apps/opencs/model/tools/classcheck.hpp | 4 ++-- apps/opencs/model/tools/factioncheck.hpp | 4 ++-- apps/opencs/model/tools/mandatoryid.hpp | 4 ++-- apps/opencs/model/tools/racecheck.hpp | 4 ++-- apps/opencs/model/tools/regioncheck.hpp | 4 ++-- apps/opencs/model/tools/skillcheck.hpp | 4 ++-- apps/opencs/model/tools/soundcheck.hpp | 4 ++-- apps/opencs/model/tools/spellcheck.hpp | 4 ++-- apps/opencs/model/tools/stage.cpp | 4 ---- apps/opencs/model/tools/tools.cpp | 8 ++++---- apps/opencs/model/tools/tools.hpp | 10 +++++++--- apps/opencs/model/tools/verifier.hpp | 4 ++-- 18 files changed, 54 insertions(+), 51 deletions(-) rename apps/opencs/model/{tools => doc}/operation.cpp (82%) rename apps/opencs/model/{tools => doc}/operation.hpp (92%) create mode 100644 apps/opencs/model/doc/stage.cpp rename apps/opencs/model/{tools => doc}/stage.hpp (65%) delete mode 100644 apps/opencs/model/tools/stage.cpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index aa6f6ba766..367c43eb73 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -5,11 +5,11 @@ opencs_units (. editor) set (CMAKE_BUILD_TYPE DEBUG) opencs_units (model/doc - document + document operation ) opencs_units_noqt (model/doc - documentmanager + documentmanager stage ) opencs_hdrs_noqt (model/doc @@ -33,11 +33,11 @@ opencs_hdrs_noqt (model/world opencs_units (model/tools - tools operation reportmodel + tools reportmodel ) opencs_units_noqt (model/tools - stage verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck + verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck birthsigncheck spellcheck ) diff --git a/apps/opencs/model/tools/operation.cpp b/apps/opencs/model/doc/operation.cpp similarity index 82% rename from apps/opencs/model/tools/operation.cpp rename to apps/opencs/model/doc/operation.cpp index 71761cdaea..8f7472c5f6 100644 --- a/apps/opencs/model/tools/operation.cpp +++ b/apps/opencs/model/doc/operation.cpp @@ -6,11 +6,10 @@ #include -#include "../doc/state.hpp" - +#include "state.hpp" #include "stage.hpp" -void CSMTools::Operation::prepareStages() +void CSMDoc::Operation::prepareStages() { mCurrentStage = mStages.begin(); mCurrentStep = 0; @@ -24,15 +23,15 @@ void CSMTools::Operation::prepareStages() } } -CSMTools::Operation::Operation (int type) : mType (type) {} +CSMDoc::Operation::Operation (int type) : mType (type) {} -CSMTools::Operation::~Operation() +CSMDoc::Operation::~Operation() { for (std::vector >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter) delete iter->first; } -void CSMTools::Operation::run() +void CSMDoc::Operation::run() { prepareStages(); @@ -45,17 +44,17 @@ void CSMTools::Operation::run() exec(); } -void CSMTools::Operation::appendStage (Stage *stage) +void CSMDoc::Operation::appendStage (Stage *stage) { mStages.push_back (std::make_pair (stage, 0)); } -void CSMTools::Operation::abort() +void CSMDoc::Operation::abort() { exit(); } -void CSMTools::Operation::verify() +void CSMDoc::Operation::verify() { std::vector messages; diff --git a/apps/opencs/model/tools/operation.hpp b/apps/opencs/model/doc/operation.hpp similarity index 92% rename from apps/opencs/model/tools/operation.hpp rename to apps/opencs/model/doc/operation.hpp index 4731c58fa8..703098852f 100644 --- a/apps/opencs/model/tools/operation.hpp +++ b/apps/opencs/model/doc/operation.hpp @@ -1,11 +1,11 @@ -#ifndef CSM_TOOLS_OPERATION_H -#define CSM_TOOLS_OPERATION_H +#ifndef CSM_DOC_OPERATION_H +#define CSM_DOC_OPERATION_H #include #include -namespace CSMTools +namespace CSMDoc { class Stage; diff --git a/apps/opencs/model/doc/stage.cpp b/apps/opencs/model/doc/stage.cpp new file mode 100644 index 0000000000..99b7657709 --- /dev/null +++ b/apps/opencs/model/doc/stage.cpp @@ -0,0 +1,4 @@ + +#include "stage.hpp" + +CSMDoc::Stage::~Stage() {} \ No newline at end of file diff --git a/apps/opencs/model/tools/stage.hpp b/apps/opencs/model/doc/stage.hpp similarity index 65% rename from apps/opencs/model/tools/stage.hpp rename to apps/opencs/model/doc/stage.hpp index 3020936f32..1f96c60b43 100644 --- a/apps/opencs/model/tools/stage.hpp +++ b/apps/opencs/model/doc/stage.hpp @@ -1,10 +1,10 @@ -#ifndef CSM_TOOLS_STAGE_H -#define CSM_TOOLS_STAGE_H +#ifndef CSM_DOC_STAGE_H +#define CSM_DOC_STAGE_H #include #include -namespace CSMTools +namespace CSMDoc { class Stage { @@ -16,7 +16,7 @@ namespace CSMTools ///< \return number of steps virtual void perform (int stage, std::vector& messages) = 0; - ///< Messages resulting from this tage will be appended to \a messages. + ///< Messages resulting from this stage will be appended to \a messages. }; } diff --git a/apps/opencs/model/tools/birthsigncheck.hpp b/apps/opencs/model/tools/birthsigncheck.hpp index 42b5a6b244..bdd65b44ab 100644 --- a/apps/opencs/model/tools/birthsigncheck.hpp +++ b/apps/opencs/model/tools/birthsigncheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that birthsign records are internally consistent - class BirthsignCheckStage : public Stage + class BirthsignCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mBirthsigns; diff --git a/apps/opencs/model/tools/classcheck.hpp b/apps/opencs/model/tools/classcheck.hpp index a29d7c8b78..3604b451c5 100644 --- a/apps/opencs/model/tools/classcheck.hpp +++ b/apps/opencs/model/tools/classcheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that class records are internally consistent - class ClassCheckStage : public Stage + class ClassCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mClasses; diff --git a/apps/opencs/model/tools/factioncheck.hpp b/apps/opencs/model/tools/factioncheck.hpp index 8686505727..7cd80347db 100644 --- a/apps/opencs/model/tools/factioncheck.hpp +++ b/apps/opencs/model/tools/factioncheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that faction records are internally consistent - class FactionCheckStage : public Stage + class FactionCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mFactions; diff --git a/apps/opencs/model/tools/mandatoryid.hpp b/apps/opencs/model/tools/mandatoryid.hpp index 342e2d7540..5fddf08d32 100644 --- a/apps/opencs/model/tools/mandatoryid.hpp +++ b/apps/opencs/model/tools/mandatoryid.hpp @@ -6,7 +6,7 @@ #include "../world/universalid.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMWorld { @@ -16,7 +16,7 @@ namespace CSMWorld namespace CSMTools { /// \brief Verify stage: make sure that records with specific IDs exist. - class MandatoryIdStage : public Stage + class MandatoryIdStage : public CSMDoc::Stage { const CSMWorld::CollectionBase& mIdCollection; CSMWorld::UniversalId mCollectionId; diff --git a/apps/opencs/model/tools/racecheck.hpp b/apps/opencs/model/tools/racecheck.hpp index 155f799021..ff9948bf6d 100644 --- a/apps/opencs/model/tools/racecheck.hpp +++ b/apps/opencs/model/tools/racecheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that race records are internally consistent - class RaceCheckStage : public Stage + class RaceCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mRaces; bool mPlayable; diff --git a/apps/opencs/model/tools/regioncheck.hpp b/apps/opencs/model/tools/regioncheck.hpp index b421356514..c8c437cbd2 100644 --- a/apps/opencs/model/tools/regioncheck.hpp +++ b/apps/opencs/model/tools/regioncheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that region records are internally consistent - class RegionCheckStage : public Stage + class RegionCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mRegions; diff --git a/apps/opencs/model/tools/skillcheck.hpp b/apps/opencs/model/tools/skillcheck.hpp index 30a3f01cad..662bdadee1 100644 --- a/apps/opencs/model/tools/skillcheck.hpp +++ b/apps/opencs/model/tools/skillcheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that skill records are internally consistent - class SkillCheckStage : public Stage + class SkillCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mSkills; diff --git a/apps/opencs/model/tools/soundcheck.hpp b/apps/opencs/model/tools/soundcheck.hpp index a309763a12..00b45cd935 100644 --- a/apps/opencs/model/tools/soundcheck.hpp +++ b/apps/opencs/model/tools/soundcheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that sound records are internally consistent - class SoundCheckStage : public Stage + class SoundCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mSounds; diff --git a/apps/opencs/model/tools/spellcheck.hpp b/apps/opencs/model/tools/spellcheck.hpp index 0566392193..880ddafcd2 100644 --- a/apps/opencs/model/tools/spellcheck.hpp +++ b/apps/opencs/model/tools/spellcheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that spell records are internally consistent - class SpellCheckStage : public Stage + class SpellCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mSpells; diff --git a/apps/opencs/model/tools/stage.cpp b/apps/opencs/model/tools/stage.cpp deleted file mode 100644 index 6f4567e579..0000000000 --- a/apps/opencs/model/tools/stage.cpp +++ /dev/null @@ -1,4 +0,0 @@ - -#include "stage.hpp" - -CSMTools::Stage::~Stage() {} \ No newline at end of file diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 803861203c..1d49028ed3 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -21,7 +21,7 @@ #include "birthsigncheck.hpp" #include "spellcheck.hpp" -CSMTools::Operation *CSMTools::Tools::get (int type) +CSMDoc::Operation *CSMTools::Tools::get (int type) { switch (type) { @@ -31,7 +31,7 @@ CSMTools::Operation *CSMTools::Tools::get (int type) return 0; } -const CSMTools::Operation *CSMTools::Tools::get (int type) const +const CSMDoc::Operation *CSMTools::Tools::get (int type) const { return const_cast (this)->get (type); } @@ -103,7 +103,7 @@ CSMWorld::UniversalId CSMTools::Tools::runVerifier() void CSMTools::Tools::abortOperation (int type) { - if (Operation *operation = get (type)) + if (CSMDoc::Operation *operation = get (type)) operation->abort(); } @@ -118,7 +118,7 @@ int CSMTools::Tools::getRunningOperations() const int result = 0; for (int i=0; sOperations[i]!=-1; ++i) - if (const Operation *operation = get (sOperations[i])) + if (const CSMDoc::Operation *operation = get (sOperations[i])) if (operation->isRunning()) result |= sOperations[i]; diff --git a/apps/opencs/model/tools/tools.hpp b/apps/opencs/model/tools/tools.hpp index 652345c6da..693bdaa595 100644 --- a/apps/opencs/model/tools/tools.hpp +++ b/apps/opencs/model/tools/tools.hpp @@ -11,10 +11,14 @@ namespace CSMWorld class UniversalId; } +namespace CSMDoc +{ + class Operation; +} + namespace CSMTools { class Verifier; - class Operation; class ReportModel; class Tools : public QObject @@ -33,10 +37,10 @@ namespace CSMTools Verifier *getVerifier(); - Operation *get (int type); + CSMDoc::Operation *get (int type); ///< Returns a 0-pointer, if operation hasn't been used yet. - const Operation *get (int type) const; + const CSMDoc::Operation *get (int type) const; ///< Returns a 0-pointer, if operation hasn't been used yet. public: diff --git a/apps/opencs/model/tools/verifier.hpp b/apps/opencs/model/tools/verifier.hpp index 054f87169c..59edbc41b0 100644 --- a/apps/opencs/model/tools/verifier.hpp +++ b/apps/opencs/model/tools/verifier.hpp @@ -1,11 +1,11 @@ #ifndef CSM_TOOLS_VERIFIER_H #define CSM_TOOLS_VERIFIER_H -#include "operation.hpp" +#include "../doc/operation.hpp" namespace CSMTools { - class Verifier : public Operation + class Verifier : public CSMDoc::Operation { public: From f4c03c6a299cc5938aaa4c85e074aacb7c261c2b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 14 Sep 2013 15:12:24 +0200 Subject: [PATCH 026/148] added ordered-flag to Operation (currently ignored) --- apps/opencs/model/doc/operation.cpp | 2 +- apps/opencs/model/doc/operation.hpp | 4 +++- apps/opencs/model/tools/verifier.cpp | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/doc/operation.cpp b/apps/opencs/model/doc/operation.cpp index 8f7472c5f6..83a374eb28 100644 --- a/apps/opencs/model/doc/operation.cpp +++ b/apps/opencs/model/doc/operation.cpp @@ -23,7 +23,7 @@ void CSMDoc::Operation::prepareStages() } } -CSMDoc::Operation::Operation (int type) : mType (type) {} +CSMDoc::Operation::Operation (int type, bool ordered) : mType (type), mOrdered (ordered) {} CSMDoc::Operation::~Operation() { diff --git a/apps/opencs/model/doc/operation.hpp b/apps/opencs/model/doc/operation.hpp index 703098852f..c7d9a038e7 100644 --- a/apps/opencs/model/doc/operation.hpp +++ b/apps/opencs/model/doc/operation.hpp @@ -19,12 +19,14 @@ namespace CSMDoc int mCurrentStep; int mCurrentStepTotal; int mTotalSteps; + int mOrdered; void prepareStages(); public: - Operation (int type); + Operation (int type, bool ordered); + ///< \param parallel Stages must be executed in the given order. virtual ~Operation(); diff --git a/apps/opencs/model/tools/verifier.cpp b/apps/opencs/model/tools/verifier.cpp index 9c00d4ea7e..d5f2071c4d 100644 --- a/apps/opencs/model/tools/verifier.cpp +++ b/apps/opencs/model/tools/verifier.cpp @@ -3,5 +3,5 @@ #include "../doc/state.hpp" -CSMTools::Verifier::Verifier() : Operation (CSMDoc::State_Verifying) +CSMTools::Verifier::Verifier() : Operation (CSMDoc::State_Verifying, false) {} From b7bffc8a7917d1b8fe1cf6009b4b6fc8726a8c6c Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 14 Sep 2013 15:16:31 +0200 Subject: [PATCH 027/148] removed Verifier class (using Operation class without subclassing now) --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/tools/tools.cpp | 7 +++---- apps/opencs/model/tools/tools.hpp | 5 ++--- apps/opencs/model/tools/verifier.cpp | 7 ------- apps/opencs/model/tools/verifier.hpp | 17 ----------------- 5 files changed, 6 insertions(+), 32 deletions(-) delete mode 100644 apps/opencs/model/tools/verifier.cpp delete mode 100644 apps/opencs/model/tools/verifier.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 367c43eb73..8d09eb6454 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -37,7 +37,7 @@ opencs_units (model/tools ) opencs_units_noqt (model/tools - verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck + mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck birthsigncheck spellcheck ) diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 1d49028ed3..1e8f4def5a 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -3,9 +3,8 @@ #include -#include "verifier.hpp" - #include "../doc/state.hpp" +#include "../doc/operation.hpp" #include "../world/data.hpp" #include "../world/universalid.hpp" @@ -36,11 +35,11 @@ const CSMDoc::Operation *CSMTools::Tools::get (int type) const return const_cast (this)->get (type); } -CSMTools::Verifier *CSMTools::Tools::getVerifier() +CSMDoc::Operation *CSMTools::Tools::getVerifier() { if (!mVerifier) { - mVerifier = new Verifier; + mVerifier = new CSMDoc::Operation (CSMDoc::State_Verifying, false); connect (mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int))); connect (mVerifier, SIGNAL (finished()), this, SLOT (verifierDone())); diff --git a/apps/opencs/model/tools/tools.hpp b/apps/opencs/model/tools/tools.hpp index 693bdaa595..79c9097242 100644 --- a/apps/opencs/model/tools/tools.hpp +++ b/apps/opencs/model/tools/tools.hpp @@ -18,7 +18,6 @@ namespace CSMDoc namespace CSMTools { - class Verifier; class ReportModel; class Tools : public QObject @@ -26,7 +25,7 @@ namespace CSMTools Q_OBJECT CSMWorld::Data& mData; - Verifier *mVerifier; + CSMDoc::Operation *mVerifier; std::map mReports; int mNextReportNumber; std::map mActiveReports; // type, report number @@ -35,7 +34,7 @@ namespace CSMTools Tools (const Tools&); Tools& operator= (const Tools&); - Verifier *getVerifier(); + CSMDoc::Operation *getVerifier(); CSMDoc::Operation *get (int type); ///< Returns a 0-pointer, if operation hasn't been used yet. diff --git a/apps/opencs/model/tools/verifier.cpp b/apps/opencs/model/tools/verifier.cpp deleted file mode 100644 index d5f2071c4d..0000000000 --- a/apps/opencs/model/tools/verifier.cpp +++ /dev/null @@ -1,7 +0,0 @@ - -#include "verifier.hpp" - -#include "../doc/state.hpp" - -CSMTools::Verifier::Verifier() : Operation (CSMDoc::State_Verifying, false) -{} diff --git a/apps/opencs/model/tools/verifier.hpp b/apps/opencs/model/tools/verifier.hpp deleted file mode 100644 index 59edbc41b0..0000000000 --- a/apps/opencs/model/tools/verifier.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef CSM_TOOLS_VERIFIER_H -#define CSM_TOOLS_VERIFIER_H - -#include "../doc/operation.hpp" - -namespace CSMTools -{ - class Verifier : public CSMDoc::Operation - { - public: - - Verifier(); - - }; -} - -#endif From a5aebfb76026d6e139e0ec636430031679481ea1 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 15 Sep 2013 09:32:20 +0200 Subject: [PATCH 028/148] minor cleanup --- apps/opencs/model/doc/operation.cpp | 4 ++-- apps/opencs/model/doc/operation.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/doc/operation.cpp b/apps/opencs/model/doc/operation.cpp index 83a374eb28..d5c310b8dd 100644 --- a/apps/opencs/model/doc/operation.cpp +++ b/apps/opencs/model/doc/operation.cpp @@ -37,7 +37,7 @@ void CSMDoc::Operation::run() QTimer timer; - timer.connect (&timer, SIGNAL (timeout()), this, SLOT (verify())); + timer.connect (&timer, SIGNAL (timeout()), this, SLOT (executeStage())); timer.start (0); @@ -54,7 +54,7 @@ void CSMDoc::Operation::abort() exit(); } -void CSMDoc::Operation::verify() +void CSMDoc::Operation::executeStage() { std::vector messages; diff --git a/apps/opencs/model/doc/operation.hpp b/apps/opencs/model/doc/operation.hpp index c7d9a038e7..7b8114ecc5 100644 --- a/apps/opencs/model/doc/operation.hpp +++ b/apps/opencs/model/doc/operation.hpp @@ -49,7 +49,7 @@ namespace CSMDoc private slots: - void verify(); + void executeStage(); }; } From 414e6abb9575258dc0654082aee694cff8e86f3a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 15 Sep 2013 11:35:12 +0200 Subject: [PATCH 029/148] more signal cleanup --- apps/opencs/model/doc/operation.cpp | 10 +++++++++- apps/opencs/model/doc/operation.hpp | 4 ++++ apps/opencs/model/tools/tools.cpp | 7 +------ apps/opencs/model/tools/tools.hpp | 2 -- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/apps/opencs/model/doc/operation.cpp b/apps/opencs/model/doc/operation.cpp index d5c310b8dd..7f47e8c706 100644 --- a/apps/opencs/model/doc/operation.cpp +++ b/apps/opencs/model/doc/operation.cpp @@ -23,7 +23,10 @@ void CSMDoc::Operation::prepareStages() } } -CSMDoc::Operation::Operation (int type, bool ordered) : mType (type), mOrdered (ordered) {} +CSMDoc::Operation::Operation (int type, bool ordered) : mType (type), mOrdered (ordered) +{ + connect (this, SIGNAL (finished()), this, SLOT (operationDone())); +} CSMDoc::Operation::~Operation() { @@ -80,4 +83,9 @@ void CSMDoc::Operation::executeStage() if (mCurrentStage==mStages.end()) exit(); +} + +void CSMDoc::Operation::operationDone() +{ + emit done (mType); } \ No newline at end of file diff --git a/apps/opencs/model/doc/operation.hpp b/apps/opencs/model/doc/operation.hpp index 7b8114ecc5..2fadbda556 100644 --- a/apps/opencs/model/doc/operation.hpp +++ b/apps/opencs/model/doc/operation.hpp @@ -43,6 +43,8 @@ namespace CSMDoc void reportMessage (const QString& message, int type); + void done (int type); + public slots: void abort(); @@ -50,6 +52,8 @@ namespace CSMDoc private slots: void executeStage(); + + void operationDone(); }; } diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 1e8f4def5a..cd4653280e 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -42,7 +42,7 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier() mVerifier = new CSMDoc::Operation (CSMDoc::State_Verifying, false); connect (mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int))); - connect (mVerifier, SIGNAL (finished()), this, SLOT (verifierDone())); + connect (mVerifier, SIGNAL (done (int)), this, SIGNAL (done (int))); connect (mVerifier, SIGNAL (reportMessage (const QString&, int)), this, SLOT (verifierMessage (const QString&, int))); @@ -132,11 +132,6 @@ CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& return mReports.at (id.getIndex()); } -void CSMTools::Tools::verifierDone() -{ - emit done (CSMDoc::State_Verifying); -} - void CSMTools::Tools::verifierMessage (const QString& message, int type) { std::map::iterator iter = mActiveReports.find (type); diff --git a/apps/opencs/model/tools/tools.hpp b/apps/opencs/model/tools/tools.hpp index 79c9097242..0079fab34e 100644 --- a/apps/opencs/model/tools/tools.hpp +++ b/apps/opencs/model/tools/tools.hpp @@ -61,8 +61,6 @@ namespace CSMTools private slots: - void verifierDone(); - void verifierMessage (const QString& message, int type); signals: From d71d2829527dc61ba19084b0e3c891c69ec81e86 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 15 Sep 2013 12:03:36 +0200 Subject: [PATCH 030/148] more Operation enhancements in preparation for save operation --- apps/opencs/model/doc/operation.cpp | 35 ++++++++++++++++++++++++++--- apps/opencs/model/doc/operation.hpp | 9 ++++++-- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/doc/operation.cpp b/apps/opencs/model/doc/operation.cpp index 7f47e8c706..8af5a2c0dc 100644 --- a/apps/opencs/model/doc/operation.cpp +++ b/apps/opencs/model/doc/operation.cpp @@ -15,6 +15,7 @@ void CSMDoc::Operation::prepareStages() mCurrentStep = 0; mCurrentStepTotal = 0; mTotalSteps = 0; + mError = false; for (std::vector >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter) { @@ -23,7 +24,8 @@ void CSMDoc::Operation::prepareStages() } } -CSMDoc::Operation::Operation (int type, bool ordered) : mType (type), mOrdered (ordered) +CSMDoc::Operation::Operation (int type, bool ordered, bool finalAlways) +: mType (type), mOrdered (ordered), mFinalAlways (finalAlways) { connect (this, SIGNAL (finished()), this, SLOT (operationDone())); } @@ -52,9 +54,28 @@ void CSMDoc::Operation::appendStage (Stage *stage) mStages.push_back (std::make_pair (stage, 0)); } +bool CSMDoc::Operation::hasError() const +{ + return mError; +} + void CSMDoc::Operation::abort() { - exit(); + if (!isRunning()) + return; + + mError = true; + + if (mFinalAlways) + { + if (mStages.begin()!=mStages.end() && mCurrentStage!=--mStages.end()) + { + mCurrentStep = 0; + mCurrentStage = --mStages.end(); + } + } + else + mCurrentStage = mStages.end(); } void CSMDoc::Operation::executeStage() @@ -70,7 +91,15 @@ void CSMDoc::Operation::executeStage() } else { - mCurrentStage->first->perform (mCurrentStep++, messages); + try + { + mCurrentStage->first->perform (mCurrentStep++, messages); + } + catch (const std::exception&) + { + abort(); + } + ++mCurrentStepTotal; break; } diff --git a/apps/opencs/model/doc/operation.hpp b/apps/opencs/model/doc/operation.hpp index 2fadbda556..316eda78fd 100644 --- a/apps/opencs/model/doc/operation.hpp +++ b/apps/opencs/model/doc/operation.hpp @@ -20,13 +20,16 @@ namespace CSMDoc int mCurrentStepTotal; int mTotalSteps; int mOrdered; + bool mFinalAlways; + bool mError; void prepareStages(); public: - Operation (int type, bool ordered); - ///< \param parallel Stages must be executed in the given order. + Operation (int type, bool ordered, bool finalAlways = false); + ///< \param ordered Stages must be executed in the given order. + /// \param finalAlways Execute last stage even if an error occurred during earlier stages. virtual ~Operation(); @@ -37,6 +40,8 @@ namespace CSMDoc /// /// \attention Do no call this function while this Operation is running. + bool hasError() const; + signals: void progress (int current, int max, int type); From 8326ac9b6f271d18d7f5ff0af0b49435e2bba5f4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 15 Sep 2013 12:48:57 +0200 Subject: [PATCH 031/148] replaced dummy save implementation with a threaded dummy save implementation --- apps/opencs/CMakeLists.txt | 4 +-- apps/opencs/model/doc/document.cpp | 47 +++++++------------------- apps/opencs/model/doc/document.hpp | 8 ++--- apps/opencs/model/doc/saving.cpp | 14 ++++++++ apps/opencs/model/doc/saving.hpp | 25 ++++++++++++++ apps/opencs/model/doc/savingstages.cpp | 30 ++++++++++++++++ apps/opencs/model/doc/savingstages.hpp | 28 +++++++++++++++ apps/opencs/model/doc/savingstate.cpp | 13 +++++++ apps/opencs/model/doc/savingstate.hpp | 22 ++++++++++++ 9 files changed, 149 insertions(+), 42 deletions(-) create mode 100644 apps/opencs/model/doc/saving.cpp create mode 100644 apps/opencs/model/doc/saving.hpp create mode 100644 apps/opencs/model/doc/savingstages.cpp create mode 100644 apps/opencs/model/doc/savingstages.hpp create mode 100644 apps/opencs/model/doc/savingstate.cpp create mode 100644 apps/opencs/model/doc/savingstate.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 8d09eb6454..b6de9295de 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -5,11 +5,11 @@ opencs_units (. editor) set (CMAKE_BUILD_TYPE DEBUG) opencs_units (model/doc - document operation + document operation saving ) opencs_units_noqt (model/doc - documentmanager stage + documentmanager stage savingstate savingstages ) opencs_hdrs_noqt (model/doc diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index d7138f6714..9b22175591 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2141,7 +2141,7 @@ void CSMDoc::Document::createBase() CSMDoc::Document::Document (const std::vector& files, const boost::filesystem::path& savePath, bool new_) -: mSavePath (savePath), mTools (mData) +: mSavePath (savePath), mTools (mData), mSaving (*this) { if (files.empty()) throw std::runtime_error ("Empty content file sequence"); @@ -2166,9 +2166,8 @@ CSMDoc::Document::Document (const std::vector& files, connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); connect (&mTools, SIGNAL (done (int)), this, SLOT (operationDone (int))); - // dummy implementation -> remove when proper save is implemented. - mSaveCount = 0; - connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving())); + connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); + connect (&mSaving, SIGNAL (done (int)), this, SLOT (operationDone (int))); } CSMDoc::Document::~Document() @@ -2187,7 +2186,7 @@ int CSMDoc::Document::getState() const if (!mUndoStack.isClean()) state |= State_Modified; - if (mSaveCount) + if (mSaving.isRunning()) state |= State_Locked | State_Saving | State_Operation; if (int operations = mTools.getRunningOperations()) @@ -2203,10 +2202,13 @@ const boost::filesystem::path& CSMDoc::Document::getSavePath() const void CSMDoc::Document::save() { - mSaveCount = 1; - mSaveTimer.start (500); + if (mSaving.isRunning()) + throw std::logic_error ( + "Failed to initiate save, because a save operation is already running."); + + mSaving.start(); + emit stateChanged (getState(), this); - emit progress (1, 16, State_Saving, 1, this); } CSMWorld::UniversalId CSMDoc::Document::verify() @@ -2218,17 +2220,12 @@ CSMWorld::UniversalId CSMDoc::Document::verify() void CSMDoc::Document::abortOperation (int type) { - mTools.abortOperation (type); - if (type==State_Saving) - { - mSaveCount=0; - mSaveTimer.stop(); - emit stateChanged (getState(), this); - } + mSaving.abort(); + else + mTools.abortOperation (type); } - void CSMDoc::Document::modificationStateChanged (bool clean) { emit stateChanged (getState(), this); @@ -2240,24 +2237,6 @@ void CSMDoc::Document::operationDone (int type) emit stateChanged (getState(), this); } -void CSMDoc::Document::saving() -{ - ++mSaveCount; - - emit progress (mSaveCount, 16, State_Saving, 1, this); - - if (mSaveCount>15) - { - //clear the stack before resetting the save state - //to avoid emitting incorrect states - mUndoStack.setClean(); - - mSaveCount = 0; - mSaveTimer.stop(); - emit stateChanged (getState(), this); - } -} - const CSMWorld::Data& CSMDoc::Document::getData() const { return mData; diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 3532721ead..5a03955105 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -14,6 +14,7 @@ #include "../tools/tools.hpp" #include "state.hpp" +#include "saving.hpp" class QAbstractItemModel; @@ -34,14 +35,12 @@ namespace CSMDoc boost::filesystem::path mSavePath; CSMWorld::Data mData; CSMTools::Tools mTools; + Saving mSaving; // It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is // using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late. QUndoStack mUndoStack; - int mSaveCount; ///< dummy implementation -> remove when proper save is implemented. - QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented. - // not implemented Document (const Document&); Document& operator= (const Document&); @@ -100,9 +99,6 @@ namespace CSMDoc void operationDone (int type); - void saving(); - ///< dummy implementation -> remove when proper save is implemented. - public slots: void progress (int current, int max, int type); diff --git a/apps/opencs/model/doc/saving.cpp b/apps/opencs/model/doc/saving.cpp new file mode 100644 index 0000000000..dda4ad12a8 --- /dev/null +++ b/apps/opencs/model/doc/saving.cpp @@ -0,0 +1,14 @@ + +#include "saving.hpp" + +#include "state.hpp" + +#include "savingstages.hpp" + +CSMDoc::Saving::Saving (Document& document) +: Operation (State_Saving, true, true), mDocument (document), mState (*this) +{ + + + appendStage (new FinalSavingStage (mDocument, mState)); +} \ No newline at end of file diff --git a/apps/opencs/model/doc/saving.hpp b/apps/opencs/model/doc/saving.hpp new file mode 100644 index 0000000000..b89ba5f6db --- /dev/null +++ b/apps/opencs/model/doc/saving.hpp @@ -0,0 +1,25 @@ +#ifndef CSM_DOC_SAVING_H +#define CSM_DOC_SAVING_H + +#include "operation.hpp" +#include "savingstate.hpp" + +namespace CSMDoc +{ + class Document; + + class Saving : public Operation + { + Q_OBJECT + + Document& mDocument; + SavingState mState; + + public: + + Saving (Document& document); + + }; +} + +#endif diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp new file mode 100644 index 0000000000..97facf6124 --- /dev/null +++ b/apps/opencs/model/doc/savingstages.cpp @@ -0,0 +1,30 @@ + +#include "savingstages.hpp" + +#include + +#include "document.hpp" +#include "savingstate.hpp" + +CSMDoc::FinalSavingStage::FinalSavingStage (Document& document, SavingState& state) +: mDocument (document), mState (state) +{} + +int CSMDoc::FinalSavingStage::setup() +{ + return 1; +} + +void CSMDoc::FinalSavingStage::perform (int stage, std::vector& messages) +{ + if (mState.hasError()) + { + /// \todo close stream + /// \todo delete tmp file + } + else + { + /// \todo delete file, rename tmp file + mDocument.getUndoStack().setClean(); + } +} \ No newline at end of file diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp new file mode 100644 index 0000000000..1549c9640e --- /dev/null +++ b/apps/opencs/model/doc/savingstages.hpp @@ -0,0 +1,28 @@ +#ifndef CSM_DOC_SAVINGSTAGES_H +#define CSM_DOC_SAVINGSTAGES_H + +#include "stage.hpp" + +namespace CSMDoc +{ + class Document; + class SavingState; + + class FinalSavingStage : public Stage + { + Document& mDocument; + SavingState& mState; + + public: + + FinalSavingStage (Document& document, SavingState& state); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/doc/savingstate.cpp b/apps/opencs/model/doc/savingstate.cpp new file mode 100644 index 0000000000..3798708593 --- /dev/null +++ b/apps/opencs/model/doc/savingstate.cpp @@ -0,0 +1,13 @@ + +#include "savingstate.hpp" + +#include "operation.hpp" + +CSMDoc::SavingState::SavingState (Operation& operation) +: mOperation (operation) +{} + +bool CSMDoc::SavingState::hasError() const +{ + return mOperation.hasError(); +} \ No newline at end of file diff --git a/apps/opencs/model/doc/savingstate.hpp b/apps/opencs/model/doc/savingstate.hpp new file mode 100644 index 0000000000..b8b6f38782 --- /dev/null +++ b/apps/opencs/model/doc/savingstate.hpp @@ -0,0 +1,22 @@ +#ifndef CSM_DOC_SAVINGSTATE_H +#define CSM_DOC_SAVINGSTATE_H + +namespace CSMDoc +{ + class Operation; + + class SavingState + { + Operation& mOperation; + + public: + + SavingState (Operation& operation); + + bool hasError() const; + }; + + +} + +#endif \ No newline at end of file From bcd36bd37843404257f1cdf65c66efbb248ef309 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 15 Sep 2013 14:55:40 +0200 Subject: [PATCH 032/148] various ESMWriter fixes --- components/esm/esmwriter.cpp | 335 +++++++++++++++++------------------ components/esm/esmwriter.hpp | 181 ++++++++++--------- components/esm/loadtes3.hpp | 2 +- 3 files changed, 262 insertions(+), 256 deletions(-) diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 3ea6bd350a..a6aa82665a 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -2,185 +2,182 @@ #include #include -#include - -bool count = true; +#include namespace ESM { + ESMWriter::ESMWriter() : mRecordCount (0), mCounting (false) {} -int ESMWriter::getVersion() -{ - return mHeader.mData.version; -} - -void ESMWriter::setVersion(int ver) -{ - mHeader.mData.version = ver; -} - -void ESMWriter::setAuthor(const std::string& auth) -{ - mHeader.mData.author.assign (auth); -} - -void ESMWriter::setDescription(const std::string& desc) -{ - mHeader.mData.desc.assign (desc); -} - -void ESMWriter::setRecordCount (int count) -{ - mHeader.mData.records = count; -} - -void ESMWriter::setFormat (int format) -{ - mHeader.mFormat = format; -} - -void ESMWriter::addMaster(const std::string& name, uint64_t size) -{ - Header::MasterData d; - d.name = name; - d.size = size; - mHeader.mMaster.push_back(d); -} - -void ESMWriter::save(const std::string& file) -{ - std::ofstream fs(file.c_str(), std::ios_base::out | std::ios_base::trunc); - save(fs); -} - -void ESMWriter::save(std::ostream& file) -{ - m_recordCount = 0; - m_stream = &file; - - startRecord("TES3", 0); - - mHeader.save (*this); - - endRecord("TES3"); -} - -void ESMWriter::close() -{ - m_stream->flush(); - - if (!m_records.empty()) - throw "Unclosed record remaining"; -} - -void ESMWriter::startRecord(const std::string& name, uint32_t flags) -{ - m_recordCount++; - - writeName(name); - RecordData rec; - rec.name = name; - rec.position = m_stream->tellp(); - rec.size = 0; - writeT(0); // Size goes here - writeT(0); // Unused header? - writeT(flags); - m_records.push_back(rec); - - assert(m_records.back().size == 0); -} - -void ESMWriter::startSubRecord(const std::string& name) -{ - writeName(name); - RecordData rec; - rec.name = name; - rec.position = m_stream->tellp(); - rec.size = 0; - writeT(0); // Size goes here - m_records.push_back(rec); - - assert(m_records.back().size == 0); -} - -void ESMWriter::endRecord(const std::string& name) -{ - RecordData rec = m_records.back(); - assert(rec.name == name); - m_records.pop_back(); - - m_stream->seekp(rec.position); - - count = false; - write((char*)&rec.size, sizeof(int)); - count = true; - - m_stream->seekp(0, std::ios::end); - -} - -void ESMWriter::writeHNString(const std::string& name, const std::string& data) -{ - startSubRecord(name); - writeHString(data); - endRecord(name); -} - -void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) -{ - assert(data.size() <= size); - startSubRecord(name); - writeHString(data); - - if (data.size() < size) + unsigned int ESMWriter::getVersion() const { - for (size_t i = data.size(); i < size; ++i) - write("\0",1); + return mHeader.mData.version; } - endRecord(name); -} - -void ESMWriter::writeHString(const std::string& data) -{ - if (data.size() == 0) - write("\0", 1); - else + void ESMWriter::setVersion(unsigned int ver) { - // Convert to UTF8 and return - std::string ascii = m_encoder->getLegacyEnc(data); - - write(ascii.c_str(), ascii.size()); - } -} - -void ESMWriter::writeHCString(const std::string& data) -{ - writeHString(data); - if (data.size() > 0 && data[data.size()-1] != '\0') - write("\0", 1); -} - -void ESMWriter::writeName(const std::string& name) -{ - assert((name.size() == 4 && name[3] != '\0')); - write(name.c_str(), name.size()); -} - -void ESMWriter::write(const char* data, size_t size) -{ - if (count && !m_records.empty()) - { - for (std::list::iterator it = m_records.begin(); it != m_records.end(); ++it) - it->size += size; + mHeader.mData.version = ver; } - m_stream->write(data, size); -} + void ESMWriter::setAuthor(const std::string& auth) + { + mHeader.mData.author.assign (auth); + } -void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) -{ - m_encoder = encoder; -} + void ESMWriter::setDescription(const std::string& desc) + { + mHeader.mData.desc.assign (desc); + } + void ESMWriter::setRecordCount (int count) + { + mHeader.mData.records = count; + } + + void ESMWriter::setFormat (int format) + { + mHeader.mFormat = format; + } + + void ESMWriter::addMaster(const std::string& name, uint64_t size) + { + Header::MasterData d; + d.name = name; + d.size = size; + mHeader.mMaster.push_back(d); + } + + void ESMWriter::save(const std::string& file) + { + std::ofstream fs(file.c_str(), std::ios_base::out | std::ios_base::trunc); + save(fs); + } + + void ESMWriter::save(std::ostream& file) + { + mRecordCount = 0; + mRecords.clear(); + mStream = &file; + + startRecord("TES3", 0); + + mHeader.save (*this); + + endRecord("TES3"); + } + + void ESMWriter::close() + { + if (!mRecords.empty()) + throw std::runtime_error ("Unclosed record remaining"); + } + + void ESMWriter::startRecord(const std::string& name, uint32_t flags) + { + mRecordCount++; + + writeName(name); + RecordData rec; + rec.name = name; + rec.position = mStream->tellp(); + rec.size = 0; + writeT(0); // Size goes here + writeT(0); // Unused header? + writeT(flags); + mRecords.push_back(rec); + + assert(mRecords.back().size == 0); + } + + void ESMWriter::startSubRecord(const std::string& name) + { + writeName(name); + RecordData rec; + rec.name = name; + rec.position = mStream->tellp(); + rec.size = 0; + writeT(0); // Size goes here + mRecords.push_back(rec); + + assert(mRecords.back().size == 0); + } + + void ESMWriter::endRecord(const std::string& name) + { + RecordData rec = mRecords.back(); + assert(rec.name == name); + mRecords.pop_back(); + + mStream->seekp(rec.position); + + mCounting = false; + write (reinterpret_cast (&rec.size), sizeof(int)); + mCounting = true; + + mStream->seekp(0, std::ios::end); + + } + + void ESMWriter::writeHNString(const std::string& name, const std::string& data) + { + startSubRecord(name); + writeHString(data); + endRecord(name); + } + + void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) + { + assert(data.size() <= size); + startSubRecord(name); + writeHString(data); + + if (data.size() < size) + { + for (size_t i = data.size(); i < size; ++i) + write("\0",1); + } + + endRecord(name); + } + + void ESMWriter::writeHString(const std::string& data) + { + if (data.size() == 0) + write("\0", 1); + else + { + // Convert to UTF8 and return + std::string ascii = mEncoder->getLegacyEnc(data); + + write(ascii.c_str(), ascii.size()); + } + } + + void ESMWriter::writeHCString(const std::string& data) + { + writeHString(data); + if (data.size() > 0 && data[data.size()-1] != '\0') + write("\0", 1); + } + + void ESMWriter::writeName(const std::string& name) + { + assert((name.size() == 4 && name[3] != '\0')); + write(name.c_str(), name.size()); + } + + void ESMWriter::write(const char* data, size_t size) + { + if (mCounting && !mRecords.empty()) + { + for (std::list::iterator it = mRecords.begin(); it != mRecords.end(); ++it) + it->size += size; + } + + mStream->write(data, size); + } + + void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) + { + mEncoder = encoder; + } } diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index be3ae33abe..b0925463a0 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -13,92 +13,101 @@ namespace ESM { class ESMWriter { - struct RecordData - { - std::string name; - std::streampos position; - size_t size; + struct RecordData + { + std::string name; + std::streampos position; + size_t size; + }; + + public: + + ESMWriter(); + + unsigned int getVersion() const; + void setVersion(unsigned int ver = 0x3fa66666); + void setEncoder(ToUTF8::Utf8Encoder *encoding); + void setAuthor(const std::string& author); + void setDescription(const std::string& desc); + void setRecordCount (int count); + void setFormat (int format); + + void addMaster(const std::string& name, uint64_t size); + + void save(const std::string& file); + ///< Start saving a file by writing the TES3 header. + + void save(std::ostream& file); + ///< Start saving a file by writing the TES3 header. + + void close(); + ///< \note Does not close the stream. + + void writeHNString(const std::string& name, const std::string& data); + void writeHNString(const std::string& name, const std::string& data, size_t size); + void writeHNCString(const std::string& name, const std::string& data) + { + startSubRecord(name); + writeHCString(data); + endRecord(name); + } + void writeHNOString(const std::string& name, const std::string& data) + { + if (!data.empty()) + writeHNString(name, data); + } + void writeHNOCString(const std::string& name, const std::string& data) + { + if (!data.empty()) + writeHNCString(name, data); + } + + template + void writeHNT(const std::string& name, const T& data) + { + startSubRecord(name); + writeT(data); + endRecord(name); + } + + template + void writeHNT(const std::string& name, const T& data, int size) + { + startSubRecord(name); + writeT(data, size); + endRecord(name); + } + + template + void writeT(const T& data) + { + write((char*)&data, sizeof(T)); + } + + template + void writeT(const T& data, size_t size) + { + write((char*)&data, size); + } + + void startRecord(const std::string& name, uint32_t flags); + void startSubRecord(const std::string& name); + void endRecord(const std::string& name); + void writeHString(const std::string& data); + void writeHCString(const std::string& data); + void writeName(const std::string& data); + void write(const char* data, size_t size); + + private: + std::list mRecords; + std::ostream* mStream; + std::streampos mHeaderPos; + ToUTF8::Utf8Encoder* mEncoder; + int mRecordCount; + bool mCounting; + + Header mHeader; }; - -public: - int getVersion(); - void setVersion(int ver); - void setEncoder(ToUTF8::Utf8Encoder *encoding); // Write strings as UTF-8? - void setAuthor(const std::string& author); - void setDescription(const std::string& desc); - void setRecordCount (int count); - void setFormat (int format); - - void addMaster(const std::string& name, uint64_t size); - - void save(const std::string& file); - void save(std::ostream& file); - void close(); - - void writeHNString(const std::string& name, const std::string& data); - void writeHNString(const std::string& name, const std::string& data, size_t size); - void writeHNCString(const std::string& name, const std::string& data) - { - startSubRecord(name); - writeHCString(data); - endRecord(name); - } - void writeHNOString(const std::string& name, const std::string& data) - { - if (!data.empty()) - writeHNString(name, data); - } - void writeHNOCString(const std::string& name, const std::string& data) - { - if (!data.empty()) - writeHNCString(name, data); - } - - template - void writeHNT(const std::string& name, const T& data) - { - startSubRecord(name); - writeT(data); - endRecord(name); - } - - template - void writeHNT(const std::string& name, const T& data, int size) - { - startSubRecord(name); - writeT(data, size); - endRecord(name); - } - - template - void writeT(const T& data) - { - write((char*)&data, sizeof(T)); - } - - template - void writeT(const T& data, size_t size) - { - write((char*)&data, size); - } - - void startRecord(const std::string& name, uint32_t flags); - void startSubRecord(const std::string& name); - void endRecord(const std::string& name); - void writeHString(const std::string& data); - void writeHCString(const std::string& data); - void writeName(const std::string& data); - void write(const char* data, size_t size); - -private: - std::list m_records; - std::ostream* m_stream; - std::streampos m_headerPos; - ToUTF8::Utf8Encoder* m_encoder; - int m_recordCount; - - Header mHeader; -}; - } + #endif diff --git a/components/esm/loadtes3.hpp b/components/esm/loadtes3.hpp index b73a4c31e4..5614d295f6 100644 --- a/components/esm/loadtes3.hpp +++ b/components/esm/loadtes3.hpp @@ -24,7 +24,7 @@ namespace ESM versions are 1.2 and 1.3. These correspond to: 1.2 = 0x3f99999a and 1.3 = 0x3fa66666 */ - int version; + unsigned int version; int type; // 0=esp, 1=esm, 32=ess (unused) NAME32 author; // Author's name NAME256 desc; // File description From fa25a068a8681202a8327cde663273d9910fd838 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 15 Sep 2013 15:00:41 +0200 Subject: [PATCH 033/148] basic saving (no data is written to file yet) --- apps/opencs/model/doc/saving.cpp | 3 ++ apps/opencs/model/doc/savingstages.cpp | 53 ++++++++++++++++++++++++-- apps/opencs/model/doc/savingstages.hpp | 31 +++++++++++++++ apps/opencs/model/doc/savingstate.cpp | 45 +++++++++++++++++++++- apps/opencs/model/doc/savingstate.hpp | 22 +++++++++++ 5 files changed, 149 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/doc/saving.cpp b/apps/opencs/model/doc/saving.cpp index dda4ad12a8..5607883adf 100644 --- a/apps/opencs/model/doc/saving.cpp +++ b/apps/opencs/model/doc/saving.cpp @@ -8,7 +8,10 @@ CSMDoc::Saving::Saving (Document& document) : Operation (State_Saving, true, true), mDocument (document), mState (*this) { + appendStage (new OpenSaveStage (mDocument, mState)); + appendStage (new CloseSaveStage (mState)); + appendStage (new FinalSavingStage (mDocument, mState)); } \ No newline at end of file diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index 97facf6124..078762992a 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -1,11 +1,53 @@ #include "savingstages.hpp" +#include + +#include + #include #include "document.hpp" #include "savingstate.hpp" +CSMDoc::OpenSaveStage::OpenSaveStage (Document& document, SavingState& state) +: mDocument (document), mState (state) +{} + +int CSMDoc::OpenSaveStage::setup() +{ + return 1; +} + +void CSMDoc::OpenSaveStage::perform (int stage, std::vector& messages) +{ + mState.start (mDocument); + + mState.getStream().open (mState.getTmpPath().string().c_str()); + + if (!mState.getStream().is_open()) + throw std::runtime_error ("failed to open stream for saving"); +} + + +CSMDoc::CloseSaveStage::CloseSaveStage (SavingState& state) +: mState (state) +{} + +int CSMDoc::CloseSaveStage::setup() +{ + return 1; +} + +void CSMDoc::CloseSaveStage::perform (int stage, std::vector& messages) +{ + mState.getStream().close(); + + if (!mState.getStream()) + throw std::runtime_error ("saving failed"); +} + + CSMDoc::FinalSavingStage::FinalSavingStage (Document& document, SavingState& state) : mDocument (document), mState (state) {} @@ -19,12 +61,17 @@ void CSMDoc::FinalSavingStage::perform (int stage, std::vector& mes { if (mState.hasError()) { - /// \todo close stream - /// \todo delete tmp file + mState.getWriter().close(); + mState.getStream().close(); + + if (boost::filesystem::exists (mState.getTmpPath())) + boost::filesystem::remove (mState.getTmpPath()); } else { - /// \todo delete file, rename tmp file + boost::filesystem::remove (mState.getPath()); + boost::filesystem::rename (mState.getTmpPath(), mState.getPath()); + mDocument.getUndoStack().setClean(); } } \ No newline at end of file diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp index 1549c9640e..0b64896f76 100644 --- a/apps/opencs/model/doc/savingstages.hpp +++ b/apps/opencs/model/doc/savingstages.hpp @@ -8,6 +8,37 @@ namespace CSMDoc class Document; class SavingState; + class OpenSaveStage : public Stage + { + Document& mDocument; + SavingState& mState; + + public: + + OpenSaveStage (Document& document, SavingState& state); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + + class CloseSaveStage : public Stage + { + SavingState& mState; + + public: + + CloseSaveStage (SavingState& state); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + class FinalSavingStage : public Stage { Document& mDocument; diff --git a/apps/opencs/model/doc/savingstate.cpp b/apps/opencs/model/doc/savingstate.cpp index 3798708593..a49a0699b2 100644 --- a/apps/opencs/model/doc/savingstate.cpp +++ b/apps/opencs/model/doc/savingstate.cpp @@ -2,12 +2,53 @@ #include "savingstate.hpp" #include "operation.hpp" +#include "document.hpp" CSMDoc::SavingState::SavingState (Operation& operation) -: mOperation (operation) -{} +: mOperation (operation), + /// \todo set encoding properly, once config implementation has been fixed. + mEncoder (ToUTF8::calculateEncoding ("win1252")) +{ + mWriter.setEncoder (&mEncoder); +} bool CSMDoc::SavingState::hasError() const { return mOperation.hasError(); +} + +void CSMDoc::SavingState::start (Document& document) +{ + if (mStream.is_open()) + mStream.close(); + + mStream.clear(); + + mPath = document.getSavePath(); + + boost::filesystem::path file (mPath.filename().string() + ".tmp"); + + mTmpPath = mPath.parent_path(); + + mTmpPath /= file; +} + +const boost::filesystem::path& CSMDoc::SavingState::getPath() const +{ + return mPath; +} + +const boost::filesystem::path& CSMDoc::SavingState::getTmpPath() const +{ + return mTmpPath; +} + +std::ofstream& CSMDoc::SavingState::getStream() +{ + return mStream; +} + +ESM::ESMWriter& CSMDoc::SavingState::getWriter() +{ + return mWriter; } \ No newline at end of file diff --git a/apps/opencs/model/doc/savingstate.hpp b/apps/opencs/model/doc/savingstate.hpp index b8b6f38782..3f42b4653d 100644 --- a/apps/opencs/model/doc/savingstate.hpp +++ b/apps/opencs/model/doc/savingstate.hpp @@ -1,19 +1,41 @@ #ifndef CSM_DOC_SAVINGSTATE_H #define CSM_DOC_SAVINGSTATE_H +#include + +#include + +#include + namespace CSMDoc { class Operation; + class Document; class SavingState { Operation& mOperation; + boost::filesystem::path mPath; + boost::filesystem::path mTmpPath; + ToUTF8::Utf8Encoder mEncoder; + std::ofstream mStream; + ESM::ESMWriter mWriter; public: SavingState (Operation& operation); bool hasError() const; + + void start (Document& document); + + const boost::filesystem::path& getPath() const; + + const boost::filesystem::path& getTmpPath() const; + + std::ofstream& getStream(); + + ESM::ESMWriter& getWriter(); }; From 231419028d7bdd83bc61e17fbed2ef25500bf0bd Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 15 Sep 2013 15:03:17 +0200 Subject: [PATCH 034/148] minor fix --- apps/opencs/model/doc/savingstages.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index 078762992a..e7c9799ecd 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -69,7 +69,9 @@ void CSMDoc::FinalSavingStage::perform (int stage, std::vector& mes } else { - boost::filesystem::remove (mState.getPath()); + if (boost::filesystem::exists (mState.getPath())) + boost::filesystem::remove (mState.getPath()); + boost::filesystem::rename (mState.getTmpPath(), mState.getPath()); mDocument.getUndoStack().setClean(); From 1ee228a56614c0a512c2abf4fbb82ac235a1a2c5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 15 Sep 2013 15:30:17 +0200 Subject: [PATCH 035/148] fix for the ESMWriter fix --- components/esm/esmwriter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index a6aa82665a..95ad44811d 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -6,7 +6,7 @@ namespace ESM { - ESMWriter::ESMWriter() : mRecordCount (0), mCounting (false) {} + ESMWriter::ESMWriter() : mRecordCount (0), mCounting (true) {} unsigned int ESMWriter::getVersion() const { @@ -56,6 +56,7 @@ namespace ESM { mRecordCount = 0; mRecords.clear(); + mCounting = true; mStream = &file; startRecord("TES3", 0); From db70095148a7df4c2c171fe58d51c907cc2c1492 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 15 Sep 2013 15:31:44 +0200 Subject: [PATCH 036/148] write TES3 header --- apps/opencs/model/doc/saving.cpp | 3 +++ apps/opencs/model/doc/savingstages.cpp | 26 ++++++++++++++++++++++++++ apps/opencs/model/doc/savingstages.hpp | 16 ++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/apps/opencs/model/doc/saving.cpp b/apps/opencs/model/doc/saving.cpp index 5607883adf..67073ca434 100644 --- a/apps/opencs/model/doc/saving.cpp +++ b/apps/opencs/model/doc/saving.cpp @@ -10,6 +10,9 @@ CSMDoc::Saving::Saving (Document& document) { appendStage (new OpenSaveStage (mDocument, mState)); + appendStage (new WriteHeaderStage (mDocument, mState)); + + appendStage (new CloseSaveStage (mState)); diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index e7c9799ecd..797b32eae8 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -30,6 +30,32 @@ void CSMDoc::OpenSaveStage::perform (int stage, std::vector& messag } +CSMDoc::WriteHeaderStage::WriteHeaderStage (Document& document, SavingState& state) +: mDocument (document), mState (state) +{} + +int CSMDoc::WriteHeaderStage::setup() +{ + return 1; +} + +void CSMDoc::WriteHeaderStage::perform (int stage, std::vector& messages) +{ + mState.getWriter().setVersion(); + + mState.getWriter().setFormat (0); + + /// \todo fill in missing header information + mState.getWriter().setAuthor (""); + mState.getWriter().setDescription (""); + mState.getWriter().setRecordCount (0); + + /// \todo fill in dependency list + + mState.getWriter().save (mState.getStream()); +} + + CSMDoc::CloseSaveStage::CloseSaveStage (SavingState& state) : mState (state) {} diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp index 0b64896f76..914a2d585a 100644 --- a/apps/opencs/model/doc/savingstages.hpp +++ b/apps/opencs/model/doc/savingstages.hpp @@ -24,6 +24,22 @@ namespace CSMDoc ///< Messages resulting from this stage will be appended to \a messages. }; + class WriteHeaderStage : public Stage + { + Document& mDocument; + SavingState& mState; + + public: + + WriteHeaderStage (Document& document, SavingState& state); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + class CloseSaveStage : public Stage { SavingState& mState; From 874ce26bef34ed6b14498b80aa1ec37a5120c377 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 16 Sep 2013 12:32:35 +0200 Subject: [PATCH 037/148] added missing consts to record save functions --- components/esm/aipackage.cpp | 4 ++-- components/esm/aipackage.hpp | 2 +- components/esm/cellref.cpp | 2 +- components/esm/cellref.hpp | 2 +- components/esm/effectlist.cpp | 4 ++-- components/esm/effectlist.hpp | 4 ++-- components/esm/filter.cpp | 2 +- components/esm/filter.hpp | 2 +- components/esm/loadacti.cpp | 2 +- components/esm/loadacti.hpp | 2 +- components/esm/loadalch.cpp | 2 +- components/esm/loadalch.hpp | 2 +- components/esm/loadappa.cpp | 2 +- components/esm/loadappa.hpp | 2 +- components/esm/loadarmo.cpp | 6 +++--- components/esm/loadarmo.hpp | 4 ++-- components/esm/loadbody.cpp | 2 +- components/esm/loadbody.hpp | 2 +- components/esm/loadbook.cpp | 2 +- components/esm/loadbook.hpp | 2 +- components/esm/loadbsgn.cpp | 2 +- components/esm/loadbsgn.hpp | 2 +- components/esm/loadcell.cpp | 2 +- components/esm/loadcell.hpp | 2 +- components/esm/loadclas.cpp | 2 +- components/esm/loadclas.hpp | 2 +- components/esm/loadclot.cpp | 2 +- components/esm/loadclot.hpp | 2 +- components/esm/loadcont.cpp | 6 +++--- components/esm/loadcont.hpp | 4 ++-- components/esm/loadcrea.cpp | 2 +- components/esm/loadcrea.hpp | 2 +- components/esm/loadcrec.hpp | 4 ++-- components/esm/loaddial.cpp | 2 +- components/esm/loaddial.hpp | 2 +- components/esm/loaddoor.cpp | 2 +- components/esm/loaddoor.hpp | 2 +- components/esm/loadench.cpp | 2 +- components/esm/loadench.hpp | 2 +- components/esm/loadfact.cpp | 4 ++-- components/esm/loadfact.hpp | 2 +- components/esm/loadglob.cpp | 2 +- components/esm/loadglob.hpp | 2 +- components/esm/loadgmst.cpp | 2 +- components/esm/loadgmst.hpp | 4 ++-- components/esm/loadinfo.cpp | 4 ++-- components/esm/loadinfo.hpp | 2 +- components/esm/loadingr.cpp | 2 +- components/esm/loadingr.hpp | 2 +- components/esm/loadland.cpp | 18 +++--------------- components/esm/loadland.hpp | 2 +- components/esm/loadlevlist.cpp | 4 ++-- components/esm/loadlevlist.hpp | 2 +- components/esm/loadligh.cpp | 2 +- components/esm/loadligh.hpp | 2 +- components/esm/loadlock.cpp | 2 +- components/esm/loadlock.hpp | 2 +- components/esm/loadltex.cpp | 2 +- components/esm/loadltex.hpp | 2 +- components/esm/loadmgef.cpp | 10 +++------- components/esm/loadmgef.hpp | 2 +- components/esm/loadmisc.cpp | 2 +- components/esm/loadmisc.hpp | 2 +- components/esm/loadnpc.cpp | 4 ++-- components/esm/loadnpc.hpp | 2 +- components/esm/loadnpcc.hpp | 2 +- components/esm/loadpgrd.cpp | 10 +++++----- components/esm/loadpgrd.hpp | 2 +- components/esm/loadprob.cpp | 2 +- components/esm/loadprob.hpp | 2 +- components/esm/loadrace.cpp | 2 +- components/esm/loadrace.hpp | 2 +- components/esm/loadregn.cpp | 4 ++-- components/esm/loadregn.hpp | 2 +- components/esm/loadrepa.cpp | 2 +- components/esm/loadrepa.hpp | 2 +- components/esm/loadscpt.cpp | 6 +++--- components/esm/loadscpt.hpp | 2 +- components/esm/loadskil.cpp | 2 +- components/esm/loadskil.hpp | 2 +- components/esm/loadsndg.cpp | 2 +- components/esm/loadsndg.hpp | 2 +- components/esm/loadsoun.cpp | 2 +- components/esm/loadsoun.hpp | 2 +- components/esm/loadspel.cpp | 2 +- components/esm/loadspel.hpp | 2 +- components/esm/loadsscr.cpp | 2 +- components/esm/loadsscr.hpp | 2 +- components/esm/loadstat.cpp | 2 +- components/esm/loadstat.hpp | 2 +- components/esm/loadweap.cpp | 2 +- components/esm/loadweap.hpp | 2 +- components/esm/spelllist.cpp | 4 ++-- components/esm/spelllist.hpp | 2 +- 94 files changed, 121 insertions(+), 137 deletions(-) diff --git a/components/esm/aipackage.cpp b/components/esm/aipackage.cpp index 1440dbd138..cf4951de7e 100644 --- a/components/esm/aipackage.cpp +++ b/components/esm/aipackage.cpp @@ -44,9 +44,9 @@ namespace ESM } } - void AIPackageList::save(ESMWriter &esm) + void AIPackageList::save(ESMWriter &esm) const { - typedef std::vector::iterator PackageIter; + typedef std::vector::const_iterator PackageIter; for (PackageIter it = mList.begin(); it != mList.end(); ++it) { switch (it->mType) { case AI_Wander: diff --git a/components/esm/aipackage.hpp b/components/esm/aipackage.hpp index 38499b2dd8..b06cb529a7 100644 --- a/components/esm/aipackage.hpp +++ b/components/esm/aipackage.hpp @@ -93,7 +93,7 @@ namespace ESM /// it needs to use retSubName() if needed. But, hey, there /// is only one field left (XSCL) and only two records uses AI void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 95cf24d331..e91059b26f 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -3,7 +3,7 @@ #include "esmwriter.hpp" -void ESM::CellRef::save(ESMWriter &esm) +void ESM::CellRef::save(ESMWriter &esm) const { esm.writeHNT("FRMR", mRefnum); esm.writeHNCString("NAME", mRefID); diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 31889914ce..47cb0b99ed 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -83,7 +83,7 @@ namespace ESM // Position and rotation of this object within the cell Position mPos; - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); }; diff --git a/components/esm/effectlist.cpp b/components/esm/effectlist.cpp index 88f87d6e29..bc126846b1 100644 --- a/components/esm/effectlist.cpp +++ b/components/esm/effectlist.cpp @@ -14,9 +14,9 @@ void EffectList::load(ESMReader &esm) } } -void EffectList::save(ESMWriter &esm) +void EffectList::save(ESMWriter &esm) const { - for (std::vector::iterator it = mList.begin(); it != mList.end(); ++it) { + for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNT("ENAM", *it, 24); } } diff --git a/components/esm/effectlist.hpp b/components/esm/effectlist.hpp index 9f5b87aeda..04adcc5cd8 100644 --- a/components/esm/effectlist.hpp +++ b/components/esm/effectlist.hpp @@ -35,9 +35,9 @@ namespace ESM std::vector mList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; - + } #endif diff --git a/components/esm/filter.cpp b/components/esm/filter.cpp index 7d4851a5f6..96cc19d43d 100644 --- a/components/esm/filter.cpp +++ b/components/esm/filter.cpp @@ -10,7 +10,7 @@ void ESM::Filter::load (ESMReader& esm) mDescription = esm.getHNString ("DESC"); } -void ESM::Filter::save (ESMWriter& esm) +void ESM::Filter::save (ESMWriter& esm) const { esm.writeHNCString ("FILT", mFilter); esm.writeHNCString ("DESC", mDescription); diff --git a/components/esm/filter.hpp b/components/esm/filter.hpp index 0fd564361e..a44d1b1980 100644 --- a/components/esm/filter.hpp +++ b/components/esm/filter.hpp @@ -17,7 +17,7 @@ namespace ESM std::string mFilter; void load (ESMReader& esm); - void save (ESMWriter& esm); + void save (ESMWriter& esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadacti.cpp b/components/esm/loadacti.cpp index fd022af7e6..dcae845d0a 100644 --- a/components/esm/loadacti.cpp +++ b/components/esm/loadacti.cpp @@ -11,7 +11,7 @@ void Activator::load(ESMReader &esm) mName = esm.getHNString("FNAM"); mScript = esm.getHNOString("SCRI"); } -void Activator::save(ESMWriter &esm) +void Activator::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadacti.hpp b/components/esm/loadacti.hpp index a62990590d..6b072ee11e 100644 --- a/components/esm/loadacti.hpp +++ b/components/esm/loadacti.hpp @@ -14,7 +14,7 @@ struct Activator std::string mId, mName, mScript, mModel; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadalch.cpp b/components/esm/loadalch.cpp index dbb69c066f..187069c2ef 100644 --- a/components/esm/loadalch.cpp +++ b/components/esm/loadalch.cpp @@ -14,7 +14,7 @@ void Potion::load(ESMReader &esm) esm.getHNT(mData, "ALDT", 12); mEffects.load(esm); } -void Potion::save(ESMWriter &esm) +void Potion::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("TEXT", mIcon); diff --git a/components/esm/loadalch.hpp b/components/esm/loadalch.hpp index 3ede853424..8f0435292e 100644 --- a/components/esm/loadalch.hpp +++ b/components/esm/loadalch.hpp @@ -29,7 +29,7 @@ struct Potion EffectList mEffects; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadappa.cpp b/components/esm/loadappa.cpp index 4b8d2b763c..01233a0558 100644 --- a/components/esm/loadappa.cpp +++ b/components/esm/loadappa.cpp @@ -28,7 +28,7 @@ void Apparatus::load(ESMReader &esm) } } -void Apparatus::save(ESMWriter &esm) +void Apparatus::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadappa.hpp b/components/esm/loadappa.hpp index ed9d335be6..d47643c6ce 100644 --- a/components/esm/loadappa.hpp +++ b/components/esm/loadappa.hpp @@ -35,7 +35,7 @@ struct Apparatus std::string mId, mModel, mIcon, mScript, mName; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadarmo.cpp b/components/esm/loadarmo.cpp index e64c8705d7..4dbdf1314d 100644 --- a/components/esm/loadarmo.cpp +++ b/components/esm/loadarmo.cpp @@ -18,9 +18,9 @@ void PartReferenceList::load(ESMReader &esm) } } -void PartReferenceList::save(ESMWriter &esm) +void PartReferenceList::save(ESMWriter &esm) const { - for (std::vector::iterator it = mParts.begin(); it != mParts.end(); ++it) + for (std::vector::const_iterator it = mParts.begin(); it != mParts.end(); ++it) { esm.writeHNT("INDX", it->mPart); esm.writeHNOString("BNAM", it->mMale); @@ -39,7 +39,7 @@ void Armor::load(ESMReader &esm) mEnchant = esm.getHNOString("ENAM"); } -void Armor::save(ESMWriter &esm) +void Armor::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadarmo.hpp b/components/esm/loadarmo.hpp index eaef42be83..5a38605e3d 100644 --- a/components/esm/loadarmo.hpp +++ b/components/esm/loadarmo.hpp @@ -56,7 +56,7 @@ struct PartReferenceList std::vector mParts; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; struct Armor @@ -89,7 +89,7 @@ struct Armor std::string mId, mName, mModel, mIcon, mScript, mEnchant; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadbody.cpp b/components/esm/loadbody.cpp index e95a8a8603..a5d986f65d 100644 --- a/components/esm/loadbody.cpp +++ b/components/esm/loadbody.cpp @@ -12,7 +12,7 @@ void BodyPart::load(ESMReader &esm) mRace = esm.getHNString("FNAM"); esm.getHNT(mData, "BYDT", 4); } -void BodyPart::save(ESMWriter &esm) +void BodyPart::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mRace); diff --git a/components/esm/loadbody.hpp b/components/esm/loadbody.hpp index 3ad9b1b958..a8fd36aef4 100644 --- a/components/esm/loadbody.hpp +++ b/components/esm/loadbody.hpp @@ -57,7 +57,7 @@ struct BodyPart std::string mId, mModel, mRace; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } #endif diff --git a/components/esm/loadbook.cpp b/components/esm/loadbook.cpp index 3a70ac7869..d9db118899 100644 --- a/components/esm/loadbook.cpp +++ b/components/esm/loadbook.cpp @@ -16,7 +16,7 @@ void Book::load(ESMReader &esm) mText = esm.getHNOString("TEXT"); mEnchant = esm.getHNOString("ENAM"); } -void Book::save(ESMWriter &esm) +void Book::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loadbook.hpp b/components/esm/loadbook.hpp index 68042e246e..688e9dd75e 100644 --- a/components/esm/loadbook.hpp +++ b/components/esm/loadbook.hpp @@ -25,7 +25,7 @@ struct Book std::string mId; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadbsgn.cpp b/components/esm/loadbsgn.cpp index cb500f6748..9d19f02c7e 100644 --- a/components/esm/loadbsgn.cpp +++ b/components/esm/loadbsgn.cpp @@ -15,7 +15,7 @@ void BirthSign::load(ESMReader &esm) mPowers.load(esm); } -void BirthSign::save(ESMWriter &esm) +void BirthSign::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mName); esm.writeHNOCString("TNAM", mTexture); diff --git a/components/esm/loadbsgn.hpp b/components/esm/loadbsgn.hpp index 434ddf68ea..1ecb5e418e 100644 --- a/components/esm/loadbsgn.hpp +++ b/components/esm/loadbsgn.hpp @@ -19,7 +19,7 @@ struct BirthSign SpellList mPowers; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index d8d0c12912..57d3278d8d 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -89,7 +89,7 @@ void Cell::postLoad(ESMReader &esm) esm.skipRecord(); } -void Cell::save(ESMWriter &esm) +void Cell::save(ESMWriter &esm) const { esm.writeHNT("DATA", mData, 12); if (mData.mFlags & Interior) diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 51288b2919..c417fceab8 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -102,7 +102,7 @@ struct Cell // This method is left in for compatibility with esmtool. Parsing moved references currently requires // passing ESMStore, bit it does not know about this parameter, so we do it this way. void load(ESMReader &esm, bool saveContext = true); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; bool isExterior() const { diff --git a/components/esm/loadclas.cpp b/components/esm/loadclas.cpp index bdc4614625..ef07430c7a 100644 --- a/components/esm/loadclas.cpp +++ b/components/esm/loadclas.cpp @@ -47,7 +47,7 @@ void Class::load(ESMReader &esm) mDescription = esm.getHNOString("DESC"); } -void Class::save(ESMWriter &esm) +void Class::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mName); esm.writeHNT("CLDT", mData, 60); diff --git a/components/esm/loadclas.hpp b/components/esm/loadclas.hpp index 4f85e6ee8b..f241dca8da 100644 --- a/components/esm/loadclas.hpp +++ b/components/esm/loadclas.hpp @@ -70,7 +70,7 @@ struct Class CLDTstruct mData; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadclot.cpp b/components/esm/loadclot.cpp index 10b00970fb..c623155dfa 100644 --- a/components/esm/loadclot.cpp +++ b/components/esm/loadclot.cpp @@ -20,7 +20,7 @@ void Clothing::load(ESMReader &esm) mEnchant = esm.getHNOString("ENAM"); } -void Clothing::save(ESMWriter &esm) +void Clothing::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loadclot.hpp b/components/esm/loadclot.hpp index 816d03cb23..13fae865b4 100644 --- a/components/esm/loadclot.hpp +++ b/components/esm/loadclot.hpp @@ -45,7 +45,7 @@ struct Clothing std::string mId, mName, mModel, mIcon, mEnchant, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadcont.cpp b/components/esm/loadcont.cpp index 853c8bd500..0cbb4acd1e 100644 --- a/components/esm/loadcont.cpp +++ b/components/esm/loadcont.cpp @@ -16,9 +16,9 @@ void InventoryList::load(ESMReader &esm) } } -void InventoryList::save(ESMWriter &esm) +void InventoryList::save(ESMWriter &esm) const { - for (std::vector::iterator it = mList.begin(); it != mList.end(); ++it) + for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNT("NPCO", *it, 36); } @@ -41,7 +41,7 @@ void Container::load(ESMReader &esm) mInventory.load(esm); } -void Container::save(ESMWriter &esm) +void Container::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loadcont.hpp b/components/esm/loadcont.hpp index b2bbab73d0..c854b52907 100644 --- a/components/esm/loadcont.hpp +++ b/components/esm/loadcont.hpp @@ -27,7 +27,7 @@ struct InventoryList std::vector mList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; struct Container @@ -46,7 +46,7 @@ struct Container InventoryList mInventory; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadcrea.cpp b/components/esm/loadcrea.cpp index 86d05b8a57..30b70b35b6 100644 --- a/components/esm/loadcrea.cpp +++ b/components/esm/loadcrea.cpp @@ -35,7 +35,7 @@ void Creature::load(ESMReader &esm) esm.skipRecord(); } -void Creature::save(ESMWriter &esm) +void Creature::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("CNAM", mOriginal); diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index 279e2ea3f4..80e0fbd1c9 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -86,7 +86,7 @@ struct Creature AIPackageList mAiPackage; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadcrec.hpp b/components/esm/loadcrec.hpp index 6904df15a4..2b840ccf46 100644 --- a/components/esm/loadcrec.hpp +++ b/components/esm/loadcrec.hpp @@ -24,7 +24,7 @@ struct LoadCREC esm.skipRecord(); } - void save(ESMWriter &esm) + void save(ESMWriter &esm) const { } }; @@ -39,7 +39,7 @@ struct LoadCNTC esm.skipRecord(); } - void save(ESMWriter &esm) + void save(ESMWriter &esm) const { } }; diff --git a/components/esm/loaddial.cpp b/components/esm/loaddial.cpp index fb50d5e9f5..e014ca37e8 100644 --- a/components/esm/loaddial.cpp +++ b/components/esm/loaddial.cpp @@ -25,7 +25,7 @@ void Dialogue::load(ESMReader &esm) esm.fail("Unknown sub record size"); } -void Dialogue::save(ESMWriter &esm) +void Dialogue::save(ESMWriter &esm) const { if (mType != Deleted) esm.writeHNT("DATA", mType); diff --git a/components/esm/loaddial.hpp b/components/esm/loaddial.hpp index 61f3f763de..0fe5027dc9 100644 --- a/components/esm/loaddial.hpp +++ b/components/esm/loaddial.hpp @@ -34,7 +34,7 @@ struct Dialogue std::vector mInfo; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } #endif diff --git a/components/esm/loaddoor.cpp b/components/esm/loaddoor.cpp index a4c7b7d58b..f666ac67a9 100644 --- a/components/esm/loaddoor.cpp +++ b/components/esm/loaddoor.cpp @@ -15,7 +15,7 @@ void Door::load(ESMReader &esm) mCloseSound = esm.getHNOString("ANAM"); } -void Door::save(ESMWriter &esm) +void Door::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loaddoor.hpp b/components/esm/loaddoor.hpp index 77ffc64899..2b927c56e0 100644 --- a/components/esm/loaddoor.hpp +++ b/components/esm/loaddoor.hpp @@ -14,7 +14,7 @@ struct Door std::string mId, mName, mModel, mScript, mOpenSound, mCloseSound; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadench.cpp b/components/esm/loadench.cpp index c4e278368e..4b4c3a1ec5 100644 --- a/components/esm/loadench.cpp +++ b/components/esm/loadench.cpp @@ -12,7 +12,7 @@ void Enchantment::load(ESMReader &esm) mEffects.load(esm); } -void Enchantment::save(ESMWriter &esm) +void Enchantment::save(ESMWriter &esm) const { esm.writeHNT("ENDT", mData, 16); mEffects.save(esm); diff --git a/components/esm/loadench.hpp b/components/esm/loadench.hpp index 999f93ad97..3cdc3a0bd0 100644 --- a/components/esm/loadench.hpp +++ b/components/esm/loadench.hpp @@ -39,7 +39,7 @@ struct Enchantment EffectList mEffects; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } #endif diff --git a/components/esm/loadfact.cpp b/components/esm/loadfact.cpp index e2712d462d..c8be518028 100644 --- a/components/esm/loadfact.cpp +++ b/components/esm/loadfact.cpp @@ -47,7 +47,7 @@ void Faction::load(ESMReader &esm) mReactions.push_back(r); } } -void Faction::save(ESMWriter &esm) +void Faction::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mName); @@ -61,7 +61,7 @@ void Faction::save(ESMWriter &esm) esm.writeHNT("FADT", mData, 240); - for (std::vector::iterator it = mReactions.begin(); it != mReactions.end(); ++it) + for (std::vector::const_iterator it = mReactions.begin(); it != mReactions.end(); ++it) { esm.writeHNString("ANAM", it->mFaction); esm.writeHNT("INTV", it->mReaction); diff --git a/components/esm/loadfact.hpp b/components/esm/loadfact.hpp index 891b996473..11f65a87f5 100644 --- a/components/esm/loadfact.hpp +++ b/components/esm/loadfact.hpp @@ -63,7 +63,7 @@ struct Faction std::string mRanks[10]; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadglob.cpp b/components/esm/loadglob.cpp index ccb519acd4..e1c2d44087 100644 --- a/components/esm/loadglob.cpp +++ b/components/esm/loadglob.cpp @@ -7,7 +7,7 @@ namespace ESM mValue.read (esm, ESM::Variant::Format_Global); } - void Global::save (ESMWriter &esm) + void Global::save (ESMWriter &esm) const { mValue.write (esm, ESM::Variant::Format_Global); } diff --git a/components/esm/loadglob.hpp b/components/esm/loadglob.hpp index 72e16c0ce5..06ff97ef20 100644 --- a/components/esm/loadglob.hpp +++ b/components/esm/loadglob.hpp @@ -21,7 +21,7 @@ struct Global Variant mValue; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadgmst.cpp b/components/esm/loadgmst.cpp index fe1cc1b047..3a7df45065 100644 --- a/components/esm/loadgmst.cpp +++ b/components/esm/loadgmst.cpp @@ -7,7 +7,7 @@ namespace ESM mValue.read (esm, ESM::Variant::Format_Gmst); } - void GameSetting::save (ESMWriter &esm) + void GameSetting::save (ESMWriter &esm) const { mValue.write (esm, ESM::Variant::Format_Gmst); } diff --git a/components/esm/loadgmst.hpp b/components/esm/loadgmst.hpp index a6e0c2ecbe..9c37c7da09 100644 --- a/components/esm/loadgmst.hpp +++ b/components/esm/loadgmst.hpp @@ -24,7 +24,7 @@ struct GameSetting void load(ESMReader &esm); - /// \todo remove the get* functions (redundant, since mValue as equivalent functions now). + /// \todo remove the get* functions (redundant, since mValue has equivalent functions now). int getInt() const; ///< Throws an exception if GMST is not of type int or float. @@ -35,7 +35,7 @@ struct GameSetting std::string getString() const; ///< Throwns an exception if GMST is not of type string. - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadinfo.cpp b/components/esm/loadinfo.cpp index 90f8fcf35b..1985da2cd9 100644 --- a/components/esm/loadinfo.cpp +++ b/components/esm/loadinfo.cpp @@ -120,7 +120,7 @@ void DialInfo::load(ESMReader &esm) esm.skipRecord(); } -void DialInfo::save(ESMWriter &esm) +void DialInfo::save(ESMWriter &esm) const { esm.writeHNCString("INAM", mId); esm.writeHNCString("PNAM", mPrev); @@ -135,7 +135,7 @@ void DialInfo::save(ESMWriter &esm) esm.writeHNOCString("SNAM", mSound); esm.writeHNOString("NAME", mResponse); - for (std::vector::iterator it = mSelects.begin(); it != mSelects.end(); ++it) + for (std::vector::const_iterator it = mSelects.begin(); it != mSelects.end(); ++it) { esm.writeHNString("SCVR", it->mSelectRule); it->mValue.write (esm, Variant::Format_Info); diff --git a/components/esm/loadinfo.hpp b/components/esm/loadinfo.hpp index 2361ed9eb5..351768e96f 100644 --- a/components/esm/loadinfo.hpp +++ b/components/esm/loadinfo.hpp @@ -99,7 +99,7 @@ struct DialInfo }; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } diff --git a/components/esm/loadingr.cpp b/components/esm/loadingr.cpp index 7e31a4116d..1bc9ae41c6 100644 --- a/components/esm/loadingr.cpp +++ b/components/esm/loadingr.cpp @@ -37,7 +37,7 @@ void Ingredient::load(ESMReader &esm) } } -void Ingredient::save(ESMWriter &esm) +void Ingredient::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadingr.hpp b/components/esm/loadingr.hpp index 5e286535f4..03e67924c5 100644 --- a/components/esm/loadingr.hpp +++ b/components/esm/loadingr.hpp @@ -28,7 +28,7 @@ struct Ingredient std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 60c475040f..8e54bcc5c9 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -16,14 +16,14 @@ void Land::LandData::save(ESMWriter &esm) offsets.mHeightOffset = mHeights[0] / HEIGHT_SCALE; offsets.mUnk1 = mUnk1; offsets.mUnk2 = mUnk2; - + float prevY = mHeights[0], prevX; int number = 0; // avoid multiplication for (int i = 0; i < LAND_SIZE; ++i) { float diff = (mHeights[number] - prevY) / HEIGHT_SCALE; offsets.mHeightData[number] = (diff >= 0) ? (int8_t) (diff + 0.5) : (int8_t) (diff - 0.5); - + prevX = prevY = mHeights[number]; ++number; @@ -132,7 +132,7 @@ void Land::load(ESMReader &esm) mLandData = NULL; } -void Land::save(ESMWriter &esm) +void Land::save(ESMWriter &esm) const { esm.startSubRecord("INTV"); esm.writeT(mX); @@ -140,18 +140,6 @@ void Land::save(ESMWriter &esm) esm.endRecord("INTV"); esm.writeHNT("DATA", mFlags); - - // TODO: Land! - bool wasLoaded = mDataLoaded; - if (mDataTypes) { - // Try to load all available data before saving - loadData(mDataTypes); - } - if (mDataLoaded) - mLandData->save(esm); - - if (!wasLoaded) - unloadData(); // Don't need to keep the data loaded if it wasn't already } /// \todo remove memory allocation when only defaults needed diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 9c1fd1f5c6..3d3bcd67bb 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -94,7 +94,7 @@ struct Land LandData *mLandData; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; /** * Actually loads data diff --git a/components/esm/loadlevlist.cpp b/components/esm/loadlevlist.cpp index b54a912760..ab3f5e9e66 100644 --- a/components/esm/loadlevlist.cpp +++ b/components/esm/loadlevlist.cpp @@ -33,13 +33,13 @@ void LeveledListBase::load(ESMReader &esm) esm.getHNT(li.mLevel, "INTV"); } } -void LeveledListBase::save(ESMWriter &esm) +void LeveledListBase::save(ESMWriter &esm) const { esm.writeHNT("DATA", mFlags); esm.writeHNT("NNAM", mChanceNone); esm.writeHNT("INDX", mList.size()); - for (std::vector::iterator it = mList.begin(); it != mList.end(); ++it) + for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNCString(mRecName, it->mId); esm.writeHNT("INTV", it->mLevel); diff --git a/components/esm/loadlevlist.hpp b/components/esm/loadlevlist.hpp index 7339cac56f..f5fb7fd5b1 100644 --- a/components/esm/loadlevlist.hpp +++ b/components/esm/loadlevlist.hpp @@ -51,7 +51,7 @@ struct LeveledListBase std::vector mList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadligh.cpp b/components/esm/loadligh.cpp index 89a2b8c65b..3f279c7c83 100644 --- a/components/esm/loadligh.cpp +++ b/components/esm/loadligh.cpp @@ -16,7 +16,7 @@ void Light::load(ESMReader &esm) mScript = esm.getHNOString("SCRI"); mSound = esm.getHNOString("SNAM"); } -void Light::save(ESMWriter &esm) +void Light::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loadligh.hpp b/components/esm/loadligh.hpp index 3f0b76d6e2..9a341f0de5 100644 --- a/components/esm/loadligh.hpp +++ b/components/esm/loadligh.hpp @@ -44,7 +44,7 @@ struct Light std::string mSound, mScript, mModel, mIcon, mName, mId; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadlock.cpp b/components/esm/loadlock.cpp index 03eac52bd5..318769ec08 100644 --- a/components/esm/loadlock.cpp +++ b/components/esm/loadlock.cpp @@ -17,7 +17,7 @@ void Lockpick::load(ESMReader &esm) mIcon = esm.getHNOString("ITEX"); } -void Lockpick::save(ESMWriter &esm) +void Lockpick::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadlock.hpp b/components/esm/loadlock.hpp index 953066cb2f..aea5a4f31d 100644 --- a/components/esm/loadlock.hpp +++ b/components/esm/loadlock.hpp @@ -24,7 +24,7 @@ struct Lockpick std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadltex.cpp b/components/esm/loadltex.cpp index e523e9fa7f..dc1bc164b4 100644 --- a/components/esm/loadltex.cpp +++ b/components/esm/loadltex.cpp @@ -11,7 +11,7 @@ void LandTexture::load(ESMReader &esm) esm.getHNT(mIndex, "INTV"); mTexture = esm.getHNString("DATA"); } -void LandTexture::save(ESMWriter &esm) +void LandTexture::save(ESMWriter &esm) const { esm.writeHNT("INTV", mIndex); esm.writeHNCString("DATA", mTexture); diff --git a/components/esm/loadltex.hpp b/components/esm/loadltex.hpp index 6e6d987d49..3d08169484 100644 --- a/components/esm/loadltex.hpp +++ b/components/esm/loadltex.hpp @@ -31,7 +31,7 @@ struct LandTexture int mIndex; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } #endif diff --git a/components/esm/loadmgef.cpp b/components/esm/loadmgef.cpp index 060645b5f4..9eaeff7046 100644 --- a/components/esm/loadmgef.cpp +++ b/components/esm/loadmgef.cpp @@ -58,15 +58,11 @@ void MagicEffect::load(ESMReader &esm) mDescription = esm.getHNOString("DESC"); } -void MagicEffect::save(ESMWriter &esm) +void MagicEffect::save(ESMWriter &esm) const { esm.writeHNT("INDX", mIndex); - mData.mFlags &= 0xe00; esm.writeHNT("MEDT", mData, 36); - if (mIndex>=0 && mIndex::iterator DestIter; + typedef std::vector::const_iterator DestIter; for (DestIter it = mTransport.begin(); it != mTransport.end(); ++it) { esm.writeHNT("DODT", it->mPos, sizeof(it->mPos)); esm.writeHNOCString("DNAM", it->mCellName); diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index 009bc5ef3b..009548c596 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -117,7 +117,7 @@ struct NPC std::string mHair, mHead; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; bool isMale() const; diff --git a/components/esm/loadnpcc.hpp b/components/esm/loadnpcc.hpp index 79d92397f8..f023fd2172 100644 --- a/components/esm/loadnpcc.hpp +++ b/components/esm/loadnpcc.hpp @@ -84,7 +84,7 @@ struct LoadNPCC { esm.skipRecord(); } - void save(ESMWriter &esm) + void save(ESMWriter &esm) const { } }; diff --git a/components/esm/loadpgrd.cpp b/components/esm/loadpgrd.cpp index 882addcb9d..65d1f80553 100644 --- a/components/esm/loadpgrd.cpp +++ b/components/esm/loadpgrd.cpp @@ -70,25 +70,25 @@ void Pathgrid::load(ESMReader &esm) } } } -void Pathgrid::save(ESMWriter &esm) +void Pathgrid::save(ESMWriter &esm) const { esm.writeHNT("DATA", mData, 12); esm.writeHNCString("NAME", mCell); - + if (!mPoints.empty()) { esm.startSubRecord("PGRP"); - for (PointList::iterator it = mPoints.begin(); it != mPoints.end(); ++it) + for (PointList::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it) { esm.writeT(*it); } esm.endRecord("PGRP"); } - + if (!mEdges.empty()) { esm.startSubRecord("PGRC"); - for (std::vector::iterator it = mEdges.begin(); it != mEdges.end(); ++it) + for (std::vector::const_iterator it = mEdges.begin(); it != mEdges.end(); ++it) { esm.writeT(it->mV1); } diff --git a/components/esm/loadpgrd.hpp b/components/esm/loadpgrd.hpp index c3f50fc4da..d14433a786 100644 --- a/components/esm/loadpgrd.hpp +++ b/components/esm/loadpgrd.hpp @@ -46,7 +46,7 @@ struct Pathgrid EdgeList mEdges; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } #endif diff --git a/components/esm/loadprob.cpp b/components/esm/loadprob.cpp index 729f8404e5..0fb4c97507 100644 --- a/components/esm/loadprob.cpp +++ b/components/esm/loadprob.cpp @@ -17,7 +17,7 @@ void Probe::load(ESMReader &esm) mIcon = esm.getHNOString("ITEX"); } -void Probe::save(ESMWriter &esm) +void Probe::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadprob.hpp b/components/esm/loadprob.hpp index 55b896bcda..d0a8256ab6 100644 --- a/components/esm/loadprob.hpp +++ b/components/esm/loadprob.hpp @@ -24,7 +24,7 @@ struct Probe std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadrace.cpp b/components/esm/loadrace.cpp index 955424e2b9..e9e1d0d797 100644 --- a/components/esm/loadrace.cpp +++ b/components/esm/loadrace.cpp @@ -22,7 +22,7 @@ void Race::load(ESMReader &esm) mPowers.load(esm); mDescription = esm.getHNOString("DESC"); } -void Race::save(ESMWriter &esm) +void Race::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mName); esm.writeHNT("RADT", mData, 140); diff --git a/components/esm/loadrace.hpp b/components/esm/loadrace.hpp index 6ecec8ebb9..a53a980701 100644 --- a/components/esm/loadrace.hpp +++ b/components/esm/loadrace.hpp @@ -65,7 +65,7 @@ struct Race SpellList mPowers; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadregn.cpp b/components/esm/loadregn.cpp index 41c7f507ae..fd42b9ee83 100644 --- a/components/esm/loadregn.cpp +++ b/components/esm/loadregn.cpp @@ -28,7 +28,7 @@ void Region::load(ESMReader &esm) mSoundList.push_back(sr); } } -void Region::save(ESMWriter &esm) +void Region::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mName); @@ -40,7 +40,7 @@ void Region::save(ESMWriter &esm) esm.writeHNOCString("BNAM", mSleepList); esm.writeHNT("CNAM", mMapColor); - for (std::vector::iterator it = mSoundList.begin(); it != mSoundList.end(); ++it) + for (std::vector::const_iterator it = mSoundList.begin(); it != mSoundList.end(); ++it) { esm.writeHNT("SNAM", *it); } diff --git a/components/esm/loadregn.hpp b/components/esm/loadregn.hpp index f2a3d9a108..a6075d65ae 100644 --- a/components/esm/loadregn.hpp +++ b/components/esm/loadregn.hpp @@ -47,7 +47,7 @@ struct Region std::vector mSoundList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadrepa.cpp b/components/esm/loadrepa.cpp index ced6daa2e9..59bfa01695 100644 --- a/components/esm/loadrepa.cpp +++ b/components/esm/loadrepa.cpp @@ -17,7 +17,7 @@ void Repair::load(ESMReader &esm) mIcon = esm.getHNOString("ITEX"); } -void Repair::save(ESMWriter &esm) +void Repair::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadrepa.hpp b/components/esm/loadrepa.hpp index 83812bad9c..771e7ead07 100644 --- a/components/esm/loadrepa.hpp +++ b/components/esm/loadrepa.hpp @@ -24,7 +24,7 @@ struct Repair std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 2c1b018d97..8afb85602d 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -50,11 +50,11 @@ void Script::load(ESMReader &esm) // Script text mScriptText = esm.getHNOString("SCTX"); } -void Script::save(ESMWriter &esm) +void Script::save(ESMWriter &esm) const { std::string varNameString; if (!mVarNames.empty()) - for (std::vector::iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) + for (std::vector::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) varNameString.append(*it); SCHD data; @@ -68,7 +68,7 @@ void Script::save(ESMWriter &esm) if (!mVarNames.empty()) { esm.startSubRecord("SCVR"); - for (std::vector::iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) + for (std::vector::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) { esm.writeHCString(*it); } diff --git a/components/esm/loadscpt.hpp b/components/esm/loadscpt.hpp index be7e839002..450224faa4 100644 --- a/components/esm/loadscpt.hpp +++ b/components/esm/loadscpt.hpp @@ -56,7 +56,7 @@ public: std::string mScriptText; // Uncompiled script void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadskil.cpp b/components/esm/loadskil.cpp index 676a835c3b..f6a2c49503 100644 --- a/components/esm/loadskil.cpp +++ b/components/esm/loadskil.cpp @@ -137,7 +137,7 @@ void Skill::load(ESMReader &esm) mId = indexToId (mIndex); } -void Skill::save(ESMWriter &esm) +void Skill::save(ESMWriter &esm) const { esm.writeHNT("INDX", mIndex); esm.writeHNT("SKDT", mData, 24); diff --git a/components/esm/loadskil.hpp b/components/esm/loadskil.hpp index 384f874545..2436173cbb 100644 --- a/components/esm/loadskil.hpp +++ b/components/esm/loadskil.hpp @@ -75,7 +75,7 @@ struct Skill static const boost::array sSkillIds; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadsndg.cpp b/components/esm/loadsndg.cpp index 42d524226d..9b992c9606 100644 --- a/components/esm/loadsndg.cpp +++ b/components/esm/loadsndg.cpp @@ -13,7 +13,7 @@ void SoundGenerator::load(ESMReader &esm) mCreature = esm.getHNOString("CNAM"); mSound = esm.getHNOString("SNAM"); } -void SoundGenerator::save(ESMWriter &esm) +void SoundGenerator::save(ESMWriter &esm) const { esm.writeHNT("DATA", mType, 4); esm.writeHNOCString("CNAM", mCreature); diff --git a/components/esm/loadsndg.hpp b/components/esm/loadsndg.hpp index a6226c1545..2756676ef1 100644 --- a/components/esm/loadsndg.hpp +++ b/components/esm/loadsndg.hpp @@ -33,7 +33,7 @@ struct SoundGenerator std::string mId, mCreature, mSound; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } #endif diff --git a/components/esm/loadsoun.cpp b/components/esm/loadsoun.cpp index 07af2b5e91..0f6b0f84a7 100644 --- a/components/esm/loadsoun.cpp +++ b/components/esm/loadsoun.cpp @@ -17,7 +17,7 @@ void Sound::load(ESMReader &esm) << endl; */ } -void Sound::save(ESMWriter &esm) +void Sound::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mSound); esm.writeHNT("DATA", mData, 3); diff --git a/components/esm/loadsoun.hpp b/components/esm/loadsoun.hpp index f8e38ac092..6c9bb1fed3 100644 --- a/components/esm/loadsoun.hpp +++ b/components/esm/loadsoun.hpp @@ -20,7 +20,7 @@ struct Sound std::string mId, mSound; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadspel.cpp b/components/esm/loadspel.cpp index 8149fe4cef..5c0bd956f7 100644 --- a/components/esm/loadspel.cpp +++ b/components/esm/loadspel.cpp @@ -13,7 +13,7 @@ void Spell::load(ESMReader &esm) mEffects.load(esm); } -void Spell::save(ESMWriter &esm) +void Spell::save(ESMWriter &esm) const { esm.writeHNOCString("FNAM", mName); esm.writeHNT("SPDT", mData, 12); diff --git a/components/esm/loadspel.hpp b/components/esm/loadspel.hpp index 3a620962d1..b34bd29f18 100644 --- a/components/esm/loadspel.hpp +++ b/components/esm/loadspel.hpp @@ -42,7 +42,7 @@ struct Spell EffectList mEffects; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadsscr.cpp b/components/esm/loadsscr.cpp index ae50de517c..f51b7be479 100644 --- a/components/esm/loadsscr.cpp +++ b/components/esm/loadsscr.cpp @@ -11,7 +11,7 @@ void StartScript::load(ESMReader &esm) mData = esm.getHNString("DATA"); mScript = esm.getHNString("NAME"); } -void StartScript::save(ESMWriter &esm) +void StartScript::save(ESMWriter &esm) const { esm.writeHNString("DATA", mData); esm.writeHNString("NAME", mScript); diff --git a/components/esm/loadsscr.hpp b/components/esm/loadsscr.hpp index 713fe96b52..2326f00f43 100644 --- a/components/esm/loadsscr.hpp +++ b/components/esm/loadsscr.hpp @@ -24,7 +24,7 @@ struct StartScript // Load a record and add it to the list void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } diff --git a/components/esm/loadstat.cpp b/components/esm/loadstat.cpp index c9346dafca..38206422bb 100644 --- a/components/esm/loadstat.cpp +++ b/components/esm/loadstat.cpp @@ -10,7 +10,7 @@ void Static::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); } -void Static::save(ESMWriter &esm) +void Static::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); } diff --git a/components/esm/loadstat.hpp b/components/esm/loadstat.hpp index 1adb7d05be..df42c0c491 100644 --- a/components/esm/loadstat.hpp +++ b/components/esm/loadstat.hpp @@ -25,7 +25,7 @@ struct Static std::string mId, mModel; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadweap.cpp b/components/esm/loadweap.cpp index 2537123969..e21d8924a1 100644 --- a/components/esm/loadweap.cpp +++ b/components/esm/loadweap.cpp @@ -15,7 +15,7 @@ void Weapon::load(ESMReader &esm) mIcon = esm.getHNOString("ITEX"); mEnchant = esm.getHNOString("ENAM"); } -void Weapon::save(ESMWriter &esm) +void Weapon::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loadweap.hpp b/components/esm/loadweap.hpp index b62179ccb1..42810d3afb 100644 --- a/components/esm/loadweap.hpp +++ b/components/esm/loadweap.hpp @@ -59,7 +59,7 @@ struct Weapon std::string mId, mName, mModel, mIcon, mEnchant, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/spelllist.cpp b/components/esm/spelllist.cpp index dd886cf7ff..24d3c3d0a5 100644 --- a/components/esm/spelllist.cpp +++ b/components/esm/spelllist.cpp @@ -12,9 +12,9 @@ void SpellList::load(ESMReader &esm) } } -void SpellList::save(ESMWriter &esm) +void SpellList::save(ESMWriter &esm) const { - for (std::vector::iterator it = mList.begin(); it != mList.end(); ++it) { + for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNString("NPCS", *it, 32); } } diff --git a/components/esm/spelllist.hpp b/components/esm/spelllist.hpp index 52999270a0..934bdda7ad 100644 --- a/components/esm/spelllist.hpp +++ b/components/esm/spelllist.hpp @@ -17,7 +17,7 @@ namespace ESM std::vector mList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } From bf0fba68af5926ee3abd03ce77d43132315ba6f9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 16 Sep 2013 12:51:57 +0200 Subject: [PATCH 038/148] added save stage for globals --- apps/opencs/model/doc/document.cpp | 6 +-- apps/opencs/model/doc/saving.cpp | 9 +++- apps/opencs/model/doc/savingstages.hpp | 62 ++++++++++++++++++++++++++ components/esm/esmwriter.hpp | 2 +- 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 9b22175591..525f18a206 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2058,9 +2058,9 @@ void CSMDoc::Document::addOptionalGlobals() { static const char *sGlobals[] = { - "dayspassed", - "pcwerewolf", - "pcyear", + "DaysPassed", + "PCWerewolf", + "PCYear", 0 }; diff --git a/apps/opencs/model/doc/saving.cpp b/apps/opencs/model/doc/saving.cpp index 67073ca434..e0180bee4e 100644 --- a/apps/opencs/model/doc/saving.cpp +++ b/apps/opencs/model/doc/saving.cpp @@ -1,9 +1,14 @@ #include "saving.hpp" -#include "state.hpp" +#include +#include "../world/data.hpp" +#include "../world/idcollection.hpp" + +#include "state.hpp" #include "savingstages.hpp" +#include "document.hpp" CSMDoc::Saving::Saving (Document& document) : Operation (State_Saving, true, true), mDocument (document), mState (*this) @@ -12,6 +17,8 @@ CSMDoc::Saving::Saving (Document& document) appendStage (new WriteHeaderStage (mDocument, mState)); + appendStage (new WriteCollectionStage > + (mDocument.getData().getGlobals(), mState, ESM::REC_GLOB)); appendStage (new CloseSaveStage (mState)); diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp index 914a2d585a..9787679c6f 100644 --- a/apps/opencs/model/doc/savingstages.hpp +++ b/apps/opencs/model/doc/savingstages.hpp @@ -1,8 +1,14 @@ #ifndef CSM_DOC_SAVINGSTAGES_H #define CSM_DOC_SAVINGSTAGES_H +#include + #include "stage.hpp" +#include "savingstate.hpp" + +#include "../world/record.hpp" + namespace CSMDoc { class Document; @@ -40,6 +46,62 @@ namespace CSMDoc ///< Messages resulting from this stage will be appended to \a messages. }; + + template + class WriteCollectionStage : public Stage + { + const CollectionT& mCollection; + SavingState& mState; + ESM::RecNameInts mRecordType; + + public: + + WriteCollectionStage (const CollectionT& collection, SavingState& state, + ESM::RecNameInts recordType); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + + template + WriteCollectionStage::WriteCollectionStage (const CollectionT& collection, + SavingState& state, ESM::RecNameInts recordType) + : mCollection (collection), mState (state), mRecordType (recordType) + {} + + template + int WriteCollectionStage::setup() + { + return mCollection.getSize(); + } + + template + void WriteCollectionStage::perform (int stage, std::vector& messages) + { + CSMWorld::RecordBase::State state = mCollection.getRecord (stage).mState; + + if (state==CSMWorld::RecordBase::State_Modified || + state==CSMWorld::RecordBase::State_ModifiedOnly) + { + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic (change ESMWriter interface?) + type += reinterpret_cast (&mRecordType)[i]; + + mState.getWriter().startRecord (type); + mCollection.getRecord (stage).mModified.save (mState.getWriter()); + mState.getWriter().endRecord (type); + } + else if (state==CSMWorld::RecordBase::State_Deleted) + { + /// \todo write record with delete flag + } + } + + class CloseSaveStage : public Stage { SavingState& mState; diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index b0925463a0..fc64c4a137 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -90,7 +90,7 @@ class ESMWriter write((char*)&data, size); } - void startRecord(const std::string& name, uint32_t flags); + void startRecord(const std::string& name, uint32_t flags = 0); void startSubRecord(const std::string& name); void endRecord(const std::string& name); void writeHString(const std::string& data); From 03054c816071bd53800d59c95170b18dcc8c8241 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 16 Sep 2013 14:10:05 +0200 Subject: [PATCH 039/148] forgot to write record ID --- apps/opencs/model/doc/savingstages.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp index 9787679c6f..96b1fe17f6 100644 --- a/apps/opencs/model/doc/savingstages.hpp +++ b/apps/opencs/model/doc/savingstages.hpp @@ -92,6 +92,7 @@ namespace CSMDoc type += reinterpret_cast (&mRecordType)[i]; mState.getWriter().startRecord (type); + mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage)); mCollection.getRecord (stage).mModified.save (mState.getWriter()); mState.getWriter().endRecord (type); } From acfd78c62ad6ee84c7160e1772dcdc19fd96d831 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 16 Sep 2013 14:17:04 +0200 Subject: [PATCH 040/148] implemented saving for all supported record types except cells, referencables and references --- apps/opencs/model/doc/saving.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/apps/opencs/model/doc/saving.cpp b/apps/opencs/model/doc/saving.cpp index e0180bee4e..7e0b10d66b 100644 --- a/apps/opencs/model/doc/saving.cpp +++ b/apps/opencs/model/doc/saving.cpp @@ -20,6 +20,36 @@ CSMDoc::Saving::Saving (Document& document) appendStage (new WriteCollectionStage > (mDocument.getData().getGlobals(), mState, ESM::REC_GLOB)); + appendStage (new WriteCollectionStage > + (mDocument.getData().getGmsts(), mState, ESM::REC_GMST)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getSkills(), mState, ESM::REC_SKIL)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getClasses(), mState, ESM::REC_CLAS)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getFactions(), mState, ESM::REC_FACT)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getRaces(), mState, ESM::REC_RACE)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getSounds(), mState, ESM::REC_SOUN)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getScripts(), mState, ESM::REC_SCPT)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getRegions(), mState, ESM::REC_REGN)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getBirthsigns(), mState, ESM::REC_BSGN)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getSpells(), mState, ESM::REC_SPEL)); + appendStage (new CloseSaveStage (mState)); From 0eb06ada39ca9ca8857325e816b61048348d896c Mon Sep 17 00:00:00 2001 From: graffy76 Date: Wed, 18 Sep 2013 02:36:23 -0500 Subject: [PATCH 041/148] Implemneting drag and drop --- apps/launcher/datafilespage.cpp | 32 +- apps/launcher/datafilespage.hpp | 1 + components/CMakeLists.txt | 2 +- components/esxselector/model/contentmodel.cpp | 343 ++++++---- components/esxselector/model/contentmodel.hpp | 9 +- .../esxselector/model/datafilesmodel.cpp | 612 ++++++++++-------- components/esxselector/model/esmfile.cpp | 64 +- components/esxselector/model/esmfile.hpp | 28 +- .../esxselector/model/masterproxymodel.cpp | 31 - .../esxselector/model/pluginsproxymodel.cpp | 26 - .../esxselector/view/contentselector.cpp | 150 ++--- .../esxselector/view/contentselector.hpp | 13 +- 12 files changed, 725 insertions(+), 586 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 070f455e4e..7069737eff 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -7,7 +7,6 @@ #include -#include #include #include @@ -20,6 +19,7 @@ #include "settings/launchersettings.hpp" #include "utils/textinputdialog.hpp" +#include "components/esxselector/view/contentselector.hpp" #include @@ -27,8 +27,8 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam : mCfgMgr(cfg) , mGameSettings(gameSettings) , mLauncherSettings(launcherSettings) - , ContentSelector(parent) { + mContentSelector.setParent(parent); QMetaObject::connectSlotsByName(this); projectGroupBox->hide(); @@ -51,24 +51,21 @@ void DataFilesPage::createActions() void DataFilesPage::setupDataFiles() { - if (!mDataFilesModel) - qDebug() << "data files model undefined"; - // Set the encoding to the one found in openmw.cfg or the default - mDataFilesModel->setEncoding(mGameSettings.value(QString("encoding"), QString("win1252"))); + mContentSelector.setEncoding(mGameSettings.value(QString("encoding"), QString("win1252"))); QStringList paths = mGameSettings.getDataDirs(); foreach (const QString &path, paths) { - mDataFilesModel->addFiles(path); + mContentSelector.addFiles(path); } QString dataLocal = mGameSettings.getDataLocal(); if (!dataLocal.isEmpty()) - mDataFilesModel->addFiles(dataLocal); + mContentSelector.addFiles(dataLocal); // Sort by date accessed for now - mDataFilesModel->sort(3); + //mContentSelector->sort(3); QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/")); QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); @@ -107,11 +104,11 @@ void DataFilesPage::loadSettings() if (profile.isEmpty()) return; - mDataFilesModel->uncheckAll(); + // mContentSelector.uncheckAll(); QStringList masters = mLauncherSettings.values(QString("Profiles/") + profile + QString("/master"), Qt::MatchExactly); QStringList plugins = mLauncherSettings.values(QString("Profiles/") + profile + QString("/plugin"), Qt::MatchExactly); - +/* foreach (const QString &master, masters) { QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(master)); if (index.isValid()) @@ -123,12 +120,13 @@ void DataFilesPage::loadSettings() if (index.isValid()) mDataFilesModel->setCheckState(index, Qt::Checked); } + */ } void DataFilesPage::saveSettings() { - if (mDataFilesModel->rowCount() < 1) - return; +// if (mDataFilesModel->rowCount() < 1) +// return; QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); @@ -143,8 +141,8 @@ void DataFilesPage::saveSettings() mGameSettings.remove(QString("master")); mGameSettings.remove(QString("plugin")); - EsxModel::EsmFileList items = mDataFilesModel->checkedItems(); - + // EsxModel::EsmFileList items = mDataFilesModel->checkedItems(); +/* foreach(const EsxModel::EsmFile *item, items) { if (item->masters().size() == 0) { @@ -156,7 +154,7 @@ void DataFilesPage::saveSettings() mGameSettings.setMultiValue(QString("plugin"), item->fileName()); } } - +*/ } void DataFilesPage::updateOkButton(const QString &text) @@ -241,7 +239,7 @@ void DataFilesPage::setPluginsCheckstates(Qt::CheckState state) if (!sourceIndex.isValid()) return; - mDataFilesModel->setCheckState(sourceIndex, state); + //mDataFilesModel->setCheckState(sourceIndex, state); } } diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index f3792b1f13..ed92da749d 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -52,6 +52,7 @@ private slots: private: QMenu *mContextMenu; + ContentSelector mContentSelector; Files::ConfigurationManager &mCfgMgr; diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 0f7c5017b7..b79fa027ee 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -72,7 +72,7 @@ find_package(Qt4 COMPONENTS QtCore QtGui) if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) add_component_qt_dir (esxselector - model/masterproxymodel model/modelitem model/datafilesmodel + model/masterproxymodel model/modelitem model/pluginsproxymodel model/esmfile model/naturalsort model/contentmodel view/profilescombobox view/comboboxlineedit diff --git a/components/esxselector/model/contentmodel.cpp b/components/esxselector/model/contentmodel.cpp index 673665775a..bfbb1bef7a 100644 --- a/components/esxselector/model/contentmodel.cpp +++ b/components/esxselector/model/contentmodel.cpp @@ -21,20 +21,7 @@ int EsxModel::ContentModel::columnCount(const QModelIndex &parent) const 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()) @@ -43,6 +30,53 @@ int EsxModel::ContentModel::rowCount(const QModelIndex &parent) const return mFiles.size(); } +EsxModel::EsmFile* EsxModel::ContentModel::item(int row) const +{ + if (row >= 0 && row < mFiles.count()) + return mFiles.at(row); + + return 0; +} + +EsxModel::EsmFile* EsxModel::ContentModel::findItem(const QString &name) +{ + for (int i = 0; i < mFiles.size(); ++i) + { + if (name == item(i)->fileName()) + return item(i); + } + + return 0; +} + +QModelIndex EsxModel::ContentModel::indexFromItem(EsmFile *item) const +{ + if (item) + return index(mFiles.indexOf(item),0); + + return QModelIndex(); +} + +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; +} + QVariant EsxModel::ContentModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) @@ -70,18 +104,14 @@ QVariant EsxModel::ContentModel::data(const QModelIndex &index, int role) const 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: + case 3: return file->version(); - case 6: + case 4: return file->path(); - case 7: + case 5: return file->masters().join(", "); - case 8: + case 6: return file->description(); } @@ -97,8 +127,6 @@ QVariant EsxModel::ContentModel::data(const QModelIndex &index, int role) const return Qt::AlignLeft + Qt::AlignVCenter; case 2: case 3: - case 4: - case 5: return Qt::AlignRight + Qt::AlignVCenter; default: return Qt::AlignLeft + Qt::AlignVCenter; @@ -124,47 +152,88 @@ QVariant EsxModel::ContentModel::data(const QModelIndex &index, int role) const .arg(file->masters().join(", ")); } + case Qt::CheckStateRole: + if (!file->isMaster()) + return isChecked(file->fileName()); + break; + case Qt::UserRole: { - if (file->masters().size() == 0) + if (file->isMaster()) return "game"; else return "addon"; } - default: - return QVariant(); + case Qt::UserRole + 1: + return isChecked(file->fileName()); + break; } -} - -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; + return QVariant(); } bool EsxModel::ContentModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (index.isValid() && role == Qt::EditRole) + if(!index.isValid()) + return false; + + EsmFile *file = item(index.row()); + QString fileName = file->fileName(); + + switch(role) { - QString fname = value.value(); - mFiles.replace(index.row(), findItem(fname)); - emit dataChanged(index, index); - return true; + case Qt::EditRole: + { + QStringList list = value.toStringList(); + + //iterate the string list, assigning values to proeprties + //index-enum correspondence 1:1 + for (int i = 0; i < EsxModel::Property_Master; i++) + file->setProperty(static_cast(i), list.at(i)); + + //iterate the remainder of the string list, assifning everything + // as + for (int i = EsxModel::Property_Master; i < list.size(); i++) + file->setProperty (EsxModel::Property_Master, list.at(i)); + + //emit data changed for the item itself + emit dataChanged(index, index); + + return true; + } + break; + + case Qt::UserRole+1: + { + setCheckState(fileName, value.toBool()); + + emit dataChanged(index, index); + + for(int i = 0; i < mFiles.size(); i++) + { + + if (mFiles.at(i)->masters().contains(fileName)) + { + QModelIndex idx = QAbstractTableModel::index(i, 0); + emit dataChanged(idx, idx); + } + } + + return true; + } + break; + + case Qt::CheckStateRole: + { + bool checked = ((value.toInt() == Qt::Checked) && !isChecked(fileName)); + + setCheckState(fileName, checked); + + emit dataChanged(index, index); + + return true; + } + break; } return false; @@ -172,24 +241,31 @@ bool EsxModel::ContentModel::setData(const QModelIndex &index, const QVariant &v bool EsxModel::ContentModel::insertRows(int position, int rows, const QModelIndex &parent) { + if (parent.isValid()) + return false; + beginInsertRows(parent, position, position+rows-1); + { + for (int row = 0; row < rows; ++row) + mFiles.insert(position, new EsmFile); - for (int row = 0; row < rows; ++row) - mFiles.insert(position, new EsmFile); + } endInsertRows(); - endInsertRows(); return true; } bool EsxModel::ContentModel::removeRows(int position, int rows, const QModelIndex &parent) { + if (parent.isValid()) + return false; + beginRemoveRows(parent, position, position+rows-1); + { + for (int row = 0; row < rows; ++row) + delete mFiles.takeAt(position); - for (int row = 0; row < rows; ++row) - mFiles.removeAt(position); + } endRemoveRows(); - endRemoveRows(); - emit dataChanged(index(0,0,parent), index(rowCount()-1, 0, parent)); return true; } @@ -201,27 +277,30 @@ Qt::DropActions EsxModel::ContentModel::supportedDropActions() const 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; - } + if (!index.isValid()) + continue; + + QByteArray fileData = item(index.row())->encodedData(); + + foreach (const char c, fileData) + encodedData.append(c); } + QMimeData *mimeData = new QMimeData(); mimeData->setData("application/omwcontent", encodedData); + return mimeData; } @@ -240,31 +319,66 @@ bool EsxModel::ContentModel::dropMimeData(const QMimeData *data, Qt::DropAction 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; + QStringList values; + + for (int i = 0; i < EsmFile::sPropertyCount; ++i) + stream >> values; + + insertRows(beginRow, 1); + + QModelIndex idx = index(beginRow++, 0, QModelIndex()); + setData(idx, values, Qt::EditRole); } - insertRows(beginRow, rows, QModelIndex()); + return true; +} - foreach (const QString &text, newItems) +bool EsxModel::ContentModel::canBeChecked(const EsmFile *file) const +{ + //element can be checked if all its dependencies are + foreach (const QString &master, file->masters()) { - QModelIndex idx = index(beginRow, 0, QModelIndex()); - setData(idx, text); - beginRow++; + {// if the master is not found in checkstates + // or it is not specifically checked, return false + if (!mCheckStates.contains(master)) + return false; + + if (!isChecked(master)) + return false; + } + + bool found = false; + + //iterate each file, if it is not a master and + //does not have a master that is currently checked, + //return false. + foreach(const EsmFile *file, mFiles) + { + QString filename = file->fileName(); + + found = (filename == master); + + if (found) + { + if (!isChecked(filename)) + return false; + } + } + + if (!found) + return false; } return true; @@ -272,9 +386,10 @@ bool EsxModel::ContentModel::dropMimeData(const QMimeData *data, Qt::DropAction void EsxModel::ContentModel::addFile(EsmFile *file) { - emit beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); - mFiles.append(file); - emit endInsertRows(); + beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); + { + mFiles.append(file); + } endInsertRows(); } void EsxModel::ContentModel::addFiles(const QString &path) @@ -319,9 +434,9 @@ void EsxModel::ContentModel::addFiles(const QString &path) } file->setAuthor(decoder->toUnicode(fileReader.getAuthor().c_str())); - file->setSize(info.size()); - file->setDates(info.lastModified(), info.lastRead()); - file->setVersion(fileReader.getFVer()); + //file->setSize(info.size()); + file->setDate(info.lastModified()); + file->setVersion(0.0f); file->setPath(info.absoluteFilePath()); file->setMasters(masters); file->setDescription(decoder->toUnicode(fileReader.getDesc().c_str())); @@ -342,66 +457,22 @@ void EsxModel::ContentModel::addFiles(const QString &path) delete decoder; } -EsxModel::EsmFile* EsxModel::ContentModel::findItem(const QString &name) +bool EsxModel::ContentModel::isChecked(const QString& name) const { - for (int i = 0; i < mFiles.size(); ++i) - { - if (name == item(i)->fileName()) - return item(i); - } - - // Not found - return 0; + return (mCheckStates[name] == Qt::Checked); } -EsxModel::EsmFile* EsxModel::ContentModel::item(int row) const +void EsxModel::ContentModel::setCheckState(const QString &name, bool isChecked) { - 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()) + if (name.isEmpty()) return; - QString name = item(index.row())->fileName(); + Qt::CheckState state = Qt::Unchecked; + + if (isChecked) + state = Qt::Checked; + 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 diff --git a/components/esxselector/model/contentmodel.hpp b/components/esxselector/model/contentmodel.hpp index a585ab63b8..61c823ab38 100644 --- a/components/esxselector/model/contentmodel.hpp +++ b/components/esxselector/model/contentmodel.hpp @@ -37,14 +37,11 @@ namespace EsxModel 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); + bool isChecked(const QString &name) const; + void setCheckState(const QString &name, bool isChecked); 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); diff --git a/components/esxselector/model/datafilesmodel.cpp b/components/esxselector/model/datafilesmodel.cpp index ee940bb273..c98f70b162 100644 --- a/components/esxselector/model/datafilesmodel.cpp +++ b/components/esxselector/model/datafilesmodel.cpp @@ -24,6 +24,340 @@ EsxModel::DataFilesModel::~DataFilesModel() { } +int EsxModel::DataFilesModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : mFiles.count(); +} + +int EsxModel::DataFilesModel::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : 1; +} + +const EsxModel::EsmFile* EsxModel::DataFilesModel::findItem(const QString &name) +{ + for (int i = 0; i < mFiles.size(); ++i) + { + const EsmFile *file = item(i); + + if (name == file->fileName()) + return file; + } + + return 0; +} + +const EsxModel::EsmFile* EsxModel::DataFilesModel::item(int row) const +{ + if (row >= 0 && row < mFiles.count()) + return mFiles.at(row); + + return 0; +} + +Qt::ItemFlags EsxModel::DataFilesModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + const EsmFile *file = item(index.row()); + + if (!file) + return Qt::NoItemFlags; + + Qt::ItemFlags dragDropFlags = Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + Qt::ItemFlags checkFlags = Qt::ItemIsUserCheckable | Qt::ItemIsEditable; + Qt::ItemFlags defaultFlags = Qt::ItemIsDropEnabled | Qt::ItemIsSelectable; + + if (canBeChecked(file)) + return defaultFlags | dragDropFlags | checkFlags | Qt::ItemIsEnabled; + else + return defaultFlags; +} + +QVariant EsxModel::DataFilesModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= mFiles.size()) + return QVariant(); + + const 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 file->modified().toString(Qt::ISODate); + case 3: + return file->version(); + case 4: + return file->path(); + case 5: + return file->masters().join(", "); + case 6: + return file->description(); + } + break; + } + + case Qt::TextAlignmentRole: + { + switch (column) + { + case 0: + case 1: + return Qt::AlignLeft + Qt::AlignVCenter; + case 2: + case 3: + return Qt::AlignRight + Qt::AlignVCenter; + default: + return Qt::AlignLeft + Qt::AlignVCenter; + } + break; + } + + case Qt::ToolTipRole: + { + if (column != 0) + return QVariant(); + + if (file->version() == 0.0f) + return QVariant(); // Data not set + + QString tooltip = + QString("Author: %1
\ + Version: %2
\ +
Description:
%3
\ +
Dependencies: %4
") + .arg(file->author()) + .arg(QString::number(file->version())) + .arg(file->description()) + .arg(file->masters().join(", ")); + + + return tooltip; + break; + } + + case Qt::UserRole: + { + if (file->masters().size() == 0) + return "game"; + else + return "addon"; + + break; + } + + case Qt::UserRole + 1: + //return check state here + break; + + default: + return QVariant(); + break; + } + +} + +bool EsxModel::DataFilesModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (!index.isValid()) + return false; + + switch (role) + { + case Qt::EditRole: + { + const EsmFile *file = item(index.row()); + + // iterate loop to repopulate file pointer with data in string list. + QStringList list = value.toStringList(); + for (int i = 0; i <999; ++i) + { + file->setProperty(i, value.at(i)); + } + + //populate master list here (emit data changed for each master and + //each item (other than the dropped item) which share each of the masters + file->masters().append(masterList); + + emit dataChanged(index, index); + return true; + } + break; + + case Qt::UserRole + 1: + { + EsmFile *file = item(index.row()); + //set file's checkstate to the passed checkstate + emit dataChanged(index, index); + + for (int i = 0; i < mFiles.size(); ++i) + if (mFiles.at(i)->getMasters().contains(file->fileName())) + emit dataChanged(QAbstractTableModel::index(i,0), QAbstractTableModel::index(i,0)); + + return true; + } + break; + + case Qt::CheckStateRole: + { + EsmFile *file = item(index.row()); + + if ((value.toInt() == Qt::Checked) && !file->isChecked()) + file->setChecked(true); + else if (value.toInt() == Qt::Checked && file->isChecked()) + file->setChecked(false); + else if (value.toInt() == Qt::UnChecked) + file->setChecked(false); + + emit dataChanged(index, index); + + return true; + } + break; + } + return false; +} + +bool EsxModel::DataFilesModel::insertRows(int row, int count, const QModelIndex &parent) +{ + if (parent.isValid()) + return false; + + beginInsertRows(QModelIndex(),row, row+count-1); + { + for (int i = 0; i < count; ++i) + mFiles.insert(row, new EsmFile()); + } endInsertRows(); + + return true; +} + +bool EsxModel::DataFilesModel::removeRows(int row, int count, const QModelIndex &parent) +{ + if (parent.isValid()) + return false; + + beginRemoveRows(QModelIndex(), row, row+count-1); + { + for (int i = 0; i < count; ++i) + delete mFiles.takeAt(row); + } endRemoveRows(); + + 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 +{ + QMimeData *mimeData = new QMimeData(); + QByteArray encodedData; + + QDataStream stream (&encodedData, QIODevice::WriteOnly); + + foreach (const QModelIndex &index, indexes) + { + if (index.isValid()) + { + EsmFile *file = item (index.row()); + + for (int i = 0; i < file->propertyCount(); ++i) + stream << data(index, Qt::DisplayRole).toString(); + + EsmFile *file = item(index.row()); + stream << file->getMasters(); + } + } + + mimeData->setData("application/omwcontent", encodedData); + + return mimeData; +} + +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; +} + void EsxModel::DataFilesModel::setEncoding(const QString &encoding) { mEncoding = encoding; @@ -51,17 +385,6 @@ Qt::CheckState EsxModel::DataFilesModel::checkState(const QModelIndex &index) return mCheckStates[item(index.row())->fileName()]; } -int EsxModel::DataFilesModel::columnCount(const QModelIndex &parent) const -{ - return parent.isValid() ? 0 : 1; -} - -int EsxModel::DataFilesModel::rowCount(const QModelIndex &parent) const -{ - return parent.isValid() ? 0 : mFiles.count(); -} - - bool EsxModel::DataFilesModel::moveRow(int oldrow, int row, const QModelIndex &parent) { if (oldrow < 0 || row < 0 || oldrow == row) @@ -76,124 +399,6 @@ bool EsxModel::DataFilesModel::moveRow(int oldrow, int row, const QModelIndex &p return true; } -QVariant EsxModel::DataFilesModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - const 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::TextDate); - 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(); - } - } - - 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; - } - } - - case Qt::ToolTipRole: - { - if (column != 0) - return QVariant(); - - if (file->version() == 0.0f) - return QVariant(); // Data not set - - QString tooltip = - QString("Author: %1
\ - Version: %2
\ -
Description:
%3
\ -
Dependencies: %4
") - .arg(file->author()) - .arg(QString::number(file->version())) - .arg(file->description()) - .arg(file->masters().join(", ")); - - - return tooltip; - - } - - case Qt::UserRole: - { - if (file->masters().size() == 0) - return "game"; - else - return "addon"; - } - - default: - return QVariant(); - } - -} - -Qt::ItemFlags EsxModel::DataFilesModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return Qt::NoItemFlags; - - 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 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 { if (role != Qt::DisplayRole) @@ -215,22 +420,6 @@ QVariant EsxModel::DataFilesModel::headerData(int section, Qt::Orientation orien return QVariant(); } -bool EsxModel::DataFilesModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (!index.isValid()) - 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) { @@ -345,32 +534,6 @@ QModelIndex EsxModel::DataFilesModel::indexFromItem(const EsmFile *item) const return QModelIndex(); } -const EsxModel::EsmFile* EsxModel::DataFilesModel::findItem(const QString &name) -{ - EsmFileList::ConstIterator it; - EsmFileList::ConstIterator itEnd = mFiles.constEnd(); - - int i = 0; - for (it = mFiles.constBegin(); it != itEnd; ++it) { - const EsmFile *file = item(i); - ++i; - - if (name == file->fileName()) - return file; - } - - // Not found - return 0; -} - -const EsxModel::EsmFile* EsxModel::DataFilesModel::item(int row) const -{ - if (row >= 0 && row < mFiles.count()) - return mFiles.at(row); - - return 0; -} - EsxModel::EsmFileList EsxModel::DataFilesModel::checkedItems() { EsmFileList list; @@ -443,110 +606,3 @@ bool EsxModel::DataFilesModel::canBeChecked(const 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/esmfile.cpp b/components/esxselector/model/esmfile.cpp index 95cf703121..0e7f18373a 100644 --- a/components/esxselector/model/esmfile.cpp +++ b/components/esxselector/model/esmfile.cpp @@ -1,7 +1,12 @@ #include "esmfile.hpp" +#include +#include + +int EsxModel::EsmFile::sPropertyCount = 7; + EsxModel::EsmFile::EsmFile(QString fileName, ModelItem *parent) - : ModelItem(parent), mFileName(fileName), mSize(0), mVersion(0.0f) + : ModelItem(parent), mFileName(fileName), mVersion(0.0f) {} /* EsxModel::EsmFile::EsmFile(const EsmFile &file) @@ -22,15 +27,9 @@ void EsxModel::EsmFile::setAuthor(const QString &author) mAuthor = author; } -void EsxModel::EsmFile::setSize(const int size) -{ - mSize = size; -} - -void EsxModel::EsmFile::setDates(const QDateTime &modified, const QDateTime &accessed) +void EsxModel::EsmFile::setDate(const QDateTime &modified) { mModified = modified; - mAccessed = accessed; } void EsxModel::EsmFile::setVersion(float version) @@ -52,3 +51,52 @@ void EsxModel::EsmFile::setDescription(const QString &description) { mDescription = description; } + +QByteArray EsxModel::EsmFile::encodedData() const +{ + QByteArray encodedData; + QDataStream stream(&encodedData, QIODevice::WriteOnly); + + stream << mFileName << mAuthor << QString::number(mVersion) + << mModified.toString() << mPath << mDescription + << mMasters; + + return encodedData; +} + +void EsxModel::EsmFile::setProperty (const EsmFileProperty prop, const QString &value) +{ + switch (prop) + { + case Property_FileName: + mFileName = value; + break; + + case Property_Author: + mAuthor = value; + break; + + case Property_Version: + mVersion = value.toFloat(); + break; + + case Property_DateModified: + mModified = QDateTime::fromString(value); + break; + + case Property_Path: + mPath = value; + break; + + case Property_Description: + mDescription = value; + break; + + case Property_Master: + mMasters << value; + break; + + default: + break; + } +} diff --git a/components/esxselector/model/esmfile.hpp b/components/esxselector/model/esmfile.hpp index 0cda018b3c..9a1ea8af1f 100644 --- a/components/esxselector/model/esmfile.hpp +++ b/components/esxselector/model/esmfile.hpp @@ -6,8 +6,21 @@ #include "modelitem.hpp" +class QMimeData; + namespace EsxModel { + enum EsmFileProperty + { + Property_FileName = 0, + Property_Author = 1, + Property_Version = 2, + Property_DateModified = 3, + Property_Path = 4, + Property_Description = 5, + Property_Master = 6 + }; + class EsmFile : public ModelItem { Q_OBJECT @@ -21,10 +34,12 @@ namespace EsxModel ~EsmFile() {} + void setProperty (const EsmFileProperty prop, const QString &value); + void setFileName(const QString &fileName); void setAuthor(const QString &author); void setSize(const int size); - void setDates(const QDateTime &modified, const QDateTime &accessed); + void setDate(const QDateTime &modified); void setVersion(const float version); void setPath(const QString &path); void setMasters(const QStringList &masters); @@ -32,22 +47,23 @@ namespace EsxModel inline QString fileName() const { return mFileName; } inline QString author() const { return mAuthor; } - inline int size() const { return mSize; } inline QDateTime modified() const { return mModified; } - inline QDateTime accessed() const { return mAccessed; } inline float version() const { return mVersion; } inline QString path() const { return mPath; } inline QStringList masters() const { return mMasters; } inline QString description() const { return mDescription; } - //inline ModelItem *parent() const { return ModelItem::parent(); this->} + inline bool isMaster() const { return (mMasters.size() == 0); } + QByteArray encodedData() const; + + public: + static int sPropertyCount; private: + QString mFileName; QString mAuthor; - int mSize; QDateTime mModified; - QDateTime mAccessed; float mVersion; QString mPath; QStringList mMasters; diff --git a/components/esxselector/model/masterproxymodel.cpp b/components/esxselector/model/masterproxymodel.cpp index 46d68ca513..df74d03569 100644 --- a/components/esxselector/model/masterproxymodel.cpp +++ b/components/esxselector/model/masterproxymodel.cpp @@ -10,35 +10,4 @@ EsxModel::MasterProxyModel::MasterProxyModel(QObject *parent, QAbstractTableMode if (model) setSourceModel (model); - //connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(slotSourceModelChanged(QModelIndex, QModelIndex))); -} -/* -QVariant EsxModel::MasterProxyModel::data(const QModelIndex &index, int role) const -{ - 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/pluginsproxymodel.cpp b/components/esxselector/model/pluginsproxymodel.cpp index 412367b649..c543672b09 100644 --- a/components/esxselector/model/pluginsproxymodel.cpp +++ b/components/esxselector/model/pluginsproxymodel.cpp @@ -12,29 +12,3 @@ EsxModel::PluginsProxyModel::PluginsProxyModel(QObject *parent, ContentModel *mo if (model) setSourceModel (model); } - -EsxModel::PluginsProxyModel::~PluginsProxyModel() -{ -} - -QVariant EsxModel::PluginsProxyModel::data(const QModelIndex &index, int role) const -{ - switch (role) - { - case Qt::CheckStateRole: - { - if (index.column() != 0) - return QVariant(); - - 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 success; -} diff --git a/components/esxselector/view/contentselector.cpp b/components/esxselector/view/contentselector.cpp index bc7cc2b8bb..1f5e36d65b 100644 --- a/components/esxselector/view/contentselector.cpp +++ b/components/esxselector/view/contentselector.cpp @@ -1,8 +1,6 @@ #include "contentselector.hpp" #include "../model/datafilesmodel.hpp" -#include "../model/masterproxymodel.hpp" -#include "../model/pluginsproxymodel.hpp" #include "../model/contentmodel.hpp" #include "../model/esmfile.hpp" @@ -16,55 +14,73 @@ EsxView::ContentSelector::ContentSelector(QWidget *parent) : QDialog(parent) { setupUi(this); - // buildModelsAndViews(); - buildDragDropModelView(); + + buildSourceModel(); + buildMasterView(); + buildPluginsView(); + buildProfilesView(); + + updateViews(); + } -void EsxView::ContentSelector::buildDragDropModelView() + +void EsxView::ContentSelector::buildSourceModel() { mContentModel = new EsxModel::ContentModel(); + connect(mContentModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); +} - //mContentModel->addFiles("/home/joel/Projects/OpenMW/Data_Files"); - mMasterProxyModel = new EsxModel::MasterProxyModel(this, mContentModel); - mPluginsProxyModel = new EsxModel::PluginsProxyModel(this, mContentModel); - - tableView->setModel (mPluginsProxyModel); +void EsxView::ContentSelector::buildMasterView() +{ + mMasterProxyModel = new QSortFilterProxyModel(this); + mMasterProxyModel->setFilterRegExp(QString("game")); + mMasterProxyModel->setFilterRole (Qt::UserRole); + mMasterProxyModel->setSourceModel (mContentModel); masterView->setPlaceholderText(QString("Select a game file...")); masterView->setModel(mMasterProxyModel); + + connect(masterView, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentMasterIndexChanged(int))); + + masterView->setCurrentIndex(-1); + masterView->setCurrentIndex(0); +} + +void EsxView::ContentSelector::buildPluginsView() +{ + mPluginsProxyModel = new QSortFilterProxyModel(this); + mPluginsProxyModel->setFilterRegExp (QString("addon")); + mPluginsProxyModel->setFilterRole (Qt::UserRole); + mPluginsProxyModel->setDynamicSortFilter (true); + mPluginsProxyModel->setSourceModel (mContentModel); + + tableView->setModel (mPluginsProxyModel); 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() +void EsxView::ContentSelector::buildProfilesView() { - // Models - mDataFilesModel = new EsxModel::DataFilesModel (this); - - // 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))); connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); } +void EsxView::ContentSelector::updateViews() +{ + // Ensure the columns are hidden because sort() re-enables them + pluginView->setColumnHidden(1, true); + pluginView->setColumnHidden(2, true); + pluginView->setColumnHidden(3, true); + pluginView->setColumnHidden(4, true); + pluginView->setColumnHidden(5, true); + pluginView->setColumnHidden(6, true); + pluginView->setColumnHidden(7, true); + pluginView->setColumnHidden(8, true); + pluginView->resizeColumnsToContents(); +} + void EsxView::ContentSelector::addFiles(const QString &path) { mContentModel->addFiles(path); @@ -78,24 +94,6 @@ void EsxView::ContentSelector::setEncoding(const QString &encoding) mContentModel->setEncoding(encoding); } -void EsxView::ContentSelector::setCheckState(QModelIndex index, QSortFilterProxyModel *model) -{ - if (!index.isValid()) - return; - - if (!model) - return; - - QModelIndex sourceIndex = model->mapToSource(index); - - if (sourceIndex.isValid()) - { - (mContentModel->checkState(sourceIndex) == Qt::Checked) - ? mContentModel->setCheckState(sourceIndex, Qt::Unchecked) - : mContentModel->setCheckState(sourceIndex, Qt::Checked); - } -} - QStringList EsxView::ContentSelector::checkedItemsPaths() { QStringList itemPaths; @@ -106,21 +104,6 @@ QStringList EsxView::ContentSelector::checkedItemsPaths() return itemPaths; } -void EsxView::ContentSelector::updateViews() -{ - // Ensure the columns are hidden because sort() re-enables them - pluginView->setColumnHidden(1, true); - pluginView->setColumnHidden(2, true); - pluginView->setColumnHidden(3, true); - pluginView->setColumnHidden(4, true); - pluginView->setColumnHidden(5, true); - pluginView->setColumnHidden(6, true); - pluginView->setColumnHidden(7, true); - pluginView->setColumnHidden(8, true); - pluginView->resizeColumnsToContents(); - -} - void EsxView::ContentSelector::slotCurrentProfileIndexChanged(int index) { emit profileChanged(index); @@ -128,17 +111,40 @@ void EsxView::ContentSelector::slotCurrentProfileIndexChanged(int index) void EsxView::ContentSelector::slotCurrentMasterIndexChanged(int index) { - QObject *object = QObject::sender(); + static int oldIndex = -1; - // Not a signal-slot call - if (!object) - return; + QAbstractItemModel *const model = masterView->model(); + QSortFilterProxyModel *proxy = dynamic_cast(model); - setCheckState(mMasterProxyModel->index(index, 0), mMasterProxyModel); + if (proxy) + proxy->setDynamicSortFilter(false); + + if (oldIndex > -1) + model->setData(model->index(oldIndex, 0), false, Qt::UserRole + 1); + + oldIndex = index; + + model->setData(model->index(index, 0), true, Qt::UserRole + 1); + + if (proxy) + proxy->setDynamicSortFilter(true); } void EsxView::ContentSelector::slotPluginTableItemClicked(const QModelIndex &index) { qDebug() << "setting checkstate in plugin..."; - setCheckState(index, mPluginsProxyModel); + + QAbstractItemModel *const model = pluginView->model(); + QSortFilterProxyModel *proxy = dynamic_cast(model); + + if (proxy) + proxy->setDynamicSortFilter(false); + + if (model->data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked) + model->setData(index, Qt::Checked, Qt::CheckStateRole); + else + model->setData(index, Qt::Unchecked, Qt::CheckStateRole); + + if (proxy) + proxy->setDynamicSortFilter(true); } diff --git a/components/esxselector/view/contentselector.hpp b/components/esxselector/view/contentselector.hpp index 06cc8f3f0b..e074fe6880 100644 --- a/components/esxselector/view/contentselector.hpp +++ b/components/esxselector/view/contentselector.hpp @@ -9,8 +9,6 @@ namespace EsxModel { class ContentModel; class DataFilesModel; - class PluginsProxyModel; - class MasterProxyModel; } class QSortFilterProxyModel; @@ -25,8 +23,8 @@ namespace EsxView EsxModel::DataFilesModel *mDataFilesModel; EsxModel::ContentModel *mContentModel; - EsxModel::MasterProxyModel *mMasterProxyModel; - EsxModel::PluginsProxyModel *mPluginsProxyModel; + QSortFilterProxyModel *mMasterProxyModel; + QSortFilterProxyModel *mPluginsProxyModel; public: explicit ContentSelector(QWidget *parent = 0); @@ -39,7 +37,12 @@ namespace EsxView void setCheckState(QModelIndex index, QSortFilterProxyModel *model); QStringList checkedItemsPaths(); void on_checkAction_triggered(); - void buildDragDropModelView(); + + private: + void buildSourceModel(); + void buildMasterView(); + void buildPluginsView(); + void buildProfilesView(); signals: void profileChanged(int index); From 244e5819529e2be6e4c0a790442d228fc4a133fc Mon Sep 17 00:00:00 2001 From: graffy76 Date: Thu, 19 Sep 2013 06:53:09 -0500 Subject: [PATCH 042/148] Finished implementing drag / drop refactored datafilesmodel (now contentmodel) refactored esmfil --- components/esxselector/model/contentmodel.cpp | 302 ++++++++---------- components/esxselector/model/contentmodel.hpp | 19 +- components/esxselector/model/esmfile.cpp | 67 +++- components/esxselector/model/esmfile.hpp | 50 +-- .../esxselector/view/contentselector.cpp | 6 +- files/ui/datafilespage.ui | 39 +-- 6 files changed, 224 insertions(+), 259 deletions(-) diff --git a/components/esxselector/model/contentmodel.cpp b/components/esxselector/model/contentmodel.cpp index bfbb1bef7a..d23ea78014 100644 --- a/components/esxselector/model/contentmodel.cpp +++ b/components/esxselector/model/contentmodel.cpp @@ -1,17 +1,37 @@ #include "contentmodel.hpp" #include "esmfile.hpp" -#include + #include #include #include +#include EsxModel::ContentModel::ContentModel(QObject *parent) : - QAbstractTableModel(parent), mEncoding("win1252") -{} + QAbstractTableModel(parent), + mMimeType ("application/omwcontent"), + mMimeTypes (QStringList() << mMimeType), + mColumnCount (1), + mDragDropFlags (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled), + mDefaultFlags (Qt::ItemIsDropEnabled | Qt::ItemIsSelectable), + mDropActions (Qt::CopyAction | Qt::MoveAction) +{ + setEncoding ("win1252"); + uncheckAll(); +} void EsxModel::ContentModel::setEncoding(const QString &encoding) { - mEncoding = encoding; + if (encoding == QLatin1String("win1252")) + mCodec = QTextCodec::codecForName("windows-1252"); + + else if (encoding == QLatin1String("win1251")) + mCodec = QTextCodec::codecForName("windows-1251"); + + else if (encoding == QLatin1String("win1250")) + mCodec = QTextCodec::codecForName("windows-1250"); + + else + return; // This should never happen; } int EsxModel::ContentModel::columnCount(const QModelIndex &parent) const @@ -19,7 +39,7 @@ int EsxModel::ContentModel::columnCount(const QModelIndex &parent) const if (parent.isValid()) return 0; - return 1; + return mColumnCount; } int EsxModel::ContentModel::rowCount(const QModelIndex &parent) const @@ -30,22 +50,28 @@ int EsxModel::ContentModel::rowCount(const QModelIndex &parent) const return mFiles.size(); } -EsxModel::EsmFile* EsxModel::ContentModel::item(int row) const +const EsxModel::EsmFile* EsxModel::ContentModel::item(int row) const +{ + if (row >= 0 && row < mFiles.size()) + return mFiles.at(row); + + return 0; +} + +EsxModel::EsmFile *EsxModel::ContentModel::item(int row) { if (row >= 0 && row < mFiles.count()) return mFiles.at(row); return 0; } - -EsxModel::EsmFile* EsxModel::ContentModel::findItem(const QString &name) +const EsxModel::EsmFile *EsxModel::ContentModel::findItem(const QString &name) const { - for (int i = 0; i < mFiles.size(); ++i) + foreach (const EsmFile *file, mFiles) { - if (name == item(i)->fileName()) - return item(i); + if (name == file->fileName()) + return file; } - return 0; } @@ -62,19 +88,15 @@ Qt::ItemFlags EsxModel::ContentModel::flags(const QModelIndex &index) const if (!index.isValid()) return Qt::NoItemFlags; - EsmFile *file = item(index.row()); + const 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; + return Qt::ItemIsEnabled | mDragDropFlags | mDefaultFlags; + + return mDefaultFlags; } QVariant EsxModel::ContentModel::data(const QModelIndex &index, int role) const @@ -85,7 +107,7 @@ QVariant EsxModel::ContentModel::data(const QModelIndex &index, int role) const if (index.row() >= mFiles.size()) return QVariant(); - EsmFile *file = item(index.row()); + const EsmFile *file = item(index.row()); if (!file) return QVariant(); @@ -97,25 +119,11 @@ QVariant EsxModel::ContentModel::data(const QModelIndex &index, int role) const case Qt::EditRole: case Qt::DisplayRole: { - switch (column) - { - case 0: - return file->fileName(); - case 1: - return file->author(); - case 2: - return file->modified().toString(Qt::ISODate); - case 3: - return file->version(); - case 4: - return file->path(); - case 5: - return file->masters().join(", "); - case 6: - return file->description(); - } + if (column >=0 && column <=EsmFile::FileProperty_Master) + return file->fileProperty(static_cast(column)); return QVariant(); + break; } case Qt::TextAlignmentRole: @@ -132,6 +140,7 @@ QVariant EsxModel::ContentModel::data(const QModelIndex &index, int role) const return Qt::AlignLeft + Qt::AlignVCenter; } return QVariant(); + break; } case Qt::ToolTipRole: @@ -139,23 +148,16 @@ QVariant EsxModel::ContentModel::data(const QModelIndex &index, int role) const 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(", ")); + return file->toolTip(); + break; } case Qt::CheckStateRole: + { if (!file->isMaster()) return isChecked(file->fileName()); break; + } case Qt::UserRole: { @@ -179,64 +181,63 @@ bool EsxModel::ContentModel::setData(const QModelIndex &index, const QVariant &v EsmFile *file = item(index.row()); QString fileName = file->fileName(); + bool success = false; switch(role) { case Qt::EditRole: - { - QStringList list = value.toStringList(); + { + QStringList list = value.toStringList(); - //iterate the string list, assigning values to proeprties - //index-enum correspondence 1:1 - for (int i = 0; i < EsxModel::Property_Master; i++) - file->setProperty(static_cast(i), list.at(i)); + for (int i = 0; i < EsmFile::FileProperty_Master; i++) + file->setFileProperty(static_cast(i), list.at(i)); - //iterate the remainder of the string list, assifning everything - // as - for (int i = EsxModel::Property_Master; i < list.size(); i++) - file->setProperty (EsxModel::Property_Master, list.at(i)); + for (int i = EsmFile::FileProperty_Master; i < list.size(); i++) + file->setFileProperty (EsmFile::FileProperty_Master, list.at(i)); - //emit data changed for the item itself - emit dataChanged(index, index); + emit dataChanged(index, index); - return true; - } - break; + success = true; + } + break; case Qt::UserRole+1: + { + setCheckState(fileName, value.toBool()); + + emit dataChanged(index, index); + + foreach (EsmFile *file, mFiles) { - setCheckState(fileName, value.toBool()); - - emit dataChanged(index, index); - - for(int i = 0; i < mFiles.size(); i++) + if (file->masters().contains(fileName)) { - - if (mFiles.at(i)->masters().contains(fileName)) - { - QModelIndex idx = QAbstractTableModel::index(i, 0); - emit dataChanged(idx, idx); - } + QModelIndex idx = indexFromItem(file); + emit dataChanged(idx, idx); } - - return true; } - break; + success = true; + } + break; case Qt::CheckStateRole: - { - bool checked = ((value.toInt() == Qt::Checked) && !isChecked(fileName)); + { + int checkValue = value.toInt(); - setCheckState(fileName, checked); + if ((checkValue==Qt::Checked) && !isChecked(fileName)) + setCheckState(fileName, true); + else if ((checkValue == Qt::Checked) && isChecked (fileName)) + setCheckState(fileName, false); + else if (checkValue == Qt::Unchecked) + setCheckState(fileName, false); - emit dataChanged(index, index); + emit dataChanged(index, index); - return true; - } - break; + success = true; + } + break; } - return false; + return success; } bool EsxModel::ContentModel::insertRows(int position, int rows, const QModelIndex &parent) @@ -271,16 +272,12 @@ bool EsxModel::ContentModel::removeRows(int position, int rows, const QModelInde Qt::DropActions EsxModel::ContentModel::supportedDropActions() const { - return Qt::CopyAction | Qt::MoveAction; + return mDropActions; } QStringList EsxModel::ContentModel::mimeTypes() const { - QStringList types; - - types << "application/omwcontent"; - - return types; + return mMimeTypes; } QMimeData *EsxModel::ContentModel::mimeData(const QModelIndexList &indexes) const @@ -292,14 +289,11 @@ QMimeData *EsxModel::ContentModel::mimeData(const QModelIndexList &indexes) cons if (!index.isValid()) continue; - QByteArray fileData = item(index.row())->encodedData(); - - foreach (const char c, fileData) - encodedData.append(c); + encodedData.append(item(index.row())->encodedData()); } QMimeData *mimeData = new QMimeData(); - mimeData->setData("application/omwcontent", encodedData); + mimeData->setData(mMimeType, encodedData); return mimeData; } @@ -309,13 +303,13 @@ bool EsxModel::ContentModel::dropMimeData(const QMimeData *data, Qt::DropAction if (action == Qt::IgnoreAction) return true; - if (!data->hasFormat("application/omwcontent")) - return false; - if (column > 0) return false; - int beginRow; + if (!data->hasFormat(mMimeType)) + return false; + + int beginRow = rowCount(); if (row != -1) beginRow = row; @@ -323,23 +317,28 @@ bool EsxModel::ContentModel::dropMimeData(const QMimeData *data, Qt::DropAction else if (parent.isValid()) beginRow = parent.row(); - else - beginRow = rowCount(); - - QByteArray encodedData = data->data("application/omwcontent"); + QByteArray encodedData = data->data(mMimeType); QDataStream stream(&encodedData, QIODevice::ReadOnly); while (!stream.atEnd()) { - QStringList values; - for (int i = 0; i < EsmFile::sPropertyCount; ++i) - stream >> values; + QString value; + QStringList values; + QStringList masters; + + for (int i = 0; i < EsmFile::FileProperty_Master; ++i) + { + stream >> value; + values << value; + } + + stream >> masters; insertRows(beginRow, 1); QModelIndex idx = index(beginRow++, 0, QModelIndex()); - setData(idx, values, Qt::EditRole); + setData(idx, QStringList() << values << masters, Qt::EditRole); } return true; @@ -349,37 +348,8 @@ bool EsxModel::ContentModel::canBeChecked(const EsmFile *file) const { //element can be checked if all its dependencies are foreach (const QString &master, file->masters()) - { - {// if the master is not found in checkstates - // or it is not specifically checked, return false - if (!mCheckStates.contains(master)) - return false; - - if (!isChecked(master)) - return false; - } - - bool found = false; - - //iterate each file, if it is not a master and - //does not have a master that is currently checked, - //return false. - foreach(const EsmFile *file, mFiles) - { - QString filename = file->fileName(); - - found = (filename == master); - - if (found) - { - if (!isChecked(filename)) - return false; - } - } - - if (!found) + if (!isChecked(master)) return false; - } return true; } @@ -387,9 +357,8 @@ bool EsxModel::ContentModel::canBeChecked(const EsmFile *file) const void EsxModel::ContentModel::addFile(EsmFile *file) { beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); - { mFiles.append(file); - } endInsertRows(); + endInsertRows(); } void EsxModel::ContentModel::addFiles(const QString &path) @@ -400,45 +369,26 @@ void EsxModel::ContentModel::addFiles(const QString &path) dir.setNameFilters(filters); // Create a decoder for non-latin characters in esx metadata - QTextCodec *codec; + QTextDecoder *decoder = mCodec->makeDecoder(); - 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()) { + 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())); + ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(QString(mCodec->name()).toStdString())); fileReader.setEncoder(&encoder); fileReader.open(dir.absoluteFilePath(path).toStdString()); - std::vector mlist = fileReader.getMasters(); + foreach (const ESM::Header::MasterData &item, fileReader.getMasters()) + file->addMaster(QString::fromStdString(item.name)); - 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->setDate(info.lastModified()); - file->setVersion(0.0f); - file->setPath(info.absoluteFilePath()); - file->setMasters(masters); + file->setAuthor (decoder->toUnicode(fileReader.getAuthor().c_str())); + file->setDate (info.lastModified()); + file->setVersion (fileReader.getFVer()); + file->setPath (info.absoluteFilePath()); file->setDescription(decoder->toUnicode(fileReader.getDesc().c_str())); @@ -459,7 +409,10 @@ void EsxModel::ContentModel::addFiles(const QString &path) bool EsxModel::ContentModel::isChecked(const QString& name) const { - return (mCheckStates[name] == Qt::Checked); + if (mCheckStates.contains(name)) + return (mCheckStates[name] == Qt::Checked); + + return false; } void EsxModel::ContentModel::setCheckState(const QString &name, bool isChecked) @@ -479,12 +432,9 @@ EsxModel::ContentFileList EsxModel::ContentModel::checkedItems() const { ContentFileList list; - for (int i = 0; i < mFiles.size(); ++i) + foreach (EsmFile *file, mFiles) { - EsmFile *file = item(i); - - // Only add the items that are in the checked list and available - if (mCheckStates[file->fileName()] == Qt::Checked && canBeChecked(file)) + if (isChecked(file->fileName())) list << file; } diff --git a/components/esxselector/model/contentmodel.hpp b/components/esxselector/model/contentmodel.hpp index 61c823ab38..6a2dd88ca5 100644 --- a/components/esxselector/model/contentmodel.hpp +++ b/components/esxselector/model/contentmodel.hpp @@ -2,7 +2,7 @@ #define CONTENTMODEL_HPP #include - +#include namespace EsxModel { class EsmFile; @@ -35,7 +35,7 @@ namespace EsxModel void addFiles(const QString &path); QModelIndex indexFromItem(EsmFile *item) const; - EsxModel::EsmFile *findItem(const QString &name); + const EsxModel::EsmFile *findItem(const QString &name) const; bool isChecked(const QString &name) const; void setCheckState(const QString &name, bool isChecked); @@ -45,16 +45,21 @@ namespace EsxModel private: void addFile(EsmFile *file); - EsmFile* item(int row) const; + const EsmFile *item(int row) const; + EsmFile *item(int row); bool canBeChecked(const EsmFile *file) const; ContentFileList mFiles; QHash mCheckStates; - QString mEncoding; + QTextCodec *mCodec; - signals: - - public slots: + public: + QString mMimeType; + QStringList mMimeTypes; + int mColumnCount; + Qt::ItemFlags mDragDropFlags; + Qt::ItemFlags mDefaultFlags; + Qt::DropActions mDropActions; }; } diff --git a/components/esxselector/model/esmfile.cpp b/components/esxselector/model/esmfile.cpp index 0e7f18373a..bd76dc4a00 100644 --- a/components/esxselector/model/esmfile.cpp +++ b/components/esxselector/model/esmfile.cpp @@ -4,19 +4,15 @@ #include int EsxModel::EsmFile::sPropertyCount = 7; +QString EsxModel::EsmFile::sToolTip = QString("Author: %1
\ + Version: %2
\ +
Description:
%3
\ +
Dependencies: %4
"); + EsxModel::EsmFile::EsmFile(QString fileName, ModelItem *parent) : ModelItem(parent), mFileName(fileName), 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; @@ -64,35 +60,72 @@ QByteArray EsxModel::EsmFile::encodedData() const return encodedData; } -void EsxModel::EsmFile::setProperty (const EsmFileProperty prop, const QString &value) +QVariant EsxModel::EsmFile::fileProperty(const FileProperty prop) const { switch (prop) { - case Property_FileName: + case FileProperty_FileName: + return mFileName; + break; + + case FileProperty_Author: + return mAuthor; + break; + + case FileProperty_Version: + return mVersion; + break; + + case FileProperty_DateModified: + return mModified.toString(Qt::ISODate); + break; + + case FileProperty_Path: + return mPath; + break; + + case FileProperty_Description: + return mDescription; + break; + + case FileProperty_Master: + return mMasters; + break; + + default: + break; + } + return QVariant(); +} +void EsxModel::EsmFile::setFileProperty (const FileProperty prop, const QString &value) +{ + switch (prop) + { + case FileProperty_FileName: mFileName = value; break; - case Property_Author: + case FileProperty_Author: mAuthor = value; break; - case Property_Version: + case FileProperty_Version: mVersion = value.toFloat(); break; - case Property_DateModified: + case FileProperty_DateModified: mModified = QDateTime::fromString(value); break; - case Property_Path: + case FileProperty_Path: mPath = value; break; - case Property_Description: + case FileProperty_Description: mDescription = value; break; - case Property_Master: + case FileProperty_Master: mMasters << value; break; diff --git a/components/esxselector/model/esmfile.hpp b/components/esxselector/model/esmfile.hpp index 9a1ea8af1f..4f6d7a6244 100644 --- a/components/esxselector/model/esmfile.hpp +++ b/components/esxselector/model/esmfile.hpp @@ -10,17 +10,6 @@ class QMimeData; namespace EsxModel { - enum EsmFileProperty - { - Property_FileName = 0, - Property_Author = 1, - Property_Version = 2, - Property_DateModified = 3, - Property_Path = 4, - Property_Description = 5, - Property_Master = 6 - }; - class EsmFile : public ModelItem { Q_OBJECT @@ -28,13 +17,24 @@ namespace EsxModel public: + enum FileProperty + { + FileProperty_FileName = 0, + FileProperty_Author = 1, + FileProperty_Version = 2, + FileProperty_DateModified = 3, + FileProperty_Path = 4, + FileProperty_Description = 5, + FileProperty_Master = 6 + }; + EsmFile(QString fileName = QString(), ModelItem *parent = 0); // EsmFile(const EsmFile &); ~EsmFile() {} - void setProperty (const EsmFileProperty prop, const QString &value); + void setFileProperty (const FileProperty prop, const QString &value); void setFileName(const QString &fileName); void setAuthor(const QString &author); @@ -45,19 +45,28 @@ namespace EsxModel void setMasters(const QStringList &masters); void setDescription(const QString &description); - inline QString fileName() const { return mFileName; } - inline QString author() const { return mAuthor; } - inline QDateTime modified() const { return mModified; } - inline float version() const { return mVersion; } - inline QString path() const { return mPath; } - inline QStringList masters() const { return mMasters; } - inline QString description() const { return mDescription; } + inline void addMaster (const QString &name) {mMasters.append(name); } + QVariant fileProperty (const FileProperty prop) const; - inline bool isMaster() const { return (mMasters.size() == 0); } + inline QString fileName() const { return mFileName; } + inline QString author() const { return mAuthor; } + inline QDateTime modified() const { return mModified; } + inline float version() const { return mVersion; } + inline QString path() const { return mPath; } + inline const QStringList &masters() const { return mMasters; } + inline QString description() const { return mDescription; } + inline QString toolTip() const { return sToolTip.arg(mAuthor) + .arg(mVersion) + .arg(mDescription) + .arg(mMasters.join(", ")); + } + + inline bool isMaster() const { return (mMasters.size() == 0); } QByteArray encodedData() const; public: static int sPropertyCount; + static QString sToolTip; private: @@ -68,6 +77,7 @@ namespace EsxModel QString mPath; QStringList mMasters; QString mDescription; + QString mToolTip; }; } diff --git a/components/esxselector/view/contentselector.cpp b/components/esxselector/view/contentselector.cpp index 1f5e36d65b..4f8ac7253a 100644 --- a/components/esxselector/view/contentselector.cpp +++ b/components/esxselector/view/contentselector.cpp @@ -54,11 +54,9 @@ void EsxView::ContentSelector::buildPluginsView() mPluginsProxyModel->setDynamicSortFilter (true); mPluginsProxyModel->setSourceModel (mContentModel); - tableView->setModel (mPluginsProxyModel); pluginView->setModel(mPluginsProxyModel); connect(pluginView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotPluginTableItemClicked(const QModelIndex &))); - connect(tableView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotPluginTableItemClicked(const QModelIndex &))); } void EsxView::ContentSelector::buildProfilesView() @@ -120,10 +118,12 @@ void EsxView::ContentSelector::slotCurrentMasterIndexChanged(int index) proxy->setDynamicSortFilter(false); if (oldIndex > -1) + qDebug() << "clearing old master check state"; model->setData(model->index(oldIndex, 0), false, Qt::UserRole + 1); oldIndex = index; + qDebug() << "setting new master check state"; model->setData(model->index(index, 0), true, Qt::UserRole + 1); if (proxy) @@ -132,8 +132,6 @@ void EsxView::ContentSelector::slotCurrentMasterIndexChanged(int index) void EsxView::ContentSelector::slotPluginTableItemClicked(const QModelIndex &index) { - qDebug() << "setting checkstate in plugin..."; - QAbstractItemModel *const model = pluginView->model(); QSortFilterProxyModel *proxy = dynamic_cast(model); diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 76689627b0..5b0a6d2292 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -7,7 +7,7 @@ 0 0 518 - 310 + 313 @@ -44,6 +44,9 @@ Qt::DefaultContextMenu + + true + QAbstractItemView::NoEditTriggers @@ -85,40 +88,6 @@ - - - - QAbstractItemView::NoEditTriggers - - - true - - - false - - - QAbstractItemView::DragDrop - - - Qt::MoveAction - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - false - - - false - - - false - - - From cfdc19c4272804b9ed682704e8fa30d31a85a062 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sat, 21 Sep 2013 23:06:29 -0500 Subject: [PATCH 043/148] Renamed esxSelector to contentSelector Fixed datafilespage model implementation in launcher Filtered addons in table view by selected game file --- apps/esmtool/esmtool.cpp | 4 +- apps/launcher/datafilespage.cpp | 195 ++++-- apps/launcher/datafilespage.hpp | 32 +- apps/launcher/graphicspage.cpp | 2 +- apps/launcher/utils/textinputdialog.cpp | 4 +- apps/launcher/utils/textinputdialog.hpp | 7 +- apps/opencs/editor.cpp | 4 +- apps/opencs/main.cpp | 2 +- apps/opencs/view/doc/filedialog.cpp | 12 +- apps/opencs/view/doc/filedialog.hpp | 6 +- apps/openmw/mwworld/esmstore.cpp | 2 +- components/CMakeLists.txt | 7 +- .../model/contentmodel.cpp | 137 ++-- .../model/contentmodel.hpp | 15 +- .../model/esmfile.cpp | 53 +- .../model/esmfile.hpp | 42 +- .../contentselector/model/modelitem.cpp | 69 ++ .../model/modelitem.hpp | 2 +- .../model/naturalsort.cpp | 0 .../model/naturalsort.hpp | 0 .../view/comboboxlineedit.cpp | 6 +- .../view/comboboxlineedit.hpp | 2 +- .../contentselector/view/contentselector.cpp | 134 ++++ .../view/contentselector.hpp | 32 +- .../view/lineedit.cpp | 6 +- .../view/lineedit.hpp | 2 +- .../view/profilescombobox.cpp | 14 +- .../view/profilescombobox.hpp | 2 +- components/esm/esmreader.hpp | 2 +- components/esm/loadcell.cpp | 4 +- .../esxselector/model/datafilesmodel.cpp | 608 ------------------ .../esxselector/model/datafilesmodel.hpp | 80 --- .../esxselector/model/masterproxymodel.cpp | 13 - .../esxselector/model/masterproxymodel.hpp | 25 - components/esxselector/model/modelitem.cpp | 69 -- .../esxselector/model/pluginsproxymodel.cpp | 14 - .../esxselector/model/pluginsproxymodel.hpp | 28 - components/esxselector/model/sourcemodel.cpp | 6 - components/esxselector/model/sourcemodel.h | 18 - .../esxselector/view/contentselector.cpp | 148 ----- files/ui/datafilespage.ui | 12 +- 41 files changed, 585 insertions(+), 1235 deletions(-) rename components/{esxselector => contentselector}/model/contentmodel.cpp (64%) rename components/{esxselector => contentselector}/model/contentmodel.hpp (86%) rename components/{esxselector => contentselector}/model/esmfile.cpp (54%) rename components/{esxselector => contentselector}/model/esmfile.hpp (58%) create mode 100644 components/contentselector/model/modelitem.cpp rename components/{esxselector => contentselector}/model/modelitem.hpp (96%) rename components/{esxselector => contentselector}/model/naturalsort.cpp (100%) rename components/{esxselector => contentselector}/model/naturalsort.hpp (100%) rename components/{esxselector => contentselector}/view/comboboxlineedit.cpp (83%) rename components/{esxselector => contentselector}/view/comboboxlineedit.hpp (96%) create mode 100644 components/contentselector/view/contentselector.cpp rename components/{esxselector => contentselector}/view/contentselector.hpp (51%) rename components/{esxselector => contentselector}/view/lineedit.cpp (87%) rename components/{esxselector => contentselector}/view/lineedit.hpp (96%) rename components/{esxselector => contentselector}/view/profilescombobox.cpp (83%) rename components/{esxselector => contentselector}/view/profilescombobox.hpp (96%) delete mode 100644 components/esxselector/model/datafilesmodel.cpp delete mode 100644 components/esxselector/model/datafilesmodel.hpp delete mode 100644 components/esxselector/model/masterproxymodel.cpp delete mode 100644 components/esxselector/model/masterproxymodel.hpp delete mode 100644 components/esxselector/model/modelitem.cpp delete mode 100644 components/esxselector/model/pluginsproxymodel.cpp delete mode 100644 components/esxselector/model/pluginsproxymodel.hpp delete mode 100644 components/esxselector/model/sourcemodel.cpp delete mode 100644 components/esxselector/model/sourcemodel.h delete mode 100644 components/esxselector/view/contentselector.cpp diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index a60e9f0e20..6ccf9c3f3b 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -305,14 +305,14 @@ int load(Arguments& info) info.data.author = esm.getAuthor(); info.data.description = esm.getDesc(); - info.data.masters = esm.getMasters(); + info.data.masters = esm.getGameFiles(); if (!quiet) { std::cout << "Author: " << esm.getAuthor() << std::endl << "Description: " << esm.getDesc() << std::endl << "File format version: " << esm.getFVer() << std::endl; - std::vector m = esm.getMasters(); + std::vector m = esm.getGameFiles(); if (!m.empty()) { std::cout << "Masters:" << std::endl; diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 7069737eff..1526b705ae 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -4,22 +4,22 @@ #include #include #include +#include #include -#include -#include +#include -#include -#include -#include +#include +#include +#include -#include "components/esxselector/model/masterproxymodel.hpp" #include "settings/gamesettings.hpp" #include "settings/launchersettings.hpp" #include "utils/textinputdialog.hpp" -#include "components/esxselector/view/contentselector.hpp" +#include "components/contentselector/view/contentselector.hpp" +#include "components/contentselector/model/contentmodel.hpp" #include @@ -28,8 +28,10 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam , mGameSettings(gameSettings) , mLauncherSettings(launcherSettings) { - mContentSelector.setParent(parent); - QMetaObject::connectSlotsByName(this); + setupUi(this); + // mContentSelector.setParent(parent); + + // QMetaObject::connectSlotsByName(this); projectGroupBox->hide(); @@ -38,8 +40,82 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam connect(mNewProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString))); + + + buildContentModel(); + buildGameFileView(); + buildAddonView(); + buildProfilesView(); + + createActions(); setupDataFiles(); + + + updateViews(); +} + +void DataFilesPage::buildContentModel() +{ + mContentModel = new ContentSelectorModel::ContentModel(); + connect(mContentModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); +} + +void DataFilesPage::buildGameFileView() +{ + mGameFileProxyModel = new QSortFilterProxyModel(this); + mGameFileProxyModel->setFilterRegExp(QString::number((int)ContentSelectorModel::ContentType_GameFile)); + mGameFileProxyModel->setFilterRole (Qt::UserRole); + mGameFileProxyModel->setSourceModel (mContentModel); + + gameFileView->setPlaceholderText(QString("Select a game file...")); + gameFileView->setModel(mGameFileProxyModel); + + connect(gameFileView, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentGameFileIndexChanged(int))); + + gameFileView->setCurrentIndex(-1); + gameFileView->setCurrentIndex(0); +} + +void DataFilesPage::buildAddonView() +{ + mAddonProxyModel = new QSortFilterProxyModel(this); + mAddonProxyModel->setFilterRegExp (QString::number((int)ContentSelectorModel::ContentType_Addon)); + mAddonProxyModel->setFilterRole (Qt::UserRole); + mAddonProxyModel->setDynamicSortFilter (true); + mAddonProxyModel->setSourceModel (mContentModel); + + addonView->setModel(mAddonProxyModel); + + connect(addonView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotAddonTableItemClicked(const QModelIndex &))); +} + +void DataFilesPage::buildProfilesView() +{ + profilesComboBox->setPlaceholderText(QString("Select a profile...")); + connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); +} + +void DataFilesPage::updateViews() +{ + // Ensure the columns are hidden because sort() re-enables them + addonView->setColumnHidden(1, true); + addonView->setColumnHidden(2, true); + addonView->setColumnHidden(3, true); + addonView->setColumnHidden(4, true); + addonView->setColumnHidden(5, true); + addonView->setColumnHidden(6, true); + addonView->setColumnHidden(7, true); + addonView->setColumnHidden(8, true); + addonView->resizeColumnsToContents(); +} + +void ContentSelectorView::ContentSelector::addFiles(const QString &path) +{ + mContentModel->addFiles(path); + //mContentModel->sort(3); // Sort by date accessed + gameFileView->setCurrentIndex(-1); + mContentModel->uncheckAll(); } void DataFilesPage::createActions() @@ -52,17 +128,19 @@ void DataFilesPage::createActions() void DataFilesPage::setupDataFiles() { // Set the encoding to the one found in openmw.cfg or the default - mContentSelector.setEncoding(mGameSettings.value(QString("encoding"), QString("win1252"))); + //mContentSelector.setEncoding(mGameSettings.value(QString("encoding"), QString("win1252"))); QStringList paths = mGameSettings.getDataDirs(); foreach (const QString &path, paths) { - mContentSelector.addFiles(path); + //mContentSelector. + mContentModel->addFiles(path); } QString dataLocal = mGameSettings.getDataLocal(); if (!dataLocal.isEmpty()) - mContentSelector.addFiles(dataLocal); + //mContentSelector. + mContentModel->addFiles(dataLocal); // Sort by date accessed for now //mContentSelector->sort(3); @@ -95,6 +173,7 @@ void DataFilesPage::setupDataFiles() loadSettings(); + gameFileView->setCurrentIndex(-1); } void DataFilesPage::loadSettings() @@ -104,29 +183,17 @@ void DataFilesPage::loadSettings() if (profile.isEmpty()) return; - // mContentSelector.uncheckAll(); + // mContentSelector. + mContentModel->uncheckAll(); - QStringList masters = mLauncherSettings.values(QString("Profiles/") + profile + QString("/master"), Qt::MatchExactly); - QStringList plugins = mLauncherSettings.values(QString("Profiles/") + profile + QString("/plugin"), Qt::MatchExactly); -/* - foreach (const QString &master, masters) { - QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(master)); - if (index.isValid()) - mDataFilesModel->setCheckState(index, Qt::Checked); - } - - foreach (const QString &plugin, plugins) { - QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(plugin)); - if (index.isValid()) - mDataFilesModel->setCheckState(index, Qt::Checked); - } - */ + QStringList gameFiles = mLauncherSettings.values(QString("Profiles/") + profile + QString("/master"), Qt::MatchExactly); + QStringList addons = mLauncherSettings.values(QString("Profiles/") + profile + QString("/plugin"), Qt::MatchExactly); } void DataFilesPage::saveSettings() { -// if (mDataFilesModel->rowCount() < 1) -// return; + if (mContentModel->rowCount() < 1) + return; QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); @@ -141,11 +208,11 @@ void DataFilesPage::saveSettings() mGameSettings.remove(QString("master")); mGameSettings.remove(QString("plugin")); - // EsxModel::EsmFileList items = mDataFilesModel->checkedItems(); -/* - foreach(const EsxModel::EsmFile *item, items) { + ContentSelectorModel::ContentFileList items = mContentModel->checkedItems(); - if (item->masters().size() == 0) { + foreach(const ContentSelectorModel::EsmFile *item, items) { + + if (item->gameFiles().size() == 0) { mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/master"), item->fileName()); mGameSettings.setMultiValue(QString("master"), item->fileName()); @@ -154,7 +221,7 @@ void DataFilesPage::saveSettings() mGameSettings.setMultiValue(QString("plugin"), item->fileName()); } } -*/ + } void DataFilesPage::updateOkButton(const QString &text) @@ -223,23 +290,25 @@ void DataFilesPage::on_deleteProfileAction_triggered() void DataFilesPage::setPluginsCheckstates(Qt::CheckState state) { - if (!pluginView->selectionModel()->hasSelection()) { + if (!addonView->selectionModel()->hasSelection()) { return; } - QModelIndexList indexes = pluginView->selectionModel()->selectedIndexes(); + QModelIndexList indexes = addonView->selectionModel()->selectedIndexes(); foreach (const QModelIndex &index, indexes) { if (!index.isValid()) return; - QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(index); + QModelIndex sourceIndex = mAddonProxyModel->mapToSource(index); if (!sourceIndex.isValid()) return; - //mDataFilesModel->setCheckState(sourceIndex, state); + bool isChecked = ( state == Qt::Checked ); + + mContentModel->setData(sourceIndex, state, Qt::CheckStateRole); } } @@ -287,3 +356,51 @@ void DataFilesPage::profileRenamed(const QString &previous, const QString &curre loadSettings(); } +//////////////////////////// + +QStringList DataFilesPage::checkedItemsPaths() +{ + QStringList itemPaths; + + foreach( const ContentSelectorModel::EsmFile *file, mContentModel->checkedItems()) + itemPaths << file->path(); + + return itemPaths; +} + +void DataFilesPage::slotCurrentProfileIndexChanged(int index) +{ + emit profileChanged(index); +} + +void DataFilesPage::slotCurrentGameFileIndexChanged(int index) +{ + static int oldIndex = -1; + + QAbstractItemModel *const model = gameFileView->model(); + QSortFilterProxyModel *proxy = dynamic_cast(model); + + if (proxy) + proxy->setDynamicSortFilter(false); + + if (oldIndex > -1) + model->setData(model->index(oldIndex, 0), false, Qt::UserRole + 1); + + oldIndex = index; + + model->setData(model->index(index, 0), true, Qt::UserRole + 1); + + if (proxy) + proxy->setDynamicSortFilter(true); +} + +void DataFilesPage::slotAddonTableItemClicked(const QModelIndex &index) +{ + QAbstractItemModel *const model = addonView->model(); + QSortFilterProxyModel *proxy = dynamic_cast(model); + + if (model->data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked) + model->setData(index, Qt::Checked, Qt::CheckStateRole); + else + model->setData(index, Qt::Unchecked, Qt::CheckStateRole); +} diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index ed92da749d..9c7b0538ed 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -5,24 +5,24 @@ #include #include "ui_datafilespage.h" -#include "components/esxselector/view/contentselector.hpp" +#include "components/contentselector/view/contentselector.hpp" class QSortFilterProxyModel; class QAbstractItemModel; class QAction; class QMenu; -class DataFilesModel; class TextInputDialog; class GameSettings; class LauncherSettings; -class PluginsProxyModel; + namespace Files { struct ConfigurationManager; } -class DataFilesPage : public EsxView::ContentSelector +class DataFilesPage : public QWidget, private Ui::DataFilesPage { Q_OBJECT + public: DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent = 0); @@ -42,7 +42,7 @@ public slots: void profileChanged(const QString &previous, const QString ¤t); void profileRenamed(const QString &previous, const QString ¤t); void updateOkButton(const QString &text); - + void updateViews(); // Action slots void on_newProfileAction_triggered(); void on_deleteProfileAction_triggered(); @@ -52,14 +52,16 @@ private slots: private: QMenu *mContextMenu; - ContentSelector mContentSelector; - + //ContentSelectorView::ContentSelector mContentSelector; + ContentSelectorModel::ContentModel *mContentModel; Files::ConfigurationManager &mCfgMgr; GameSettings &mGameSettings; LauncherSettings &mLauncherSettings; TextInputDialog *mNewProfileDialog; + QSortFilterProxyModel *mGameFileProxyModel; + QSortFilterProxyModel *mAddonProxyModel; void setPluginsCheckstates(Qt::CheckState state); @@ -70,6 +72,22 @@ private: void loadSettings(); + ////////////////////////////////////// + void buildContentModel(); + void buildGameFileView(); + void buildAddonView(); + void buildProfilesView(); + + //void addFiles(const QString &path); + + QStringList checkedItemsPaths(); + +private slots: + void slotCurrentProfileIndexChanged(int index); + void slotCurrentGameFileIndexChanged(int index); + void slotAddonTableItemClicked(const QModelIndex &index); + + }; #endif diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index e71fc429fe..4d9ce14d6d 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include "settings/graphicssettings.hpp" diff --git a/apps/launcher/utils/textinputdialog.cpp b/apps/launcher/utils/textinputdialog.cpp index 052fc58e40..51928c09a7 100644 --- a/apps/launcher/utils/textinputdialog.cpp +++ b/apps/launcher/utils/textinputdialog.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) : QDialog(parent) @@ -19,7 +19,7 @@ TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWid // Line edit QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore - mLineEdit = new EsxView::LineEdit(this); + mLineEdit = new ContentSelectorView::LineEdit(this); mLineEdit->setValidator(validator); mLineEdit->setCompleter(0); diff --git a/apps/launcher/utils/textinputdialog.hpp b/apps/launcher/utils/textinputdialog.hpp index 2fb6e0f6b4..de3a9fb723 100644 --- a/apps/launcher/utils/textinputdialog.hpp +++ b/apps/launcher/utils/textinputdialog.hpp @@ -2,11 +2,10 @@ #define TEXTINPUTDIALOG_HPP #include -//#include "lineedit.hpp" class QDialogButtonBox; -namespace EsxView { +namespace ContentSelectorView { class LineEdit; } @@ -16,10 +15,10 @@ class TextInputDialog : public QDialog Q_OBJECT public: explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0); - inline EsxView::LineEdit *lineEdit() { return mLineEdit; } + inline ContentSelectorView::LineEdit *lineEdit() { return mLineEdit; } void setOkButtonEnabled(bool enabled); - EsxView::LineEdit *mLineEdit; + ContentSelectorView::LineEdit *mLineEdit; int exec(); diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 9a6832ec00..ba1dfb57ec 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -79,8 +79,8 @@ void CS::Editor::setupDataFiles() } // Set the charset for reading the esm/esp files - QString encoding = QString::fromStdString(variables["encoding"].as()); - mFileDialog.setEncoding(encoding); + // QString encoding = QString::fromStdString(variables["encoding"].as()); + //mFileDialog.setEncoding(encoding); dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index ef7123c204..e5e7514ce0 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -42,7 +42,7 @@ int main(int argc, char *argv[]) if(!editor.makeIPCServer()) { editor.connectToIPCServer(); - return 0; + // return 0; } return editor.run(); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index fb031fe5f5..1d6bed7a7c 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -10,24 +10,18 @@ #include #include -#include -#include -#include - -#include - -#include "components/esxselector/model/masterproxymodel.hpp" +#include +#include CSVDoc::FileDialog::FileDialog(QWidget *parent) : ContentSelector(parent) { // Hide the profile elements profileGroupBox->hide(); - pluginView->showColumn(2); + addonView->showColumn(2); resize(400, 400); - // 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/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index c749099d48..d0c3461b9f 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -4,7 +4,7 @@ #include #include -#include "components/esxselector/view/contentselector.hpp" +#include "components/contentselector/view/contentselector.hpp" #include "ui_datafilespage.h" class QDialogButtonBox; @@ -19,14 +19,14 @@ class QLabel; class DataFilesModel; class PluginsProxyModel; -namespace EsxView +namespace ContentSelectorView { class LineEdit; } namespace CSVDoc { - class FileDialog : public EsxView::ContentSelector + class FileDialog : public ContentSelectorView::ContentSelector { Q_OBJECT public: diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 7703f2d233..963316070a 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -36,7 +36,7 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) // all files/readers used by the engine. This will greaty accelerate // refnumber mangling, as required for handling moved references. int index = ~0; - const std::vector &masters = esm.getMasters(); + const std::vector &masters = esm.getGameFiles(); std::vector *allPlugins = esm.getGlobalReaderList(); for (size_t j = 0; j < masters.size(); j++) { ESM::Header::MasterData &mast = const_cast(masters[j]); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 34ef43ab5c..7053bb973b 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -79,10 +79,9 @@ set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui find_package(Qt4 COMPONENTS QtCore QtGui) if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) - add_component_qt_dir (esxselector - model/masterproxymodel model/modelitem - model/pluginsproxymodel model/esmfile model/naturalsort - model/contentmodel + add_component_qt_dir (contentselector + model/modelitem model/esmfile + model/naturalsort model/contentmodel view/profilescombobox view/comboboxlineedit view/lineedit view/contentselector ) diff --git a/components/esxselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp similarity index 64% rename from components/esxselector/model/contentmodel.cpp rename to components/contentselector/model/contentmodel.cpp index d23ea78014..b85da25c67 100644 --- a/components/esxselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -6,7 +6,7 @@ #include #include -EsxModel::ContentModel::ContentModel(QObject *parent) : +ContentSelectorModel::ContentModel::ContentModel(QObject *parent) : QAbstractTableModel(parent), mMimeType ("application/omwcontent"), mMimeTypes (QStringList() << mMimeType), @@ -15,11 +15,11 @@ EsxModel::ContentModel::ContentModel(QObject *parent) : mDefaultFlags (Qt::ItemIsDropEnabled | Qt::ItemIsSelectable), mDropActions (Qt::CopyAction | Qt::MoveAction) { - setEncoding ("win1252"); + // setEncoding ("win1252"); uncheckAll(); } - -void EsxModel::ContentModel::setEncoding(const QString &encoding) +/* +void ContentSelectorModel::ContentModel::setEncoding(const QString &encoding) { if (encoding == QLatin1String("win1252")) mCodec = QTextCodec::codecForName("windows-1252"); @@ -33,8 +33,8 @@ void EsxModel::ContentModel::setEncoding(const QString &encoding) else return; // This should never happen; } - -int EsxModel::ContentModel::columnCount(const QModelIndex &parent) const +*/ +int ContentSelectorModel::ContentModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; @@ -42,7 +42,7 @@ int EsxModel::ContentModel::columnCount(const QModelIndex &parent) const return mColumnCount; } -int EsxModel::ContentModel::rowCount(const QModelIndex &parent) const +int ContentSelectorModel::ContentModel::rowCount(const QModelIndex &parent) const { if(parent.isValid()) return 0; @@ -50,7 +50,7 @@ int EsxModel::ContentModel::rowCount(const QModelIndex &parent) const return mFiles.size(); } -const EsxModel::EsmFile* EsxModel::ContentModel::item(int row) const +const ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(int row) const { if (row >= 0 && row < mFiles.size()) return mFiles.at(row); @@ -58,14 +58,14 @@ const EsxModel::EsmFile* EsxModel::ContentModel::item(int row) const return 0; } -EsxModel::EsmFile *EsxModel::ContentModel::item(int row) +ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(int row) { if (row >= 0 && row < mFiles.count()) return mFiles.at(row); return 0; } -const EsxModel::EsmFile *EsxModel::ContentModel::findItem(const QString &name) const +const ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::findItem(const QString &name) const { foreach (const EsmFile *file, mFiles) { @@ -75,15 +75,18 @@ const EsxModel::EsmFile *EsxModel::ContentModel::findItem(const QString &name) c return 0; } -QModelIndex EsxModel::ContentModel::indexFromItem(EsmFile *item) const +QModelIndex ContentSelectorModel::ContentModel::indexFromItem(const EsmFile *item) const { + //workaround: non-const pointer cast for calls from outside contentmodel/contentselector + EsmFile *non_const_file_ptr = const_cast(item); + if (item) - return index(mFiles.indexOf(item),0); + return index(mFiles.indexOf(non_const_file_ptr),0); return QModelIndex(); } -Qt::ItemFlags EsxModel::ContentModel::flags(const QModelIndex &index) const +Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index) const { if (!index.isValid()) return Qt::NoItemFlags; @@ -99,7 +102,7 @@ Qt::ItemFlags EsxModel::ContentModel::flags(const QModelIndex &index) const return mDefaultFlags; } -QVariant EsxModel::ContentModel::data(const QModelIndex &index, int role) const +QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); @@ -119,7 +122,7 @@ QVariant EsxModel::ContentModel::data(const QModelIndex &index, int role) const case Qt::EditRole: case Qt::DisplayRole: { - if (column >=0 && column <=EsmFile::FileProperty_Master) + if (column >=0 && column <=EsmFile::FileProperty_GameFile) return file->fileProperty(static_cast(column)); return QVariant(); @@ -154,17 +157,20 @@ QVariant EsxModel::ContentModel::data(const QModelIndex &index, int role) const case Qt::CheckStateRole: { - if (!file->isMaster()) + if (!file->isGameFile()) return isChecked(file->fileName()); break; } case Qt::UserRole: { - if (file->isMaster()) - return "game"; + if (file->isGameFile()) + return ContentType_GameFile; else - return "addon"; + if (flags(index) & Qt::ItemIsEnabled) + return ContentType_Addon; + + break; } case Qt::UserRole + 1: @@ -174,7 +180,7 @@ QVariant EsxModel::ContentModel::data(const QModelIndex &index, int role) const return QVariant(); } -bool EsxModel::ContentModel::setData(const QModelIndex &index, const QVariant &value, int role) +bool ContentSelectorModel::ContentModel::setData(const QModelIndex &index, const QVariant &value, int role) { if(!index.isValid()) return false; @@ -189,11 +195,11 @@ bool EsxModel::ContentModel::setData(const QModelIndex &index, const QVariant &v { QStringList list = value.toStringList(); - for (int i = 0; i < EsmFile::FileProperty_Master; i++) + for (int i = 0; i < EsmFile::FileProperty_GameFile; i++) file->setFileProperty(static_cast(i), list.at(i)); - for (int i = EsmFile::FileProperty_Master; i < list.size(); i++) - file->setFileProperty (EsmFile::FileProperty_Master, list.at(i)); + for (int i = EsmFile::FileProperty_GameFile; i < list.size(); i++) + file->setFileProperty (EsmFile::FileProperty_GameFile, list.at(i)); emit dataChanged(index, index); @@ -209,7 +215,7 @@ bool EsxModel::ContentModel::setData(const QModelIndex &index, const QVariant &v foreach (EsmFile *file, mFiles) { - if (file->masters().contains(fileName)) + if (file->gameFiles().contains(fileName)) { QModelIndex idx = indexFromItem(file); emit dataChanged(idx, idx); @@ -222,15 +228,36 @@ bool EsxModel::ContentModel::setData(const QModelIndex &index, const QVariant &v case Qt::CheckStateRole: { int checkValue = value.toInt(); - + bool success = false; + bool setState = false; if ((checkValue==Qt::Checked) && !isChecked(fileName)) - setCheckState(fileName, true); + { + setState = true; + success = true; + } else if ((checkValue == Qt::Checked) && isChecked (fileName)) - setCheckState(fileName, false); + setState = true; else if (checkValue == Qt::Unchecked) - setCheckState(fileName, false); + setState = true; - emit dataChanged(index, index); + if (setState) + { + setCheckState(fileName, success); + emit dataChanged(index, index); + + } + else + return success; + + + foreach (EsmFile *file, mFiles) + { + if (file->gameFiles().contains(fileName)) + { + QModelIndex idx = indexFromItem(file); + emit dataChanged(idx, idx); + } + } success = true; } @@ -240,7 +267,7 @@ bool EsxModel::ContentModel::setData(const QModelIndex &index, const QVariant &v return success; } -bool EsxModel::ContentModel::insertRows(int position, int rows, const QModelIndex &parent) +bool ContentSelectorModel::ContentModel::insertRows(int position, int rows, const QModelIndex &parent) { if (parent.isValid()) return false; @@ -255,7 +282,7 @@ bool EsxModel::ContentModel::insertRows(int position, int rows, const QModelInde return true; } -bool EsxModel::ContentModel::removeRows(int position, int rows, const QModelIndex &parent) +bool ContentSelectorModel::ContentModel::removeRows(int position, int rows, const QModelIndex &parent) { if (parent.isValid()) return false; @@ -270,17 +297,17 @@ bool EsxModel::ContentModel::removeRows(int position, int rows, const QModelInde return true; } -Qt::DropActions EsxModel::ContentModel::supportedDropActions() const +Qt::DropActions ContentSelectorModel::ContentModel::supportedDropActions() const { return mDropActions; } -QStringList EsxModel::ContentModel::mimeTypes() const +QStringList ContentSelectorModel::ContentModel::mimeTypes() const { return mMimeTypes; } -QMimeData *EsxModel::ContentModel::mimeData(const QModelIndexList &indexes) const +QMimeData *ContentSelectorModel::ContentModel::mimeData(const QModelIndexList &indexes) const { QByteArray encodedData; @@ -298,7 +325,7 @@ QMimeData *EsxModel::ContentModel::mimeData(const QModelIndexList &indexes) cons return mimeData; } -bool EsxModel::ContentModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) +bool ContentSelectorModel::ContentModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (action == Qt::IgnoreAction) return true; @@ -325,51 +352,53 @@ bool EsxModel::ContentModel::dropMimeData(const QMimeData *data, Qt::DropAction QString value; QStringList values; - QStringList masters; + QStringList gamefiles; - for (int i = 0; i < EsmFile::FileProperty_Master; ++i) + for (int i = 0; i < EsmFile::FileProperty_GameFile; ++i) { stream >> value; values << value; } - stream >> masters; + stream >> gamefiles; insertRows(beginRow, 1); QModelIndex idx = index(beginRow++, 0, QModelIndex()); - setData(idx, QStringList() << values << masters, Qt::EditRole); + setData(idx, QStringList() << values << gamefiles, Qt::EditRole); } return true; } -bool EsxModel::ContentModel::canBeChecked(const EsmFile *file) const +bool ContentSelectorModel::ContentModel::canBeChecked(const EsmFile *file) const { //element can be checked if all its dependencies are - foreach (const QString &master, file->masters()) - if (!isChecked(master)) + foreach (const QString &gamefile, file->gameFiles()) + if (!isChecked(gamefile)) return false; return true; } -void EsxModel::ContentModel::addFile(EsmFile *file) +void ContentSelectorModel::ContentModel::addFile(EsmFile *file) { beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); mFiles.append(file); endInsertRows(); } -void EsxModel::ContentModel::addFiles(const QString &path) +void ContentSelectorModel::ContentModel::addFiles(const QString &path) { QDir dir(path); QStringList filters; filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon"; dir.setNameFilters(filters); + QTextCodec *codec = QTextCodec::codecForName("UTF8"); + // Create a decoder for non-latin characters in esx metadata - QTextDecoder *decoder = mCodec->makeDecoder(); + QTextDecoder *decoder = codec->makeDecoder(); foreach (const QString &path, dir.entryList()) { @@ -378,16 +407,16 @@ void EsxModel::ContentModel::addFiles(const QString &path) try { ESM::ESMReader fileReader; - ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(QString(mCodec->name()).toStdString())); - fileReader.setEncoder(&encoder); + ToUTF8::Utf8Encoder encoder(); //ToUTF8::calculateEncoding(QString(mCodec->name()).toStdString())); + //fileReader.setEncoder(&encoder); fileReader.open(dir.absoluteFilePath(path).toStdString()); - foreach (const ESM::Header::MasterData &item, fileReader.getMasters()) - file->addMaster(QString::fromStdString(item.name)); + foreach (const ESM::Header::MasterData &item, fileReader.getGameFiles()) + file->addGameFile(QString::fromStdString(item.name)); file->setAuthor (decoder->toUnicode(fileReader.getAuthor().c_str())); file->setDate (info.lastModified()); - file->setVersion (fileReader.getFVer()); + file->setFormat (fileReader.getFormat()); file->setPath (info.absoluteFilePath()); file->setDescription(decoder->toUnicode(fileReader.getDesc().c_str())); @@ -407,7 +436,7 @@ void EsxModel::ContentModel::addFiles(const QString &path) delete decoder; } -bool EsxModel::ContentModel::isChecked(const QString& name) const +bool ContentSelectorModel::ContentModel::isChecked(const QString& name) const { if (mCheckStates.contains(name)) return (mCheckStates[name] == Qt::Checked); @@ -415,7 +444,7 @@ bool EsxModel::ContentModel::isChecked(const QString& name) const return false; } -void EsxModel::ContentModel::setCheckState(const QString &name, bool isChecked) +void ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool isChecked) { if (name.isEmpty()) return; @@ -428,7 +457,7 @@ void EsxModel::ContentModel::setCheckState(const QString &name, bool isChecked) mCheckStates[name] = state; } -EsxModel::ContentFileList EsxModel::ContentModel::checkedItems() const +ContentSelectorModel::ContentFileList ContentSelectorModel::ContentModel::checkedItems() const { ContentFileList list; @@ -441,7 +470,7 @@ EsxModel::ContentFileList EsxModel::ContentModel::checkedItems() const return list; } -void EsxModel::ContentModel::uncheckAll() +void ContentSelectorModel::ContentModel::uncheckAll() { emit layoutAboutToBeChanged(); mCheckStates.clear(); diff --git a/components/esxselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp similarity index 86% rename from components/esxselector/model/contentmodel.hpp rename to components/contentselector/model/contentmodel.hpp index 6a2dd88ca5..a8ff103da7 100644 --- a/components/esxselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -3,19 +3,26 @@ #include #include -namespace EsxModel + +namespace ContentSelectorModel { class EsmFile; typedef QList ContentFileList; + enum ContentType + { + ContentType_GameFile, + ContentType_Addon + }; + class ContentModel : public QAbstractTableModel { Q_OBJECT public: explicit ContentModel(QObject *parent = 0); - void setEncoding(const QString &encoding); + //void setEncoding(const QString &encoding); int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; @@ -34,8 +41,8 @@ namespace EsxModel void addFiles(const QString &path); - QModelIndex indexFromItem(EsmFile *item) const; - const EsxModel::EsmFile *findItem(const QString &name) const; + QModelIndex indexFromItem(const EsmFile *item) const; + const EsmFile *findItem(const QString &name) const; bool isChecked(const QString &name) const; void setCheckState(const QString &name, bool isChecked); diff --git a/components/esxselector/model/esmfile.cpp b/components/contentselector/model/esmfile.cpp similarity index 54% rename from components/esxselector/model/esmfile.cpp rename to components/contentselector/model/esmfile.cpp index bd76dc4a00..9dfe49ebaf 100644 --- a/components/esxselector/model/esmfile.cpp +++ b/components/contentselector/model/esmfile.cpp @@ -3,64 +3,65 @@ #include #include -int EsxModel::EsmFile::sPropertyCount = 7; -QString EsxModel::EsmFile::sToolTip = QString("Author: %1
\ +int ContentSelectorModel::EsmFile::sPropertyCount = 7; +QString ContentSelectorModel::EsmFile::sToolTip = QString("Author: %1
\ Version: %2
\
Description:
%3
\
Dependencies: %4
"); -EsxModel::EsmFile::EsmFile(QString fileName, ModelItem *parent) - : ModelItem(parent), mFileName(fileName), mVersion(0.0f) +ContentSelectorModel::EsmFile::EsmFile(QString fileName, ModelItem *parent) + : ModelItem(parent), mFileName(fileName), mFormat(0) {} -void EsxModel::EsmFile::setFileName(const QString &fileName) + +void ContentSelectorModel::EsmFile::setFileName(const QString &fileName) { mFileName = fileName; } -void EsxModel::EsmFile::setAuthor(const QString &author) +void ContentSelectorModel::EsmFile::setAuthor(const QString &author) { mAuthor = author; } -void EsxModel::EsmFile::setDate(const QDateTime &modified) +void ContentSelectorModel::EsmFile::setDate(const QDateTime &modified) { mModified = modified; } -void EsxModel::EsmFile::setVersion(float version) +void ContentSelectorModel::EsmFile::setFormat(int format) { - mVersion = version; + mFormat = format; } -void EsxModel::EsmFile::setPath(const QString &path) +void ContentSelectorModel::EsmFile::setPath(const QString &path) { mPath = path; } -void EsxModel::EsmFile::setMasters(const QStringList &masters) +void ContentSelectorModel::EsmFile::setGameFiles(const QStringList &gamefiles) { - mMasters = masters; + mGameFiles = gamefiles; } -void EsxModel::EsmFile::setDescription(const QString &description) +void ContentSelectorModel::EsmFile::setDescription(const QString &description) { mDescription = description; } -QByteArray EsxModel::EsmFile::encodedData() const +QByteArray ContentSelectorModel::EsmFile::encodedData() const { QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); - stream << mFileName << mAuthor << QString::number(mVersion) + stream << mFileName << mAuthor << QString::number(mFormat) << mModified.toString() << mPath << mDescription - << mMasters; + << mGameFiles; return encodedData; } -QVariant EsxModel::EsmFile::fileProperty(const FileProperty prop) const +QVariant ContentSelectorModel::EsmFile::fileProperty(const FileProperty prop) const { switch (prop) { @@ -72,8 +73,8 @@ QVariant EsxModel::EsmFile::fileProperty(const FileProperty prop) const return mAuthor; break; - case FileProperty_Version: - return mVersion; + case FileProperty_Format: + return mFormat; break; case FileProperty_DateModified: @@ -88,8 +89,8 @@ QVariant EsxModel::EsmFile::fileProperty(const FileProperty prop) const return mDescription; break; - case FileProperty_Master: - return mMasters; + case FileProperty_GameFile: + return mGameFiles; break; default: @@ -97,7 +98,7 @@ QVariant EsxModel::EsmFile::fileProperty(const FileProperty prop) const } return QVariant(); } -void EsxModel::EsmFile::setFileProperty (const FileProperty prop, const QString &value) +void ContentSelectorModel::EsmFile::setFileProperty (const FileProperty prop, const QString &value) { switch (prop) { @@ -109,8 +110,8 @@ void EsxModel::EsmFile::setFileProperty (const FileProperty prop, const QString mAuthor = value; break; - case FileProperty_Version: - mVersion = value.toFloat(); + case FileProperty_Format: + mFormat = value.toInt(); break; case FileProperty_DateModified: @@ -125,8 +126,8 @@ void EsxModel::EsmFile::setFileProperty (const FileProperty prop, const QString mDescription = value; break; - case FileProperty_Master: - mMasters << value; + case FileProperty_GameFile: + mGameFiles << value; break; default: diff --git a/components/esxselector/model/esmfile.hpp b/components/contentselector/model/esmfile.hpp similarity index 58% rename from components/esxselector/model/esmfile.hpp rename to components/contentselector/model/esmfile.hpp index 4f6d7a6244..743b1c5a68 100644 --- a/components/esxselector/model/esmfile.hpp +++ b/components/contentselector/model/esmfile.hpp @@ -8,7 +8,7 @@ class QMimeData; -namespace EsxModel +namespace ContentSelectorModel { class EsmFile : public ModelItem { @@ -21,11 +21,11 @@ namespace EsxModel { FileProperty_FileName = 0, FileProperty_Author = 1, - FileProperty_Version = 2, + FileProperty_Format = 2, FileProperty_DateModified = 3, FileProperty_Path = 4, FileProperty_Description = 5, - FileProperty_Master = 6 + FileProperty_GameFile = 6 }; EsmFile(QString fileName = QString(), ModelItem *parent = 0); @@ -40,28 +40,28 @@ namespace EsxModel void setAuthor(const QString &author); void setSize(const int size); void setDate(const QDateTime &modified); - void setVersion(const float version); + void setFormat(const int format); void setPath(const QString &path); - void setMasters(const QStringList &masters); + void setGameFiles(const QStringList &gameFiles); void setDescription(const QString &description); - inline void addMaster (const QString &name) {mMasters.append(name); } + inline void addGameFile (const QString &name) {mGameFiles.append(name); } QVariant fileProperty (const FileProperty prop) const; - inline QString fileName() const { return mFileName; } - inline QString author() const { return mAuthor; } - inline QDateTime modified() const { return mModified; } - inline float version() const { return mVersion; } - inline QString path() const { return mPath; } - inline const QStringList &masters() const { return mMasters; } - inline QString description() const { return mDescription; } - inline QString toolTip() const { return sToolTip.arg(mAuthor) - .arg(mVersion) + inline QString fileName() const { return mFileName; } + inline QString author() const { return mAuthor; } + inline QDateTime modified() const { return mModified; } + inline float format() const { return mFormat; } + inline QString path() const { return mPath; } + inline const QStringList &gameFiles() const { return mGameFiles; } + inline QString description() const { return mDescription; } + inline QString toolTip() const { return sToolTip.arg(mAuthor) + .arg(mFormat) .arg(mDescription) - .arg(mMasters.join(", ")); - } + .arg(mGameFiles.join(", ")); + } - inline bool isMaster() const { return (mMasters.size() == 0); } + inline bool isGameFile() const { return (mGameFiles.size() == 0); } QByteArray encodedData() const; public: @@ -73,15 +73,13 @@ namespace EsxModel QString mFileName; QString mAuthor; QDateTime mModified; - float mVersion; + int mFormat; QString mPath; - QStringList mMasters; + QStringList mGameFiles; QString mDescription; QString mToolTip; }; } -Q_DECLARE_METATYPE (EsxModel::EsmFile *) - #endif diff --git a/components/contentselector/model/modelitem.cpp b/components/contentselector/model/modelitem.cpp new file mode 100644 index 0000000000..e1d737c2d4 --- /dev/null +++ b/components/contentselector/model/modelitem.cpp @@ -0,0 +1,69 @@ +#include "modelitem.hpp" + +ContentSelectorModel::ModelItem::ModelItem(ModelItem *parent) + : mParentItem(parent) +{ +} +/* +ContentSelectorModel::ModelItem::ModelItem(const ModelItem *parent) + // : mParentItem(parent) +{ +} +*/ + +ContentSelectorModel::ModelItem::~ModelItem() +{ + qDeleteAll(mChildItems); +} + + +ContentSelectorModel::ModelItem *ContentSelectorModel::ModelItem::parent() const +{ + return mParentItem; +} + +bool ContentSelectorModel::ModelItem::hasFormat(const QString &mimetype) const +{ + if (mimetype == "application/omwcontent") + return true; + + return QMimeData::hasFormat(mimetype); +} +int ContentSelectorModel::ModelItem::row() const +{ + if (mParentItem) + return 1; + //return mParentItem->childRow(const_cast(this)); + //return mParentItem->mChildItems.indexOf(const_cast(this)); + + return -1; +} + + +int ContentSelectorModel::ModelItem::childCount() const +{ + return mChildItems.count(); +} + +int ContentSelectorModel::ModelItem::childRow(ModelItem *child) const +{ + Q_ASSERT(child); + + return mChildItems.indexOf(child); +} + +ContentSelectorModel::ModelItem *ContentSelectorModel::ModelItem::child(int row) +{ + return mChildItems.value(row); +} + + +void ContentSelectorModel::ModelItem::appendChild(ModelItem *item) +{ + mChildItems.append(item); +} + +void ContentSelectorModel::ModelItem::removeChild(int row) +{ + mChildItems.removeAt(row); +} diff --git a/components/esxselector/model/modelitem.hpp b/components/contentselector/model/modelitem.hpp similarity index 96% rename from components/esxselector/model/modelitem.hpp rename to components/contentselector/model/modelitem.hpp index 5ee5e417ed..57214b09cf 100644 --- a/components/esxselector/model/modelitem.hpp +++ b/components/contentselector/model/modelitem.hpp @@ -4,7 +4,7 @@ #include #include -namespace EsxModel +namespace ContentSelectorModel { class ModelItem : public QMimeData { diff --git a/components/esxselector/model/naturalsort.cpp b/components/contentselector/model/naturalsort.cpp similarity index 100% rename from components/esxselector/model/naturalsort.cpp rename to components/contentselector/model/naturalsort.cpp diff --git a/components/esxselector/model/naturalsort.hpp b/components/contentselector/model/naturalsort.hpp similarity index 100% rename from components/esxselector/model/naturalsort.hpp rename to components/contentselector/model/naturalsort.hpp diff --git a/components/esxselector/view/comboboxlineedit.cpp b/components/contentselector/view/comboboxlineedit.cpp similarity index 83% rename from components/esxselector/view/comboboxlineedit.cpp rename to components/contentselector/view/comboboxlineedit.cpp index 815a1130b5..df647a4a09 100644 --- a/components/esxselector/view/comboboxlineedit.cpp +++ b/components/contentselector/view/comboboxlineedit.cpp @@ -3,7 +3,7 @@ #include "comboboxlineedit.hpp" -EsxView::ComboBoxLineEdit::ComboBoxLineEdit(QWidget *parent) +ContentSelectorView::ComboBoxLineEdit::ComboBoxLineEdit(QWidget *parent) : QLineEdit(parent) { mClearButton = new QToolButton(this); @@ -21,7 +21,7 @@ EsxView::ComboBoxLineEdit::ComboBoxLineEdit(QWidget *parent) setStyleSheet(QString("ComboBoxLineEdit { background-color: transparent; padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1)); } -void EsxView::ComboBoxLineEdit::resizeEvent(QResizeEvent *) +void ContentSelectorView::ComboBoxLineEdit::resizeEvent(QResizeEvent *) { QSize sz = mClearButton->sizeHint(); int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); @@ -29,7 +29,7 @@ void EsxView::ComboBoxLineEdit::resizeEvent(QResizeEvent *) (rect().bottom() + 1 - sz.height())/2); } -void EsxView::ComboBoxLineEdit::updateClearButton(const QString& text) +void ContentSelectorView::ComboBoxLineEdit::updateClearButton(const QString& text) { mClearButton->setVisible(!text.isEmpty()); } diff --git a/components/esxselector/view/comboboxlineedit.hpp b/components/contentselector/view/comboboxlineedit.hpp similarity index 96% rename from components/esxselector/view/comboboxlineedit.hpp rename to components/contentselector/view/comboboxlineedit.hpp index f3b251955d..1aef2f57b6 100644 --- a/components/esxselector/view/comboboxlineedit.hpp +++ b/components/contentselector/view/comboboxlineedit.hpp @@ -14,7 +14,7 @@ class QToolButton; -namespace EsxView +namespace ContentSelectorView { class ComboBoxLineEdit : public QLineEdit { diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp new file mode 100644 index 0000000000..615e9a846d --- /dev/null +++ b/components/contentselector/view/contentselector.cpp @@ -0,0 +1,134 @@ +#include "contentselector.hpp" + +#include "../model/contentmodel.hpp" +#include "../model/esmfile.hpp" + +#include + +#include +#include +#include + +ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) : + QDialog(parent) +{ + setupUi(this); + + buildContentModel(); + buildGameFileView(); + buildAddonView(); + buildProfilesView(); + + updateViews(); + +} + +void ContentSelectorView::ContentSelector::buildContentModel() +{ + mContentModel = new ContentSelectorModel::ContentModel(); + connect(mContentModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); +} + +void ContentSelectorView::ContentSelector::buildGameFileView() +{ + mGameFileProxyModel = new QSortFilterProxyModel(this); + mGameFileProxyModel->setFilterRegExp(QString::number((int)ContentSelectorModel::ContentType_GameFile)); + mGameFileProxyModel->setFilterRole (Qt::UserRole); + mGameFileProxyModel->setSourceModel (mContentModel); + + gameFileView->setPlaceholderText(QString("Select a game file...")); + gameFileView->setModel(mGameFileProxyModel); + + connect(gameFileView, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentGameFileIndexChanged(int))); + + gameFileView->setCurrentIndex(-1); + gameFileView->setCurrentIndex(0); +} + +void ContentSelectorView::ContentSelector::buildAddonView() +{ + mAddonProxyModel = new QSortFilterProxyModel(this); + mAddonProxyModel->setFilterRegExp (QString::number((int)ContentSelectorModel::ContentType_Addon)); + mAddonProxyModel->setFilterRole (Qt::UserRole); + mAddonProxyModel->setDynamicSortFilter (true); + mAddonProxyModel->setSourceModel (mContentModel); + + addonView->setModel(mAddonProxyModel); + + connect(addonView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotAddonTableItemClicked(const QModelIndex &))); +} + +void ContentSelectorView::ContentSelector::buildProfilesView() +{ + profilesComboBox->setPlaceholderText(QString("Select a profile...")); + connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); +} + +void ContentSelectorView::ContentSelector::updateViews() +{ + // Ensure the columns are hidden because sort() re-enables them + addonView->setColumnHidden(1, true); + addonView->setColumnHidden(2, true); + addonView->setColumnHidden(3, true); + addonView->setColumnHidden(4, true); + addonView->setColumnHidden(5, true); + addonView->setColumnHidden(6, true); + addonView->setColumnHidden(7, true); + addonView->setColumnHidden(8, true); + addonView->resizeColumnsToContents(); +} + +void ContentSelectorView::ContentSelector::addFiles(const QString &path) +{ + mContentModel->addFiles(path); + //mContentModel->sort(3); // Sort by date accessed + gameFileView->setCurrentIndex(-1); + mContentModel->uncheckAll(); +} + +QStringList ContentSelectorView::ContentSelector::checkedItemsPaths() +{ + QStringList itemPaths; + + foreach( const ContentSelectorModel::EsmFile *file, mContentModel->checkedItems()) + itemPaths << file->path(); + + return itemPaths; +} + +void ContentSelectorView::ContentSelector::slotCurrentProfileIndexChanged(int index) +{ + emit profileChanged(index); +} + +void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int index) +{ + static int oldIndex = -1; + + QAbstractItemModel *const model = gameFileView->model(); + QSortFilterProxyModel *proxy = dynamic_cast(model); + + if (proxy) + proxy->setDynamicSortFilter(false); + + if (oldIndex > -1) + model->setData(model->index(oldIndex, 0), false, Qt::UserRole + 1); + + oldIndex = index; + + model->setData(model->index(index, 0), true, Qt::UserRole + 1); + + if (proxy) + proxy->setDynamicSortFilter(true); +} + +void ContentSelectorView::ContentSelector::slotAddonTableItemClicked(const QModelIndex &index) +{ + QAbstractItemModel *const model = addonView->model(); + QSortFilterProxyModel *proxy = dynamic_cast(model); + + if (model->data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked) + model->setData(index, Qt::Checked, Qt::CheckStateRole); + else + model->setData(index, Qt::Unchecked, Qt::CheckStateRole); +} diff --git a/components/esxselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp similarity index 51% rename from components/esxselector/view/contentselector.hpp rename to components/contentselector/view/contentselector.hpp index e074fe6880..8032b04495 100644 --- a/components/esxselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -5,15 +5,11 @@ #include "ui_datafilespage.h" -namespace EsxModel -{ - class ContentModel; - class DataFilesModel; -} +namespace ContentSelectorModel { class ContentModel; } class QSortFilterProxyModel; -namespace EsxView +namespace ContentSelectorView { class ContentSelector : public QDialog, protected Ui::DataFilesPage { @@ -21,27 +17,25 @@ namespace EsxView protected: - EsxModel::DataFilesModel *mDataFilesModel; - EsxModel::ContentModel *mContentModel; - QSortFilterProxyModel *mMasterProxyModel; - QSortFilterProxyModel *mPluginsProxyModel; + ContentSelectorModel::ContentModel *mContentModel; + QSortFilterProxyModel *mGameFileProxyModel; + QSortFilterProxyModel *mAddonProxyModel; public: + explicit ContentSelector(QWidget *parent = 0); - void buildModelsAndViews(); + static ContentSelector &cast(QWidget *subject); //static constructor function for singleton performance. void addFiles(const QString &path); - void setEncoding(const QString &encoding); - void setPluginCheckState(); void setCheckState(QModelIndex index, QSortFilterProxyModel *model); QStringList checkedItemsPaths(); - void on_checkAction_triggered(); private: - void buildSourceModel(); - void buildMasterView(); - void buildPluginsView(); + + void buildContentModel(); + void buildGameFileView(); + void buildAddonView(); void buildProfilesView(); signals: @@ -50,8 +44,8 @@ namespace EsxView private slots: void updateViews(); void slotCurrentProfileIndexChanged(int index); - void slotCurrentMasterIndexChanged(int index); - void slotPluginTableItemClicked(const QModelIndex &index); + void slotCurrentGameFileIndexChanged(int index); + void slotAddonTableItemClicked(const QModelIndex &index); }; } diff --git a/components/esxselector/view/lineedit.cpp b/components/contentselector/view/lineedit.cpp similarity index 87% rename from components/esxselector/view/lineedit.cpp rename to components/contentselector/view/lineedit.cpp index 48be2f022f..b6fdfa805c 100644 --- a/components/esxselector/view/lineedit.cpp +++ b/components/contentselector/view/lineedit.cpp @@ -4,7 +4,7 @@ #include "lineedit.hpp" -EsxView::LineEdit::LineEdit(QWidget *parent) +ContentSelectorView::LineEdit::LineEdit(QWidget *parent) : QLineEdit(parent) { mClearButton = new QToolButton(this); @@ -25,7 +25,7 @@ EsxView::LineEdit::LineEdit(QWidget *parent) qMax(msz.height(), mClearButton->sizeHint().height() + frameWidth * 2 + 2)); } -void EsxView::LineEdit::resizeEvent(QResizeEvent *) +void ContentSelectorView::LineEdit::resizeEvent(QResizeEvent *) { QSize sz = mClearButton->sizeHint(); int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); @@ -33,7 +33,7 @@ void EsxView::LineEdit::resizeEvent(QResizeEvent *) (rect().bottom() + 1 - sz.height())/2); } -void EsxView::LineEdit::updateClearButton(const QString& text) +void ContentSelectorView::LineEdit::updateClearButton(const QString& text) { mClearButton->setVisible(!text.isEmpty()); } diff --git a/components/esxselector/view/lineedit.hpp b/components/contentselector/view/lineedit.hpp similarity index 96% rename from components/esxselector/view/lineedit.hpp rename to components/contentselector/view/lineedit.hpp index 4e0cbe3390..ab1c37203a 100644 --- a/components/esxselector/view/lineedit.hpp +++ b/components/contentselector/view/lineedit.hpp @@ -14,7 +14,7 @@ class QToolButton; -namespace EsxView +namespace ContentSelectorView { class LineEdit : public QLineEdit { diff --git a/components/esxselector/view/profilescombobox.cpp b/components/contentselector/view/profilescombobox.cpp similarity index 83% rename from components/esxselector/view/profilescombobox.cpp rename to components/contentselector/view/profilescombobox.cpp index 0d709aa50c..cb0ba7b77e 100644 --- a/components/esxselector/view/profilescombobox.cpp +++ b/components/contentselector/view/profilescombobox.cpp @@ -7,7 +7,7 @@ #include "profilescombobox.hpp" #include "comboboxlineedit.hpp" -EsxView::ProfilesComboBox::ProfilesComboBox(QWidget *parent) : +ContentSelectorView::ProfilesComboBox::ProfilesComboBox(QWidget *parent) : QComboBox(parent) { mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore @@ -21,7 +21,7 @@ EsxView::ProfilesComboBox::ProfilesComboBox(QWidget *parent) : setInsertPolicy(QComboBox::NoInsert); } -void EsxView::ProfilesComboBox::setEditEnabled(bool editable) +void ContentSelectorView::ProfilesComboBox::setEditEnabled(bool editable) { if (isEditable() == editable) return; @@ -47,7 +47,7 @@ void EsxView::ProfilesComboBox::setEditEnabled(bool editable) SLOT(slotTextChanged(QString))); } -void EsxView::ProfilesComboBox::slotTextChanged(const QString &text) +void ContentSelectorView::ProfilesComboBox::slotTextChanged(const QString &text) { QPalette *palette = new QPalette(); palette->setColor(QPalette::Text,Qt::red); @@ -61,7 +61,7 @@ void EsxView::ProfilesComboBox::slotTextChanged(const QString &text) } } -void EsxView::ProfilesComboBox::slotEditingFinished() +void ContentSelectorView::ProfilesComboBox::slotEditingFinished() { QString current = currentText(); QString previous = itemText(currentIndex()); @@ -82,7 +82,7 @@ void EsxView::ProfilesComboBox::slotEditingFinished() emit(profileRenamed(previous, current)); } -void EsxView::ProfilesComboBox::slotIndexChanged(int index) +void ContentSelectorView::ProfilesComboBox::slotIndexChanged(int index) { if (index == -1) return; @@ -91,7 +91,7 @@ void EsxView::ProfilesComboBox::slotIndexChanged(int index) mOldProfile = itemText(index); } -void EsxView::ProfilesComboBox::paintEvent(QPaintEvent *) +void ContentSelectorView::ProfilesComboBox::paintEvent(QPaintEvent *) { QStylePainter painter(this); painter.setPen(palette().color(QPalette::Text)); @@ -107,7 +107,7 @@ void EsxView::ProfilesComboBox::paintEvent(QPaintEvent *) painter.drawControl(QStyle::CE_ComboBoxLabel, opt); } -void EsxView::ProfilesComboBox::setPlaceholderText(const QString &text) +void ContentSelectorView::ProfilesComboBox::setPlaceholderText(const QString &text) { mPlaceholderText = text; } diff --git a/components/esxselector/view/profilescombobox.hpp b/components/contentselector/view/profilescombobox.hpp similarity index 96% rename from components/esxselector/view/profilescombobox.hpp rename to components/contentselector/view/profilescombobox.hpp index 28740783ba..d81c1e6a5a 100644 --- a/components/esxselector/view/profilescombobox.hpp +++ b/components/contentselector/view/profilescombobox.hpp @@ -6,7 +6,7 @@ class QString; class QRegExpValidator; -namespace EsxView +namespace ContentSelectorView { class ProfilesComboBox : public QComboBox { diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index edc724cd2a..3bf194c4e3 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -34,7 +34,7 @@ public: float getFVer() const { if(mHeader.mData.version == VER_12) return 1.2; else return 1.3; } const std::string getAuthor() const { return mHeader.mData.author.toString(); } const std::string getDesc() const { return mHeader.mData.desc.toString(); } - const std::vector &getMasters() const { return mHeader.mMaster; } + const std::vector &getGameFiles() const { return mHeader.mMaster; } int getFormat() const; const NAME &retSubName() const { return mCtx.subName; } uint32_t getSubSize() const { return mCtx.leftSub; } diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index d8d0c12912..9793391ed8 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -172,7 +172,7 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) // If the most significant 8 bits are used, then this reference already exists. // In this case, do not spawn a new reference, but overwrite the old one. ref.mRefnum &= 0x00ffffff; // delete old plugin ID - const std::vector &masters = esm.getMasters(); + const std::vector &masters = esm.getGameFiles(); global = masters[local-1].index + 1; ref.mRefnum |= global << 24; // insert global plugin ID } @@ -276,7 +276,7 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) int local = (mref.mRefnum & 0xff000000) >> 24; size_t global = esm.getIndex() + 1; mref.mRefnum &= 0x00ffffff; // delete old plugin ID - const std::vector &masters = esm.getMasters(); + const std::vector &masters = esm.getGameFiles(); global = masters[local-1].index + 1; mref.mRefnum |= global << 24; // insert global plugin ID diff --git a/components/esxselector/model/datafilesmodel.cpp b/components/esxselector/model/datafilesmodel.cpp deleted file mode 100644 index c98f70b162..0000000000 --- a/components/esxselector/model/datafilesmodel.cpp +++ /dev/null @@ -1,608 +0,0 @@ -#include -#include -#include -#include -#include - -#include - -#include - -#include "esmfile.hpp" - -#include "datafilesmodel.hpp" - -#include - -EsxModel::DataFilesModel::DataFilesModel(QObject *parent) : - QAbstractTableModel(parent) -{ - mEncoding = QString("win1252"); -} - -EsxModel::DataFilesModel::~DataFilesModel() -{ -} - -int EsxModel::DataFilesModel::rowCount(const QModelIndex &parent) const -{ - return parent.isValid() ? 0 : mFiles.count(); -} - -int EsxModel::DataFilesModel::columnCount(const QModelIndex &parent) const -{ - return parent.isValid() ? 0 : 1; -} - -const EsxModel::EsmFile* EsxModel::DataFilesModel::findItem(const QString &name) -{ - for (int i = 0; i < mFiles.size(); ++i) - { - const EsmFile *file = item(i); - - if (name == file->fileName()) - return file; - } - - return 0; -} - -const EsxModel::EsmFile* EsxModel::DataFilesModel::item(int row) const -{ - if (row >= 0 && row < mFiles.count()) - return mFiles.at(row); - - return 0; -} - -Qt::ItemFlags EsxModel::DataFilesModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return Qt::NoItemFlags; - - const EsmFile *file = item(index.row()); - - if (!file) - return Qt::NoItemFlags; - - Qt::ItemFlags dragDropFlags = Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; - Qt::ItemFlags checkFlags = Qt::ItemIsUserCheckable | Qt::ItemIsEditable; - Qt::ItemFlags defaultFlags = Qt::ItemIsDropEnabled | Qt::ItemIsSelectable; - - if (canBeChecked(file)) - return defaultFlags | dragDropFlags | checkFlags | Qt::ItemIsEnabled; - else - return defaultFlags; -} - -QVariant EsxModel::DataFilesModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - if (index.row() >= mFiles.size()) - return QVariant(); - - const 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 file->modified().toString(Qt::ISODate); - case 3: - return file->version(); - case 4: - return file->path(); - case 5: - return file->masters().join(", "); - case 6: - return file->description(); - } - break; - } - - case Qt::TextAlignmentRole: - { - switch (column) - { - case 0: - case 1: - return Qt::AlignLeft + Qt::AlignVCenter; - case 2: - case 3: - return Qt::AlignRight + Qt::AlignVCenter; - default: - return Qt::AlignLeft + Qt::AlignVCenter; - } - break; - } - - case Qt::ToolTipRole: - { - if (column != 0) - return QVariant(); - - if (file->version() == 0.0f) - return QVariant(); // Data not set - - QString tooltip = - QString("Author: %1
\ - Version: %2
\ -
Description:
%3
\ -
Dependencies: %4
") - .arg(file->author()) - .arg(QString::number(file->version())) - .arg(file->description()) - .arg(file->masters().join(", ")); - - - return tooltip; - break; - } - - case Qt::UserRole: - { - if (file->masters().size() == 0) - return "game"; - else - return "addon"; - - break; - } - - case Qt::UserRole + 1: - //return check state here - break; - - default: - return QVariant(); - break; - } - -} - -bool EsxModel::DataFilesModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (!index.isValid()) - return false; - - switch (role) - { - case Qt::EditRole: - { - const EsmFile *file = item(index.row()); - - // iterate loop to repopulate file pointer with data in string list. - QStringList list = value.toStringList(); - for (int i = 0; i <999; ++i) - { - file->setProperty(i, value.at(i)); - } - - //populate master list here (emit data changed for each master and - //each item (other than the dropped item) which share each of the masters - file->masters().append(masterList); - - emit dataChanged(index, index); - return true; - } - break; - - case Qt::UserRole + 1: - { - EsmFile *file = item(index.row()); - //set file's checkstate to the passed checkstate - emit dataChanged(index, index); - - for (int i = 0; i < mFiles.size(); ++i) - if (mFiles.at(i)->getMasters().contains(file->fileName())) - emit dataChanged(QAbstractTableModel::index(i,0), QAbstractTableModel::index(i,0)); - - return true; - } - break; - - case Qt::CheckStateRole: - { - EsmFile *file = item(index.row()); - - if ((value.toInt() == Qt::Checked) && !file->isChecked()) - file->setChecked(true); - else if (value.toInt() == Qt::Checked && file->isChecked()) - file->setChecked(false); - else if (value.toInt() == Qt::UnChecked) - file->setChecked(false); - - emit dataChanged(index, index); - - return true; - } - break; - } - return false; -} - -bool EsxModel::DataFilesModel::insertRows(int row, int count, const QModelIndex &parent) -{ - if (parent.isValid()) - return false; - - beginInsertRows(QModelIndex(),row, row+count-1); - { - for (int i = 0; i < count; ++i) - mFiles.insert(row, new EsmFile()); - } endInsertRows(); - - return true; -} - -bool EsxModel::DataFilesModel::removeRows(int row, int count, const QModelIndex &parent) -{ - if (parent.isValid()) - return false; - - beginRemoveRows(QModelIndex(), row, row+count-1); - { - for (int i = 0; i < count; ++i) - delete mFiles.takeAt(row); - } endRemoveRows(); - - 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 -{ - QMimeData *mimeData = new QMimeData(); - QByteArray encodedData; - - QDataStream stream (&encodedData, QIODevice::WriteOnly); - - foreach (const QModelIndex &index, indexes) - { - if (index.isValid()) - { - EsmFile *file = item (index.row()); - - for (int i = 0; i < file->propertyCount(); ++i) - stream << data(index, Qt::DisplayRole).toString(); - - EsmFile *file = item(index.row()); - stream << file->getMasters(); - } - } - - mimeData->setData("application/omwcontent", encodedData); - - return mimeData; -} - -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; -} - -void EsxModel::DataFilesModel::setEncoding(const QString &encoding) -{ - mEncoding = encoding; -} - -void EsxModel::DataFilesModel::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()); - -} - -Qt::CheckState EsxModel::DataFilesModel::checkState(const QModelIndex &index) -{ - return mCheckStates[item(index.row())->fileName()]; -} - -bool EsxModel::DataFilesModel::moveRow(int oldrow, int row, const QModelIndex &parent) -{ - if (oldrow < 0 || row < 0 || oldrow == row) - return false; - - emit layoutAboutToBeChanged(); - //emit beginMoveRows(parent, oldrow, oldrow, parent, row); - mFiles.swap(oldrow, row); - //emit endInsertRows(); - emit layoutChanged(); - - return true; -} - -QVariant EsxModel::DataFilesModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role != Qt::DisplayRole) - return QVariant(); - - if (orientation == Qt::Horizontal) { - switch (section) { - case 0: return tr("Name"); - case 1: return tr("Author"); - case 2: return tr("Size"); - case 3: return tr("Modified"); - case 4: return tr("Accessed"); - case 5: return tr("Version"); - case 6: return tr("Path"); - case 7: return tr("Masters"); - case 8: return tr("Description"); - } - } - return QVariant(); -} - -//!!!!!!!!!!!!!!!!!!!!!!! -bool lessThanEsmFile(const EsxModel::EsmFile *e1, const EsxModel::EsmFile *e2) -{ - //Masters first then alphabetically - if (e1->fileName().endsWith(".esm") && !e2->fileName().endsWith(".esm")) - return true; - if (!e1->fileName().endsWith(".esm") && e2->fileName().endsWith(".esm")) - return false; - - 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)) - return true; - else - return false; -} -//!!!!!!!!!!!!!!!!!!!!!!! -void EsxModel::DataFilesModel::sort(int column, Qt::SortOrder order) -{ - emit layoutAboutToBeChanged(); - - if (column == 3) { - qSort(mFiles.begin(), mFiles.end(), lessThanDate); - } else { - qSort(mFiles.begin(), mFiles.end(), lessThanEsmFile); - } - - emit layoutChanged(); -} - -void EsxModel::DataFilesModel::addFile(const EsmFile *file) -{ - emit beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); - mFiles.append(file); - emit endInsertRows(); -} - -void EsxModel::DataFilesModel::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; -} - -QModelIndex EsxModel::DataFilesModel::indexFromItem(const EsmFile *item) const -{ - if (item) - //return createIndex(mFiles.indexOf(item), 0); - return index(mFiles.indexOf(item),0); - - return QModelIndex(); -} - -EsxModel::EsmFileList EsxModel::DataFilesModel::checkedItems() -{ - EsmFileList list; - - EsmFileList::ConstIterator it; - EsmFileList::ConstIterator itEnd = mFiles.constEnd(); - - for (it = mFiles.constBegin(); it != itEnd; ++it) - { - // Only add the items that are in the checked list and available - if (mCheckStates[(*it)->fileName()] == Qt::Checked && canBeChecked(*it)) - list << (*it); - } - - return list; -} - -QStringList EsxModel::DataFilesModel::checkedItemsPaths() -{ - QStringList list; - - EsmFileList::ConstIterator it; - EsmFileList::ConstIterator itEnd = mFiles.constEnd(); - - int i = 0; - for (it = mFiles.constBegin(); it != itEnd; ++it) { - const EsmFile *file = item(i); - ++i; - - if (mCheckStates[file->fileName()] == Qt::Checked && canBeChecked(file)) - list << file->path(); - } - - return list; -} -void EsxModel::DataFilesModel::uncheckAll() -{ - emit layoutAboutToBeChanged(); - mCheckStates.clear(); - emit layoutChanged(); -} - -/* -EsxModel::EsmFileList EsxModel::DataFilesModel::uncheckedItems() -{ - EsmFileList list; - EsmFileList checked = checkedItems(); - - EsmFileList::ConstIterator it; - - 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)) - list << file; - } - - return list; -} -*/ -bool EsxModel::DataFilesModel::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; -} diff --git a/components/esxselector/model/datafilesmodel.hpp b/components/esxselector/model/datafilesmodel.hpp deleted file mode 100644 index 4f23cd5307..0000000000 --- a/components/esxselector/model/datafilesmodel.hpp +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef DATAFILESMODEL_HPP -#define DATAFILESMODEL_HPP - -#include -#include -#include -#include - -namespace EsxModel -{ - class EsmFile; - - typedef QList EsmFileList; - - class DataFilesModel : public QAbstractTableModel - { - Q_OBJECT - - public: - explicit DataFilesModel(QObject *parent = 0); - 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()); - - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - - virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); - - inline QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const - { - QModelIndex idx = QAbstractTableModel::index(row, 0, parent); - return idx; - } - - void setEncoding(const QString &encoding); - - void addFiles(const QString &path); - - 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(); - - Qt::CheckState checkState(const QModelIndex &index); - void setCheckState(const QModelIndex &index, Qt::CheckState state); - - 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(const EsmFile *file) const; - void addFile(const EsmFile *file); - - EsmFileList mFiles; - QHash mCheckStates; - - QString mEncoding; - - }; -} -#endif // DATAFILESMODEL_HPP diff --git a/components/esxselector/model/masterproxymodel.cpp b/components/esxselector/model/masterproxymodel.cpp deleted file mode 100644 index df74d03569..0000000000 --- a/components/esxselector/model/masterproxymodel.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "masterproxymodel.hpp" -#include -#include - -EsxModel::MasterProxyModel::MasterProxyModel(QObject *parent, QAbstractTableModel* model) : - QSortFilterProxyModel(parent) -{ - setFilterRegExp(QString("game")); - setFilterRole (Qt::UserRole); - - if (model) - setSourceModel (model); -} diff --git a/components/esxselector/model/masterproxymodel.hpp b/components/esxselector/model/masterproxymodel.hpp deleted file mode 100644 index 8a5c730325..0000000000 --- a/components/esxselector/model/masterproxymodel.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef MASTERPROXYMODEL_HPP -#define MASTERPROXYMODEL_HPP - -#include -#include -#include - -class QAbstractTableModel; - -namespace EsxModel -{ - class MasterProxyModel : public QSortFilterProxyModel - { - Q_OBJECT - public: - explicit MasterProxyModel(QObject *parent = 0, QAbstractTableModel *model = 0); - // 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 deleted file mode 100644 index 03b19f6910..0000000000 --- a/components/esxselector/model/modelitem.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "modelitem.hpp" - -EsxModel::ModelItem::ModelItem(ModelItem *parent) - : mParentItem(parent) -{ -} -/* -EsxModel::ModelItem::ModelItem(const ModelItem *parent) - // : mParentItem(parent) -{ -} -*/ - -EsxModel::ModelItem::~ModelItem() -{ - qDeleteAll(mChildItems); -} - - -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) - return 1; - //return mParentItem->childRow(const_cast(this)); - //return mParentItem->mChildItems.indexOf(const_cast(this)); - - return -1; -} - - -int EsxModel::ModelItem::childCount() const -{ - return mChildItems.count(); -} - -int EsxModel::ModelItem::childRow(ModelItem *child) const -{ - Q_ASSERT(child); - - return mChildItems.indexOf(child); -} - -EsxModel::ModelItem *EsxModel::ModelItem::child(int row) -{ - return mChildItems.value(row); -} - - -void EsxModel::ModelItem::appendChild(ModelItem *item) -{ - mChildItems.append(item); -} - -void EsxModel::ModelItem::removeChild(int row) -{ - mChildItems.removeAt(row); -} diff --git a/components/esxselector/model/pluginsproxymodel.cpp b/components/esxselector/model/pluginsproxymodel.cpp deleted file mode 100644 index c543672b09..0000000000 --- a/components/esxselector/model/pluginsproxymodel.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "pluginsproxymodel.hpp" -#include "contentmodel.hpp" -#include - -EsxModel::PluginsProxyModel::PluginsProxyModel(QObject *parent, ContentModel *model) : - QSortFilterProxyModel(parent) -{ - setFilterRegExp (QString("addon")); - setFilterRole (Qt::UserRole); - setDynamicSortFilter (true); - - if (model) - setSourceModel (model); -} diff --git a/components/esxselector/model/pluginsproxymodel.hpp b/components/esxselector/model/pluginsproxymodel.hpp deleted file mode 100644 index 4415df716e..0000000000 --- a/components/esxselector/model/pluginsproxymodel.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef PLUGINSPROXYMODEL_HPP -#define PLUGINSPROXYMODEL_HPP - -#include - -class QVariant; -class QAbstractTableModel; - -namespace EsxModel -{ - class ContentModel; - - class PluginsProxyModel : public QSortFilterProxyModel - { - Q_OBJECT - - public: - - 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); - }; -} - -#endif // PLUGINSPROXYMODEL_HPP diff --git a/components/esxselector/model/sourcemodel.cpp b/components/esxselector/model/sourcemodel.cpp deleted file mode 100644 index 7b54adba91..0000000000 --- a/components/esxselector/model/sourcemodel.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "sourcemodel.h" - -SourceModel::SourceModel(QObject *parent) : - QAbstractTableClass(parent) -{ -} diff --git a/components/esxselector/model/sourcemodel.h b/components/esxselector/model/sourcemodel.h deleted file mode 100644 index cf51456755..0000000000 --- a/components/esxselector/model/sourcemodel.h +++ /dev/null @@ -1,18 +0,0 @@ -#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 deleted file mode 100644 index 4f8ac7253a..0000000000 --- a/components/esxselector/view/contentselector.cpp +++ /dev/null @@ -1,148 +0,0 @@ -#include "contentselector.hpp" - -#include "../model/datafilesmodel.hpp" -#include "../model/contentmodel.hpp" -#include "../model/esmfile.hpp" - -#include - -#include -#include -#include - -EsxView::ContentSelector::ContentSelector(QWidget *parent) : - QDialog(parent) -{ - setupUi(this); - - buildSourceModel(); - buildMasterView(); - buildPluginsView(); - buildProfilesView(); - - updateViews(); - -} - -void EsxView::ContentSelector::buildSourceModel() -{ - mContentModel = new EsxModel::ContentModel(); - connect(mContentModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); -} - -void EsxView::ContentSelector::buildMasterView() -{ - mMasterProxyModel = new QSortFilterProxyModel(this); - mMasterProxyModel->setFilterRegExp(QString("game")); - mMasterProxyModel->setFilterRole (Qt::UserRole); - mMasterProxyModel->setSourceModel (mContentModel); - - masterView->setPlaceholderText(QString("Select a game file...")); - masterView->setModel(mMasterProxyModel); - - connect(masterView, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentMasterIndexChanged(int))); - - masterView->setCurrentIndex(-1); - masterView->setCurrentIndex(0); -} - -void EsxView::ContentSelector::buildPluginsView() -{ - mPluginsProxyModel = new QSortFilterProxyModel(this); - mPluginsProxyModel->setFilterRegExp (QString("addon")); - mPluginsProxyModel->setFilterRole (Qt::UserRole); - mPluginsProxyModel->setDynamicSortFilter (true); - mPluginsProxyModel->setSourceModel (mContentModel); - - pluginView->setModel(mPluginsProxyModel); - - connect(pluginView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotPluginTableItemClicked(const QModelIndex &))); -} - -void EsxView::ContentSelector::buildProfilesView() -{ - profilesComboBox->setPlaceholderText(QString("Select a profile...")); - connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); -} - -void EsxView::ContentSelector::updateViews() -{ - // Ensure the columns are hidden because sort() re-enables them - pluginView->setColumnHidden(1, true); - pluginView->setColumnHidden(2, true); - pluginView->setColumnHidden(3, true); - pluginView->setColumnHidden(4, true); - pluginView->setColumnHidden(5, true); - pluginView->setColumnHidden(6, true); - pluginView->setColumnHidden(7, true); - pluginView->setColumnHidden(8, true); - pluginView->resizeColumnsToContents(); -} - -void EsxView::ContentSelector::addFiles(const QString &path) -{ - mContentModel->addFiles(path); - mContentModel->sort(3); // Sort by date accessed - masterView->setCurrentIndex(-1); - mContentModel->uncheckAll(); -} - -void EsxView::ContentSelector::setEncoding(const QString &encoding) -{ - mContentModel->setEncoding(encoding); -} - -QStringList EsxView::ContentSelector::checkedItemsPaths() -{ - QStringList itemPaths; - - foreach( const EsxModel::EsmFile *file, mContentModel->checkedItems()) - itemPaths << file->path(); - - return itemPaths; -} - -void EsxView::ContentSelector::slotCurrentProfileIndexChanged(int index) -{ - emit profileChanged(index); -} - -void EsxView::ContentSelector::slotCurrentMasterIndexChanged(int index) -{ - static int oldIndex = -1; - - QAbstractItemModel *const model = masterView->model(); - QSortFilterProxyModel *proxy = dynamic_cast(model); - - if (proxy) - proxy->setDynamicSortFilter(false); - - if (oldIndex > -1) - qDebug() << "clearing old master check state"; - model->setData(model->index(oldIndex, 0), false, Qt::UserRole + 1); - - oldIndex = index; - - qDebug() << "setting new master check state"; - model->setData(model->index(index, 0), true, Qt::UserRole + 1); - - if (proxy) - proxy->setDynamicSortFilter(true); -} - -void EsxView::ContentSelector::slotPluginTableItemClicked(const QModelIndex &index) -{ - QAbstractItemModel *const model = pluginView->model(); - QSortFilterProxyModel *proxy = dynamic_cast(model); - - if (proxy) - proxy->setDynamicSortFilter(false); - - if (model->data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked) - model->setData(index, Qt::Checked, Qt::CheckStateRole); - else - model->setData(index, Qt::Unchecked, Qt::CheckStateRole); - - if (proxy) - proxy->setDynamicSortFilter(true); -} diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 5b0a6d2292..82d00922b0 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -23,7 +23,7 @@ - + false @@ -34,7 +34,7 @@ - + 0 @@ -102,7 +102,7 @@ - + Enter project name... @@ -159,7 +159,7 @@ 6
- + true @@ -251,12 +251,12 @@ EsxView::ProfilesComboBox QComboBox -

components/esxselector/view/profilescombobox.hpp
+
components/contentselector/view/profilescombobox.hpp
EsxView::LineEdit QLineEdit -
components/esxselector/view/lineedit.hpp
+
components/contentselector/view/lineedit.hpp
From 513f0c4b3ef1d15160619b49eb95d6a23a411c59 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 22 Sep 2013 23:52:53 -0500 Subject: [PATCH 044/148] Implemented file/adjuster widgets into new addon creation dialog --- apps/launcher/datafilespage.cpp | 4 +- apps/opencs/view/doc/adjusterwidget.cpp | 6 +- apps/opencs/view/doc/adjusterwidget.hpp | 5 +- apps/opencs/view/doc/filedialog.cpp | 53 ++++++++++-- apps/opencs/view/doc/filedialog.hpp | 17 +++- apps/opencs/view/doc/filewidget.cpp | 7 +- apps/opencs/view/doc/filewidget.hpp | 2 + .../contentselector/view/contentselector.cpp | 5 +- .../contentselector/view/contentselector.hpp | 1 + files/ui/datafilespage.ui | 80 ++++++++++++++++--- 10 files changed, 152 insertions(+), 28 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 1526b705ae..44392794bd 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -306,7 +306,7 @@ void DataFilesPage::setPluginsCheckstates(Qt::CheckState state) if (!sourceIndex.isValid()) return; - bool isChecked = ( state == Qt::Checked ); + //bool isChecked = ( state == Qt::Checked ); mContentModel->setData(sourceIndex, state, Qt::CheckStateRole); } @@ -397,7 +397,7 @@ void DataFilesPage::slotCurrentGameFileIndexChanged(int index) void DataFilesPage::slotAddonTableItemClicked(const QModelIndex &index) { QAbstractItemModel *const model = addonView->model(); - QSortFilterProxyModel *proxy = dynamic_cast(model); + //QSortFilterProxyModel *proxy = dynamic_cast(model); if (model->data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked) model->setData(index, Qt::Checked, Qt::CheckStateRole); diff --git a/apps/opencs/view/doc/adjusterwidget.cpp b/apps/opencs/view/doc/adjusterwidget.cpp index 9108197006..2784bca8c4 100644 --- a/apps/opencs/view/doc/adjusterwidget.cpp +++ b/apps/opencs/view/doc/adjusterwidget.cpp @@ -43,6 +43,10 @@ boost::filesystem::path CSVDoc::AdjusterWidget::getPath() const return mResultPath; } +bool CSVDoc::AdjusterWidget::isValid() const +{ + return mValid; +} void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon) { QString message; @@ -88,4 +92,4 @@ void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon) pixmap (QSize (16, 16))); emit stateChanged (mValid); -} \ No newline at end of file +} diff --git a/apps/opencs/view/doc/adjusterwidget.hpp b/apps/opencs/view/doc/adjusterwidget.hpp index f578dc4aeb..d970cffee6 100644 --- a/apps/opencs/view/doc/adjusterwidget.hpp +++ b/apps/opencs/view/doc/adjusterwidget.hpp @@ -25,6 +25,9 @@ namespace CSVDoc void setLocalData (const boost::filesystem::path& localData); + QString getText() const; + bool isValid() const; + boost::filesystem::path getPath() const; ///< This function must not be called if there is no valid path. @@ -38,4 +41,4 @@ namespace CSVDoc }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 1d6bed7a7c..9dce090a1e 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -9,12 +9,22 @@ #include #include #include +#include #include #include +#include "filewidget.hpp" +#include "adjusterwidget.hpp" + +#include + CSVDoc::FileDialog::FileDialog(QWidget *parent) : - ContentSelector(parent) + ContentSelector(parent), + mFileWidget (new FileWidget (this)), + mAdjusterWidget (new AdjusterWidget (this)), + mEnable_1(false), + mEnable_2(false) { // Hide the profile elements profileGroupBox->hide(); @@ -22,12 +32,20 @@ CSVDoc::FileDialog::FileDialog(QWidget *parent) : resize(400, 400); - connect(projectNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateCreateButton(QString))); + mFileWidget->setType(true); + mFileWidget->extensionLabelIsVisible(false); connect(projectCreateButton, SIGNAL(clicked()), this, SIGNAL(createNewFile())); connect(projectButtonBox, SIGNAL(accepted()), this, SIGNAL(openFiles())); connect(projectButtonBox, SIGNAL(rejected()), this, SLOT(reject())); + + connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)), + mAdjusterWidget, SLOT (setName (const QString&, bool))); + + connect (mAdjusterWidget, SIGNAL (stateChanged (bool)), this, SLOT (slotAdjusterChanged(bool))); + connect (this, SIGNAL (signalGameFileChanged(int)), this, SLOT (slotGameFileSelected(int))); + connect (this, SIGNAL (signalUpdateCreateButton(bool, int)), this, SLOT (slotEnableCreateButton(bool, int))); } void CSVDoc::FileDialog::updateOpenButton(const QStringList &items) @@ -40,24 +58,30 @@ void CSVDoc::FileDialog::updateOpenButton(const QStringList &items) openButton->setEnabled(!items.isEmpty()); } -void CSVDoc::FileDialog::updateCreateButton(const QString &name) +void CSVDoc::FileDialog::slotEnableCreateButton(bool enable, int widgetNumber) { - if (!projectCreateButton->isVisible()) - return; - projectCreateButton->setEnabled(!name.isEmpty()); + if (widgetNumber == 1) + mEnable_1 = enable; + + if (widgetNumber == 2) + mEnable_2 = enable; + + qDebug() << "update enabled" << mEnable_1 << mEnable_2 << enable; + projectCreateButton->setEnabled(mEnable_1 && mEnable_2); } QString CSVDoc::FileDialog::fileName() { - return projectNameLineEdit->text(); + return mFileWidget->getName(); } void CSVDoc::FileDialog::openFile() { setWindowTitle(tr("Open")); - projectNameLineEdit->hide(); + mFileWidget->hide(); + adjusterWidgetFrame->hide(); projectCreateButton->hide(); projectGroupBox->setTitle(tr("")); projectButtonBox->button(QDialogButtonBox::Open)->setEnabled(false); @@ -71,6 +95,9 @@ void CSVDoc::FileDialog::newFile() { setWindowTitle(tr("New")); + fileWidgetFrame->layout()->addWidget(mFileWidget); + adjusterWidgetFrame->layout()->addWidget(mAdjusterWidget); + projectButtonBox->setStandardButtons(QDialogButtonBox::Cancel); projectButtonBox->addButton(projectCreateButton, QDialogButtonBox::ActionRole); @@ -78,3 +105,13 @@ void CSVDoc::FileDialog::newFile() raise(); activateWindow(); } + +void CSVDoc::FileDialog::slotAdjusterChanged(bool value) +{ + emit signalUpdateCreateButton(mAdjusterWidget->isValid(), 2); +} + +void CSVDoc::FileDialog::slotGameFileSelected(int value) +{ + emit signalUpdateCreateButton(value > -1, 1); +} diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index d0c3461b9f..7782dd94ea 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -26,9 +26,19 @@ namespace ContentSelectorView namespace CSVDoc { + class FileWidget; + class AdjusterWidget; + class FileDialog : public ContentSelectorView::ContentSelector { Q_OBJECT + + FileWidget *mFileWidget; + AdjusterWidget *mAdjusterWidget; + + bool mEnable_1; + bool mEnable_2; + public: explicit FileDialog(QWidget *parent = 0); @@ -41,12 +51,17 @@ namespace CSVDoc void openFiles(); void createNewFile(); + void signalUpdateCreateButton (bool, int); + void signalUpdateCreateButtonFlags(int); + public slots: private slots: //void updateViews(); void updateOpenButton(const QStringList &items); - void updateCreateButton(const QString &name); + void slotEnableCreateButton(bool enable, int widgetNumber); + void slotAdjusterChanged(bool value); + void slotGameFileSelected(int value); }; } #endif // FILEDIALOG_HPP diff --git a/apps/opencs/view/doc/filewidget.cpp b/apps/opencs/view/doc/filewidget.cpp index c8f33e92d5..9cd2fad422 100644 --- a/apps/opencs/view/doc/filewidget.cpp +++ b/apps/opencs/view/doc/filewidget.cpp @@ -50,4 +50,9 @@ QString CSVDoc::FileWidget::getName() const void CSVDoc::FileWidget::textChanged (const QString& text) { emit nameChanged (getName(), mAddon); -} \ No newline at end of file +} + +void CSVDoc::FileWidget::extensionLabelIsVisible(bool visible) +{ + mType->setVisible(visible); +} diff --git a/apps/opencs/view/doc/filewidget.hpp b/apps/opencs/view/doc/filewidget.hpp index c51c29632e..ff09d71a39 100644 --- a/apps/opencs/view/doc/filewidget.hpp +++ b/apps/opencs/view/doc/filewidget.hpp @@ -27,6 +27,8 @@ namespace CSVDoc QString getName() const; + void extensionLabelIsVisible(bool visible); + private slots: void textChanged (const QString& text); diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 615e9a846d..e6ed0ec567 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -40,6 +40,7 @@ void ContentSelectorView::ContentSelector::buildGameFileView() gameFileView->setModel(mGameFileProxyModel); connect(gameFileView, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentGameFileIndexChanged(int))); + connect(gameFileView, SIGNAL(currentIndexChanged(int)), this, SIGNAL(signalGameFileChanged(int))); gameFileView->setCurrentIndex(-1); gameFileView->setCurrentIndex(0); @@ -120,12 +121,14 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i if (proxy) proxy->setDynamicSortFilter(true); + + emit signalGameFileChanged(true); } void ContentSelectorView::ContentSelector::slotAddonTableItemClicked(const QModelIndex &index) { QAbstractItemModel *const model = addonView->model(); - QSortFilterProxyModel *proxy = dynamic_cast(model); + //QSortFilterProxyModel *proxy = dynamic_cast(model); if (model->data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked) model->setData(index, Qt::Checked, Qt::CheckStateRole); diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index 8032b04495..5af53dc464 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -40,6 +40,7 @@ namespace ContentSelectorView signals: void profileChanged(int index); + void signalGameFileChanged(int value); private slots: void updateViews(); diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 82d00922b0..9494077591 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -10,6 +10,12 @@ 313 + + + 0 + 0 + + Qt::DefaultContextMenu @@ -97,15 +103,44 @@
+ + + 0 + 0 + + Project + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + - - - Enter project name... + + + + 0 + 0 + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 0 + + + 0 + + @@ -126,9 +161,33 @@
- projectButtonBox - projectCreateButton - projectNameLineEdit +
+
+ + + + + 1 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + + 0 + + + 0 + + @@ -249,14 +308,9 @@
- EsxView::ProfilesComboBox + ContentSelectorView::ProfilesComboBox QComboBox -
components/contentselector/view/profilescombobox.hpp
-
- - EsxView::LineEdit - QLineEdit -
components/contentselector/view/lineedit.hpp
+
components/contentselector/view/profilescombobox.hpp
From 63e0cf5154572f90b0c4234a64703d6056464123 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 23 Sep 2013 11:26:29 +0200 Subject: [PATCH 045/148] fixed missing initialisation of adjuster widget in file dialogue --- apps/opencs/editor.cpp | 1 + apps/opencs/view/doc/filedialog.cpp | 5 +++++ apps/opencs/view/doc/filedialog.hpp | 9 +++++++++ apps/opencs/view/doc/newgame.hpp | 3 +++ 4 files changed, 18 insertions(+) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index ba1dfb57ec..104afa03b5 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -17,6 +17,7 @@ CS::Editor::Editor() : mViewManager (mDocumentManager) setupDataFiles(); mNewGame.setLocalData (mLocal); + mFileDialog.setLocalData (mLocal); connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ())); connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ())); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 9dce090a1e..4d7c5bc159 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -48,6 +48,11 @@ CSVDoc::FileDialog::FileDialog(QWidget *parent) : connect (this, SIGNAL (signalUpdateCreateButton(bool, int)), this, SLOT (slotEnableCreateButton(bool, int))); } +void CSVDoc::FileDialog::setLocalData (const boost::filesystem::path& localData) +{ + mAdjusterWidget->setLocalData (localData); +} + void CSVDoc::FileDialog::updateOpenButton(const QStringList &items) { QPushButton *openButton = projectButtonBox->button(QDialogButtonBox::Open); diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 7782dd94ea..0c914b932a 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -4,9 +4,16 @@ #include #include +#include + #include "components/contentselector/view/contentselector.hpp" #include "ui_datafilespage.h" +#ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED +#define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED +Q_DECLARE_METATYPE (boost::filesystem::path) +#endif + class QDialogButtonBox; class QSortFilterProxyModel; class QAbstractItemModel; @@ -42,6 +49,8 @@ namespace CSVDoc public: explicit FileDialog(QWidget *parent = 0); + void setLocalData (const boost::filesystem::path& localData); + void openFile(); void newFile(); diff --git a/apps/opencs/view/doc/newgame.hpp b/apps/opencs/view/doc/newgame.hpp index aa97682ff6..9ad7ea1690 100644 --- a/apps/opencs/view/doc/newgame.hpp +++ b/apps/opencs/view/doc/newgame.hpp @@ -6,7 +6,10 @@ #include #include +#ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED +#define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED Q_DECLARE_METATYPE (boost::filesystem::path) +#endif class QPushButton; From 74d683b5303e5b95a34fec6d082b9999aacecc3b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 23 Sep 2013 11:58:11 +0200 Subject: [PATCH 046/148] fixed save path for newly created addons --- apps/opencs/editor.cpp | 9 ++++----- apps/opencs/editor.hpp | 2 +- apps/opencs/view/doc/filedialog.cpp | 9 +++++++-- apps/opencs/view/doc/filedialog.hpp | 3 ++- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 104afa03b5..800f3984e9 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -30,7 +30,8 @@ CS::Editor::Editor() : mViewManager (mDocumentManager) connect (&mStartup, SIGNAL (editConfig()), this, SLOT (showSettings ())); connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles())); - connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile())); + connect (&mFileDialog, SIGNAL(createNewFile (const boost::filesystem::path&)), + this, SLOT(createNewFile (const boost::filesystem::path&))); connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)), this, SLOT (createNewGame (const boost::filesystem::path&))); @@ -138,7 +139,7 @@ void CS::Editor::openFiles() mFileDialog.hide(); } -void CS::Editor::createNewFile() +void CS::Editor::createNewFile (const boost::filesystem::path& savePath) { std::vector files; QStringList paths = mFileDialog.checkedItemsPaths(); @@ -149,9 +150,7 @@ void CS::Editor::createNewFile() files.push_back(mFileDialog.fileName().toStdString()); - /// \todo Get the save path from the file dialogue. - - CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), true); + CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, true); mViewManager.addView (document); mFileDialog.hide(); diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 248ebf2c54..abf9496e4b 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -60,7 +60,7 @@ namespace CS void loadDocument(); void openFiles(); - void createNewFile(); + void createNewFile (const boost::filesystem::path& savePath); void createNewGame (const boost::filesystem::path& file); void showStartup(); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 4d7c5bc159..4426d2e49c 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -35,7 +35,7 @@ CSVDoc::FileDialog::FileDialog(QWidget *parent) : mFileWidget->setType(true); mFileWidget->extensionLabelIsVisible(false); - connect(projectCreateButton, SIGNAL(clicked()), this, SIGNAL(createNewFile())); + connect(projectCreateButton, SIGNAL(clicked()), this, SLOT(createNewFile())); connect(projectButtonBox, SIGNAL(accepted()), this, SIGNAL(openFiles())); connect(projectButtonBox, SIGNAL(rejected()), this, SLOT(reject())); @@ -118,5 +118,10 @@ void CSVDoc::FileDialog::slotAdjusterChanged(bool value) void CSVDoc::FileDialog::slotGameFileSelected(int value) { - emit signalUpdateCreateButton(value > -1, 1); + emit signalUpdateCreateButton(value > -1, 1); } + +void CSVDoc::FileDialog::createNewFile() +{ + emit createNewFile (mAdjusterWidget->getPath()); +} \ No newline at end of file diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 0c914b932a..37119ff0d0 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -58,7 +58,7 @@ namespace CSVDoc signals: void openFiles(); - void createNewFile(); + void createNewFile (const boost::filesystem::path& savePath); void signalUpdateCreateButton (bool, int); void signalUpdateCreateButtonFlags(int); @@ -71,6 +71,7 @@ namespace CSVDoc void slotEnableCreateButton(bool enable, int widgetNumber); void slotAdjusterChanged(bool value); void slotGameFileSelected(int value); + void createNewFile(); }; } #endif // FILEDIALOG_HPP From 6d9ff39390f5495535c2b18c2664554836cf83aa Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 23 Sep 2013 12:16:56 +0200 Subject: [PATCH 047/148] set dependencies when saving (requires further refinements) --- apps/opencs/model/doc/document.cpp | 7 ++++++- apps/opencs/model/doc/document.hpp | 5 +++++ apps/opencs/model/doc/savingstages.cpp | 13 ++++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 525f18a206..37294bcd16 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2141,7 +2141,7 @@ void CSMDoc::Document::createBase() CSMDoc::Document::Document (const std::vector& files, const boost::filesystem::path& savePath, bool new_) -: mSavePath (savePath), mTools (mData), mSaving (*this) +: mSavePath (savePath), mContentFiles (files), mTools (mData), mSaving (*this) { if (files.empty()) throw std::runtime_error ("Empty content file sequence"); @@ -2200,6 +2200,11 @@ const boost::filesystem::path& CSMDoc::Document::getSavePath() const return mSavePath; } +const std::vector& CSMDoc::Document::getContentFiles() const +{ + return mContentFiles; +} + void CSMDoc::Document::save() { if (mSaving.isRunning()) diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 5a03955105..b1c6a02731 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -33,6 +33,7 @@ namespace CSMDoc private: boost::filesystem::path mSavePath; + std::vector mContentFiles; CSMWorld::Data mData; CSMTools::Tools mTools; Saving mSaving; @@ -74,6 +75,10 @@ namespace CSMDoc const boost::filesystem::path& getSavePath() const; + const std::vector& getContentFiles() const; + ///< \attention The last element in this collection is the file that is being edited, + /// but with its original path instead of the save path. + void save(); CSMWorld::UniversalId verify(); diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index 797b32eae8..fd62345940 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -50,7 +50,18 @@ void CSMDoc::WriteHeaderStage::perform (int stage, std::vector& mes mState.getWriter().setDescription (""); mState.getWriter().setRecordCount (0); - /// \todo fill in dependency list + /// \todo refine dependency list (at least remove redundant dependencies) + std::vector dependencies = mDocument.getContentFiles(); + std::vector::const_iterator end (--dependencies.end()); + + for (std::vector::const_iterator iter (dependencies.begin()); + iter!=end; ++iter) + { + std::string name = iter->filename().string(); + uint64_t size = boost::filesystem::file_size (*iter); + + mState.getWriter().addMaster (name, size); + } mState.getWriter().save (mState.getStream()); } From d7cff6361e091990e7b3a90fc94f824284543c13 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Mon, 23 Sep 2013 06:51:49 -0500 Subject: [PATCH 048/148] Fixed filter issue (all addons for a gamefile are enabled for checking). Note: Other dependencies are not yet automatically selected when an addon is checked. --- apps/opencs/view/doc/adjusterwidget.hpp | 1 - apps/opencs/view/doc/filedialog.cpp | 3 +- .../contentselector/model/contentmodel.cpp | 32 +++++++++++++------ .../contentselector/model/contentmodel.hpp | 3 +- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/apps/opencs/view/doc/adjusterwidget.hpp b/apps/opencs/view/doc/adjusterwidget.hpp index d970cffee6..461cfb3452 100644 --- a/apps/opencs/view/doc/adjusterwidget.hpp +++ b/apps/opencs/view/doc/adjusterwidget.hpp @@ -25,7 +25,6 @@ namespace CSVDoc void setLocalData (const boost::filesystem::path& localData); - QString getText() const; bool isValid() const; boost::filesystem::path getPath() const; diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 9dce090a1e..68aab27d5f 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -67,7 +67,6 @@ void CSVDoc::FileDialog::slotEnableCreateButton(bool enable, int widgetNumber) if (widgetNumber == 2) mEnable_2 = enable; - qDebug() << "update enabled" << mEnable_1 << mEnable_2 << enable; projectCreateButton->setEnabled(mEnable_1 && mEnable_2); } @@ -113,5 +112,5 @@ void CSVDoc::FileDialog::slotAdjusterChanged(bool value) void CSVDoc::FileDialog::slotGameFileSelected(int value) { - emit signalUpdateCreateButton(value > -1, 1); + emit signalUpdateCreateButton(value > -1, 1); } diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index b85da25c67..c1394ef47d 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -65,7 +65,7 @@ ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(int row) return 0; } -const ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::findItem(const QString &name) const +const ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(const QString &name) const { foreach (const EsmFile *file, mFiles) { @@ -99,7 +99,7 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index if (canBeChecked(file)) return Qt::ItemIsEnabled | mDragDropFlags | mDefaultFlags; - return mDefaultFlags; + return Qt::NoItemFlags; } QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int role) const @@ -167,7 +167,7 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int if (file->isGameFile()) return ContentType_GameFile; else - if (flags(index) & Qt::ItemIsEnabled) + if (flags(index) & mDefaultFlags) return ContentType_Addon; break; @@ -373,12 +373,26 @@ bool ContentSelectorModel::ContentModel::dropMimeData(const QMimeData *data, Qt: bool ContentSelectorModel::ContentModel::canBeChecked(const EsmFile *file) const { - //element can be checked if all its dependencies are - foreach (const QString &gamefile, file->gameFiles()) - if (!isChecked(gamefile)) - return false; + //game files can always be checked + if (file->isGameFile()) + return true; - return true; + //addon can be checked if its gamefile is + foreach (const QString &fileName, file->gameFiles()) + { + const EsmFile *dependency = item(fileName); + + if (!dependency) + continue; + + if (dependency->isGameFile()) + { + if (isChecked(fileName)) + return true; + } + } + + return false; } void ContentSelectorModel::ContentModel::addFile(EsmFile *file) @@ -422,7 +436,7 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) // Put the file in the table - if (findItem(path) == 0) + if (item(path) == 0) addFile(file); } catch(std::runtime_error &e) { diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index a8ff103da7..a2a57f850a 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -42,7 +42,7 @@ namespace ContentSelectorModel void addFiles(const QString &path); QModelIndex indexFromItem(const EsmFile *item) const; - const EsmFile *findItem(const QString &name) const; + const EsmFile *item(const QString &name) const; bool isChecked(const QString &name) const; void setCheckState(const QString &name, bool isChecked); @@ -54,6 +54,7 @@ namespace ContentSelectorModel void addFile(EsmFile *file); const EsmFile *item(int row) const; EsmFile *item(int row); + bool canBeChecked(const EsmFile *file) const; ContentFileList mFiles; From 9d358dd44c4df478ac90650338809321aa837aa8 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Mon, 23 Sep 2013 22:01:44 -0500 Subject: [PATCH 049/148] Further implemented auto-checking / unchecking of dependencies --- .../contentselector/model/contentmodel.cpp | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index c1394ef47d..db6431810d 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -458,17 +458,51 @@ bool ContentSelectorModel::ContentModel::isChecked(const QString& name) const return false; } -void ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool isChecked) +void ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool checkState) { if (name.isEmpty()) return; Qt::CheckState state = Qt::Unchecked; - if (isChecked) + if (checkState) state = Qt::Checked; mCheckStates[name] = state; + + const EsmFile *file = item(name); + + if (state == Qt::Checked) + { + foreach (const QString &upstreamName, file->gameFiles()) + { + const EsmFile *upstreamFile = item(upstreamName); + + if (!upstreamFile) + continue; + + if (!isChecked(upstreamName)) + { + mCheckStates[upstreamName] = Qt::Checked; + emit dataChanged(indexFromItem(upstreamFile), indexFromItem(upstreamFile)); + } + + } + } + else if (state == Qt::Unchecked) + { + foreach (const EsmFile *downstreamFile, mFiles) + { + if (downstreamFile->gameFiles().contains(name)) + { + if (mCheckStates.contains(downstreamFile->fileName())) + { + mCheckStates[downstreamFile->fileName()] = Qt::Unchecked; + emit dataChanged(indexFromItem(downstreamFile), indexFromItem(downstreamFile)); + } + } + } + } } ContentSelectorModel::ContentFileList ContentSelectorModel::ContentModel::checkedItems() const From c42e74dadf1160a908dff63a4a1c48763a766202 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 24 Sep 2013 13:17:28 +0200 Subject: [PATCH 050/148] make 4 byte record type accessable from record struct --- apps/opencs/model/doc/saving.cpp | 24 +++++++++++------------- apps/opencs/model/doc/savingstages.hpp | 12 ++++-------- components/esm/loadacti.cpp | 3 +++ components/esm/loadacti.hpp | 2 ++ components/esm/loadalch.cpp | 3 +++ components/esm/loadalch.hpp | 2 ++ components/esm/loadappa.cpp | 3 +++ components/esm/loadappa.hpp | 2 ++ components/esm/loadarmo.cpp | 3 +++ components/esm/loadarmo.hpp | 2 ++ components/esm/loadbody.cpp | 3 +++ components/esm/loadbody.hpp | 2 ++ components/esm/loadbook.cpp | 2 ++ components/esm/loadbook.hpp | 2 ++ components/esm/loadbsgn.cpp | 2 ++ components/esm/loadbsgn.hpp | 2 ++ components/esm/loadcell.cpp | 2 ++ components/esm/loadcell.hpp | 3 ++- components/esm/loadclas.cpp | 2 ++ components/esm/loadclas.hpp | 2 ++ components/esm/loadclot.cpp | 2 ++ components/esm/loadclot.hpp | 2 ++ components/esm/loadcont.cpp | 3 +++ components/esm/loadcont.hpp | 2 ++ components/esm/loadcrea.cpp | 3 +++ components/esm/loadcrea.hpp | 2 ++ components/esm/loadcrec.hpp | 2 ++ components/esm/loaddial.cpp | 2 ++ components/esm/loaddial.hpp | 2 ++ components/esm/loaddoor.cpp | 2 ++ components/esm/loaddoor.hpp | 2 ++ components/esm/loadench.cpp | 2 ++ components/esm/loadench.hpp | 2 ++ components/esm/loadfact.cpp | 3 +++ components/esm/loadfact.hpp | 2 ++ components/esm/loadglob.cpp | 4 ++++ components/esm/loadglob.hpp | 2 ++ components/esm/loadgmst.cpp | 4 ++++ components/esm/loadgmst.hpp | 2 ++ components/esm/loadinfo.cpp | 2 ++ components/esm/loadinfo.hpp | 2 ++ components/esm/loadingr.cpp | 2 ++ components/esm/loadingr.hpp | 2 ++ components/esm/loadland.cpp | 2 ++ components/esm/loadland.hpp | 2 ++ components/esm/loadlevlist.cpp | 5 +++++ components/esm/loadlevlist.hpp | 4 ++++ components/esm/loadligh.cpp | 2 ++ components/esm/loadligh.hpp | 2 ++ components/esm/loadlock.cpp | 2 ++ components/esm/loadlock.hpp | 2 ++ components/esm/loadltex.cpp | 2 ++ components/esm/loadltex.hpp | 2 ++ components/esm/loadmgef.cpp | 2 ++ components/esm/loadmgef.hpp | 2 ++ components/esm/loadmisc.cpp | 2 ++ components/esm/loadmisc.hpp | 2 ++ components/esm/loadnpc.cpp | 2 ++ components/esm/loadnpc.hpp | 2 ++ components/esm/loadnpcc.hpp | 2 ++ components/esm/loadpgrd.cpp | 2 ++ components/esm/loadpgrd.hpp | 2 ++ components/esm/loadprob.cpp | 2 ++ components/esm/loadprob.hpp | 2 ++ components/esm/loadrace.cpp | 3 +++ components/esm/loadrace.hpp | 2 ++ components/esm/loadregn.cpp | 2 ++ components/esm/loadregn.hpp | 2 ++ components/esm/loadrepa.cpp | 2 ++ components/esm/loadrepa.hpp | 2 ++ components/esm/loadscpt.cpp | 3 +++ components/esm/loadscpt.hpp | 2 ++ components/esm/loadskil.cpp | 3 +++ components/esm/loadskil.hpp | 2 ++ components/esm/loadsndg.cpp | 2 ++ components/esm/loadsndg.hpp | 2 ++ components/esm/loadsoun.cpp | 2 ++ components/esm/loadsoun.hpp | 2 ++ components/esm/loadspel.cpp | 2 ++ components/esm/loadspel.hpp | 2 ++ components/esm/loadsscr.cpp | 2 ++ components/esm/loadsscr.hpp | 2 ++ components/esm/loadstat.cpp | 2 ++ components/esm/loadstat.hpp | 2 ++ components/esm/loadtes3.cpp | 1 + components/esm/loadweap.cpp | 2 ++ components/esm/loadweap.hpp | 2 ++ 87 files changed, 204 insertions(+), 22 deletions(-) diff --git a/apps/opencs/model/doc/saving.cpp b/apps/opencs/model/doc/saving.cpp index 7e0b10d66b..c9035cfd43 100644 --- a/apps/opencs/model/doc/saving.cpp +++ b/apps/opencs/model/doc/saving.cpp @@ -1,8 +1,6 @@ #include "saving.hpp" -#include - #include "../world/data.hpp" #include "../world/idcollection.hpp" @@ -18,37 +16,37 @@ CSMDoc::Saving::Saving (Document& document) appendStage (new WriteHeaderStage (mDocument, mState)); appendStage (new WriteCollectionStage > - (mDocument.getData().getGlobals(), mState, ESM::REC_GLOB)); + (mDocument.getData().getGlobals(), mState)); appendStage (new WriteCollectionStage > - (mDocument.getData().getGmsts(), mState, ESM::REC_GMST)); + (mDocument.getData().getGmsts(), mState)); appendStage (new WriteCollectionStage > - (mDocument.getData().getSkills(), mState, ESM::REC_SKIL)); + (mDocument.getData().getSkills(), mState)); appendStage (new WriteCollectionStage > - (mDocument.getData().getClasses(), mState, ESM::REC_CLAS)); + (mDocument.getData().getClasses(), mState)); appendStage (new WriteCollectionStage > - (mDocument.getData().getFactions(), mState, ESM::REC_FACT)); + (mDocument.getData().getFactions(), mState)); appendStage (new WriteCollectionStage > - (mDocument.getData().getRaces(), mState, ESM::REC_RACE)); + (mDocument.getData().getRaces(), mState)); appendStage (new WriteCollectionStage > - (mDocument.getData().getSounds(), mState, ESM::REC_SOUN)); + (mDocument.getData().getSounds(), mState)); appendStage (new WriteCollectionStage > - (mDocument.getData().getScripts(), mState, ESM::REC_SCPT)); + (mDocument.getData().getScripts(), mState)); appendStage (new WriteCollectionStage > - (mDocument.getData().getRegions(), mState, ESM::REC_REGN)); + (mDocument.getData().getRegions(), mState)); appendStage (new WriteCollectionStage > - (mDocument.getData().getBirthsigns(), mState, ESM::REC_BSGN)); + (mDocument.getData().getBirthsigns(), mState)); appendStage (new WriteCollectionStage > - (mDocument.getData().getSpells(), mState, ESM::REC_SPEL)); + (mDocument.getData().getSpells(), mState)); appendStage (new CloseSaveStage (mState)); diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp index 96b1fe17f6..d5c4a69af7 100644 --- a/apps/opencs/model/doc/savingstages.hpp +++ b/apps/opencs/model/doc/savingstages.hpp @@ -1,8 +1,6 @@ #ifndef CSM_DOC_SAVINGSTAGES_H #define CSM_DOC_SAVINGSTAGES_H -#include - #include "stage.hpp" #include "savingstate.hpp" @@ -52,12 +50,10 @@ namespace CSMDoc { const CollectionT& mCollection; SavingState& mState; - ESM::RecNameInts mRecordType; public: - WriteCollectionStage (const CollectionT& collection, SavingState& state, - ESM::RecNameInts recordType); + WriteCollectionStage (const CollectionT& collection, SavingState& state); virtual int setup(); ///< \return number of steps @@ -68,8 +64,8 @@ namespace CSMDoc template WriteCollectionStage::WriteCollectionStage (const CollectionT& collection, - SavingState& state, ESM::RecNameInts recordType) - : mCollection (collection), mState (state), mRecordType (recordType) + SavingState& state) + : mCollection (collection), mState (state) {} template @@ -89,7 +85,7 @@ namespace CSMDoc std::string type; for (int i=0; i<4; ++i) /// \todo make endianess agnostic (change ESMWriter interface?) - type += reinterpret_cast (&mRecordType)[i]; + type += reinterpret_cast (&mCollection.getRecord (stage).mModified.sRecordId)[i]; mState.getWriter().startRecord (type); mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage)); diff --git a/components/esm/loadacti.cpp b/components/esm/loadacti.cpp index dcae845d0a..6ba0df0b36 100644 --- a/components/esm/loadacti.cpp +++ b/components/esm/loadacti.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Activator::sRecordId = REC_ACTI; + void Activator::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); diff --git a/components/esm/loadacti.hpp b/components/esm/loadacti.hpp index 6b072ee11e..88f27de27e 100644 --- a/components/esm/loadacti.hpp +++ b/components/esm/loadacti.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct Activator { + static unsigned int sRecordId; + std::string mId, mName, mScript, mModel; void load(ESMReader &esm); diff --git a/components/esm/loadalch.cpp b/components/esm/loadalch.cpp index 187069c2ef..f6bfc6a11d 100644 --- a/components/esm/loadalch.cpp +++ b/components/esm/loadalch.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Potion::sRecordId = REC_ALCH; + void Potion::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); diff --git a/components/esm/loadalch.hpp b/components/esm/loadalch.hpp index 8f0435292e..141765aa82 100644 --- a/components/esm/loadalch.hpp +++ b/components/esm/loadalch.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Potion { + static unsigned int sRecordId; + struct ALDTstruct { float mWeight; diff --git a/components/esm/loadappa.cpp b/components/esm/loadappa.cpp index 01233a0558..29ea78acc0 100644 --- a/components/esm/loadappa.cpp +++ b/components/esm/loadappa.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Apparatus::sRecordId = REC_APPA; + void Apparatus::load(ESMReader &esm) { // we will not treat duplicated subrecords as errors here diff --git a/components/esm/loadappa.hpp b/components/esm/loadappa.hpp index d47643c6ce..adc8e071f1 100644 --- a/components/esm/loadappa.hpp +++ b/components/esm/loadappa.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct Apparatus { + static unsigned int sRecordId; + enum AppaType { MortarPestle = 0, diff --git a/components/esm/loadarmo.cpp b/components/esm/loadarmo.cpp index 4dbdf1314d..ec8ff4f20c 100644 --- a/components/esm/loadarmo.cpp +++ b/components/esm/loadarmo.cpp @@ -2,6 +2,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -28,6 +29,8 @@ void PartReferenceList::save(ESMWriter &esm) const } } +unsigned int Armor::sRecordId = REC_ARMO; + void Armor::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); diff --git a/components/esm/loadarmo.hpp b/components/esm/loadarmo.hpp index 5a38605e3d..991f4e1855 100644 --- a/components/esm/loadarmo.hpp +++ b/components/esm/loadarmo.hpp @@ -61,6 +61,8 @@ struct PartReferenceList struct Armor { + static unsigned int sRecordId; + enum Type { Helmet = 0, diff --git a/components/esm/loadbody.cpp b/components/esm/loadbody.cpp index a5d986f65d..4015e6c91a 100644 --- a/components/esm/loadbody.cpp +++ b/components/esm/loadbody.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int BodyPart::sRecordId = REC_BODY; + void BodyPart::load(ESMReader &esm) { diff --git a/components/esm/loadbody.hpp b/components/esm/loadbody.hpp index a8fd36aef4..9623caa31e 100644 --- a/components/esm/loadbody.hpp +++ b/components/esm/loadbody.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct BodyPart { + static unsigned int sRecordId; + enum MeshPart { MP_Head = 0, diff --git a/components/esm/loadbook.cpp b/components/esm/loadbook.cpp index d9db118899..c8b7e94789 100644 --- a/components/esm/loadbook.cpp +++ b/components/esm/loadbook.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Book::sRecordId = REC_BOOK; void Book::load(ESMReader &esm) { diff --git a/components/esm/loadbook.hpp b/components/esm/loadbook.hpp index 688e9dd75e..f96fbd709b 100644 --- a/components/esm/loadbook.hpp +++ b/components/esm/loadbook.hpp @@ -14,6 +14,8 @@ class ESMWriter; struct Book { + static unsigned int sRecordId; + struct BKDTstruct { float mWeight; diff --git a/components/esm/loadbsgn.cpp b/components/esm/loadbsgn.cpp index 9d19f02c7e..55e1e7f65a 100644 --- a/components/esm/loadbsgn.cpp +++ b/components/esm/loadbsgn.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int BirthSign::sRecordId = REC_BSGN; void BirthSign::load(ESMReader &esm) { diff --git a/components/esm/loadbsgn.hpp b/components/esm/loadbsgn.hpp index 1ecb5e418e..9f9435c8f1 100644 --- a/components/esm/loadbsgn.hpp +++ b/components/esm/loadbsgn.hpp @@ -13,6 +13,8 @@ class ESMWriter; struct BirthSign { + static unsigned int sRecordId; + std::string mId, mName, mDescription, mTexture; // List of powers and abilities that come with this birth sign. diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index 9b3aa09e86..c22c1b22b6 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -7,9 +7,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Cell::sRecordId = REC_CELL; /// Some overloaded compare operators. bool operator==(const MovedCellRef& ref, int pRefnum) diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index c417fceab8..61d586b9d8 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -16,7 +16,6 @@ namespace MWWorld namespace ESM { - class ESMReader; class ESMWriter; @@ -55,6 +54,8 @@ typedef std::list CellRefTracker; */ struct Cell { + static unsigned int sRecordId; + enum Flags { Interior = 0x01, // Interior cell diff --git a/components/esm/loadclas.cpp b/components/esm/loadclas.cpp index ef07430c7a..33489eec46 100644 --- a/components/esm/loadclas.cpp +++ b/components/esm/loadclas.cpp @@ -4,9 +4,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Class::sRecordId = REC_CLAS; const Class::Specialization Class::sSpecializationIds[3] = { Class::Combat, diff --git a/components/esm/loadclas.hpp b/components/esm/loadclas.hpp index f241dca8da..3e489bb58a 100644 --- a/components/esm/loadclas.hpp +++ b/components/esm/loadclas.hpp @@ -17,6 +17,8 @@ class ESMWriter; // class struct Class { + static unsigned int sRecordId; + enum AutoCalc { Weapon = 0x00001, diff --git a/components/esm/loadclot.cpp b/components/esm/loadclot.cpp index c623155dfa..d64564d77f 100644 --- a/components/esm/loadclot.cpp +++ b/components/esm/loadclot.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Clothing::sRecordId = REC_CLOT; void Clothing::load(ESMReader &esm) { diff --git a/components/esm/loadclot.hpp b/components/esm/loadclot.hpp index 13fae865b4..50896622aa 100644 --- a/components/esm/loadclot.hpp +++ b/components/esm/loadclot.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Clothing { + static unsigned int sRecordId; + enum Type { Pants = 0, diff --git a/components/esm/loadcont.cpp b/components/esm/loadcont.cpp index 0cbb4acd1e..7bdf9f05b1 100644 --- a/components/esm/loadcont.cpp +++ b/components/esm/loadcont.cpp @@ -2,6 +2,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -24,6 +25,8 @@ void InventoryList::save(ESMWriter &esm) const } } + unsigned int Container::sRecordId = REC_CONT; + void Container::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); diff --git a/components/esm/loadcont.hpp b/components/esm/loadcont.hpp index c854b52907..2808b67b56 100644 --- a/components/esm/loadcont.hpp +++ b/components/esm/loadcont.hpp @@ -32,6 +32,8 @@ struct InventoryList struct Container { + static unsigned int sRecordId; + enum Flags { Organic = 1, // Objects cannot be placed in this container diff --git a/components/esm/loadcrea.cpp b/components/esm/loadcrea.cpp index 30b70b35b6..650de08011 100644 --- a/components/esm/loadcrea.cpp +++ b/components/esm/loadcrea.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Creature::sRecordId = REC_CREA; + void Creature::load(ESMReader &esm) { mPersistent = esm.getRecordFlags() & 0x0400; diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index 80e0fbd1c9..99c4f52257 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -20,6 +20,8 @@ class ESMWriter; struct Creature { + static unsigned int sRecordId; + // Default is 0x48? enum Flags { diff --git a/components/esm/loadcrec.hpp b/components/esm/loadcrec.hpp index 2b840ccf46..280739acad 100644 --- a/components/esm/loadcrec.hpp +++ b/components/esm/loadcrec.hpp @@ -17,6 +17,8 @@ class ESMWriter; /// Changes a creature struct LoadCREC { + static unsigned int sRecordId; + std::string mId; void load(ESMReader &esm) diff --git a/components/esm/loaddial.cpp b/components/esm/loaddial.cpp index e014ca37e8..f64ecb5a06 100644 --- a/components/esm/loaddial.cpp +++ b/components/esm/loaddial.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Dialogue::sRecordId = REC_DIAL; void Dialogue::load(ESMReader &esm) { diff --git a/components/esm/loaddial.hpp b/components/esm/loaddial.hpp index 0fe5027dc9..3997d77537 100644 --- a/components/esm/loaddial.hpp +++ b/components/esm/loaddial.hpp @@ -19,6 +19,8 @@ class ESMWriter; struct Dialogue { + static unsigned int sRecordId; + enum Type { Topic = 0, diff --git a/components/esm/loaddoor.cpp b/components/esm/loaddoor.cpp index f666ac67a9..c56b063379 100644 --- a/components/esm/loaddoor.cpp +++ b/components/esm/loaddoor.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Door::sRecordId = REC_DOOR; void Door::load(ESMReader &esm) { diff --git a/components/esm/loaddoor.hpp b/components/esm/loaddoor.hpp index 2b927c56e0..ee2b7f7ac5 100644 --- a/components/esm/loaddoor.hpp +++ b/components/esm/loaddoor.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct Door { + static unsigned int sRecordId; + std::string mId, mName, mModel, mScript, mOpenSound, mCloseSound; void load(ESMReader &esm); diff --git a/components/esm/loadench.cpp b/components/esm/loadench.cpp index 4b4c3a1ec5..a1e885f23b 100644 --- a/components/esm/loadench.cpp +++ b/components/esm/loadench.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Enchantment::sRecordId = REC_ENCH; void Enchantment::load(ESMReader &esm) { diff --git a/components/esm/loadench.hpp b/components/esm/loadench.hpp index 3cdc3a0bd0..f6ba8c6ab3 100644 --- a/components/esm/loadench.hpp +++ b/components/esm/loadench.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Enchantment { + static unsigned int sRecordId; + enum Type { CastOnce = 0, diff --git a/components/esm/loadfact.cpp b/components/esm/loadfact.cpp index c8be518028..61fa902639 100644 --- a/components/esm/loadfact.cpp +++ b/components/esm/loadfact.cpp @@ -4,9 +4,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Faction::sRecordId = REC_FACT; + int& Faction::FADTstruct::getSkill (int index, bool ignored) { if (index<0 || index>=6) diff --git a/components/esm/loadfact.hpp b/components/esm/loadfact.hpp index 11f65a87f5..9c257e068e 100644 --- a/components/esm/loadfact.hpp +++ b/components/esm/loadfact.hpp @@ -29,6 +29,8 @@ struct RankData struct Faction { + static unsigned int sRecordId; + std::string mId, mName; struct FADTstruct diff --git a/components/esm/loadglob.cpp b/components/esm/loadglob.cpp index e1c2d44087..a78ed1a1be 100644 --- a/components/esm/loadglob.cpp +++ b/components/esm/loadglob.cpp @@ -1,7 +1,11 @@ #include "loadglob.hpp" +#include "defs.hpp" + namespace ESM { + unsigned int Global::sRecordId = REC_GLOB; + void Global::load (ESMReader &esm) { mValue.read (esm, ESM::Variant::Format_Global); diff --git a/components/esm/loadglob.hpp b/components/esm/loadglob.hpp index 06ff97ef20..51b2e2dc98 100644 --- a/components/esm/loadglob.hpp +++ b/components/esm/loadglob.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Global { + static unsigned int sRecordId; + std::string mId; Variant mValue; diff --git a/components/esm/loadgmst.cpp b/components/esm/loadgmst.cpp index 3a7df45065..21d66339a9 100644 --- a/components/esm/loadgmst.cpp +++ b/components/esm/loadgmst.cpp @@ -1,7 +1,11 @@ #include "loadgmst.hpp" +#include "defs.hpp" + namespace ESM { + unsigned int GameSetting::sRecordId = REC_GMST; + void GameSetting::load (ESMReader &esm) { mValue.read (esm, ESM::Variant::Format_Gmst); diff --git a/components/esm/loadgmst.hpp b/components/esm/loadgmst.hpp index 9c37c7da09..6b66ac832d 100644 --- a/components/esm/loadgmst.hpp +++ b/components/esm/loadgmst.hpp @@ -18,6 +18,8 @@ class ESMWriter; struct GameSetting { + static unsigned int sRecordId; + std::string mId; Variant mValue; diff --git a/components/esm/loadinfo.cpp b/components/esm/loadinfo.cpp index 1985da2cd9..4f248cc65f 100644 --- a/components/esm/loadinfo.cpp +++ b/components/esm/loadinfo.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int DialInfo::sRecordId = REC_INFO; void DialInfo::load(ESMReader &esm) { diff --git a/components/esm/loadinfo.hpp b/components/esm/loadinfo.hpp index 351768e96f..2589ea7b87 100644 --- a/components/esm/loadinfo.hpp +++ b/components/esm/loadinfo.hpp @@ -20,6 +20,8 @@ class ESMWriter; struct DialInfo { + static unsigned int sRecordId; + enum Gender { Male = 0, diff --git a/components/esm/loadingr.cpp b/components/esm/loadingr.cpp index 1bc9ae41c6..0e02433621 100644 --- a/components/esm/loadingr.cpp +++ b/components/esm/loadingr.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Ingredient::sRecordId = REC_INGR; void Ingredient::load(ESMReader &esm) { diff --git a/components/esm/loadingr.hpp b/components/esm/loadingr.hpp index 03e67924c5..85f2d5e7d2 100644 --- a/components/esm/loadingr.hpp +++ b/components/esm/loadingr.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct Ingredient { + static unsigned int sRecordId; + struct IRDTstruct { float mWeight; diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 8e54bcc5c9..ede200d79d 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Land::sRecordId = REC_LAND; void Land::LandData::save(ESMWriter &esm) { diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 3d3bcd67bb..5649f99801 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Land { + static unsigned int sRecordId; + Land(); ~Land(); diff --git a/components/esm/loadlevlist.cpp b/components/esm/loadlevlist.cpp index ab3f5e9e66..6385b9a718 100644 --- a/components/esm/loadlevlist.cpp +++ b/components/esm/loadlevlist.cpp @@ -2,6 +2,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -52,4 +53,8 @@ void LeveledListBase::save(ESMWriter &esm) const mChanceNone = 0; mList.clear(); } + + unsigned int CreatureLevList::sRecordId = REC_LEVC; + + unsigned int ItemLevList::sRecordId = REC_LEVI; } diff --git a/components/esm/loadlevlist.hpp b/components/esm/loadlevlist.hpp index f5fb7fd5b1..9dcc6177a1 100644 --- a/components/esm/loadlevlist.hpp +++ b/components/esm/loadlevlist.hpp @@ -59,6 +59,8 @@ struct LeveledListBase struct CreatureLevList: LeveledListBase { + static unsigned int sRecordId; + CreatureLevList() { mRecName = "CNAM"; @@ -67,6 +69,8 @@ struct CreatureLevList: LeveledListBase struct ItemLevList: LeveledListBase { + static unsigned int sRecordId; + ItemLevList() { mRecName = "INAM"; diff --git a/components/esm/loadligh.cpp b/components/esm/loadligh.cpp index 3f279c7c83..c02bb46b69 100644 --- a/components/esm/loadligh.cpp +++ b/components/esm/loadligh.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Light::sRecordId = REC_LIGH; void Light::load(ESMReader &esm) { diff --git a/components/esm/loadligh.hpp b/components/esm/loadligh.hpp index 9a341f0de5..74eb37197e 100644 --- a/components/esm/loadligh.hpp +++ b/components/esm/loadligh.hpp @@ -16,6 +16,8 @@ class ESMWriter; struct Light { + static unsigned int sRecordId; + enum Flags { Dynamic = 0x001, diff --git a/components/esm/loadlock.cpp b/components/esm/loadlock.cpp index 318769ec08..9ffce78a7d 100644 --- a/components/esm/loadlock.cpp +++ b/components/esm/loadlock.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Lockpick::sRecordId = REC_LOCK; void Lockpick::load(ESMReader &esm) { diff --git a/components/esm/loadlock.hpp b/components/esm/loadlock.hpp index aea5a4f31d..c44e2b0063 100644 --- a/components/esm/loadlock.hpp +++ b/components/esm/loadlock.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct Lockpick { + static unsigned int sRecordId; + struct Data { float mWeight; diff --git a/components/esm/loadltex.cpp b/components/esm/loadltex.cpp index dc1bc164b4..bd28c84883 100644 --- a/components/esm/loadltex.cpp +++ b/components/esm/loadltex.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int LandTexture::sRecordId = REC_LTEX; void LandTexture::load(ESMReader &esm) { diff --git a/components/esm/loadltex.hpp b/components/esm/loadltex.hpp index 3d08169484..5e84428b29 100644 --- a/components/esm/loadltex.hpp +++ b/components/esm/loadltex.hpp @@ -27,6 +27,8 @@ class ESMWriter; struct LandTexture { + static unsigned int sRecordId; + std::string mId, mTexture; int mIndex; diff --git a/components/esm/loadmgef.cpp b/components/esm/loadmgef.cpp index 9eaeff7046..332c27786f 100644 --- a/components/esm/loadmgef.cpp +++ b/components/esm/loadmgef.cpp @@ -6,6 +6,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace { @@ -34,6 +35,7 @@ namespace namespace ESM { + unsigned int MagicEffect::sRecordId = REC_MGEF; void MagicEffect::load(ESMReader &esm) { diff --git a/components/esm/loadmgef.hpp b/components/esm/loadmgef.hpp index 220b3bdf39..613cbd2d86 100644 --- a/components/esm/loadmgef.hpp +++ b/components/esm/loadmgef.hpp @@ -12,6 +12,8 @@ class ESMWriter; struct MagicEffect { + static unsigned int sRecordId; + enum Flags { TargetSkill = 0x1, // Affects a specific skill, which is specified elsewhere in the effect structure. diff --git a/components/esm/loadmisc.cpp b/components/esm/loadmisc.cpp index a183821b74..2ca09e8aec 100644 --- a/components/esm/loadmisc.cpp +++ b/components/esm/loadmisc.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Miscellaneous::sRecordId = REC_MISC; void Miscellaneous::load(ESMReader &esm) { diff --git a/components/esm/loadmisc.hpp b/components/esm/loadmisc.hpp index 452eea53e0..576bd18c0f 100644 --- a/components/esm/loadmisc.hpp +++ b/components/esm/loadmisc.hpp @@ -16,6 +16,8 @@ class ESMWriter; struct Miscellaneous { + static unsigned int sRecordId; + struct MCDTstruct { float mWeight; diff --git a/components/esm/loadnpc.cpp b/components/esm/loadnpc.cpp index 7c26cb549b..9fff2d885d 100644 --- a/components/esm/loadnpc.cpp +++ b/components/esm/loadnpc.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int NPC::sRecordId = REC_NPC_; void NPC::load(ESMReader &esm) { diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index 009548c596..d9e691669c 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -20,6 +20,8 @@ class ESMWriter; struct NPC { + static unsigned int sRecordId; + // Services enum Services { diff --git a/components/esm/loadnpcc.hpp b/components/esm/loadnpcc.hpp index f023fd2172..c87c2545f7 100644 --- a/components/esm/loadnpcc.hpp +++ b/components/esm/loadnpcc.hpp @@ -78,6 +78,8 @@ class ESMWriter; struct LoadNPCC { + static unsigned int sRecordId; + std::string mId; void load(ESMReader &esm) diff --git a/components/esm/loadpgrd.cpp b/components/esm/loadpgrd.cpp index 65d1f80553..3b5330e9fd 100644 --- a/components/esm/loadpgrd.cpp +++ b/components/esm/loadpgrd.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Pathgrid::sRecordId = REC_PGRD; void Pathgrid::load(ESMReader &esm) { diff --git a/components/esm/loadpgrd.hpp b/components/esm/loadpgrd.hpp index d14433a786..9ee49552db 100644 --- a/components/esm/loadpgrd.hpp +++ b/components/esm/loadpgrd.hpp @@ -15,6 +15,8 @@ class ESMWriter; */ struct Pathgrid { + static unsigned int sRecordId; + struct DATAstruct { int mX, mY; // Grid location, matches cell for exterior cells diff --git a/components/esm/loadprob.cpp b/components/esm/loadprob.cpp index 0fb4c97507..caa3d7e0e7 100644 --- a/components/esm/loadprob.cpp +++ b/components/esm/loadprob.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Probe::sRecordId = REC_PROB; void Probe::load(ESMReader &esm) { diff --git a/components/esm/loadprob.hpp b/components/esm/loadprob.hpp index d0a8256ab6..b89b2ddeb6 100644 --- a/components/esm/loadprob.hpp +++ b/components/esm/loadprob.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct Probe { + static unsigned int sRecordId; + struct Data { float mWeight; diff --git a/components/esm/loadrace.cpp b/components/esm/loadrace.cpp index e9e1d0d797..e50e43a744 100644 --- a/components/esm/loadrace.cpp +++ b/components/esm/loadrace.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Race::sRecordId = REC_RACE; + int Race::MaleFemale::getValue (bool male) const { return male ? mMale : mFemale; diff --git a/components/esm/loadrace.hpp b/components/esm/loadrace.hpp index a53a980701..7d5736d9b7 100644 --- a/components/esm/loadrace.hpp +++ b/components/esm/loadrace.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Race { + static unsigned int sRecordId; + struct SkillBonus { int mSkill; // SkillEnum diff --git a/components/esm/loadregn.cpp b/components/esm/loadregn.cpp index fd42b9ee83..fa4271e26a 100644 --- a/components/esm/loadregn.cpp +++ b/components/esm/loadregn.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Region::sRecordId = REC_REGN; void Region::load(ESMReader &esm) { diff --git a/components/esm/loadregn.hpp b/components/esm/loadregn.hpp index a6075d65ae..1992c951b4 100644 --- a/components/esm/loadregn.hpp +++ b/components/esm/loadregn.hpp @@ -18,6 +18,8 @@ class ESMWriter; struct Region { + static unsigned int sRecordId; + #pragma pack(push) #pragma pack(1) struct WEATstruct diff --git a/components/esm/loadrepa.cpp b/components/esm/loadrepa.cpp index 59bfa01695..a7132828d4 100644 --- a/components/esm/loadrepa.cpp +++ b/components/esm/loadrepa.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Repair::sRecordId = REC_REPA; void Repair::load(ESMReader &esm) { diff --git a/components/esm/loadrepa.hpp b/components/esm/loadrepa.hpp index 771e7ead07..5b404b0e4c 100644 --- a/components/esm/loadrepa.hpp +++ b/components/esm/loadrepa.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct Repair { + static unsigned int sRecordId; + struct Data { float mWeight; diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 8afb85602d..30460c17a6 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -2,6 +2,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -12,6 +13,8 @@ struct SCHD Script::SCHDstruct mData; }; + unsigned int Script::sRecordId = REC_SCPT; + void Script::load(ESMReader &esm) { SCHD data; diff --git a/components/esm/loadscpt.hpp b/components/esm/loadscpt.hpp index 450224faa4..d5200d4c12 100644 --- a/components/esm/loadscpt.hpp +++ b/components/esm/loadscpt.hpp @@ -19,6 +19,8 @@ class ESMWriter; class Script { public: + static unsigned int sRecordId; + struct SCHDstruct { /* Script name. diff --git a/components/esm/loadskil.cpp b/components/esm/loadskil.cpp index f6a2c49503..b6724e9381 100644 --- a/components/esm/loadskil.cpp +++ b/components/esm/loadskil.cpp @@ -6,6 +6,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -126,6 +127,8 @@ namespace ESM HandToHand }}; + unsigned int Skill::sRecordId = REC_SKIL; + void Skill::load(ESMReader &esm) { esm.getHNT(mIndex, "INDX"); diff --git a/components/esm/loadskil.hpp b/components/esm/loadskil.hpp index 2436173cbb..1b9db5bcff 100644 --- a/components/esm/loadskil.hpp +++ b/components/esm/loadskil.hpp @@ -19,6 +19,8 @@ class ESMWriter; struct Skill { + static unsigned int sRecordId; + std::string mId; struct SKDTstruct diff --git a/components/esm/loadsndg.cpp b/components/esm/loadsndg.cpp index 9b992c9606..1a8ca63354 100644 --- a/components/esm/loadsndg.cpp +++ b/components/esm/loadsndg.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int SoundGenerator::sRecordId = REC_SNDG; void SoundGenerator::load(ESMReader &esm) { diff --git a/components/esm/loadsndg.hpp b/components/esm/loadsndg.hpp index 2756676ef1..5509661c18 100644 --- a/components/esm/loadsndg.hpp +++ b/components/esm/loadsndg.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct SoundGenerator { + static unsigned int sRecordId; + enum Type { LeftFoot = 0, diff --git a/components/esm/loadsoun.cpp b/components/esm/loadsoun.cpp index 0f6b0f84a7..49c9eb54e2 100644 --- a/components/esm/loadsoun.cpp +++ b/components/esm/loadsoun.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Sound::sRecordId = REC_SOUN; void Sound::load(ESMReader &esm) { diff --git a/components/esm/loadsoun.hpp b/components/esm/loadsoun.hpp index 6c9bb1fed3..04a0984fda 100644 --- a/components/esm/loadsoun.hpp +++ b/components/esm/loadsoun.hpp @@ -16,6 +16,8 @@ struct SOUNstruct struct Sound { + static unsigned int sRecordId; + SOUNstruct mData; std::string mId, mSound; diff --git a/components/esm/loadspel.cpp b/components/esm/loadspel.cpp index 5c0bd956f7..2c98d796d3 100644 --- a/components/esm/loadspel.cpp +++ b/components/esm/loadspel.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Spell::sRecordId = REC_SPEL; void Spell::load(ESMReader &esm) { diff --git a/components/esm/loadspel.hpp b/components/esm/loadspel.hpp index b34bd29f18..cbf5366c4b 100644 --- a/components/esm/loadspel.hpp +++ b/components/esm/loadspel.hpp @@ -13,6 +13,8 @@ class ESMWriter; struct Spell { + static unsigned int sRecordId; + enum SpellType { ST_Spell = 0, // Normal spell, must be cast and costs mana diff --git a/components/esm/loadsscr.cpp b/components/esm/loadsscr.cpp index f51b7be479..69b04bb237 100644 --- a/components/esm/loadsscr.cpp +++ b/components/esm/loadsscr.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int StartScript::sRecordId = REC_SSCR; void StartScript::load(ESMReader &esm) { diff --git a/components/esm/loadsscr.hpp b/components/esm/loadsscr.hpp index 2326f00f43..d09ad883eb 100644 --- a/components/esm/loadsscr.hpp +++ b/components/esm/loadsscr.hpp @@ -19,6 +19,8 @@ class ESMWriter; struct StartScript { + static unsigned int sRecordId; + std::string mData; std::string mId, mScript; diff --git a/components/esm/loadstat.cpp b/components/esm/loadstat.cpp index 38206422bb..a71f22dc23 100644 --- a/components/esm/loadstat.cpp +++ b/components/esm/loadstat.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Static::sRecordId = REC_STAT; void Static::load(ESMReader &esm) { diff --git a/components/esm/loadstat.hpp b/components/esm/loadstat.hpp index df42c0c491..d912d10583 100644 --- a/components/esm/loadstat.hpp +++ b/components/esm/loadstat.hpp @@ -22,6 +22,8 @@ class ESMWriter; struct Static { + static unsigned int sRecordId; + std::string mId, mModel; void load(ESMReader &esm); diff --git a/components/esm/loadtes3.cpp b/components/esm/loadtes3.cpp index 74d578ba7d..a86c9b6f4f 100644 --- a/components/esm/loadtes3.cpp +++ b/components/esm/loadtes3.cpp @@ -4,6 +4,7 @@ #include "esmcommon.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" void ESM::Header::blank() { diff --git a/components/esm/loadweap.cpp b/components/esm/loadweap.cpp index e21d8924a1..1d0b149df1 100644 --- a/components/esm/loadweap.cpp +++ b/components/esm/loadweap.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Weapon::sRecordId = REC_WEAP; void Weapon::load(ESMReader &esm) { diff --git a/components/esm/loadweap.hpp b/components/esm/loadweap.hpp index 42810d3afb..fde716b916 100644 --- a/components/esm/loadweap.hpp +++ b/components/esm/loadweap.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct Weapon { + static unsigned int sRecordId; + enum Type { ShortBladeOneHand = 0, From e4fdebc85b917e5608c9da75eb9af8180c766105 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 24 Sep 2013 13:53:19 +0200 Subject: [PATCH 051/148] added saving for referenceable records --- apps/opencs/model/doc/saving.cpp | 2 ++ apps/opencs/model/doc/savingstages.cpp | 15 ++++++++++ apps/opencs/model/doc/savingstages.hpp | 17 +++++++++++ apps/opencs/model/world/refidcollection.cpp | 5 ++++ apps/opencs/model/world/refidcollection.hpp | 7 +++++ apps/opencs/model/world/refiddata.cpp | 13 +++++++++ apps/opencs/model/world/refiddata.hpp | 32 +++++++++++++++++++++ 7 files changed, 91 insertions(+) diff --git a/apps/opencs/model/doc/saving.cpp b/apps/opencs/model/doc/saving.cpp index c9035cfd43..98af661fdf 100644 --- a/apps/opencs/model/doc/saving.cpp +++ b/apps/opencs/model/doc/saving.cpp @@ -48,6 +48,8 @@ CSMDoc::Saving::Saving (Document& document) appendStage (new WriteCollectionStage > (mDocument.getData().getSpells(), mState)); + appendStage (new WriteRefIdCollectionStage (mDocument, mState)); + appendStage (new CloseSaveStage (mState)); diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index fd62345940..5da92f014f 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -67,6 +67,21 @@ void CSMDoc::WriteHeaderStage::perform (int stage, std::vector& mes } +CSMDoc::WriteRefIdCollectionStage::WriteRefIdCollectionStage (Document& document, SavingState& state) +: mDocument (document), mState (state) +{} + +int CSMDoc::WriteRefIdCollectionStage::setup() +{ + return mDocument.getData().getReferenceables().getSize(); +} + +void CSMDoc::WriteRefIdCollectionStage::perform (int stage, std::vector& messages) +{ + mDocument.getData().getReferenceables().save (stage, mState.getWriter()); +} + + CSMDoc::CloseSaveStage::CloseSaveStage (SavingState& state) : mState (state) {} diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp index d5c4a69af7..2d32f38cbd 100644 --- a/apps/opencs/model/doc/savingstages.hpp +++ b/apps/opencs/model/doc/savingstages.hpp @@ -99,6 +99,23 @@ namespace CSMDoc } + class WriteRefIdCollectionStage : public Stage + { + Document& mDocument; + SavingState& mState; + + public: + + WriteRefIdCollectionStage (Document& document, SavingState& state); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + + class CloseSaveStage : public Stage { SavingState& mState; diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index cda2711cc7..3a6f70d310 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -539,3 +539,8 @@ std::vector CSMWorld::RefIdCollection::getIds (bool listDeleted) co { return mData.getIds (listDeleted); } + +void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const +{ + mData.save (index, writer); +} \ No newline at end of file diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index 22f83150de..a479735db5 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -9,6 +9,11 @@ #include "collectionbase.hpp" #include "refiddata.hpp" +namespace ESM +{ + class ESMWriter; +} + namespace CSMWorld { class RefIdAdapter; @@ -94,6 +99,8 @@ namespace CSMWorld ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list + + void save (int index, ESM::ESMWriter& writer) const; }; } diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 9457937f1e..8f59b0fe74 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -218,3 +218,16 @@ std::vector CSMWorld::RefIdData::getIds (bool listDeleted) const return ids; } + +void CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const +{ + LocalIndex localIndex = globalToLocalIndex (index); + + std::map::const_iterator iter = + mRecordContainers.find (localIndex.second); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + iter->second->save (localIndex.first, writer); +} \ No newline at end of file diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index e221fbc7c2..9595ab23b5 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "record.hpp" #include "universalid.hpp" @@ -51,6 +52,8 @@ namespace CSMWorld virtual void erase (int index, int count) = 0; virtual std::string getId (int index) const = 0; + + virtual void save (int index, ESM::ESMWriter& writer) const = 0; }; template @@ -71,6 +74,8 @@ namespace CSMWorld virtual void erase (int index, int count); virtual std::string getId (int index) const; + + virtual void save (int index, ESM::ESMWriter& writer) const; }; template @@ -123,6 +128,31 @@ namespace CSMWorld return mContainer.at (index).get().mId; } + template + void RefIdDataContainer::save (int index, ESM::ESMWriter& writer) const + { + CSMWorld::RecordBase::State state = mContainer.at (index).mState; + + if (state==CSMWorld::RecordBase::State_Modified || + state==CSMWorld::RecordBase::State_ModifiedOnly) + { + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic (change ESMWriter interface?) + type += reinterpret_cast (&mContainer.at (index).mModified.sRecordId)[i]; + + writer.startRecord (type); + writer.writeHNCString ("NAME", getId (index)); + mContainer.at (index).mModified.save (writer); + writer.endRecord (type); + } + else if (state==CSMWorld::RecordBase::State_Deleted) + { + /// \todo write record with delete flag + } + } + + class RefIdData { public: @@ -187,6 +217,8 @@ namespace CSMWorld ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list + + void save (int index, ESM::ESMWriter& writer) const; }; } From 830530bd063e9a14819c6cf6cb263d6e4a1b32c4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 24 Sep 2013 17:08:24 +0200 Subject: [PATCH 052/148] set record count in TES3 header --- apps/opencs/model/doc/savingstages.cpp | 5 ++++- apps/opencs/model/world/data.cpp | 29 ++++++++++++++++++++++++++ apps/opencs/model/world/data.hpp | 5 +++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index 5da92f014f..ee2943ef45 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -48,7 +48,10 @@ void CSMDoc::WriteHeaderStage::perform (int stage, std::vector& mes /// \todo fill in missing header information mState.getWriter().setAuthor (""); mState.getWriter().setDescription (""); - mState.getWriter().setRecordCount (0); + mState.getWriter().setRecordCount ( + mDocument.getData().count (CSMWorld::RecordBase::State_Modified) + + mDocument.getData().count (CSMWorld::RecordBase::State_ModifiedOnly) + + mDocument.getData().count (CSMWorld::RecordBase::State_Deleted)); /// \todo refine dependency list (at least remove redundant dependencies) std::vector dependencies = mDocument.getContentFiles(); diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 7eb96a5c38..1bd818db4c 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -43,6 +43,17 @@ void CSMWorld::Data::appendIds (std::vector& ids, const CollectionB ids.insert (ids.end(), ids2.begin(), ids2.end()); } +int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collection) +{ + int number = 0; + + for (int i=0; i); @@ -460,6 +471,24 @@ bool CSMWorld::Data::hasId (const std::string& id) const getReferenceables().searchId (id)!=-1; } +int CSMWorld::Data::count (RecordBase::State state) const +{ + return + count (state, mGlobals) + + count (state, mGmsts) + + count (state, mSkills) + + count (state, mClasses) + + count (state, mFactions) + + count (state, mRaces) + + count (state, mSounds) + + count (state, mScripts) + + count (state, mRegions) + + count (state, mBirthsigns) + + count (state, mSpells) + + count (state, mCells) + + count (state, mReferenceables); +} + std::vector CSMWorld::Data::getIds (bool listDeleted) const { std::vector ids; diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index e900bb10fb..ebbafe7118 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -66,6 +66,8 @@ namespace CSMWorld bool listDeleted); ///< Append all IDs from collection to \a ids. + static int count (RecordBase::State state, const CollectionBase& collection); + public: Data(); @@ -151,6 +153,9 @@ namespace CSMWorld /// /// \param listDeleted include deleted record in the list + int count (RecordBase::State state) const; + ///< Return number of top-level records with the given \a state. + signals: void idListChanged(); From 96fd1c35bf3e4296dc0ce012a47f049e6e87ec5d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 24 Sep 2013 17:17:01 +0200 Subject: [PATCH 053/148] preserve author/descriptin meta data --- apps/opencs/model/doc/document.cpp | 6 ++++++ apps/opencs/model/doc/savingstages.cpp | 5 ++--- apps/opencs/model/world/data.cpp | 23 +++++++++++++++++++++++ apps/opencs/model/world/data.hpp | 10 ++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 37294bcd16..f496d32ec3 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2158,6 +2158,12 @@ CSMDoc::Document::Document (const std::vector& files, load (files.begin(), end, !new_); } + if (new_) + { + mData.setDescription (""); + mData.setAuthor (""); + } + addOptionalGmsts(); addOptionalGlobals(); diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index ee2943ef45..23a88bc17a 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -45,9 +45,8 @@ void CSMDoc::WriteHeaderStage::perform (int stage, std::vector& mes mState.getWriter().setFormat (0); - /// \todo fill in missing header information - mState.getWriter().setAuthor (""); - mState.getWriter().setDescription (""); + mState.getWriter().setAuthor (mDocument.getData().getAuthor()); + mState.getWriter().setDescription (mDocument.getData().getDescription()); mState.getWriter().setRecordCount ( mDocument.getData().count (CSMWorld::RecordBase::State_Modified) + mDocument.getData().count (CSMWorld::RecordBase::State_ModifiedOnly) + diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 1bd818db4c..c6423b76a6 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -395,6 +395,9 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) reader.open (path.string()); + mAuthor = reader.getAuthor(); + mDescription = reader.getDesc(); + // Note: We do not need to send update signals here, because at this point the model is not connected // to any view. while (reader.hasMoreRecs()) @@ -489,6 +492,26 @@ int CSMWorld::Data::count (RecordBase::State state) const count (state, mReferenceables); } +void CSMWorld::Data::setDescription (const std::string& description) +{ + mDescription = description; +} + +std::string CSMWorld::Data::getDescription() const +{ + return mDescription; +} + +void CSMWorld::Data::setAuthor (const std::string& author) +{ + mAuthor = author; +} + +std::string CSMWorld::Data::getAuthor() const +{ + return mAuthor; +} + std::vector CSMWorld::Data::getIds (bool listDeleted) const { std::vector ids; diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index ebbafe7118..eb6325a257 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -54,6 +54,8 @@ namespace CSMWorld IdCollection mFilters; std::vector mModels; std::map mModelIndex; + std::string mAuthor; + std::string mDescription; // not implemented Data (const Data&); @@ -156,6 +158,14 @@ namespace CSMWorld int count (RecordBase::State state) const; ///< Return number of top-level records with the given \a state. + void setDescription (const std::string& description); + + std::string getDescription() const; + + void setAuthor (const std::string& author); + + std::string getAuthor() const; + signals: void idListChanged(); From 5779f799ab064d72cf9641aaccdd8107d50c098b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Sep 2013 11:36:06 +0200 Subject: [PATCH 054/148] create project file when saving content file --- apps/opencs/editor.cpp | 3 +- apps/opencs/editor.hpp | 2 +- apps/opencs/model/doc/document.cpp | 5 +- apps/opencs/model/doc/document.hpp | 5 +- apps/opencs/model/doc/documentmanager.cpp | 15 +++++- apps/opencs/model/doc/documentmanager.hpp | 4 +- apps/opencs/model/doc/saving.cpp | 16 +++++-- apps/opencs/model/doc/saving.hpp | 4 +- apps/opencs/model/doc/savingstages.cpp | 58 ++++++++++++++--------- apps/opencs/model/doc/savingstages.hpp | 8 +++- apps/opencs/model/doc/savingstate.cpp | 19 ++++++-- apps/opencs/model/doc/savingstate.hpp | 10 +++- components/esm/esmwriter.cpp | 5 ++ components/esm/esmwriter.hpp | 2 + components/esm/loadtes3.cpp | 1 + 15 files changed, 113 insertions(+), 44 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 800f3984e9..ead4d2a982 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -10,7 +10,8 @@ #include "model/world/data.hpp" -CS::Editor::Editor() : mViewManager (mDocumentManager) +CS::Editor::Editor() +: mDocumentManager (mCfgMgr.getUserPath() / "projects"), mViewManager (mDocumentManager) { mIpcServerName = "org.openmw.OpenCS"; diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index abf9496e4b..16f6b9516c 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -26,6 +26,7 @@ namespace CS { Q_OBJECT + Files::ConfigurationManager mCfgMgr; CSMSettings::UserSettings mUserSettings; CSMDoc::DocumentManager mDocumentManager; CSVDoc::ViewManager mViewManager; @@ -34,7 +35,6 @@ namespace CS CSVSettings::UserSettingsDialog mSettings; CSVDoc::FileDialog mFileDialog; - Files::ConfigurationManager mCfgMgr; boost::filesystem::path mLocal; void setupDataFiles(); diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index f496d32ec3..f9aa7dfc0f 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2140,8 +2140,9 @@ void CSMDoc::Document::createBase() } CSMDoc::Document::Document (const std::vector& files, - const boost::filesystem::path& savePath, bool new_) -: mSavePath (savePath), mContentFiles (files), mTools (mData), mSaving (*this) + const boost::filesystem::path& savePath, bool new_, + const boost::filesystem::path& projectPath) +: mSavePath (savePath), mContentFiles (files), mTools (mData), mSaving (*this, projectPath) { if (files.empty()) throw std::runtime_error ("Empty content file sequence"); diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index b1c6a02731..979b47734a 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -65,7 +65,10 @@ namespace CSMDoc public: Document (const std::vector& files, - const boost::filesystem::path& savePath, bool new_); + const boost::filesystem::path& savePath, bool new_, + const boost::filesystem::path& projectPath); + ///< \param projectPath Location of file that can be used to store additional data for + /// this project. ~Document(); diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp index b079109eae..1978c0e536 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -4,9 +4,16 @@ #include #include +#include + #include "document.hpp" -CSMDoc::DocumentManager::DocumentManager() {} +CSMDoc::DocumentManager::DocumentManager (const boost::filesystem::path& projectPath) +: mProjectPath (projectPath) +{ + if (!boost::filesystem::is_directory (mProjectPath)) + boost::filesystem::create_directories (mProjectPath); +} CSMDoc::DocumentManager::~DocumentManager() { @@ -17,7 +24,11 @@ CSMDoc::DocumentManager::~DocumentManager() CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector& files, const boost::filesystem::path& savePath, bool new_) { - Document *document = new Document (files, savePath, new_); + boost::filesystem::path projectFile (mProjectPath); + + projectFile /= savePath.filename().string() + ".project"; + + Document *document = new Document (files, savePath, new_, projectFile); mDocuments.push_back (document); diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp index dfded8d5c8..622a135a58 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -13,13 +13,15 @@ namespace CSMDoc class DocumentManager { std::vector mDocuments; + boost::filesystem::path mProjectPath; DocumentManager (const DocumentManager&); DocumentManager& operator= (const DocumentManager&); public: - DocumentManager(); + DocumentManager (const boost::filesystem::path& projectPath); + ///< \param projectPath Directory where additional per-project data will be stored. ~DocumentManager(); diff --git a/apps/opencs/model/doc/saving.cpp b/apps/opencs/model/doc/saving.cpp index 98af661fdf..adcfca5768 100644 --- a/apps/opencs/model/doc/saving.cpp +++ b/apps/opencs/model/doc/saving.cpp @@ -8,12 +8,20 @@ #include "savingstages.hpp" #include "document.hpp" -CSMDoc::Saving::Saving (Document& document) -: Operation (State_Saving, true, true), mDocument (document), mState (*this) +CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& projectPath) +: Operation (State_Saving, true, true), mDocument (document), mState (*this, projectPath) { - appendStage (new OpenSaveStage (mDocument, mState)); + // save project file + appendStage (new OpenSaveStage (mDocument, mState, true)); - appendStage (new WriteHeaderStage (mDocument, mState)); + appendStage (new WriteHeaderStage (mDocument, mState, true)); + + appendStage (new CloseSaveStage (mState)); + + // save content file + appendStage (new OpenSaveStage (mDocument, mState, false)); + + appendStage (new WriteHeaderStage (mDocument, mState, false)); appendStage (new WriteCollectionStage > (mDocument.getData().getGlobals(), mState)); diff --git a/apps/opencs/model/doc/saving.hpp b/apps/opencs/model/doc/saving.hpp index b89ba5f6db..cd1bbef980 100644 --- a/apps/opencs/model/doc/saving.hpp +++ b/apps/opencs/model/doc/saving.hpp @@ -1,6 +1,8 @@ #ifndef CSM_DOC_SAVING_H #define CSM_DOC_SAVING_H +#include + #include "operation.hpp" #include "savingstate.hpp" @@ -17,7 +19,7 @@ namespace CSMDoc public: - Saving (Document& document); + Saving (Document& document, const boost::filesystem::path& projectPath); }; } diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index 23a88bc17a..d48e220123 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -10,8 +10,8 @@ #include "document.hpp" #include "savingstate.hpp" -CSMDoc::OpenSaveStage::OpenSaveStage (Document& document, SavingState& state) -: mDocument (document), mState (state) +CSMDoc::OpenSaveStage::OpenSaveStage (Document& document, SavingState& state, bool projectFile) +: mDocument (document), mState (state), mProjectFile (projectFile) {} int CSMDoc::OpenSaveStage::setup() @@ -21,17 +21,17 @@ int CSMDoc::OpenSaveStage::setup() void CSMDoc::OpenSaveStage::perform (int stage, std::vector& messages) { - mState.start (mDocument); + mState.start (mDocument, mProjectFile); - mState.getStream().open (mState.getTmpPath().string().c_str()); + mState.getStream().open ((mProjectFile ? mState.getPath() : mState.getTmpPath()).string().c_str()); if (!mState.getStream().is_open()) throw std::runtime_error ("failed to open stream for saving"); } -CSMDoc::WriteHeaderStage::WriteHeaderStage (Document& document, SavingState& state) -: mDocument (document), mState (state) +CSMDoc::WriteHeaderStage::WriteHeaderStage (Document& document, SavingState& state, bool simple) +: mDocument (document), mState (state), mSimple (simple) {} int CSMDoc::WriteHeaderStage::setup() @@ -43,26 +43,38 @@ void CSMDoc::WriteHeaderStage::perform (int stage, std::vector& mes { mState.getWriter().setVersion(); + mState.getWriter().clearMaster(); + mState.getWriter().setFormat (0); - mState.getWriter().setAuthor (mDocument.getData().getAuthor()); - mState.getWriter().setDescription (mDocument.getData().getDescription()); - mState.getWriter().setRecordCount ( - mDocument.getData().count (CSMWorld::RecordBase::State_Modified) + - mDocument.getData().count (CSMWorld::RecordBase::State_ModifiedOnly) + - mDocument.getData().count (CSMWorld::RecordBase::State_Deleted)); - - /// \todo refine dependency list (at least remove redundant dependencies) - std::vector dependencies = mDocument.getContentFiles(); - std::vector::const_iterator end (--dependencies.end()); - - for (std::vector::const_iterator iter (dependencies.begin()); - iter!=end; ++iter) + if (mSimple) { - std::string name = iter->filename().string(); - uint64_t size = boost::filesystem::file_size (*iter); + mState.getWriter().setAuthor (""); + mState.getWriter().setDescription (""); + mState.getWriter().setRecordCount (0); - mState.getWriter().addMaster (name, size); + } + else + { + mState.getWriter().setAuthor (mDocument.getData().getAuthor()); + mState.getWriter().setDescription (mDocument.getData().getDescription()); + mState.getWriter().setRecordCount ( + mDocument.getData().count (CSMWorld::RecordBase::State_Modified) + + mDocument.getData().count (CSMWorld::RecordBase::State_ModifiedOnly) + + mDocument.getData().count (CSMWorld::RecordBase::State_Deleted)); + + /// \todo refine dependency list (at least remove redundant dependencies) + std::vector dependencies = mDocument.getContentFiles(); + std::vector::const_iterator end (--dependencies.end()); + + for (std::vector::const_iterator iter (dependencies.begin()); + iter!=end; ++iter) + { + std::string name = iter->filename().string(); + uint64_t size = boost::filesystem::file_size (*iter); + + mState.getWriter().addMaster (name, size); + } } mState.getWriter().save (mState.getStream()); @@ -121,7 +133,7 @@ void CSMDoc::FinalSavingStage::perform (int stage, std::vector& mes if (boost::filesystem::exists (mState.getTmpPath())) boost::filesystem::remove (mState.getTmpPath()); } - else + else if (!mState.isProjectFile()) { if (boost::filesystem::exists (mState.getPath())) boost::filesystem::remove (mState.getPath()); diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp index 2d32f38cbd..367431fc12 100644 --- a/apps/opencs/model/doc/savingstages.hpp +++ b/apps/opencs/model/doc/savingstages.hpp @@ -16,10 +16,12 @@ namespace CSMDoc { Document& mDocument; SavingState& mState; + bool mProjectFile; public: - OpenSaveStage (Document& document, SavingState& state); + OpenSaveStage (Document& document, SavingState& state, bool projectFile); + ///< \param projectFile Saving the project file instead of the content file. virtual int setup(); ///< \return number of steps @@ -32,10 +34,12 @@ namespace CSMDoc { Document& mDocument; SavingState& mState; + bool mSimple; public: - WriteHeaderStage (Document& document, SavingState& state); + WriteHeaderStage (Document& document, SavingState& state, bool simple); + ///< \param simple Simplified header (used for project files). virtual int setup(); ///< \return number of steps diff --git a/apps/opencs/model/doc/savingstate.cpp b/apps/opencs/model/doc/savingstate.cpp index a49a0699b2..4a1abb8883 100644 --- a/apps/opencs/model/doc/savingstate.cpp +++ b/apps/opencs/model/doc/savingstate.cpp @@ -4,10 +4,11 @@ #include "operation.hpp" #include "document.hpp" -CSMDoc::SavingState::SavingState (Operation& operation) +CSMDoc::SavingState::SavingState (Operation& operation, const boost::filesystem::path& projectPath) : mOperation (operation), /// \todo set encoding properly, once config implementation has been fixed. - mEncoder (ToUTF8::calculateEncoding ("win1252")) + mEncoder (ToUTF8::calculateEncoding ("win1252")), + mProjectPath (projectPath), mProjectFile (false) { mWriter.setEncoder (&mEncoder); } @@ -17,14 +18,19 @@ bool CSMDoc::SavingState::hasError() const return mOperation.hasError(); } -void CSMDoc::SavingState::start (Document& document) +void CSMDoc::SavingState::start (Document& document, bool project) { + mProjectFile = project; + if (mStream.is_open()) mStream.close(); mStream.clear(); - mPath = document.getSavePath(); + if (project) + mPath = mProjectPath; + else + mPath = document.getSavePath(); boost::filesystem::path file (mPath.filename().string() + ".tmp"); @@ -51,4 +57,9 @@ std::ofstream& CSMDoc::SavingState::getStream() ESM::ESMWriter& CSMDoc::SavingState::getWriter() { return mWriter; +} + +bool CSMDoc::SavingState::isProjectFile() const +{ + return mProjectFile; } \ No newline at end of file diff --git a/apps/opencs/model/doc/savingstate.hpp b/apps/opencs/model/doc/savingstate.hpp index 3f42b4653d..8cf7883e50 100644 --- a/apps/opencs/model/doc/savingstate.hpp +++ b/apps/opencs/model/doc/savingstate.hpp @@ -20,14 +20,17 @@ namespace CSMDoc ToUTF8::Utf8Encoder mEncoder; std::ofstream mStream; ESM::ESMWriter mWriter; + boost::filesystem::path mProjectPath; + bool mProjectFile; public: - SavingState (Operation& operation); + SavingState (Operation& operation, const boost::filesystem::path& projectPath); bool hasError() const; - void start (Document& document); + void start (Document& document, bool project); + ///< \param project Save project file instead of content file. const boost::filesystem::path& getPath() const; @@ -36,6 +39,9 @@ namespace CSMDoc std::ofstream& getStream(); ESM::ESMWriter& getWriter(); + + bool isProjectFile() const; + ///< Currently saving project file? (instead of content file) }; diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 95ad44811d..f39aa2b898 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -38,6 +38,11 @@ namespace ESM mHeader.mFormat = format; } + void ESMWriter::clearMaster() + { + mHeader.mMaster.clear(); + } + void ESMWriter::addMaster(const std::string& name, uint64_t size) { Header::MasterData d; diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index fc64c4a137..104f97f909 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -32,6 +32,8 @@ class ESMWriter void setRecordCount (int count); void setFormat (int format); + void clearMaster(); + void addMaster(const std::string& name, uint64_t size); void save(const std::string& file); diff --git a/components/esm/loadtes3.cpp b/components/esm/loadtes3.cpp index a86c9b6f4f..87a8d1d57e 100644 --- a/components/esm/loadtes3.cpp +++ b/components/esm/loadtes3.cpp @@ -14,6 +14,7 @@ void ESM::Header::blank() mData.desc.assign (""); mData.records = 0; mFormat = CurrentFormat; + mMaster.clear(); } void ESM::Header::load (ESMReader &esm) From 4ea5191d7d4c0ec38941f2e3731b4ee4a6e38925 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Sep 2013 13:17:04 +0200 Subject: [PATCH 055/148] fixed write function for ESM variant type --- components/esm/variantimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/variantimp.cpp b/components/esm/variantimp.cpp index 160402aa4b..1bacdc0770 100644 --- a/components/esm/variantimp.cpp +++ b/components/esm/variantimp.cpp @@ -193,7 +193,7 @@ void ESM::VariantIntegerData::write (ESMWriter& esm, Variant::Format format, Var } else if (format==Variant::Format_Gmst || format==Variant::Format_Info) { - if (type==VT_Int) + if (type!=VT_Int) { std::ostringstream stream; stream From 23095ec3ec5f934d74895b99dd5ad010cb42c4ce Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Sep 2013 13:54:21 +0200 Subject: [PATCH 056/148] added missing scope column to filter table --- apps/opencs/model/world/columnimp.hpp | 30 +++++++++++++++++++++++++++ apps/opencs/model/world/data.cpp | 1 + 2 files changed, 31 insertions(+) diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 1a2bf9df13..f50212e569 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -1216,6 +1216,36 @@ namespace CSMWorld return true; } }; + + template + struct ScopeColumn : public Column + { + ScopeColumn() + : Column (Columns::ColumnId_Scope, ColumnBase::Display_Integer, 0) + {} + + virtual QVariant get (const Record& record) const + { + return static_cast (record.get().mScope); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mScope = static_cast (data.toInt()); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + + virtual bool isUserEditable() const + { + return false; + } + }; } #endif diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index c6423b76a6..a5e98fc60c 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -182,6 +182,7 @@ CSMWorld::Data::Data() : mRefs (mCells) mFilters.addColumn (new RecordStateColumn); mFilters.addColumn (new FilterColumn); mFilters.addColumn (new DescriptionColumn); + mFilters.addColumn (new ScopeColumn); addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global); addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst); From 6f2c418a5cdd8d96b0d1c0afa859d8a06086349a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Sep 2013 13:54:51 +0200 Subject: [PATCH 057/148] (slightly) improved error reporting during save operations --- apps/opencs/model/doc/document.cpp | 7 +++++++ apps/opencs/model/doc/document.hpp | 2 ++ apps/opencs/model/doc/operation.cpp | 3 ++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index f9aa7dfc0f..68e164c797 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2175,6 +2175,8 @@ CSMDoc::Document::Document (const std::vector& files, connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); connect (&mSaving, SIGNAL (done (int)), this, SLOT (operationDone (int))); + connect (&mSaving, SIGNAL (reportMessage (const QString&, int)), + this, SLOT (reportMessage (const QString&, int))); } CSMDoc::Document::~Document() @@ -2243,6 +2245,11 @@ void CSMDoc::Document::modificationStateChanged (bool clean) emit stateChanged (getState(), this); } +void CSMDoc::Document::reportMessage (const QString& message, int type) +{ + /// \todo find a better way to get these messages to the user. + std::cout << message.toUtf8().constData() << std::endl; +} void CSMDoc::Document::operationDone (int type) { diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 979b47734a..7843cfbfe9 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -105,6 +105,8 @@ namespace CSMDoc void modificationStateChanged (bool clean); + void reportMessage (const QString& message, int type); + void operationDone (int type); public slots: diff --git a/apps/opencs/model/doc/operation.cpp b/apps/opencs/model/doc/operation.cpp index 8af5a2c0dc..d29cc2631b 100644 --- a/apps/opencs/model/doc/operation.cpp +++ b/apps/opencs/model/doc/operation.cpp @@ -95,8 +95,9 @@ void CSMDoc::Operation::executeStage() { mCurrentStage->first->perform (mCurrentStep++, messages); } - catch (const std::exception&) + catch (const std::exception& e) { + emit reportMessage (e.what(), mType); abort(); } From 31346dde58090fed76333272d5593ef24eae96e4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Sep 2013 13:55:28 +0200 Subject: [PATCH 058/148] fixed uninitialise scope value for filters --- apps/opencs/view/filter/filtercreator.cpp | 16 +++++++++++++++- apps/opencs/view/filter/filtercreator.hpp | 2 ++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/apps/opencs/view/filter/filtercreator.cpp b/apps/opencs/view/filter/filtercreator.cpp index 47925ea57a..640c9fe785 100644 --- a/apps/opencs/view/filter/filtercreator.cpp +++ b/apps/opencs/view/filter/filtercreator.cpp @@ -6,6 +6,11 @@ #include "../../model/filter/filter.hpp" +#include "../../model/world/data.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/columns.hpp" +#include "../../model/world/idtable.hpp" + std::string CSVFilter::FilterCreator::getNamespace() const { switch (mScope->currentIndex()) @@ -28,6 +33,15 @@ std::string CSVFilter::FilterCreator::getId() const return getNamespace() + GenericCreator::getId(); } +void CSVFilter::FilterCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const +{ + int index = + dynamic_cast (*getData().getTableModel (getCollectionId())). + findColumnIndex (CSMWorld::Columns::ColumnId_Scope); + + command.addValue (index, mScope->currentIndex()); +} + CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) : GenericCreator (data, undoStack, id) @@ -39,7 +53,7 @@ CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoS mScope->addItem ("Project"); mScope->addItem ("Session"); - /// \ŧodo re-enable for OpenMW 1.1 + /// \todo re-enable for OpenMW 1.1 // mScope->addItem ("Content"); connect (mScope, SIGNAL (currentIndexChanged (int)), this, SLOT (setScope (int))); diff --git a/apps/opencs/view/filter/filtercreator.hpp b/apps/opencs/view/filter/filtercreator.hpp index 82d38d22c7..437d01c8da 100644 --- a/apps/opencs/view/filter/filtercreator.hpp +++ b/apps/opencs/view/filter/filtercreator.hpp @@ -25,6 +25,8 @@ namespace CSVFilter virtual std::string getId() const; + virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + public: FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack, From 6ac4dedfbe7f88fb353cfdab0128cea612a85e38 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Sep 2013 13:56:23 +0200 Subject: [PATCH 059/148] added missing column enum --- apps/opencs/model/world/columns.cpp | 1 + apps/opencs/model/world/columns.hpp | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 5616a4a481..06649b01bf 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -147,6 +147,7 @@ namespace CSMWorld { ColumnId_Magical, "Magical" }, { ColumnId_Silver, "Silver" }, { ColumnId_Filter, "Filter" }, + { ColumnId_Scope, "Scope", }, { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 69b20583ae..bf1387067d 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -140,6 +140,7 @@ namespace CSMWorld ColumnId_Magical = 107, ColumnId_Silver = 108, ColumnId_Filter = 109, + ColumnId_Scope = 110, // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. From baae548106332e373cb16e3855ac588f16c1b7c4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Sep 2013 13:56:40 +0200 Subject: [PATCH 060/148] added project scope filter saving --- apps/opencs/model/doc/saving.cpp | 2 ++ apps/opencs/model/doc/savingstages.cpp | 18 +++++++++++++++++- apps/opencs/model/doc/savingstages.hpp | 17 +++++++++++++++++ components/esm/defs.hpp | 6 +++++- components/esm/filter.cpp | 3 +++ components/esm/filter.hpp | 2 ++ 6 files changed, 46 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/doc/saving.cpp b/apps/opencs/model/doc/saving.cpp index adcfca5768..2b0056e721 100644 --- a/apps/opencs/model/doc/saving.cpp +++ b/apps/opencs/model/doc/saving.cpp @@ -16,6 +16,8 @@ CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& proje appendStage (new WriteHeaderStage (mDocument, mState, true)); + appendStage (new WriteFilterStage (mDocument, mState, CSMFilter::Filter::Scope_Project)); + appendStage (new CloseSaveStage (mState)); // save content file diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index d48e220123..d68c723179 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -52,7 +52,6 @@ void CSMDoc::WriteHeaderStage::perform (int stage, std::vector& mes mState.getWriter().setAuthor (""); mState.getWriter().setDescription (""); mState.getWriter().setRecordCount (0); - } else { @@ -96,6 +95,23 @@ void CSMDoc::WriteRefIdCollectionStage::perform (int stage, std::vector > (document.getData().getFilters(), + state), + mDocument (document), mScope (scope) +{} + +void CSMDoc::WriteFilterStage::perform (int stage, std::vector& messages) +{ + const CSMWorld::Record& record = + mDocument.getData().getFilters().getRecord (stage); + + if (record.get().mScope==mScope) + WriteCollectionStage >::perform (stage, messages); +} + + CSMDoc::CloseSaveStage::CloseSaveStage (SavingState& state) : mState (state) {} diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp index 367431fc12..ff94116fdb 100644 --- a/apps/opencs/model/doc/savingstages.hpp +++ b/apps/opencs/model/doc/savingstages.hpp @@ -6,6 +6,9 @@ #include "savingstate.hpp" #include "../world/record.hpp" +#include "../world/idcollection.hpp" + +#include "../filter/filter.hpp" namespace CSMDoc { @@ -120,6 +123,20 @@ namespace CSMDoc }; + class WriteFilterStage : public WriteCollectionStage > + { + Document& mDocument; + CSMFilter::Filter::Scope mScope; + + public: + + WriteFilterStage (Document& document, SavingState& state, CSMFilter::Filter::Scope scope); + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + + class CloseSaveStage : public Stage { SavingState& mState; diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index bd86f9ba03..dd7ebfe932 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -36,6 +36,7 @@ struct Position enum RecNameInts { + // format 0 / legacy REC_ACTI = 0x49544341, REC_ALCH = 0x48434c41, REC_APPA = 0x41505041, @@ -80,7 +81,10 @@ enum RecNameInts REC_SPEL = 0x4c455053, REC_SSCR = 0x52435353, REC_STAT = 0x54415453, - REC_WEAP = 0x50414557 + REC_WEAP = 0x50414557, + + // format 1 + REC_FILT = 0x544C4946 }; } diff --git a/components/esm/filter.cpp b/components/esm/filter.cpp index 96cc19d43d..a80427bbed 100644 --- a/components/esm/filter.cpp +++ b/components/esm/filter.cpp @@ -3,6 +3,9 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" + +unsigned int ESM::Filter::sRecordId = REC_FILT; void ESM::Filter::load (ESMReader& esm) { diff --git a/components/esm/filter.hpp b/components/esm/filter.hpp index a44d1b1980..bc3dd7bdcb 100644 --- a/components/esm/filter.hpp +++ b/components/esm/filter.hpp @@ -10,6 +10,8 @@ namespace ESM struct Filter { + static unsigned int sRecordId; + std::string mId; std::string mDescription; From 62148b324702158d14366f214b56aa32984d92dc Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Sep 2013 15:04:09 +0200 Subject: [PATCH 061/148] moved implementation of searchColumnIndex and findColumnIndex functions from IdTable to CollectionBase --- apps/opencs/model/world/collectionbase.cpp | 25 ++++++++++++++++++++++ apps/opencs/model/world/collectionbase.hpp | 8 +++++++ apps/opencs/model/world/idtable.cpp | 15 ++----------- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/apps/opencs/model/world/collectionbase.cpp b/apps/opencs/model/world/collectionbase.cpp index 932ea27b58..241f198cb2 100644 --- a/apps/opencs/model/world/collectionbase.cpp +++ b/apps/opencs/model/world/collectionbase.cpp @@ -1,6 +1,31 @@ #include "collectionbase.hpp" +#include + +#include "columnbase.hpp" + CSMWorld::CollectionBase::CollectionBase() {} CSMWorld::CollectionBase::~CollectionBase() {} + +int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const +{ + int columns = getColumns(); + + for (int i=0; i #include "universalid.hpp" +#include "columns.hpp" class QVariant; @@ -83,6 +84,13 @@ namespace CSMWorld ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list + + int searchColumnIndex (Columns::ColumnId id) const; + ///< Return index of column with the given \a id. If no such column exists, -1 is returned. + + int findColumnIndex (Columns::ColumnId id) const; + ///< Return index of column with the given \a id. If no such column exists, an exception is + /// thrown. }; } diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index baaf75289c..b7b1a9db05 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -161,21 +161,10 @@ const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id) int CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const { - int columns = mIdCollection->getColumns(); - - for (int i=0; igetColumn (i).mColumnId==id) - return i; - - return -1; + return mIdCollection->searchColumnIndex (id); } int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const { - int index = searchColumnIndex (id); - - if (index==-1) - throw std::logic_error ("invalid column index"); - - return index; + return mIdCollection->findColumnIndex (id); } \ No newline at end of file From e7c48cbe5805afa378dfd8e1db90357e8e7d8ab4 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Sep 2013 15:04:30 +0200 Subject: [PATCH 062/148] load project files --- apps/opencs/model/doc/document.cpp | 19 +++++++++++++++++-- apps/opencs/model/world/data.cpp | 15 ++++++++++++++- apps/opencs/model/world/data.hpp | 4 +++- 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 68e164c797..7f609f9f7e 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -1,6 +1,9 @@ #include "document.hpp" + #include +#include + void CSMDoc::Document::load (const std::vector::const_iterator& begin, const std::vector::const_iterator& end, bool lastAsModified) { @@ -12,10 +15,10 @@ void CSMDoc::Document::load (const std::vector::const_i --end2; for (std::vector::const_iterator iter (begin); iter!=end2; ++iter) - getData().loadFile (*iter, true); + getData().loadFile (*iter, true, false); if (lastAsModified) - getData().loadFile (*end2, false); + getData().loadFile (*end2, false, false); } void CSMDoc::Document::addGmsts() @@ -2164,6 +2167,18 @@ CSMDoc::Document::Document (const std::vector& files, mData.setDescription (""); mData.setAuthor (""); } +/// \todo un-outcomment the else, once loading an existing content file works properly again. +// else + { + if (boost::filesystem::exists (projectPath)) + { + getData().loadFile (projectPath, false, true); + } + else + { + /// \todo create new project file with default filters + } + } addOptionalGmsts(); addOptionalGlobals(); diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index a5e98fc60c..9227a5965d 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -386,7 +386,7 @@ void CSMWorld::Data::merge() mGlobals.merge(); } -void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) +void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, bool project) { ESM::ESMReader reader; @@ -449,6 +449,19 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) case ESM::REC_STAT: mReferenceables.load (reader, base, UniversalId::Type_Static); break; case ESM::REC_WEAP: mReferenceables.load (reader, base, UniversalId::Type_Weapon); break; + case ESM::REC_FILT: + + if (project) + { + mFilters.load (reader, base); + mFilters.setData (mFilters.getSize()-1, + mFilters.findColumnIndex (CSMWorld::Columns::ColumnId_Scope), + static_cast (CSMFilter::Filter::Scope_Project)); + break; + } + + // fall through (filter record in a content file is an error with format 0) + default: /// \todo throw an exception instead, once all records are implemented diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index eb6325a257..5d7fbd2916 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -145,8 +145,10 @@ namespace CSMWorld void merge(); ///< Merge modified into base. - void loadFile (const boost::filesystem::path& path, bool base); + void loadFile (const boost::filesystem::path& path, bool base, bool project); ///< Merging content of a file into base or modified. + /// + /// \param project load project file instead of content file bool hasId (const std::string& id) const; From 6143ec33e0df352cdae04366c92ffa2ab977feb3 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 27 Sep 2013 15:24:58 +0200 Subject: [PATCH 063/148] giving Documents direct access to ConfigurationManager --- apps/opencs/editor.cpp | 2 +- apps/opencs/model/doc/document.cpp | 19 +++++++++++++------ apps/opencs/model/doc/document.hpp | 14 +++++++++----- apps/opencs/model/doc/documentmanager.cpp | 20 +++++++++++--------- apps/opencs/model/doc/documentmanager.hpp | 10 +++++++--- 5 files changed, 41 insertions(+), 24 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index ead4d2a982..a430597953 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -11,7 +11,7 @@ CS::Editor::Editor() -: mDocumentManager (mCfgMgr.getUserPath() / "projects"), mViewManager (mDocumentManager) +: mDocumentManager (mCfgMgr), mViewManager (mDocumentManager) { mIpcServerName = "org.openmw.OpenCS"; diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 7f609f9f7e..5c29d9f617 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -4,6 +4,10 @@ #include +#ifndef Q_MOC_RUN +#include +#endif + void CSMDoc::Document::load (const std::vector::const_iterator& begin, const std::vector::const_iterator& end, bool lastAsModified) { @@ -2142,10 +2146,13 @@ void CSMDoc::Document::createBase() } } -CSMDoc::Document::Document (const std::vector& files, - const boost::filesystem::path& savePath, bool new_, - const boost::filesystem::path& projectPath) -: mSavePath (savePath), mContentFiles (files), mTools (mData), mSaving (*this, projectPath) +CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, + const std::vector& files, + const boost::filesystem::path& savePath, bool new_) +: mSavePath (savePath), mContentFiles (files), mTools (mData), + mProjectPath ((configuration.getUserPath() / "projects") / + (savePath.filename().string() + ".project")), + mSaving (*this, mProjectPath) { if (files.empty()) throw std::runtime_error ("Empty content file sequence"); @@ -2170,9 +2177,9 @@ CSMDoc::Document::Document (const std::vector& files, /// \todo un-outcomment the else, once loading an existing content file works properly again. // else { - if (boost::filesystem::exists (projectPath)) + if (boost::filesystem::exists (mProjectPath)) { - getData().loadFile (projectPath, false, true); + getData().loadFile (mProjectPath, false, true); } else { diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 7843cfbfe9..d171dacaed 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -24,6 +24,11 @@ namespace ESM struct Global; } +namespace Files +{ + class ConfigurationManager; +} + namespace CSMDoc { class Document : public QObject @@ -36,6 +41,7 @@ namespace CSMDoc std::vector mContentFiles; CSMWorld::Data mData; CSMTools::Tools mTools; + boost::filesystem::path mProjectPath; Saving mSaving; // It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is @@ -64,11 +70,9 @@ namespace CSMDoc public: - Document (const std::vector& files, - const boost::filesystem::path& savePath, bool new_, - const boost::filesystem::path& projectPath); - ///< \param projectPath Location of file that can be used to store additional data for - /// this project. + Document (const Files::ConfigurationManager& configuration, + const std::vector& files, + const boost::filesystem::path& savePath, bool new_); ~Document(); diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp index 1978c0e536..1d6c88dccf 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -6,13 +6,19 @@ #include +#ifndef Q_MOC_RUN +#include +#endif + #include "document.hpp" -CSMDoc::DocumentManager::DocumentManager (const boost::filesystem::path& projectPath) -: mProjectPath (projectPath) +CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration) +: mConfiguration (configuration) { - if (!boost::filesystem::is_directory (mProjectPath)) - boost::filesystem::create_directories (mProjectPath); + boost::filesystem::path projectPath = configuration.getUserPath() / "projects"; + + if (!boost::filesystem::is_directory (projectPath)) + boost::filesystem::create_directories (projectPath); } CSMDoc::DocumentManager::~DocumentManager() @@ -24,11 +30,7 @@ CSMDoc::DocumentManager::~DocumentManager() CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector& files, const boost::filesystem::path& savePath, bool new_) { - boost::filesystem::path projectFile (mProjectPath); - - projectFile /= savePath.filename().string() + ".project"; - - Document *document = new Document (files, savePath, new_, projectFile); + Document *document = new Document (mConfiguration, files, savePath, new_); mDocuments.push_back (document); diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp index 622a135a58..28a21216a6 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -6,6 +6,11 @@ #include +namespace Files +{ + class ConfigurationManager; +} + namespace CSMDoc { class Document; @@ -13,15 +18,14 @@ namespace CSMDoc class DocumentManager { std::vector mDocuments; - boost::filesystem::path mProjectPath; + const Files::ConfigurationManager& mConfiguration; DocumentManager (const DocumentManager&); DocumentManager& operator= (const DocumentManager&); public: - DocumentManager (const boost::filesystem::path& projectPath); - ///< \param projectPath Directory where additional per-project data will be stored. + DocumentManager (const Files::ConfigurationManager& configuration); ~DocumentManager(); From 9c2145eda126cb05abe2f84213fce6fa242b92e5 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 29 Sep 2013 09:11:57 +0200 Subject: [PATCH 064/148] Issue #913: Merge --master and --plugin switches Merged master/plugin switches into content in openmw and mwiniimporter. Extension in content files is now required. Signed-off-by: Lukasz Gromanowski --- apps/mwiniimporter/importer.cpp | 25 ++----- apps/openmw/CMakeLists.txt | 1 + apps/openmw/engine.cpp | 40 +++-------- apps/openmw/engine.hpp | 15 ++-- apps/openmw/main.cpp | 36 +++------- apps/openmw/mwworld/contentloader.hpp | 35 +++++++++ apps/openmw/mwworld/esmloader.cpp | 31 ++++++++ apps/openmw/mwworld/esmloader.hpp | 34 +++++++++ apps/openmw/mwworld/omwloader.cpp | 17 +++++ apps/openmw/mwworld/omwloader.hpp | 21 ++++++ apps/openmw/mwworld/worldimp.cpp | 100 +++++++++++++++++--------- apps/openmw/mwworld/worldimp.hpp | 15 +++- 12 files changed, 254 insertions(+), 116 deletions(-) create mode 100644 apps/openmw/mwworld/contentloader.hpp create mode 100644 apps/openmw/mwworld/esmloader.cpp create mode 100644 apps/openmw/mwworld/esmloader.hpp create mode 100644 apps/openmw/mwworld/omwloader.cpp create mode 100644 apps/openmw/mwworld/omwloader.hpp diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 8732b3eab3..b8b7e4c9da 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -813,8 +813,7 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con } void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) const { - std::vector esmFiles; - std::vector espFiles; + std::vector contentFiles; std::string baseGameFile("Game Files:GameFile"); std::string gameFile(""); @@ -832,29 +831,19 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) co std::string filetype(entry->substr(entry->length()-3)); Misc::StringUtils::toLower(filetype); - if(filetype.compare("esm") == 0) { - esmFiles.push_back(*entry); - } - else if(filetype.compare("esp") == 0) { - espFiles.push_back(*entry); + if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) { + contentFiles.push_back(*entry); } } gameFile = ""; } - cfg.erase("master"); - cfg.insert( std::make_pair > ("master", std::vector() ) ); + cfg.erase("content"); + cfg.insert( std::make_pair("content", std::vector() ) ); - for(std::vector::const_iterator it=esmFiles.begin(); it!=esmFiles.end(); ++it) { - cfg["master"].push_back(*it); - } - - cfg.erase("plugin"); - cfg.insert( std::make_pair > ("plugin", std::vector() ) ); - - for(std::vector::const_iterator it=espFiles.begin(); it!=espFiles.end(); ++it) { - cfg["plugin"].push_back(*it); + for(std::vector::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) { + cfg["content"].push_back(*it); } } diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index b367e2a1e7..807b1b5ff1 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -58,6 +58,7 @@ add_openmw_dir (mwworld cells localscripts customdata weather inventorystore ptr actionopen actionread actionequip timestamp actionalchemy cellstore actionapply actioneat esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor + contentloader esmloader omwloader ) add_openmw_dir (mwclass diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index a2eccbaf9a..d29301ed35 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -261,34 +261,14 @@ void OMW::Engine::setCell (const std::string& cellName) mCellName = cellName; } -// Set master file (esm) -// - If the given name does not have an extension, ".esm" is added automatically - -void OMW::Engine::addMaster (const std::string& master) +void OMW::Engine::addContentFile(const std::string& file) { - mMaster.push_back(master); - std::string &str = mMaster.back(); + if (file.find_last_of(".") == std::string::npos) + { + throw std::runtime_error("Missing extension in content file!"); + } - // Append .esm if not already there - std::string::size_type sep = str.find_last_of ("."); - if (sep == std::string::npos) - { - str += ".esm"; - } -} - -// Add plugin file (esp) -void OMW::Engine::addPlugin (const std::string& plugin) -{ - mPlugins.push_back(plugin); - std::string &str = mPlugins.back(); - - // Append .esp if not already there - std::string::size_type sep = str.find_last_of ("."); - if (sep == std::string::npos) - { - str += ".esp"; - } + mContentFiles.push_back(file); } void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity) @@ -403,7 +383,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mEnvironment.getWindowManager()->setNewGame(true); // Create the world - mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins, + mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mContentFiles, mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap, mActivationDistanceOverride)); MWBase::Environment::get().getWorld()->setupPlayer(); @@ -414,8 +394,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) //Load translation data mTranslationDataStorage.setEncoder(mEncoder); - for (size_t i = 0; i < mMaster.size(); i++) - mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster[i]); + for (size_t i = 0; i < mContentFiles.size(); i++) + mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]); Compiler::registerExtensions (mExtensions); @@ -480,7 +460,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) void OMW::Engine::go() { assert (!mCellName.empty()); - assert (!mMaster.empty()); + assert (!mContentFiles.empty()); assert (!mOgre); Settings::Manager settings; diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 665b0094c1..553d290687 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -68,8 +68,7 @@ namespace OMW boost::filesystem::path mResDir; OEngine::Render::OgreRenderer *mOgre; std::string mCellName; - std::vector mMaster; - std::vector mPlugins; + std::vector mContentFiles; int mFpsLevel; bool mVerboseScripts; bool mNewGame; @@ -135,13 +134,11 @@ namespace OMW /// Set start cell name (only interiors for now) void setCell(const std::string& cellName); - /// Set master file (esm) - /// - If the given name does not have an extension, ".esm" is added automatically - void addMaster(const std::string& master); - - /// Same as "addMaster", but for plugin files (esp) - /// - If the given name does not have an extension, ".esp" is added automatically - void addPlugin(const std::string& plugin); + /** + * @brief addContentFile - Adds content file (ie. esm/esp, or omwgame/omwaddon) to the content files container. + * @param file - filename (extension is required) + */ + void addContentFile(const std::string& file); /// Enable fps counter void showFPS(int level); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 27afd734ae..33f740b311 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -110,11 +110,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("start", bpo::value()->default_value("Beshara"), "set initial cell") - ("master", bpo::value()->default_value(StringsVector(), "") - ->multitoken(), "master file(s)") - - ("plugin", bpo::value()->default_value(StringsVector(), "") - ->multitoken(), "plugin file(s)") + ("content", bpo::value()->default_value(StringsVector(), "") + ->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon") ("anim-verbose", bpo::value()->implicit_value(true) ->default_value(false), "output animation indices files") @@ -152,8 +149,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("activate-dist", bpo::value ()->default_value (-1), "activation distance override"); - ; - bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv) .options(desc).allow_unregistered().run(); @@ -211,29 +206,18 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setResourceDir(variables["resources"].as()); - // master and plugin - StringsVector master = variables["master"].as(); - if (master.empty()) + StringsVector content = variables["content"].as(); + if (content.empty()) { - std::cout << "No master file given. Aborting...\n"; - return false; + std::cout << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting..." << std::endl; + return false; } - StringsVector plugin = variables["plugin"].as(); - // Removed check for 255 files, which would be the hard-coded limit in Morrowind. - // I'll keep the following variable in, maybe we can use it for something different. - // Say, a feedback like "loading file x/cnt". - // Commenting this out for now to silence compiler warning. - //int cnt = master.size() + plugin.size(); - - // Prepare loading master/plugin files (i.e. send filenames to engine) - for (std::vector::size_type i = 0; i < master.size(); i++) + StringsVector::const_iterator it(content.begin()); + StringsVector::const_iterator end(content.end()); + for (; it != end; ++it) { - engine.addMaster(master[i]); - } - for (std::vector::size_type i = 0; i < plugin.size(); i++) - { - engine.addPlugin(plugin[i]); + engine.addContentFile(*it); } // startup-settings diff --git a/apps/openmw/mwworld/contentloader.hpp b/apps/openmw/mwworld/contentloader.hpp new file mode 100644 index 0000000000..c57935c907 --- /dev/null +++ b/apps/openmw/mwworld/contentloader.hpp @@ -0,0 +1,35 @@ +#ifndef CONTENTLOADER_HPP +#define CONTENTLOADER_HPP + +#include +#include + +#include "components/loadinglistener/loadinglistener.hpp" + +namespace MWWorld +{ + +struct ContentLoader +{ + ContentLoader(Loading::Listener& listener) + : mListener(listener) + { + } + + virtual ~ContentLoader() + { + } + + virtual void load(const boost::filesystem::path& filepath, int& index) + { + std::cout << "Loading content file " << filepath.string() << std::endl; + mListener.setLabel(filepath.string()); + } + + protected: + Loading::Listener& mListener; +}; + +} /* namespace MWWorld */ + +#endif /* CONTENTLOADER_HPP */ diff --git a/apps/openmw/mwworld/esmloader.cpp b/apps/openmw/mwworld/esmloader.cpp new file mode 100644 index 0000000000..1b8880d375 --- /dev/null +++ b/apps/openmw/mwworld/esmloader.cpp @@ -0,0 +1,31 @@ +#include "esmloader.hpp" +#include "esmstore.hpp" + +#include "components/to_utf8/to_utf8.hpp" + +namespace MWWorld +{ + +EsmLoader::EsmLoader(MWWorld::ESMStore& store, std::vector& readers, + ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener) + : ContentLoader(listener) + , mStore(store) + , mEsm(readers) + , mEncoder(encoder) +{ +} + +void EsmLoader::load(const boost::filesystem::path& filepath, int& index) +{ + ContentLoader::load(filepath.filename(), index); + + ESM::ESMReader lEsm; + lEsm.setEncoder(mEncoder); + lEsm.setIndex(index); + lEsm.setGlobalReaderList(&mEsm); + lEsm.open(filepath.string()); + mEsm[index] = lEsm; + mStore.load(mEsm[index], &mListener); +} + +} /* namespace MWWorld */ diff --git a/apps/openmw/mwworld/esmloader.hpp b/apps/openmw/mwworld/esmloader.hpp new file mode 100644 index 0000000000..d799c3f152 --- /dev/null +++ b/apps/openmw/mwworld/esmloader.hpp @@ -0,0 +1,34 @@ +#ifndef ESMLOADER_HPP +#define ESMLOADER_HPP + +#include + +#include "contentloader.hpp" +#include "components/esm/esmreader.hpp" + +namespace ToUTF8 +{ + class Utf8Encoder; +} + +namespace MWWorld +{ + +class ESMStore; + +struct EsmLoader : public ContentLoader +{ + EsmLoader(MWWorld::ESMStore& store, std::vector& readers, + ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener); + + void load(const boost::filesystem::path& filepath, int& index); + + private: + std::vector& mEsm; + MWWorld::ESMStore& mStore; + ToUTF8::Utf8Encoder* mEncoder; +}; + +} /* namespace MWWorld */ + +#endif // ESMLOADER_HPP diff --git a/apps/openmw/mwworld/omwloader.cpp b/apps/openmw/mwworld/omwloader.cpp new file mode 100644 index 0000000000..8562a4fe04 --- /dev/null +++ b/apps/openmw/mwworld/omwloader.cpp @@ -0,0 +1,17 @@ +#include "omwloader.hpp" + +namespace MWWorld +{ + +OmwLoader::OmwLoader(Loading::Listener& listener) + : ContentLoader(listener) +{ +} + +void OmwLoader::load(const boost::filesystem::path& filepath, int& index) +{ + ContentLoader::load(filepath.filename(), index); +} + +} /* namespace MWWorld */ + diff --git a/apps/openmw/mwworld/omwloader.hpp b/apps/openmw/mwworld/omwloader.hpp new file mode 100644 index 0000000000..cb9faa4303 --- /dev/null +++ b/apps/openmw/mwworld/omwloader.hpp @@ -0,0 +1,21 @@ +#ifndef OMWLOADER_HPP +#define OMWLOADER_HPP + +#include "contentloader.hpp" + +namespace MWWorld +{ + +/** + * @brief Placeholder for real OpenMW content loader + */ +struct OmwLoader : public ContentLoader +{ + OmwLoader(Loading::Listener& listener); + + void load(const boost::filesystem::path& filepath, int& index); +}; + +} /* namespace MWWorld */ + +#endif /* OMWLOADER_HPP */ diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f3d4c81b76..b7b23e5c15 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,4 +1,11 @@ #include "worldimp.hpp" +#ifdef _WIN32 +#include +#elif defined HAVE_UNORDERED_MAP +#include +#else +#include +#endif #include @@ -31,6 +38,10 @@ #include "containerstore.hpp" #include "inventorystore.hpp" +#include "contentloader.hpp" +#include "esmloader.hpp" +#include "omwloader.hpp" + using namespace Ogre; namespace @@ -80,6 +91,38 @@ namespace namespace MWWorld { + struct GameContentLoader : public ContentLoader + { + GameContentLoader(Loading::Listener& listener) + : ContentLoader(listener) + { + } + + bool addLoader(const std::string& extension, ContentLoader* loader) + { + return mLoaders.insert(std::make_pair(extension, loader)).second; + } + + void load(const boost::filesystem::path& filepath, int& index) + { + LoadersContainer::iterator it(mLoaders.find(filepath.extension().string())); + if (it != mLoaders.end()) + { + it->second->load(filepath, index); + } + else + { + std::string msg("Cannot load file: "); + msg += filepath.string(); + throw std::runtime_error(msg.c_str()); + } + } + + private: + typedef std::tr1::unordered_map LoadersContainer; + LoadersContainer mLoaders; + }; + Ptr World::getPtrViaHandle (const std::string& handle, Ptr::CellStore& cell) { if (MWWorld::LiveCellRef *ref = @@ -163,7 +206,7 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::vector& master, const std::vector& plugins, + const std::vector& contentFiles, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int mActivationDistanceOverride) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), @@ -181,44 +224,22 @@ namespace MWWorld mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); - int idx = 0; // NOTE: We might need to reserve one more for the running game / save. - mEsm.resize(master.size() + plugins.size()); + mEsm.resize(contentFiles.size()); Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); listener->loadingOn(); - for (std::vector::size_type i = 0; i < master.size(); i++, idx++) - { - boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master[i])); - std::cout << "Loading ESM " << masterPath.string() << "\n"; - listener->setLabel(masterPath.filename().string()); + GameContentLoader gameContentLoader(*listener); + EsmLoader esmLoader(mStore, mEsm, encoder, *listener); + OmwLoader omwLoader(*listener); - // This parses the ESM file - ESM::ESMReader lEsm; - lEsm.setEncoder(encoder); - lEsm.setIndex(idx); - lEsm.setGlobalReaderList(&mEsm); - lEsm.open (masterPath.string()); - mEsm[idx] = lEsm; - mStore.load (mEsm[idx], listener); - } + gameContentLoader.addLoader(".esm", &esmLoader); + gameContentLoader.addLoader(".esp", &esmLoader); + gameContentLoader.addLoader(".omwgame", &omwLoader); + gameContentLoader.addLoader(".omwaddon", &omwLoader); - for (std::vector::size_type i = 0; i < plugins.size(); i++, idx++) - { - boost::filesystem::path pluginPath (fileCollections.getCollection (".esp").getPath (plugins[i])); + loadContentFiles(fileCollections, contentFiles, gameContentLoader); - std::cout << "Loading ESP " << pluginPath.string() << "\n"; - listener->setLabel(pluginPath.filename().string()); - - // This parses the ESP file - ESM::ESMReader lEsm; - lEsm.setEncoder(encoder); - lEsm.setIndex(idx); - lEsm.setGlobalReaderList(&mEsm); - lEsm.open (pluginPath.string()); - mEsm[idx] = lEsm; - mStore.load (mEsm[idx], listener); - } listener->loadingOff(); // insert records that may not be present in all versions of MW @@ -1960,4 +1981,19 @@ namespace MWWorld return mGodMode; } + void World::loadContentFiles(const Files::Collections& fileCollections, + const std::vector& content, ContentLoader& contentLoader) + { + std::vector::const_iterator it(content.begin()); + std::vector::const_iterator end(content.end()); + for (int idx = 0; it != end; ++it, ++idx) + { + boost::filesystem::path filename(*it); + const Files::MultiDirCollection& col = fileCollections.getCollection(filename.extension().string()); + if (col.doesExist(*it)) + { + contentLoader.load(col.getPath(*it), idx); + } + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 53b01f1abf..d39189282e 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -14,6 +14,8 @@ #include "../mwbase/world.hpp" +#include "contentloader.hpp" + namespace Ogre { class Vector3; @@ -41,6 +43,8 @@ namespace MWRender class Animation; } +struct ContentLoader; + namespace MWWorld { class WeatherManager; @@ -113,6 +117,15 @@ namespace MWWorld void ensureNeededRecords(); + /** + * @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon) + * @param fileCollections- Container which holds content file names and their paths + * @param content - Container which holds content file names + * @param contentLoader - + */ + void loadContentFiles(const Files::Collections& fileCollections, + const std::vector& content, ContentLoader& contentLoader); + int mPlayIntro; bool mTeleportEnabled; @@ -121,7 +134,7 @@ namespace MWWorld World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::vector& master, const std::vector& plugins, + const std::vector& contentFiles, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int mActivationDistanceOverride); From ef617d408b7baa09e704db40b8b157608ac9da98 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 29 Sep 2013 09:14:40 +0200 Subject: [PATCH 065/148] Issue #913: Merge --master and --plugin switches Merged master/plugin switches in launcher. Signed-off-by: Lukasz Gromanowski --- apps/launcher/datafilespage.cpp | 9 +++++---- apps/launcher/settings/gamesettings.cpp | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 44392794bd..43f09d1687 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -206,19 +206,20 @@ void DataFilesPage::saveSettings() mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin")); mGameSettings.remove(QString("master")); - mGameSettings.remove(QString("plugin")); + mGameSettings.remove(QString("plugins")); + mGameSettings.remove(QString("content")); - ContentSelectorModel::ContentFileList items = mContentModel->checkedItems(); + ContentSelectorModel::ContentFileList items = mContentModel->checkedItems(); foreach(const ContentSelectorModel::EsmFile *item, items) { if (item->gameFiles().size() == 0) { mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/master"), item->fileName()); - mGameSettings.setMultiValue(QString("master"), item->fileName()); + mGameSettings.setMultiValue(QString("content"), item->fileName()); } else { mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/plugin"), item->fileName()); - mGameSettings.setMultiValue(QString("plugin"), item->fileName()); + mGameSettings.setMultiValue(QString("content"), item->fileName()); } } diff --git a/apps/launcher/settings/gamesettings.cpp b/apps/launcher/settings/gamesettings.cpp index 205879bc37..7b2356cd08 100644 --- a/apps/launcher/settings/gamesettings.cpp +++ b/apps/launcher/settings/gamesettings.cpp @@ -163,12 +163,12 @@ bool GameSettings::writeFile(QTextStream &stream) QStringList masters = mSettings.values(QString("master")); for (int i = masters.count(); i--;) { - stream << "master=" << masters.at(i) << "\n"; + stream << "content=" << masters.at(i) << "\n"; } QStringList plugins = mSettings.values(QString("plugin")); for (int i = plugins.count(); i--;) { - stream << "plugin=" << plugins.at(i) << "\n"; + stream << "content=" << plugins.at(i) << "\n"; } return true; From 24b167b7552ce8bf6e62933a535752a899c77473 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 29 Sep 2013 12:19:07 -0500 Subject: [PATCH 066/148] Implemented ContentSelector as a singleton "charm" modifier for FileDialog... --- apps/launcher/datafilespage.cpp | 11 +- apps/opencs/editor.cpp | 13 +- apps/opencs/view/doc/filedialog.cpp | 103 +++------ apps/opencs/view/doc/filedialog.hpp | 38 ++-- .../contentselector/view/contentselector.cpp | 213 +++++++++++++++--- .../contentselector/view/contentselector.hpp | 51 ++++- 6 files changed, 285 insertions(+), 144 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 44392794bd..828b2f2ba2 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -29,8 +29,7 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam , mLauncherSettings(launcherSettings) { setupUi(this); - // mContentSelector.setParent(parent); - +/* // QMetaObject::connectSlotsByName(this); projectGroupBox->hide(); @@ -52,7 +51,7 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam setupDataFiles(); - updateViews(); + updateViews();*/ } void DataFilesPage::buildContentModel() @@ -112,10 +111,10 @@ void DataFilesPage::updateViews() void ContentSelectorView::ContentSelector::addFiles(const QString &path) { - mContentModel->addFiles(path); + // mContentModel->addFiles(path); //mContentModel->sort(3); // Sort by date accessed - gameFileView->setCurrentIndex(-1); - mContentModel->uncheckAll(); + // ui.gameFileView->setCurrentIndex(-1); + // mContentModel->uncheckAll(); } void DataFilesPage::createActions() diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index ba1dfb57ec..fc9168e2ee 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -123,37 +123,34 @@ void CS::Editor::loadDocument() void CS::Editor::openFiles() { std::vector files; - QStringList paths = mFileDialog.checkedItemsPaths(); - foreach (const QString &path, paths) { + foreach (const QString &path, mFileDialog.selectedFilepaths()) { files.push_back(path.toStdString()); } /// \todo Get the save path from the file dialogue - CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), false); mViewManager.addView (document); - mFileDialog.hide(); + mFileDialog.close(); } void CS::Editor::createNewFile() { std::vector files; - QStringList paths = mFileDialog.checkedItemsPaths(); - foreach (const QString &path, paths) { + foreach (const QString &path, mFileDialog.selectedFilepaths()) { files.push_back(path.toStdString()); } - files.push_back(mFileDialog.fileName().toStdString()); + files.push_back(mFileDialog.filename().toStdString()); /// \todo Get the save path from the file dialogue. CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), true); mViewManager.addView (document); - mFileDialog.hide(); + mFileDialog.close(); } void CS::Editor::createNewGame (const boost::filesystem::path& file) diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 68aab27d5f..5a97a7a26d 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -10,107 +10,76 @@ #include #include #include +#include #include #include - -#include "filewidget.hpp" -#include "adjusterwidget.hpp" +#include "components/contentselector/view/contentselector.hpp" #include CSVDoc::FileDialog::FileDialog(QWidget *parent) : - ContentSelector(parent), - mFileWidget (new FileWidget (this)), - mAdjusterWidget (new AdjusterWidget (this)), - mEnable_1(false), - mEnable_2(false) -{ - // Hide the profile elements - profileGroupBox->hide(); - addonView->showColumn(2); + QDialog(parent), + mOpenFileFlags (ContentSelectorView::Flag_Content | ContentSelectorView::Flag_LoadAddon), + mNewFileFlags (ContentSelectorView::Flag_Content | ContentSelectorView::Flag_NewAddon) +{ resize(400, 400); - - mFileWidget->setType(true); - mFileWidget->extensionLabelIsVisible(false); - - connect(projectCreateButton, SIGNAL(clicked()), this, SIGNAL(createNewFile())); - - connect(projectButtonBox, SIGNAL(accepted()), this, SIGNAL(openFiles())); - connect(projectButtonBox, SIGNAL(rejected()), this, SLOT(reject())); - - connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)), - mAdjusterWidget, SLOT (setName (const QString&, bool))); - - connect (mAdjusterWidget, SIGNAL (stateChanged (bool)), this, SLOT (slotAdjusterChanged(bool))); - connect (this, SIGNAL (signalGameFileChanged(int)), this, SLOT (slotGameFileSelected(int))); - connect (this, SIGNAL (signalUpdateCreateButton(bool, int)), this, SLOT (slotEnableCreateButton(bool, int))); } -void CSVDoc::FileDialog::updateOpenButton(const QStringList &items) +void CSVDoc::FileDialog::addFiles(const QString &path) { - QPushButton *openButton = projectButtonBox->button(QDialogButtonBox::Open); - - if (!openButton) - return; - - openButton->setEnabled(!items.isEmpty()); + ContentSelectorView::ContentSelector::addFiles(path); } -void CSVDoc::FileDialog::slotEnableCreateButton(bool enable, int widgetNumber) +QString CSVDoc::FileDialog::filename() { - - if (widgetNumber == 1) - mEnable_1 = enable; - - if (widgetNumber == 2) - mEnable_2 = enable; - - projectCreateButton->setEnabled(mEnable_1 && mEnable_2); + return ContentSelectorView::ContentSelector::instance().filename(); } -QString CSVDoc::FileDialog::fileName() +QStringList CSVDoc::FileDialog::selectedFilepaths() { - return mFileWidget->getName(); + return ContentSelectorView::ContentSelector::instance().selectedFiles(); +} + +void CSVDoc::FileDialog::showDialog() +{ + show(); + raise(); + activateWindow(); } void CSVDoc::FileDialog::openFile() { setWindowTitle(tr("Open")); - mFileWidget->hide(); - adjusterWidgetFrame->hide(); - projectCreateButton->hide(); - projectGroupBox->setTitle(tr("")); - projectButtonBox->button(QDialogButtonBox::Open)->setEnabled(false); + ContentSelectorView::ContentSelector::configure(this, mOpenFileFlags); - show(); - raise(); - activateWindow(); + connect (&ContentSelectorView::ContentSelector::instance(), + SIGNAL (accepted()), this, SIGNAL (openFiles())); + + connect (&ContentSelectorView::ContentSelector::instance(), + SIGNAL (rejected()), this, SLOT (slotRejected())); + + showDialog(); } void CSVDoc::FileDialog::newFile() { setWindowTitle(tr("New")); - fileWidgetFrame->layout()->addWidget(mFileWidget); - adjusterWidgetFrame->layout()->addWidget(mAdjusterWidget); + ContentSelectorView::ContentSelector::configure(this, mNewFileFlags); - projectButtonBox->setStandardButtons(QDialogButtonBox::Cancel); - projectButtonBox->addButton(projectCreateButton, QDialogButtonBox::ActionRole); + connect (&ContentSelectorView::ContentSelector::instance(), + SIGNAL (accepted()), this, SIGNAL (createNewFile())); - show(); - raise(); - activateWindow(); + connect (&ContentSelectorView::ContentSelector::instance(), + SIGNAL (rejected()), this, SLOT (slotRejected())); + + showDialog(); } -void CSVDoc::FileDialog::slotAdjusterChanged(bool value) +void CSVDoc::FileDialog::slotRejected() { - emit signalUpdateCreateButton(mAdjusterWidget->isValid(), 2); -} - -void CSVDoc::FileDialog::slotGameFileSelected(int value) -{ - emit signalUpdateCreateButton(value > -1, 1); + close(); } diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 7782dd94ea..88d408b5c6 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -3,9 +3,7 @@ #include #include - -#include "components/contentselector/view/contentselector.hpp" -#include "ui_datafilespage.h" +#include "../../../../components/contentselector/view/contentselector.hpp" class QDialogButtonBox; class QSortFilterProxyModel; @@ -19,6 +17,8 @@ class QLabel; class DataFilesModel; class PluginsProxyModel; + + namespace ContentSelectorView { class LineEdit; @@ -26,42 +26,38 @@ namespace ContentSelectorView namespace CSVDoc { - class FileWidget; - class AdjusterWidget; - - class FileDialog : public ContentSelectorView::ContentSelector + class FileDialog : public QDialog { Q_OBJECT - FileWidget *mFileWidget; - AdjusterWidget *mAdjusterWidget; - - bool mEnable_1; - bool mEnable_2; + unsigned char mOpenFileFlags; + unsigned char mNewFileFlags; public: explicit FileDialog(QWidget *parent = 0); void openFile(); void newFile(); + void addFiles (const QString &path); - QString fileName(); + QString filename(); + QStringList selectedFilepaths(); + + private: + + void showDialog(); signals: + void openFiles(); void createNewFile(); - void signalUpdateCreateButton (bool, int); - void signalUpdateCreateButtonFlags(int); - public slots: + void slotRejected(); + private slots: - //void updateViews(); - void updateOpenButton(const QStringList &items); - void slotEnableCreateButton(bool enable, int widgetNumber); - void slotAdjusterChanged(bool value); - void slotGameFileSelected(int value); + }; } #endif // FILEDIALOG_HPP diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index e6ed0ec567..d9caea3bed 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -8,83 +8,205 @@ #include #include #include +#include +#include -ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) : - QDialog(parent) +#include "../../../apps/opencs/view/doc/filewidget.hpp" +#include "../../../apps/opencs/view/doc/adjusterwidget.hpp" + +ContentSelectorView::ContentSelector *ContentSelectorView::ContentSelector::mInstance = 0; +QStringList ContentSelectorView::ContentSelector::mFilePaths; + +void ContentSelectorView::ContentSelector::configure(QWidget *subject, unsigned char flags) { - setupUi(this); + assert(!mInstance); + mInstance = new ContentSelector (subject, flags); +} + +ContentSelectorView::ContentSelector& ContentSelectorView::ContentSelector::instance() +{ + assert(mInstance); + return *mInstance; +} + +ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent, unsigned char flags) : + QWidget(parent), mFlags (flags), + mAdjusterWidget (0), mFileWidget (0) +{ + ui.setupUi (this); + + parent->setLayout(new QGridLayout()); + parent->layout()->addWidget(this); buildContentModel(); buildGameFileView(); buildAddonView(); buildProfilesView(); + buildNewAddonView(); + buildLoadAddonView(); - updateViews(); + /* + //mContentModel->sort(3); // Sort by date accessed +*/ +} + +bool ContentSelectorView::ContentSelector::isFlagged(SelectorFlags flag) const +{ + return (mFlags & flag); } void ContentSelectorView::ContentSelector::buildContentModel() { + if (!isFlagged (Flag_Content)) + return; + mContentModel = new ContentSelectorModel::ContentModel(); - connect(mContentModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); + + if (mFilePaths.size()>0) + { + foreach (const QString &path, mFilePaths) + mContentModel->addFiles(path); + + mFilePaths.clear(); + } + + ui.gameFileView->setCurrentIndex(-1); + mContentModel->uncheckAll(); } void ContentSelectorView::ContentSelector::buildGameFileView() { + if (!isFlagged (Flag_Content)) + { + ui.gameFileView->setVisible(false); + return; + } + mGameFileProxyModel = new QSortFilterProxyModel(this); mGameFileProxyModel->setFilterRegExp(QString::number((int)ContentSelectorModel::ContentType_GameFile)); mGameFileProxyModel->setFilterRole (Qt::UserRole); mGameFileProxyModel->setSourceModel (mContentModel); - gameFileView->setPlaceholderText(QString("Select a game file...")); - gameFileView->setModel(mGameFileProxyModel); + ui.gameFileView->setPlaceholderText(QString("Select a game file...")); + ui.gameFileView->setModel(mGameFileProxyModel); - connect(gameFileView, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentGameFileIndexChanged(int))); - connect(gameFileView, SIGNAL(currentIndexChanged(int)), this, SIGNAL(signalGameFileChanged(int))); + connect(ui.gameFileView, SIGNAL(currentIndexChanged(int)), this, SLOT (slotCurrentGameFileIndexChanged(int))); - gameFileView->setCurrentIndex(-1); - gameFileView->setCurrentIndex(0); + ui.gameFileView->setCurrentIndex(-1); } void ContentSelectorView::ContentSelector::buildAddonView() { + if (!isFlagged (Flag_Content)) + { + ui.addonView->setVisible(false); + return; + } + mAddonProxyModel = new QSortFilterProxyModel(this); mAddonProxyModel->setFilterRegExp (QString::number((int)ContentSelectorModel::ContentType_Addon)); mAddonProxyModel->setFilterRole (Qt::UserRole); mAddonProxyModel->setDynamicSortFilter (true); mAddonProxyModel->setSourceModel (mContentModel); - addonView->setModel(mAddonProxyModel); + ui.addonView->setModel(mAddonProxyModel); - connect(addonView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotAddonTableItemClicked(const QModelIndex &))); + connect(ui.addonView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotAddonTableItemClicked(const QModelIndex &))); } void ContentSelectorView::ContentSelector::buildProfilesView() { - profilesComboBox->setPlaceholderText(QString("Select a profile...")); - connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); + if (!isFlagged (Flag_Profile)) + { + ui.profileGroupBox->setVisible(false); + return; + } + + ui.profilesComboBox->setPlaceholderText(QString("Select a profile...")); + connect(ui.profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); } -void ContentSelectorView::ContentSelector::updateViews() +void ContentSelectorView::ContentSelector::buildLoadAddonView() { - // Ensure the columns are hidden because sort() re-enables them - addonView->setColumnHidden(1, true); - addonView->setColumnHidden(2, true); - addonView->setColumnHidden(3, true); - addonView->setColumnHidden(4, true); - addonView->setColumnHidden(5, true); - addonView->setColumnHidden(6, true); - addonView->setColumnHidden(7, true); - addonView->setColumnHidden(8, true); - addonView->resizeColumnsToContents(); + if (!isFlagged (Flag_LoadAddon)) + { + ui.projectGroupBox->setVisible (false); + return; + } + + ui.projectCreateButton->setVisible (false); + // ui.projectButtonBox->setStandardButtons(QDialogButtonBox::Open | QDialogButtonBox::Cancel); + ui.projectGroupBox->setTitle (""); + + connect(ui.projectButtonBox, SIGNAL(accepted()), this, SIGNAL(accepted())); + connect(ui.projectButtonBox, SIGNAL(rejected()), this, SIGNAL(rejected())); } +void ContentSelectorView::ContentSelector::buildNewAddonView() +{ + if (!isFlagged (Flag_NewAddon)) + { + ui.profileGroupBox->setVisible (false); + return; + } + + mFileWidget = new CSVDoc::FileWidget (this); + mAdjusterWidget = new CSVDoc::AdjusterWidget (this); + + mFileWidget->setType(true); + mFileWidget->extensionLabelIsVisible(false); + + ui.fileWidgetFrame->layout()->addWidget(mFileWidget); + ui.adjusterWidgetFrame->layout()->addWidget(mAdjusterWidget); + + ui.projectButtonBox->setStandardButtons(QDialogButtonBox::Cancel); + ui.projectButtonBox->addButton(ui.projectCreateButton, QDialogButtonBox::ActionRole); + + connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)), + mAdjusterWidget, SLOT (setName (const QString&, bool))); + + connect (mAdjusterWidget, SIGNAL (stateChanged(bool)), this, SLOT (slotUpdateCreateButton(bool))); + + connect(ui.projectCreateButton, SIGNAL(clicked()), this, SIGNAL(accepted())); + connect(ui.projectButtonBox, SIGNAL(rejected()), this, SIGNAL(rejected())); +} + +QString ContentSelectorView::ContentSelector::filename() const +{ + QString filepath = ""; + + if (mAdjusterWidget) + filepath = QString::fromAscii(mAdjusterWidget->getPath().c_str()); + + return filepath; +} + +QStringList ContentSelectorView::ContentSelector::selectedFiles() const +{ + QStringList filePaths; + + if (mContentModel) + { + foreach (ContentSelectorModel::EsmFile *file, mContentModel->checkedItems()) + filePaths.append(file->path()); + } + + return filePaths; +} + + void ContentSelectorView::ContentSelector::addFiles(const QString &path) { - mContentModel->addFiles(path); - //mContentModel->sort(3); // Sort by date accessed - gameFileView->setCurrentIndex(-1); - mContentModel->uncheckAll(); + // if the model hasn't been instantiated, queue the path + if (!mInstance) + mFilePaths.append(path); + else + { + mInstance->mContentModel->addFiles(path); + mInstance->ui.gameFileView->setCurrentIndex(-1); + mInstance->mContentModel->uncheckAll(); + } } QStringList ContentSelectorView::ContentSelector::checkedItemsPaths() @@ -99,14 +221,14 @@ QStringList ContentSelectorView::ContentSelector::checkedItemsPaths() void ContentSelectorView::ContentSelector::slotCurrentProfileIndexChanged(int index) { - emit profileChanged(index); + emit signalProfileChanged(index); } void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int index) { static int oldIndex = -1; - QAbstractItemModel *const model = gameFileView->model(); + QAbstractItemModel *const model = ui.gameFileView->model(); QSortFilterProxyModel *proxy = dynamic_cast(model); if (proxy) @@ -122,16 +244,37 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i if (proxy) proxy->setDynamicSortFilter(true); - emit signalGameFileChanged(true); + slotUpdateCreateButton(true); } void ContentSelectorView::ContentSelector::slotAddonTableItemClicked(const QModelIndex &index) { - QAbstractItemModel *const model = addonView->model(); - //QSortFilterProxyModel *proxy = dynamic_cast(model); + QAbstractItemModel *const model = ui.addonView->model(); if (model->data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked) model->setData(index, Qt::Checked, Qt::CheckStateRole); else model->setData(index, Qt::Unchecked, Qt::CheckStateRole); } + +void ContentSelectorView::ContentSelector::slotUpdateOpenButton(const QStringList &items) +{ + QPushButton *openButton = ui.projectButtonBox->button(QDialogButtonBox::Open); + + if (!openButton) + return; + + openButton->setEnabled(!items.isEmpty()); +} + +void ContentSelectorView::ContentSelector::slotUpdateCreateButton(bool) +{ + //enable only if a game file is selected and the adjuster widget is non-empty + bool validGameFile = (ui.gameFileView->currentIndex() != -1); + bool validFilename = false; + + if (mAdjusterWidget) + validFilename = mAdjusterWidget->isValid(); + + ui.projectCreateButton->setEnabled(validGameFile && validFilename); +} diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index 5af53dc464..48c3ae103e 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -9,12 +9,33 @@ namespace ContentSelectorModel { class ContentModel; } class QSortFilterProxyModel; +namespace CSVDoc +{ + class FileWidget; + class AdjusterWidget; +} namespace ContentSelectorView { - class ContentSelector : public QDialog, protected Ui::DataFilesPage + enum SelectorFlags + { + Flag_Content = 0x01, // gamefile combobox & addon list view + Flag_NewAddon = 0x02, // enable project button box (Create/Cancel) and file/adjuster widgets + Flag_LoadAddon = 0x04, // enable project button box (Open/Cancel) + Flag_Profile = 0x08 // enable profile combo box + }; + + class ContentSelector : public QWidget { Q_OBJECT + unsigned char mFlags; + + static ContentSelector *mInstance; + static QStringList mFilePaths; + + CSVDoc::FileWidget *mFileWidget; + CSVDoc::AdjusterWidget *mAdjusterWidget; + protected: ContentSelectorModel::ContentModel *mContentModel; @@ -23,30 +44,46 @@ namespace ContentSelectorView public: - explicit ContentSelector(QWidget *parent = 0); + static void configure(QWidget *subject, unsigned char flags = Flag_Content); + static ContentSelector &instance(); + static void addFiles(const QString &path); - static ContentSelector &cast(QWidget *subject); //static constructor function for singleton performance. - - void addFiles(const QString &path); void setCheckState(QModelIndex index, QSortFilterProxyModel *model); QStringList checkedItemsPaths(); + QString filename() const; + QStringList selectedFiles() const; private: + explicit ContentSelector(QWidget *parent = 0, unsigned char flags = Flag_Content); + Ui::DataFilesPage ui; + void buildContentModel(); void buildGameFileView(); void buildAddonView(); void buildProfilesView(); + void buildNewAddonView(); + void buildLoadAddonView(); + + bool isFlagged(SelectorFlags flag) const; signals: - void profileChanged(int index); + void accepted(); + void rejected(); + + void signalProfileChanged(int index); void signalGameFileChanged(int value); + void signalCreateButtonClicked(); + private slots: - void updateViews(); + void slotCurrentProfileIndexChanged(int index); void slotCurrentGameFileIndexChanged(int index); void slotAddonTableItemClicked(const QModelIndex &index); + + void slotUpdateCreateButton (bool); + void slotUpdateOpenButton(const QStringList &items); }; } From 00c78a4aa1be9d465d8e7e625871bf02af3619c5 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Tue, 1 Oct 2013 21:29:45 -0500 Subject: [PATCH 067/148] Implementing ContentSelector class in DataFilesPage Moved AdjusterWidget / FileWidget to ContentSelectorView --- apps/launcher/datafilespage.cpp | 340 ++++-------------- apps/launcher/datafilespage.hpp | 46 +-- apps/launcher/maindialog.cpp | 6 +- apps/opencs/CMakeLists.txt | 3 +- apps/opencs/editor.cpp | 4 +- apps/opencs/view/doc/filedialog.cpp | 11 +- apps/opencs/view/doc/filedialog.hpp | 2 +- apps/opencs/view/doc/newgame.cpp | 4 +- components/CMakeLists.txt | 1 + .../contentselector/view}/adjusterwidget.cpp | 0 .../contentselector/view}/adjusterwidget.hpp | 0 .../contentselector/view/contentselector.cpp | 169 ++++++++- .../contentselector/view/contentselector.hpp | 35 +- .../contentselector/view}/filewidget.cpp | 0 .../contentselector/view}/filewidget.hpp | 0 .../contentselector/view/profilescombobox.cpp | 3 + .../contentselector/view/profilescombobox.hpp | 2 + files/ui/datafilespage.ui | 49 ++- 18 files changed, 316 insertions(+), 359 deletions(-) rename {apps/opencs/view/doc => components/contentselector/view}/adjusterwidget.cpp (100%) rename {apps/opencs/view/doc => components/contentselector/view}/adjusterwidget.hpp (100%) rename {apps/opencs/view/doc => components/contentselector/view}/filewidget.cpp (100%) rename {apps/opencs/view/doc => components/contentselector/view}/filewidget.hpp (100%) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 828b2f2ba2..b298f8a141 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -19,7 +19,6 @@ #include "utils/textinputdialog.hpp" #include "components/contentselector/view/contentselector.hpp" -#include "components/contentselector/model/contentmodel.hpp" #include @@ -28,176 +27,59 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam , mGameSettings(gameSettings) , mLauncherSettings(launcherSettings) { - setupUi(this); -/* - // QMetaObject::connectSlotsByName(this); + unsigned char flags; - projectGroupBox->hide(); + flags = ContentSelectorView::Flag_Content | ContentSelectorView::Flag_Profile; - // Create a dialog for the new profile name input - mNewProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this); + ContentSelectorView::ContentSelector::configure(this, flags); - connect(mNewProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString))); - - - - buildContentModel(); - buildGameFileView(); - buildAddonView(); - buildProfilesView(); - - - createActions(); setupDataFiles(); + ContentSelectorView::ContentSelector &cSelector = + ContentSelectorView::ContentSelector::instance(); - updateViews();*/ -} + connect (&cSelector, SIGNAL (signalProfileRenamed (QString, QString)), + this, SLOT (slotProfileRenamed (QString, QString))); -void DataFilesPage::buildContentModel() -{ - mContentModel = new ContentSelectorModel::ContentModel(); - connect(mContentModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); -} + connect (&cSelector, SIGNAL (signalProfileChanged (QString, QString)), + this, SLOT (slotProfileChanged (QString, QString))); -void DataFilesPage::buildGameFileView() -{ - mGameFileProxyModel = new QSortFilterProxyModel(this); - mGameFileProxyModel->setFilterRegExp(QString::number((int)ContentSelectorModel::ContentType_GameFile)); - mGameFileProxyModel->setFilterRole (Qt::UserRole); - mGameFileProxyModel->setSourceModel (mContentModel); + connect (&cSelector, SIGNAL (signalProfileDeleted (QString)), + this, SLOT (slotProfileDeleted (QString))); - gameFileView->setPlaceholderText(QString("Select a game file...")); - gameFileView->setModel(mGameFileProxyModel); - - connect(gameFileView, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentGameFileIndexChanged(int))); - - gameFileView->setCurrentIndex(-1); - gameFileView->setCurrentIndex(0); -} - -void DataFilesPage::buildAddonView() -{ - mAddonProxyModel = new QSortFilterProxyModel(this); - mAddonProxyModel->setFilterRegExp (QString::number((int)ContentSelectorModel::ContentType_Addon)); - mAddonProxyModel->setFilterRole (Qt::UserRole); - mAddonProxyModel->setDynamicSortFilter (true); - mAddonProxyModel->setSourceModel (mContentModel); - - addonView->setModel(mAddonProxyModel); - - connect(addonView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotAddonTableItemClicked(const QModelIndex &))); -} - -void DataFilesPage::buildProfilesView() -{ - profilesComboBox->setPlaceholderText(QString("Select a profile...")); - connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); -} - -void DataFilesPage::updateViews() -{ - // Ensure the columns are hidden because sort() re-enables them - addonView->setColumnHidden(1, true); - addonView->setColumnHidden(2, true); - addonView->setColumnHidden(3, true); - addonView->setColumnHidden(4, true); - addonView->setColumnHidden(5, true); - addonView->setColumnHidden(6, true); - addonView->setColumnHidden(7, true); - addonView->setColumnHidden(8, true); - addonView->resizeColumnsToContents(); -} - -void ContentSelectorView::ContentSelector::addFiles(const QString &path) -{ - // mContentModel->addFiles(path); - //mContentModel->sort(3); // Sort by date accessed - // ui.gameFileView->setCurrentIndex(-1); - // mContentModel->uncheckAll(); -} - -void DataFilesPage::createActions() -{ - // Add the actions to the toolbuttons - newProfileButton->setDefaultAction(newProfileAction); - deleteProfileButton->setDefaultAction(deleteProfileAction); -} - -void DataFilesPage::setupDataFiles() -{ - // Set the encoding to the one found in openmw.cfg or the default - //mContentSelector.setEncoding(mGameSettings.value(QString("encoding"), QString("win1252"))); - - QStringList paths = mGameSettings.getDataDirs(); - - foreach (const QString &path, paths) { - //mContentSelector. - mContentModel->addFiles(path); - } - - QString dataLocal = mGameSettings.getDataLocal(); - if (!dataLocal.isEmpty()) - //mContentSelector. - mContentModel->addFiles(dataLocal); - - // Sort by date accessed for now - //mContentSelector->sort(3); - - QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/")); - QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); - - if (!profiles.isEmpty()) - profilesComboBox->addItems(profiles); - - // Add the current profile if empty - if (profilesComboBox->findText(profile) == -1 && !profile.isEmpty()) - profilesComboBox->addItem(profile); - - if (profilesComboBox->findText(QString("Default")) == -1) - profilesComboBox->addItem(QString("Default")); - - if (profile.isEmpty() || profile == QLatin1String("Default")) { - deleteProfileAction->setEnabled(false); - profilesComboBox->setEditEnabled(false); - profilesComboBox->setCurrentIndex(profilesComboBox->findText(QString("Default"))); - } else { - profilesComboBox->setEditEnabled(true); - profilesComboBox->setCurrentIndex(profilesComboBox->findText(profile)); - } - - // We do this here to prevent deletion of profiles when initializing the combobox - connect(profilesComboBox, SIGNAL(profileRenamed(QString,QString)), this, SLOT(profileRenamed(QString,QString))); - connect(profilesComboBox, SIGNAL(profileChanged(QString,QString)), this, SLOT(profileChanged(QString,QString))); - - loadSettings(); - - gameFileView->setCurrentIndex(-1); + connect (&cSelector, SIGNAL (signalProfileAdded ()), + this, SLOT (slotProfileAdded ())); } void DataFilesPage::loadSettings() { + QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); if (profile.isEmpty()) return; - // mContentSelector. - mContentModel->uncheckAll(); - - QStringList gameFiles = mLauncherSettings.values(QString("Profiles/") + profile + QString("/master"), Qt::MatchExactly); + QStringList files = mLauncherSettings.values(QString("Profiles/") + profile + QString("/master"), Qt::MatchExactly); QStringList addons = mLauncherSettings.values(QString("Profiles/") + profile + QString("/plugin"), Qt::MatchExactly); + + foreach (const QString &file, addons) + files.append(file); + + //ContentSelectorView::ContentSelector::instance().setCheckStates(files); } void DataFilesPage::saveSettings() { - if (mContentModel->rowCount() < 1) - return; + ContentSelectorModel::ContentFileList items = + ContentSelectorView::ContentSelector::instance().selectedFiles(); + + if (items.size() == 0) + return; QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); if (profile.isEmpty()) { - profile = profilesComboBox->currentText(); + profile = ContentSelectorView::ContentSelector::instance().getProfileText(); mLauncherSettings.setValue(QString("Profiles/currentprofile"), profile); } @@ -207,8 +89,6 @@ void DataFilesPage::saveSettings() mGameSettings.remove(QString("master")); mGameSettings.remove(QString("plugin")); - ContentSelectorModel::ContentFileList items = mContentModel->checkedItems(); - foreach(const ContentSelectorModel::EsmFile *item, items) { if (item->gameFiles().size() == 0) { @@ -223,109 +103,18 @@ void DataFilesPage::saveSettings() } -void DataFilesPage::updateOkButton(const QString &text) +void DataFilesPage::slotProfileDeleted (const QString &item) { - // We do this here because we need the profiles combobox text - if (text.isEmpty()) { - mNewProfileDialog->setOkButtonEnabled(false); - return; - } - - (profilesComboBox->findText(text) == -1) - ? mNewProfileDialog->setOkButtonEnabled(true) - : mNewProfileDialog->setOkButtonEnabled(false); + mLauncherSettings.remove(QString("Profiles/") + item + QString("/master")); + mLauncherSettings.remove(QString("Profiles/") + item + QString("/plugin")); } -void DataFilesPage::setProfilesComboBoxIndex(int index) +void DataFilesPage::slotProfileChanged(const QString &previous, const QString ¤t) { - profilesComboBox->setCurrentIndex(index); -} - -QAbstractItemModel* DataFilesPage::profilesComboBoxModel() -{ - return profilesComboBox->model(); -} - -int DataFilesPage::profilesComboBoxIndex() -{ - return profilesComboBox->currentIndex(); -} - -void DataFilesPage::on_newProfileAction_triggered() -{ - if (mNewProfileDialog->exec() == QDialog::Accepted) { - QString profile = mNewProfileDialog->lineEdit()->text(); - profilesComboBox->addItem(profile); - profilesComboBox->setCurrentIndex(profilesComboBox->findText(profile)); - } -} - -void DataFilesPage::on_deleteProfileAction_triggered() -{ - QString profile = profilesComboBox->currentText(); - - if (profile.isEmpty()) - return; - - QMessageBox msgBox(this); - msgBox.setWindowTitle(tr("Delete Profile")); - msgBox.setIcon(QMessageBox::Warning); - msgBox.setStandardButtons(QMessageBox::Cancel); - msgBox.setText(tr("Are you sure you want to delete %0?").arg(profile)); - - QAbstractButton *deleteButton = - msgBox.addButton(tr("Delete"), QMessageBox::ActionRole); - - msgBox.exec(); - - if (msgBox.clickedButton() == deleteButton) { - mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master")); - mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin")); - - // Remove the profile from the combobox - profilesComboBox->removeItem(profilesComboBox->findText(profile)); - } -} - -void DataFilesPage::setPluginsCheckstates(Qt::CheckState state) -{ - if (!addonView->selectionModel()->hasSelection()) { - return; - } - - QModelIndexList indexes = addonView->selectionModel()->selectedIndexes(); - - foreach (const QModelIndex &index, indexes) - { - if (!index.isValid()) - return; - - QModelIndex sourceIndex = mAddonProxyModel->mapToSource(index); - - if (!sourceIndex.isValid()) - return; - - //bool isChecked = ( state == Qt::Checked ); - - mContentModel->setData(sourceIndex, state, Qt::CheckStateRole); - } -} - -void DataFilesPage::profileChanged(const QString &previous, const QString ¤t) -{ - // Prevent the deletion of the default profile - if (current == QLatin1String("Default")) { - deleteProfileAction->setEnabled(false); - profilesComboBox->setEditEnabled(false); - } else { - deleteProfileAction->setEnabled(true); - profilesComboBox->setEditEnabled(true); - } - if (previous.isEmpty()) return; - if (profilesComboBox->findText(previous) == -1) + if (ContentSelectorView::ContentSelector::instance().getProfileIndex (previous) == -1) return; // Profile was deleted // Store the previous profile @@ -336,7 +125,7 @@ void DataFilesPage::profileChanged(const QString &previous, const QString &curre loadSettings(); } -void DataFilesPage::profileRenamed(const QString &previous, const QString ¤t) +void DataFilesPage::slotProfileRenamed(const QString &previous, const QString ¤t) { if (previous.isEmpty()) return; @@ -350,56 +139,55 @@ void DataFilesPage::profileRenamed(const QString &previous, const QString &curre mLauncherSettings.remove(QString("Profiles/") + previous + QString("/plugin")); // Remove the profile from the combobox - profilesComboBox->removeItem(profilesComboBox->findText(previous)); + ContentSelectorView::ContentSelector::instance().removeProfile (previous); loadSettings(); - } -//////////////////////////// -QStringList DataFilesPage::checkedItemsPaths() +void DataFilesPage::slotProfileAdded() { - QStringList itemPaths; + TextInputDialog newDialog (tr("New Profile"), tr("Profile name:"), this); - foreach( const ContentSelectorModel::EsmFile *file, mContentModel->checkedItems()) - itemPaths << file->path(); + // connect(mNewDialog->lineEdit(), SIGNAL(textChanged(QString)), + // this, SLOT(updateOkButton(QString))); - return itemPaths; + if (newDialog.exec() == QDialog::Accepted) + { + QString profile = newDialog.lineEdit()->text(); + + ContentSelectorView::ContentSelector + ::instance().addProfile(profile, true); + } } -void DataFilesPage::slotCurrentProfileIndexChanged(int index) +void DataFilesPage::setProfilesComboBoxIndex(int index) { - emit profileChanged(index); + ContentSelectorView::ContentSelector::instance().setProfileIndex(index); } -void DataFilesPage::slotCurrentGameFileIndexChanged(int index) +void DataFilesPage::setupDataFiles() { - static int oldIndex = -1; + ContentSelectorView::ContentSelector &cSelector = + ContentSelectorView::ContentSelector::instance(); - QAbstractItemModel *const model = gameFileView->model(); - QSortFilterProxyModel *proxy = dynamic_cast(model); + QStringList paths = mGameSettings.getDataDirs(); - if (proxy) - proxy->setDynamicSortFilter(false); + foreach (const QString &path, paths) + cSelector.addFiles(path); - if (oldIndex > -1) - model->setData(model->index(oldIndex, 0), false, Qt::UserRole + 1); + QString dataLocal = mGameSettings.getDataLocal(); - oldIndex = index; + if (!dataLocal.isEmpty()) + cSelector.addFiles(dataLocal); - model->setData(model->index(index, 0), true, Qt::UserRole + 1); + QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/")); + QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); - if (proxy) - proxy->setDynamicSortFilter(true); -} - -void DataFilesPage::slotAddonTableItemClicked(const QModelIndex &index) -{ - QAbstractItemModel *const model = addonView->model(); - //QSortFilterProxyModel *proxy = dynamic_cast(model); - - if (model->data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked) - model->setData(index, Qt::Checked, Qt::CheckStateRole); - else - model->setData(index, Qt::Unchecked, Qt::CheckStateRole); + + foreach (const QString &item, profiles) + cSelector.addProfile (item); + + cSelector.addProfile (profile, true); + + loadSettings(); } diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 9c7b0538ed..6ed5d9ce97 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -4,9 +4,6 @@ #include #include -#include "ui_datafilespage.h" -#include "components/contentselector/view/contentselector.hpp" - class QSortFilterProxyModel; class QAbstractItemModel; class QAction; @@ -19,7 +16,7 @@ class LauncherSettings; namespace Files { struct ConfigurationManager; } -class DataFilesPage : public QWidget, private Ui::DataFilesPage +class DataFilesPage : public QWidget { Q_OBJECT @@ -36,58 +33,33 @@ signals: void profileChanged(int index); public slots: - void setProfilesComboBoxIndex(int index); + //void showContextMenu(const QPoint &point); - //void showContextMenu(const QPoint &point); - void profileChanged(const QString &previous, const QString ¤t); - void profileRenamed(const QString &previous, const QString ¤t); - void updateOkButton(const QString &text); - void updateViews(); - // Action slots - void on_newProfileAction_triggered(); - void on_deleteProfileAction_triggered(); private slots: + void slotProfileAdded(); + void slotProfileChanged(const QString &previous, const QString ¤t); + void slotProfileRenamed(const QString &previous, const QString ¤t); + void slotProfileDeleted(const QString &item); + void setProfilesComboBoxIndex(int index); + private: QMenu *mContextMenu; - //ContentSelectorView::ContentSelector mContentSelector; - ContentSelectorModel::ContentModel *mContentModel; + Files::ConfigurationManager &mCfgMgr; GameSettings &mGameSettings; LauncherSettings &mLauncherSettings; - TextInputDialog *mNewProfileDialog; - QSortFilterProxyModel *mGameFileProxyModel; - QSortFilterProxyModel *mAddonProxyModel; - void setPluginsCheckstates(Qt::CheckState state); - void createActions(); void setupDataFiles(); void setupConfig(); void readConfig(); void loadSettings(); - - ////////////////////////////////////// - void buildContentModel(); - void buildGameFileView(); - void buildAddonView(); - void buildProfilesView(); - - //void addFiles(const QString &path); - - QStringList checkedItemsPaths(); - -private slots: - void slotCurrentProfileIndexChanged(int index); - void slotCurrentGameFileIndexChanged(int index); - void slotAddonTableItemClicked(const QModelIndex &index); - - }; #endif diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 032f70916f..311e3a25cc 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -106,10 +106,10 @@ void MainDialog::createPages() mPlayPage = new PlayPage(this); mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this); mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this); - +/// reimplement datafilespage functions to provide access // Set the combobox of the play page to imitate the combobox on the datafilespage - mPlayPage->setProfilesComboBoxModel(mDataFilesPage->profilesComboBoxModel()); - mPlayPage->setProfilesComboBoxIndex(mDataFilesPage->profilesComboBoxIndex()); + // mPlayPage->setProfilesComboBoxModel(mDataFilesPage->profilesComboBoxModel()); + // mPlayPage->setProfilesComboBoxIndex(mDataFilesPage->profilesComboBoxIndex()); // Add the pages to the stacked widget pagesWidget->addWidget(mPlayPage); diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 00547a2ba6..9ae12c7a73 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -43,8 +43,7 @@ opencs_units_noqt (model/tools opencs_units (view/doc - viewmanager view operations operation subview startup filedialog newgame filewidget - adjusterwidget + viewmanager view operations operation subview startup filedialog newgame ) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index fc9168e2ee..401f3839f1 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -124,7 +124,7 @@ void CS::Editor::openFiles() { std::vector files; - foreach (const QString &path, mFileDialog.selectedFilepaths()) { + foreach (const QString &path, mFileDialog.selectedFilePaths()) { files.push_back(path.toStdString()); } @@ -139,7 +139,7 @@ void CS::Editor::createNewFile() { std::vector files; - foreach (const QString &path, mFileDialog.selectedFilepaths()) { + foreach (const QString &path, mFileDialog.selectedFilePaths()) { files.push_back(path.toStdString()); } diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 5a97a7a26d..b1b72dc1fa 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -37,9 +37,16 @@ QString CSVDoc::FileDialog::filename() return ContentSelectorView::ContentSelector::instance().filename(); } -QStringList CSVDoc::FileDialog::selectedFilepaths() +QStringList CSVDoc::FileDialog::selectedFilePaths() { - return ContentSelectorView::ContentSelector::instance().selectedFiles(); + QStringList filePaths; + + foreach (ContentSelectorModel::EsmFile *file, ContentSelectorView::ContentSelector:: + instance().selectedFiles() ) + { + filePaths.append(file->fileName()); + } + return filePaths; } void CSVDoc::FileDialog::showDialog() diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 88d408b5c6..acc35189dc 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -41,7 +41,7 @@ namespace CSVDoc void addFiles (const QString &path); QString filename(); - QStringList selectedFilepaths(); + QStringList selectedFilePaths(); private: diff --git a/apps/opencs/view/doc/newgame.cpp b/apps/opencs/view/doc/newgame.cpp index 98681c499d..265b983056 100644 --- a/apps/opencs/view/doc/newgame.cpp +++ b/apps/opencs/view/doc/newgame.cpp @@ -7,8 +7,8 @@ #include #include -#include "filewidget.hpp" -#include "adjusterwidget.hpp" +#include "components/contentselector/view/filewidget.hpp" +#include "components/contentselector/view/adjusterwidget.hpp" CSVDoc::NewGameDialogue::NewGameDialogue() { diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 7053bb973b..ebce4578bd 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -84,6 +84,7 @@ if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) model/naturalsort model/contentmodel view/profilescombobox view/comboboxlineedit view/lineedit view/contentselector + view/filewidget view/adjusterwidget ) include(${QT_USE_FILE}) diff --git a/apps/opencs/view/doc/adjusterwidget.cpp b/components/contentselector/view/adjusterwidget.cpp similarity index 100% rename from apps/opencs/view/doc/adjusterwidget.cpp rename to components/contentselector/view/adjusterwidget.cpp diff --git a/apps/opencs/view/doc/adjusterwidget.hpp b/components/contentselector/view/adjusterwidget.hpp similarity index 100% rename from apps/opencs/view/doc/adjusterwidget.hpp rename to components/contentselector/view/adjusterwidget.hpp diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index d9caea3bed..cb0774f680 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -1,7 +1,7 @@ #include "contentselector.hpp" -#include "../model/contentmodel.hpp" #include "../model/esmfile.hpp" +#include "lineedit.hpp" #include @@ -9,10 +9,11 @@ #include #include #include +#include #include -#include "../../../apps/opencs/view/doc/filewidget.hpp" -#include "../../../apps/opencs/view/doc/adjusterwidget.hpp" +#include "filewidget.hpp" +#include "adjusterwidget.hpp" ContentSelectorView::ContentSelector *ContentSelectorView::ContentSelector::mInstance = 0; QStringList ContentSelectorView::ContentSelector::mFilePaths; @@ -25,6 +26,7 @@ void ContentSelectorView::ContentSelector::configure(QWidget *subject, unsigned ContentSelectorView::ContentSelector& ContentSelectorView::ContentSelector::instance() { + assert(mInstance); return *mInstance; } @@ -33,6 +35,7 @@ ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent, unsigned QWidget(parent), mFlags (flags), mAdjusterWidget (0), mFileWidget (0) { + ui.setupUi (this); parent->setLayout(new QGridLayout()); @@ -41,15 +44,23 @@ ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent, unsigned buildContentModel(); buildGameFileView(); buildAddonView(); - buildProfilesView(); buildNewAddonView(); buildLoadAddonView(); + buildProfilesView(); /* //mContentModel->sort(3); // Sort by date accessed */ } +QString ContentSelectorView::ContentSelector::getNewProfileName() +{ + // Create a dialog for the new profile name input + //mNewProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this); + + //connect(mNewProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString))); + return ""; +} bool ContentSelectorView::ContentSelector::isFlagged(SelectorFlags flag) const { @@ -123,8 +134,17 @@ void ContentSelectorView::ContentSelector::buildProfilesView() return; } - ui.profilesComboBox->setPlaceholderText(QString("Select a profile...")); - connect(ui.profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); + // Add the actions to the toolbuttons + ui.newProfileButton->setDefaultAction (ui.newProfileAction); + ui.deleteProfileButton->setDefaultAction (ui.deleteProfileAction); + + ui.profilesComboBox->addItem ("Default"); + ui.profilesComboBox->setPlaceholderText (QString("Select a profile...")); + + connect (ui.profilesComboBox, SIGNAL (currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); + connect (ui.profilesComboBox, SIGNAL (profileRenamed(QString,QString)), this, SIGNAL(signalProfileRenamed(QString,QString))); + connect (ui.profilesComboBox, SIGNAL (profileChanged(QString,QString)), this, SIGNAL(signalProfileChanged(QString,QString))); + connect (ui.profilesComboBox, SIGNAL (signalProfileTextChanged(QString)), this, SLOT (slotProfileTextChanged (QString))); } void ContentSelectorView::ContentSelector::buildLoadAddonView() @@ -136,7 +156,6 @@ void ContentSelectorView::ContentSelector::buildLoadAddonView() } ui.projectCreateButton->setVisible (false); - // ui.projectButtonBox->setStandardButtons(QDialogButtonBox::Open | QDialogButtonBox::Cancel); ui.projectGroupBox->setTitle (""); connect(ui.projectButtonBox, SIGNAL(accepted()), this, SIGNAL(accepted())); @@ -172,6 +191,17 @@ void ContentSelectorView::ContentSelector::buildNewAddonView() connect(ui.projectButtonBox, SIGNAL(rejected()), this, SIGNAL(rejected())); } +void ContentSelectorView::ContentSelector::setCheckStates(const QStringList &list) +{ + if (list.isEmpty()) + return; + + mContentModel->uncheckAll(); + + foreach (const QString &file, list) + mContentModel->setCheckState(file, Qt::Checked); +} + QString ContentSelectorView::ContentSelector::filename() const { QString filepath = ""; @@ -182,7 +212,7 @@ QString ContentSelectorView::ContentSelector::filename() const return filepath; } -QStringList ContentSelectorView::ContentSelector::selectedFiles() const +QStringList ContentSelectorView::ContentSelector::selectedFilePaths() const { QStringList filePaths; @@ -195,6 +225,15 @@ QStringList ContentSelectorView::ContentSelector::selectedFiles() const return filePaths; } +ContentSelectorModel::ContentFileList + ContentSelectorView::ContentSelector::selectedFiles() const +{ + if (mContentModel) + return mContentModel->checkedItems(); + + return ContentSelectorModel::ContentFileList(); +} + void ContentSelectorView::ContentSelector::addFiles(const QString &path) { @@ -209,6 +248,55 @@ void ContentSelectorView::ContentSelector::addFiles(const QString &path) } } +void ContentSelectorView::ContentSelector::removeProfile(const QString &item) +{ + int idx = ui.profilesComboBox->findText(item); + + if (idx != -1) + ui.profilesComboBox->removeItem(idx); +} + +int ContentSelectorView::ContentSelector::getProfileIndex ( const QString &item) const +{ + return ui.profilesComboBox->findText (item); +} + +void ContentSelectorView::ContentSelector::setProfileIndex(int index) +{ + if (index >=0 && index < ui.profilesComboBox->count()) + ui.profilesComboBox->setCurrentIndex(index); +} + +void ContentSelectorView::ContentSelector::addProfile (const QString &item, bool setAsCurrent) +{ + if (item.isEmpty()) + return; + + if (ui.profilesComboBox->findText(item) == -1) + ui.profilesComboBox->addItem(item); + + if (setAsCurrent) + ui.profilesComboBox->setCurrentIndex(ui.profilesComboBox->findText(item)); + + enableProfilesComboBox(); +} + +QString ContentSelectorView::ContentSelector::getProfileText() const +{ + return ui.profilesComboBox->currentText(); +} + +void ContentSelectorView::ContentSelector::enableProfilesComboBox() +{ + if (!ui.profilesComboBox->isEnabled()) + ui.profilesComboBox->setEnabled(true); + + if (!ui.deleteProfileAction->isEnabled()) + ui.deleteProfileAction->setEnabled(true); + + ui.projectButtonBox->button(QDialogButtonBox::Open)->setEnabled (true); +} + QStringList ContentSelectorView::ContentSelector::checkedItemsPaths() { QStringList itemPaths; @@ -221,6 +309,12 @@ QStringList ContentSelectorView::ContentSelector::checkedItemsPaths() void ContentSelectorView::ContentSelector::slotCurrentProfileIndexChanged(int index) { + //don't allow deleting "Default" profile + bool success = (ui.profilesComboBox->itemText(index) == "Default"); + + ui.deleteProfileAction->setEnabled(success); + ui.profilesComboBox->setEditEnabled(success); + emit signalProfileChanged(index); } @@ -247,6 +341,14 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i slotUpdateCreateButton(true); } +void ContentSelectorView::ContentSelector::slotProfileTextChanged(const QString &text) +{ + QPushButton *opnBtn = ui.projectButtonBox->button(QDialogButtonBox::Open); + + if (opnBtn->isEnabled()) + opnBtn->setEnabled (false); +} + void ContentSelectorView::ContentSelector::slotAddonTableItemClicked(const QModelIndex &index) { QAbstractItemModel *const model = ui.addonView->model(); @@ -257,16 +359,6 @@ void ContentSelectorView::ContentSelector::slotAddonTableItemClicked(const QMode model->setData(index, Qt::Unchecked, Qt::CheckStateRole); } -void ContentSelectorView::ContentSelector::slotUpdateOpenButton(const QStringList &items) -{ - QPushButton *openButton = ui.projectButtonBox->button(QDialogButtonBox::Open); - - if (!openButton) - return; - - openButton->setEnabled(!items.isEmpty()); -} - void ContentSelectorView::ContentSelector::slotUpdateCreateButton(bool) { //enable only if a game file is selected and the adjuster widget is non-empty @@ -278,3 +370,44 @@ void ContentSelectorView::ContentSelector::slotUpdateCreateButton(bool) ui.projectCreateButton->setEnabled(validGameFile && validFilename); } + + +void ContentSelectorView::ContentSelector::on_newProfileAction_triggered() +{ + emit signalProfileAdded(); +} + +void ContentSelectorView::ContentSelector::on_deleteProfileAction_triggered() +{ + QString profile = ui.profilesComboBox->currentText(); + + if (profile.isEmpty()) + return; + + QMessageBox msgBox(this); + msgBox.setWindowTitle(tr("Delete Profile")); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setStandardButtons(QMessageBox::Cancel); + msgBox.setText(tr("Are you sure you want to delete %0?").arg(profile)); + + QAbstractButton *deleteButton = + msgBox.addButton(tr("Delete"), QMessageBox::ActionRole); + + msgBox.exec(); + + if (msgBox.clickedButton() != deleteButton) + return; + + // Remove the profile from the combobox + ui.profilesComboBox->removeItem(ui.profilesComboBox->findText(profile)); + + //signal for removal from model + emit signalProfileDeleted (profile); +} +/* +void ContentSelectorView::ContentSelector::slotUpdateOkButton(const QString &text) +{ + bool success = (ui.profilesComboBox->findText(text) == -1); + + mNewDialog->setOkButtonEnabled(success); +}*/ diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index 48c3ae103e..caf9cc670e 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -4,10 +4,10 @@ #include #include "ui_datafilespage.h" - -namespace ContentSelectorModel { class ContentModel; } +#include "../model/contentmodel.hpp" class QSortFilterProxyModel; +class TextInputDialog; namespace CSVDoc { @@ -36,6 +36,8 @@ namespace ContentSelectorView CSVDoc::FileWidget *mFileWidget; CSVDoc::AdjusterWidget *mAdjusterWidget; + TextInputDialog *mNewDialog; + protected: ContentSelectorModel::ContentModel *mContentModel; @@ -43,19 +45,28 @@ namespace ContentSelectorView QSortFilterProxyModel *mAddonProxyModel; public: + explicit ContentSelector(QWidget *parent = 0, unsigned char flags = Flag_Content); static void configure(QWidget *subject, unsigned char flags = Flag_Content); static ContentSelector &instance(); static void addFiles(const QString &path); - void setCheckState(QModelIndex index, QSortFilterProxyModel *model); + void setCheckStates (const QStringList &list); QStringList checkedItemsPaths(); + ContentSelectorModel::ContentFileList *CheckedItems(); + QString filename() const; - QStringList selectedFiles() const; + ContentSelectorModel::ContentFileList selectedFiles() const; + QStringList selectedFilePaths() const; + + void addProfile (const QString &item, bool setAsCurrent = false); + void removeProfile (const QString &item); + int getProfileIndex (const QString &item) const; + void setProfileIndex (int index); + QString getProfileText() const; private: - explicit ContentSelector(QWidget *parent = 0, unsigned char flags = Flag_Content); Ui::DataFilesPage ui; void buildContentModel(); @@ -66,6 +77,8 @@ namespace ContentSelectorView void buildLoadAddonView(); bool isFlagged(SelectorFlags flag) const; + QString getNewProfileName(); + void enableProfilesComboBox(); signals: void accepted(); @@ -76,14 +89,24 @@ namespace ContentSelectorView void signalCreateButtonClicked(); + void signalProfileRenamed(QString,QString); + void signalProfileChanged(QString,QString); + void signalProfileDeleted(QString); + void signalProfileAdded(); + private slots: + void slotProfileTextChanged (const QString &text); void slotCurrentProfileIndexChanged(int index); void slotCurrentGameFileIndexChanged(int index); void slotAddonTableItemClicked(const QModelIndex &index); void slotUpdateCreateButton (bool); - void slotUpdateOpenButton(const QStringList &items); + // void slotUpdateOpenButton(const QStringList &items); + + // Action slots + void on_newProfileAction_triggered(); + void on_deleteProfileAction_triggered(); }; } diff --git a/apps/opencs/view/doc/filewidget.cpp b/components/contentselector/view/filewidget.cpp similarity index 100% rename from apps/opencs/view/doc/filewidget.cpp rename to components/contentselector/view/filewidget.cpp diff --git a/apps/opencs/view/doc/filewidget.hpp b/components/contentselector/view/filewidget.hpp similarity index 100% rename from apps/opencs/view/doc/filewidget.hpp rename to components/contentselector/view/filewidget.hpp diff --git a/components/contentselector/view/profilescombobox.cpp b/components/contentselector/view/profilescombobox.cpp index cb0ba7b77e..29001189d0 100644 --- a/components/contentselector/view/profilescombobox.cpp +++ b/components/contentselector/view/profilescombobox.cpp @@ -45,6 +45,9 @@ void ContentSelectorView::ProfilesComboBox::setEditEnabled(bool editable) connect(lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString))); + + connect (lineEdit(), SIGNAL(textChanged(QString)), this, + SIGNAL (signalProfileTextChanged (QString))); } void ContentSelectorView::ProfilesComboBox::slotTextChanged(const QString &text) diff --git a/components/contentselector/view/profilescombobox.hpp b/components/contentselector/view/profilescombobox.hpp index d81c1e6a5a..560c42c10f 100644 --- a/components/contentselector/view/profilescombobox.hpp +++ b/components/contentselector/view/profilescombobox.hpp @@ -17,10 +17,12 @@ namespace ContentSelectorView void setPlaceholderText (const QString &text); signals: + void signalProfileTextChanged (const QString &item); void profileChanged(const QString &previous, const QString ¤t); void profileRenamed(const QString &oldName, const QString &newName); private slots: + void slotEditingFinished(); void slotIndexChanged(int index); void slotTextChanged(const QString &text); diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 9494077591..73d7a4902e 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -7,7 +7,7 @@ 0 0 518 - 313 + 424 @@ -26,6 +26,9 @@ Content + + 3 + @@ -116,6 +119,18 @@ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + 3 + + + 6 + + + 6 + + + 0 + @@ -143,18 +158,17 @@ - - - - QDialogButtonBox::Cancel|QDialogButtonBox::Open - - - false + + + 0 + 0 + + Create @@ -209,7 +223,7 @@ 3 - 9 + 6 0 @@ -220,7 +234,7 @@ - true + false @@ -228,6 +242,11 @@ 0 + + + Default + + @@ -262,6 +281,13 @@ + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Open + + + @@ -280,6 +306,9 @@ + + false + From a5a0f615330e9593515153bee28430519c0ac57f Mon Sep 17 00:00:00 2001 From: graffy76 Date: Tue, 1 Oct 2013 22:36:49 -0500 Subject: [PATCH 068/148] Fixed missing profiles combobox --- apps/launcher/datafilespage.cpp | 2 +- components/contentselector/view/contentselector.cpp | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index b298f8a141..eee530672d 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -65,7 +65,7 @@ void DataFilesPage::loadSettings() foreach (const QString &file, addons) files.append(file); - //ContentSelectorView::ContentSelector::instance().setCheckStates(files); + ContentSelectorView::ContentSelector::instance().setCheckStates(files); } void DataFilesPage::saveSettings() diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index cb0774f680..54199626e0 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -129,10 +129,7 @@ void ContentSelectorView::ContentSelector::buildAddonView() void ContentSelectorView::ContentSelector::buildProfilesView() { if (!isFlagged (Flag_Profile)) - { - ui.profileGroupBox->setVisible(false); return; - } // Add the actions to the toolbuttons ui.newProfileButton->setDefaultAction (ui.newProfileAction); @@ -145,6 +142,8 @@ void ContentSelectorView::ContentSelector::buildProfilesView() connect (ui.profilesComboBox, SIGNAL (profileRenamed(QString,QString)), this, SIGNAL(signalProfileRenamed(QString,QString))); connect (ui.profilesComboBox, SIGNAL (profileChanged(QString,QString)), this, SIGNAL(signalProfileChanged(QString,QString))); connect (ui.profilesComboBox, SIGNAL (signalProfileTextChanged(QString)), this, SLOT (slotProfileTextChanged (QString))); + + ui.profileGroupBox->setVisible (true); } void ContentSelectorView::ContentSelector::buildLoadAddonView() From 217a4d75b4118d8a785f93f36852e6713a0d67c3 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 6 Oct 2013 21:13:47 -0500 Subject: [PATCH 069/148] Implemented profile function in launcher datafiles page Implemented dependency sorting to ensure dependent files appear latest in the list. --- apps/launcher/CMakeLists.txt | 4 - apps/launcher/datafilespage.cpp | 173 +++++++++--------- apps/launcher/datafilespage.hpp | 22 ++- apps/launcher/graphicspage.cpp | 1 + apps/launcher/maindialog.cpp | 30 ++- apps/launcher/playpage.cpp | 12 +- apps/launcher/playpage.hpp | 7 +- apps/opencs/view/doc/filedialog.cpp | 2 +- components/CMakeLists.txt | 2 + .../contentselector/model/contentmodel.cpp | 53 +++++- .../contentselector/model/contentmodel.hpp | 3 +- .../contentselector/view/contentselector.cpp | 162 ++++++++-------- .../contentselector/view/contentselector.hpp | 27 ++- .../contentselector/view/profilescombobox.cpp | 10 +- .../contentselector/view/profilescombobox.hpp | 10 +- .../contentselector/view}/textinputdialog.cpp | 23 ++- .../contentselector/view}/textinputdialog.hpp | 15 +- files/ui/datafilespage.ui | 7 +- 18 files changed, 311 insertions(+), 252 deletions(-) rename {apps/launcher/utils => components/contentselector/view}/textinputdialog.cpp (75%) rename {apps/launcher/utils => components/contentselector/view}/textinputdialog.hpp (78%) diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 92cabffffd..49dedd8290 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -11,7 +11,6 @@ set(LAUNCHER settings/launchersettings.cpp utils/checkablemessagebox.cpp - utils/textinputdialog.cpp ${CMAKE_SOURCE_DIR}/files/launcher/launcher.rc ) @@ -32,8 +31,6 @@ set(LAUNCHER_HEADER settings/settingsbase.hpp utils/checkablemessagebox.hpp - utils/textinputdialog.hpp - ) if(NOT WIN32) LIST(APPEND LAUNCHER_HEADER unshieldthread.hpp) @@ -49,7 +46,6 @@ set(LAUNCHER_HEADER_MOC textslotmsgbox.hpp utils/checkablemessagebox.hpp - utils/textinputdialog.hpp ) if(NOT WIN32) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index eee530672d..a705ae37d8 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -17,177 +17,182 @@ #include "settings/gamesettings.hpp" #include "settings/launchersettings.hpp" -#include "utils/textinputdialog.hpp" #include "components/contentselector/view/contentselector.hpp" -#include - DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent) : mCfgMgr(cfg) , mGameSettings(gameSettings) , mLauncherSettings(launcherSettings) + , QWidget(parent) { - unsigned char flags; + setObjectName ("DataFilesPage"); - flags = ContentSelectorView::Flag_Content | ContentSelectorView::Flag_Profile; + unsigned char flags; - ContentSelectorView::ContentSelector::configure(this, flags); + flags = ContentSelectorView::Flag_Content | ContentSelectorView::Flag_Profile; + + ContentSelectorView::ContentSelector::configure(this, flags); + mSelector = &ContentSelectorView::ContentSelector::instance(); setupDataFiles(); - ContentSelectorView::ContentSelector &cSelector = - ContentSelectorView::ContentSelector::instance(); - connect (&cSelector, SIGNAL (signalProfileRenamed (QString, QString)), + connect (mSelector, SIGNAL (signalProfileRenamed (QString, QString)), this, SLOT (slotProfileRenamed (QString, QString))); - connect (&cSelector, SIGNAL (signalProfileChanged (QString, QString)), - this, SLOT (slotProfileChanged (QString, QString))); + connect (mSelector, SIGNAL (signalProfileChangedByUser (QString, QString)), + this, SLOT (slotProfileChangedByUser (QString, QString))); - connect (&cSelector, SIGNAL (signalProfileDeleted (QString)), + connect (mSelector, SIGNAL (signalProfileDeleted (QString)), this, SLOT (slotProfileDeleted (QString))); - connect (&cSelector, SIGNAL (signalProfileAdded ()), - this, SLOT (slotProfileAdded ())); + connect (mSelector, SIGNAL (signalAddNewProfile (QString)), + this, SLOT (slotAddNewProfile (QString))); } void DataFilesPage::loadSettings() { + QString profileName = mSelector->getProfileText(); - QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); + QStringList files = mLauncherSettings.values(QString("Profiles/") + profileName + QString("/game"), Qt::MatchExactly); + QStringList addons = mLauncherSettings.values(QString("Profiles/") + profileName + QString("/addon"), Qt::MatchExactly); - if (profile.isEmpty()) - return; + mSelector->clearCheckStates(); - QStringList files = mLauncherSettings.values(QString("Profiles/") + profile + QString("/master"), Qt::MatchExactly); - QStringList addons = mLauncherSettings.values(QString("Profiles/") + profile + QString("/plugin"), Qt::MatchExactly); + if (files.size() > 0) + mSelector->setGameFile(files.at(0)); + else + mSelector->setGameFile(); - foreach (const QString &file, addons) - files.append(file); - - ContentSelectorView::ContentSelector::instance().setCheckStates(files); + mSelector->setCheckStates(addons); } -void DataFilesPage::saveSettings() +void DataFilesPage::saveSettings(const QString &profile) { - ContentSelectorModel::ContentFileList items = - ContentSelectorView::ContentSelector::instance().selectedFiles(); + QString profileName = profile; - if (items.size() == 0) - return; + if (profileName.isEmpty()) + profileName = mSelector->getProfileText(); - QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); + //retrieve the files selected for the profile + ContentSelectorModel::ContentFileList items = mSelector->selectedFiles(); - if (profile.isEmpty()) { - profile = ContentSelectorView::ContentSelector::instance().getProfileText(); - mLauncherSettings.setValue(QString("Profiles/currentprofile"), profile); - } + removeProfile (profileName); - mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master")); - mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin")); + mGameSettings.remove(QString("game")); + mGameSettings.remove(QString("addon")); - mGameSettings.remove(QString("master")); - mGameSettings.remove(QString("plugin")); + //set the value of the current profile (not necessarily the profile being saved!) + mLauncherSettings.setValue(QString("Profiles/currentprofile"), mSelector->getProfileText()); foreach(const ContentSelectorModel::EsmFile *item, items) { if (item->gameFiles().size() == 0) { - mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/master"), item->fileName()); - mGameSettings.setMultiValue(QString("master"), item->fileName()); - + mLauncherSettings.setMultiValue(QString("Profiles/") + profileName + QString("/game"), item->fileName()); + mGameSettings.setMultiValue(QString("game"), item->fileName()); } else { - mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/plugin"), item->fileName()); - mGameSettings.setMultiValue(QString("plugin"), item->fileName()); + mLauncherSettings.setMultiValue(QString("Profiles/") + profileName + QString("/addon"), item->fileName()); + mGameSettings.setMultiValue(QString("addon"), item->fileName()); } } } -void DataFilesPage::slotProfileDeleted (const QString &item) +void DataFilesPage::removeProfile(const QString &profile) { - mLauncherSettings.remove(QString("Profiles/") + item + QString("/master")); - mLauncherSettings.remove(QString("Profiles/") + item + QString("/plugin")); + mLauncherSettings.remove(QString("Profiles/") + profile + QString("/game")); + mLauncherSettings.remove(QString("Profiles/") + profile + QString("/addon")); } -void DataFilesPage::slotProfileChanged(const QString &previous, const QString ¤t) +void DataFilesPage::changeProfiles(const QString &previous, const QString ¤t, bool savePrevious) { - if (previous.isEmpty()) + //abort if no change (typically a duplicate signal) + if (previous == current) return; - if (ContentSelectorView::ContentSelector::instance().getProfileIndex (previous) == -1) - return; // Profile was deleted + int index = -1; - // Store the previous profile - mLauncherSettings.setValue(QString("Profiles/currentprofile"), previous); - saveSettings(); - mLauncherSettings.setValue(QString("Profiles/currentprofile"), current); + if (!previous.isEmpty()) + index = mSelector->getProfileIndex(previous); + + // Store the previous profile if it exists + if ( (index != -1) && savePrevious) + saveSettings(previous); loadSettings(); } +void DataFilesPage::slotAddNewProfile(const QString &profile) +{ + saveSettings(); + mSelector->clearCheckStates(); + mSelector->addProfile(profile, true); + mSelector->setGameFile(); + saveSettings(); + + emit signalProfileChanged(mSelector->getProfileIndex(profile)); +} + +void DataFilesPage::slotProfileDeleted (const QString &item) +{ + removeProfile (item); +} + +void DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString ¤t) +{ + changeProfiles(previous, current); + emit signalProfileChanged(mSelector->getProfileIndex(current)); +} + void DataFilesPage::slotProfileRenamed(const QString &previous, const QString ¤t) { if (previous.isEmpty()) return; // Save the new profile name - mLauncherSettings.setValue(QString("Profiles/currentprofile"), current); saveSettings(); // Remove the old one - mLauncherSettings.remove(QString("Profiles/") + previous + QString("/master")); - mLauncherSettings.remove(QString("Profiles/") + previous + QString("/plugin")); - - // Remove the profile from the combobox - ContentSelectorView::ContentSelector::instance().removeProfile (previous); + removeProfile (previous); loadSettings(); } -void DataFilesPage::slotProfileAdded() +void DataFilesPage::slotProfileChanged(int index) { - TextInputDialog newDialog (tr("New Profile"), tr("Profile name:"), this); - - // connect(mNewDialog->lineEdit(), SIGNAL(textChanged(QString)), - // this, SLOT(updateOkButton(QString))); - - if (newDialog.exec() == QDialog::Accepted) - { - QString profile = newDialog.lineEdit()->text(); - - ContentSelectorView::ContentSelector - ::instance().addProfile(profile, true); - } -} - -void DataFilesPage::setProfilesComboBoxIndex(int index) -{ - ContentSelectorView::ContentSelector::instance().setProfileIndex(index); + mSelector->setProfile(index); } void DataFilesPage::setupDataFiles() { - ContentSelectorView::ContentSelector &cSelector = - ContentSelectorView::ContentSelector::instance(); - QStringList paths = mGameSettings.getDataDirs(); foreach (const QString &path, paths) - cSelector.addFiles(path); + mSelector->addFiles(path); QString dataLocal = mGameSettings.getDataLocal(); if (!dataLocal.isEmpty()) - cSelector.addFiles(dataLocal); + mSelector->addFiles(dataLocal); QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/")); QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); foreach (const QString &item, profiles) - cSelector.addProfile (item); + mSelector->addProfile (item); - cSelector.addProfile (profile, true); + mSelector->addProfile (profile, true); loadSettings(); } + +QAbstractItemModel *DataFilesPage::profilesModel() const +{ + return mSelector->profilesModel(); +} + +int DataFilesPage::profilesIndex() const +{ + return mSelector->getProfileIndex(mSelector->getProfileText()); +} diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 6ed5d9ce97..47e28493d0 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -15,22 +15,26 @@ class LauncherSettings; namespace Files { struct ConfigurationManager; } +namespace ContentSelectorView { class ContentSelector; } class DataFilesPage : public QWidget { Q_OBJECT + ContentSelectorView::ContentSelector *mSelector; + public: DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent = 0); - QAbstractItemModel* profilesComboBoxModel(); - int profilesComboBoxIndex(); + QAbstractItemModel* profilesModel() const; + int profilesIndex() const; void writeConfig(QString profile = QString()); - void saveSettings(); + void saveSettings(const QString &profile = ""); + void loadSettings(); signals: - void profileChanged(int index); + void signalProfileChanged(int index); public slots: //void showContextMenu(const QPoint &point); @@ -38,11 +42,11 @@ public slots: private slots: - void slotProfileAdded(); - void slotProfileChanged(const QString &previous, const QString ¤t); + void slotAddNewProfile(const QString &profile); + void slotProfileChangedByUser(const QString &previous, const QString ¤t); + void slotProfileChanged(int); void slotProfileRenamed(const QString &previous, const QString ¤t); void slotProfileDeleted(const QString &item); - void setProfilesComboBoxIndex(int index); private: @@ -58,8 +62,8 @@ private: void setupDataFiles(); void setupConfig(); void readConfig(); - - void loadSettings(); + void removeProfile (const QString &profile); + void changeProfiles(const QString &previous, const QString ¤t, bool savePrevious = true); }; #endif diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 4d9ce14d6d..2c6c711ea9 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -38,6 +38,7 @@ GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &g , mGraphicsSettings(graphicsSetting) , QWidget(parent) { + setObjectName ("GraphicsPage"); setupUi(this); // Set the maximum res we can set in windowed mode diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 311e3a25cc..fe9ca141ec 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -106,10 +106,10 @@ void MainDialog::createPages() mPlayPage = new PlayPage(this); mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this); mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this); -/// reimplement datafilespage functions to provide access + // Set the combobox of the play page to imitate the combobox on the datafilespage - // mPlayPage->setProfilesComboBoxModel(mDataFilesPage->profilesComboBoxModel()); - // mPlayPage->setProfilesComboBoxIndex(mDataFilesPage->profilesComboBoxIndex()); + mPlayPage->setProfilesModel(mDataFilesPage->profilesModel()); + mPlayPage->setProfilesIndex(mDataFilesPage->profilesIndex()); // Add the pages to the stacked widget pagesWidget->addWidget(mPlayPage); @@ -121,8 +121,8 @@ void MainDialog::createPages() connect(mPlayPage, SIGNAL(playButtonClicked()), this, SLOT(play())); - connect(mPlayPage, SIGNAL(profileChanged(int)), mDataFilesPage, SLOT(setProfilesComboBoxIndex(int))); - connect(mDataFilesPage, SIGNAL(profileChanged(int)), mPlayPage, SLOT(setProfilesComboBoxIndex(int))); + connect(mPlayPage, SIGNAL(signalProfileChanged(int)), mDataFilesPage, SLOT(slotProfileChanged(int))); + connect(mDataFilesPage, SIGNAL(signalProfileChanged(int)), mPlayPage, SLOT(setProfilesIndex(int))); } @@ -316,7 +316,25 @@ void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) if (!current) current = previous; - pagesWidget->setCurrentIndex(iconWidget->row(current)); + int currentIndex = iconWidget->row(current); + int previousIndex = iconWidget->row(previous); + + pagesWidget->setCurrentIndex(currentIndex); + + DataFilesPage *previousPage = dynamic_cast(pagesWidget->widget(previousIndex)); + DataFilesPage *currentPage = dynamic_cast(pagesWidget->widget(currentIndex)); + + //special call to update/save data files page list view when it's displayed/hidden. + if (previousPage) + { + if (previousPage->objectName() == "DataFilesPage") + previousPage->saveSettings(); + } + else if (currentPage) + { + if (currentPage->objectName() == "DataFilesPage") + currentPage->loadSettings(); + } } bool MainDialog::setupLauncherSettings() diff --git a/apps/launcher/playpage.cpp b/apps/launcher/playpage.cpp index 46900c5958..fc1ed1c69b 100644 --- a/apps/launcher/playpage.cpp +++ b/apps/launcher/playpage.cpp @@ -8,6 +8,7 @@ PlayPage::PlayPage(QWidget *parent) : QWidget(parent) { + setObjectName ("PlayPage"); setupUi(this); // Hacks to get the stylesheet look properly @@ -17,26 +18,21 @@ PlayPage::PlayPage(QWidget *parent) : QWidget(parent) #endif profilesComboBox->setView(new QListView()); - connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentIndexChanged(int))); + connect(profilesComboBox, SIGNAL(activated(int)), this, SIGNAL (signalProfileChanged(int))); connect(playButton, SIGNAL(clicked()), this, SLOT(slotPlayClicked())); } -void PlayPage::setProfilesComboBoxModel(QAbstractItemModel *model) +void PlayPage::setProfilesModel(QAbstractItemModel *model) { profilesComboBox->setModel(model); } -void PlayPage::setProfilesComboBoxIndex(int index) +void PlayPage::setProfilesIndex(int index) { profilesComboBox->setCurrentIndex(index); } -void PlayPage::slotCurrentIndexChanged(int index) -{ - emit profileChanged(index); -} - void PlayPage::slotPlayClicked() { emit playButtonClicked(); diff --git a/apps/launcher/playpage.hpp b/apps/launcher/playpage.hpp index 4306396bd2..42edfadb18 100644 --- a/apps/launcher/playpage.hpp +++ b/apps/launcher/playpage.hpp @@ -15,17 +15,16 @@ class PlayPage : public QWidget, private Ui::PlayPage public: PlayPage(QWidget *parent = 0); - void setProfilesComboBoxModel(QAbstractItemModel *model); + void setProfilesModel(QAbstractItemModel *model); signals: - void profileChanged(int index); + void signalProfileChanged(int index); void playButtonClicked(); public slots: - void setProfilesComboBoxIndex(int index); + void setProfilesIndex(int index); private slots: - void slotCurrentIndexChanged(int index); void slotPlayClicked(); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index b1b72dc1fa..efa31100ab 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -34,7 +34,7 @@ void CSVDoc::FileDialog::addFiles(const QString &path) QString CSVDoc::FileDialog::filename() { - return ContentSelectorView::ContentSelector::instance().filename(); + return ContentSelectorView::ContentSelector::instance().projectFilename(); } QStringList CSVDoc::FileDialog::selectedFilePaths() diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index ebce4578bd..103c6f4126 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -85,6 +85,8 @@ if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) view/profilescombobox view/comboboxlineedit view/lineedit view/contentselector view/filewidget view/adjusterwidget + view/textinputdialog + ) include(${QT_USE_FILE}) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index db6431810d..a9796a1fbf 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -391,7 +391,6 @@ bool ContentSelectorModel::ContentModel::canBeChecked(const EsmFile *file) const return true; } } - return false; } @@ -448,6 +447,39 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) } delete decoder; + + sortFiles(); +} + +void ContentSelectorModel::ContentModel::sortFiles() +{ + //first, sort the model such that all dependencies are ordered upstream (gamefile) first. + bool movedFiles = true; + int fileCount = mFiles.size(); + + //Dependency sort + //iterate until no sorting of files occurs + while (movedFiles) + { + movedFiles = false; + //iterate each file, obtaining a reference to it's gamefiles list + for (int i = 0; i < fileCount; i++) + { + const QStringList &gamefiles = mFiles.at(i)->gameFiles(); + //iterate each file after the current file, verifying that none of it's + //dependencies appear. + for (int j = i + 1; j < fileCount; j++) + { + if (gamefiles.contains(mFiles.at(j)->fileName())) + { + mFiles.move(j, i); + movedFiles = true; + } + } + if (movedFiles) + break; + } + } } bool ContentSelectorModel::ContentModel::isChecked(const QString& name) const @@ -460,6 +492,7 @@ bool ContentSelectorModel::ContentModel::isChecked(const QString& name) const void ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool checkState) { + if (name.isEmpty()) return; @@ -469,9 +502,14 @@ void ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool state = Qt::Checked; mCheckStates[name] = state; + emit dataChanged(indexFromItem(item(name)), indexFromItem(item(name))); const EsmFile *file = item(name); + if (file->isGameFile()) + emit dataChanged (index(0,0), index(rowCount()-1,0)); + + //if we're checking an item, ensure all "upstream" files (dependencies) are checked as well. if (state == Qt::Checked) { foreach (const QString &upstreamName, file->gameFiles()) @@ -482,24 +520,23 @@ void ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool continue; if (!isChecked(upstreamName)) - { mCheckStates[upstreamName] = Qt::Checked; - emit dataChanged(indexFromItem(upstreamFile), indexFromItem(upstreamFile)); - } + + emit dataChanged(indexFromItem(upstreamFile), indexFromItem(upstreamFile)); } } - else if (state == Qt::Unchecked) + //otherwise, if we're unchecking an item (or the file is a game file) ensure all downstream files are unchecked. + if (state == Qt::Unchecked) { foreach (const EsmFile *downstreamFile, mFiles) { if (downstreamFile->gameFiles().contains(name)) { if (mCheckStates.contains(downstreamFile->fileName())) - { mCheckStates[downstreamFile->fileName()] = Qt::Unchecked; - emit dataChanged(indexFromItem(downstreamFile), indexFromItem(downstreamFile)); - } + + emit dataChanged(indexFromItem(downstreamFile), indexFromItem(downstreamFile)); } } } diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index a2a57f850a..feea3643b3 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -56,19 +56,20 @@ namespace ContentSelectorModel EsmFile *item(int row); bool canBeChecked(const EsmFile *file) const; + void sortFiles(); ContentFileList mFiles; QHash mCheckStates; QTextCodec *mCodec; public: + QString mMimeType; QStringList mMimeTypes; int mColumnCount; Qt::ItemFlags mDragDropFlags; Qt::ItemFlags mDefaultFlags; Qt::DropActions mDropActions; - }; } #endif // CONTENTMODEL_HPP diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 54199626e0..6965c948ec 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -5,7 +5,6 @@ #include -#include #include #include #include @@ -14,6 +13,9 @@ #include "filewidget.hpp" #include "adjusterwidget.hpp" +#include "textinputdialog.hpp" + +#include ContentSelectorView::ContentSelector *ContentSelectorView::ContentSelector::mInstance = 0; QStringList ContentSelectorView::ContentSelector::mFilePaths; @@ -33,7 +35,8 @@ ContentSelectorView::ContentSelector& ContentSelectorView::ContentSelector::inst ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent, unsigned char flags) : QWidget(parent), mFlags (flags), - mAdjusterWidget (0), mFileWidget (0) + mAdjusterWidget (0), mFileWidget (0), + mIgnoreProfileSignal (false) { ui.setupUi (this); @@ -53,14 +56,6 @@ ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent, unsigned */ } -QString ContentSelectorView::ContentSelector::getNewProfileName() -{ - // Create a dialog for the new profile name input - //mNewProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this); - - //connect(mNewProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString))); - return ""; -} bool ContentSelectorView::ContentSelector::isFlagged(SelectorFlags flag) const { @@ -94,6 +89,8 @@ void ContentSelectorView::ContentSelector::buildGameFileView() return; } + ui.gameFileView->setVisible (true); + mGameFileProxyModel = new QSortFilterProxyModel(this); mGameFileProxyModel->setFilterRegExp(QString::number((int)ContentSelectorModel::ContentType_GameFile)); mGameFileProxyModel->setFilterRole (Qt::UserRole); @@ -102,7 +99,7 @@ void ContentSelectorView::ContentSelector::buildGameFileView() ui.gameFileView->setPlaceholderText(QString("Select a game file...")); ui.gameFileView->setModel(mGameFileProxyModel); - connect(ui.gameFileView, SIGNAL(currentIndexChanged(int)), this, SLOT (slotCurrentGameFileIndexChanged(int))); + connect (ui.gameFileView, SIGNAL(currentIndexChanged(int)), this, SLOT (slotCurrentGameFileIndexChanged(int))); ui.gameFileView->setCurrentIndex(-1); } @@ -115,6 +112,8 @@ void ContentSelectorView::ContentSelector::buildAddonView() return; } + ui.addonView->setVisible (true); + mAddonProxyModel = new QSortFilterProxyModel(this); mAddonProxyModel->setFilterRegExp (QString::number((int)ContentSelectorModel::ContentType_Addon)); mAddonProxyModel->setFilterRole (Qt::UserRole); @@ -129,31 +128,48 @@ void ContentSelectorView::ContentSelector::buildAddonView() void ContentSelectorView::ContentSelector::buildProfilesView() { if (!isFlagged (Flag_Profile)) + { + ui.profileGroupBox->setVisible(false); return; + } + + ui.profileGroupBox->setVisible (true); // Add the actions to the toolbuttons ui.newProfileButton->setDefaultAction (ui.newProfileAction); ui.deleteProfileButton->setDefaultAction (ui.deleteProfileAction); + //enable ui elements ui.profilesComboBox->addItem ("Default"); ui.profilesComboBox->setPlaceholderText (QString("Select a profile...")); + if (!ui.profilesComboBox->isEnabled()) + ui.profilesComboBox->setEnabled(true); + + if (!ui.deleteProfileAction->isEnabled()) + ui.deleteProfileAction->setEnabled(true); + + //establish connections connect (ui.profilesComboBox, SIGNAL (currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); connect (ui.profilesComboBox, SIGNAL (profileRenamed(QString,QString)), this, SIGNAL(signalProfileRenamed(QString,QString))); - connect (ui.profilesComboBox, SIGNAL (profileChanged(QString,QString)), this, SIGNAL(signalProfileChanged(QString,QString))); + connect (ui.profilesComboBox, SIGNAL (signalProfileChanged(QString,QString)), this, SIGNAL(signalProfileChangedByUser(QString,QString))); connect (ui.profilesComboBox, SIGNAL (signalProfileTextChanged(QString)), this, SLOT (slotProfileTextChanged (QString))); ui.profileGroupBox->setVisible (true); + ui.projectButtonBox->setVisible (false); } void ContentSelectorView::ContentSelector::buildLoadAddonView() { if (!isFlagged (Flag_LoadAddon)) { - ui.projectGroupBox->setVisible (false); + if (!isFlagged (Flag_NewAddon)) + ui.projectGroupBox->setVisible (false); + return; } + ui.projectGroupBox->setVisible (true); ui.projectCreateButton->setVisible (false); ui.projectGroupBox->setTitle (""); @@ -165,10 +181,14 @@ void ContentSelectorView::ContentSelector::buildNewAddonView() { if (!isFlagged (Flag_NewAddon)) { - ui.profileGroupBox->setVisible (false); + if (!isFlagged (Flag_LoadAddon)) + ui.projectGroupBox->setVisible (false); + return; } + ui.projectGroupBox->setVisible (true); + mFileWidget = new CSVDoc::FileWidget (this); mAdjusterWidget = new CSVDoc::AdjusterWidget (this); @@ -190,18 +210,36 @@ void ContentSelectorView::ContentSelector::buildNewAddonView() connect(ui.projectButtonBox, SIGNAL(rejected()), this, SIGNAL(rejected())); } +void ContentSelectorView::ContentSelector::setGameFile(const QString &filename) +{ + int index = -1; + + if (!filename.isEmpty()) + { + index = ui.gameFileView->findText(filename); + + //verify that the current index is also checked in the model + mContentModel->setCheckState(filename, true); + } + + ui.gameFileView->setCurrentIndex(index); +} + +void ContentSelectorView::ContentSelector::clearCheckStates() +{ + mContentModel->uncheckAll(); +} + void ContentSelectorView::ContentSelector::setCheckStates(const QStringList &list) { if (list.isEmpty()) return; - mContentModel->uncheckAll(); - foreach (const QString &file, list) mContentModel->setCheckState(file, Qt::Checked); } -QString ContentSelectorView::ContentSelector::filename() const +QString ContentSelectorView::ContentSelector::projectFilename() const { QString filepath = ""; @@ -211,26 +249,13 @@ QString ContentSelectorView::ContentSelector::filename() const return filepath; } -QStringList ContentSelectorView::ContentSelector::selectedFilePaths() const -{ - QStringList filePaths; - - if (mContentModel) - { - foreach (ContentSelectorModel::EsmFile *file, mContentModel->checkedItems()) - filePaths.append(file->path()); - } - - return filePaths; -} - ContentSelectorModel::ContentFileList ContentSelectorView::ContentSelector::selectedFiles() const { - if (mContentModel) - return mContentModel->checkedItems(); + if (!mContentModel) + return ContentSelectorModel::ContentFileList(); - return ContentSelectorModel::ContentFileList(); + return mContentModel->checkedItems(); } @@ -243,41 +268,39 @@ void ContentSelectorView::ContentSelector::addFiles(const QString &path) { mInstance->mContentModel->addFiles(path); mInstance->ui.gameFileView->setCurrentIndex(-1); - mInstance->mContentModel->uncheckAll(); } } -void ContentSelectorView::ContentSelector::removeProfile(const QString &item) -{ - int idx = ui.profilesComboBox->findText(item); - - if (idx != -1) - ui.profilesComboBox->removeItem(idx); -} - int ContentSelectorView::ContentSelector::getProfileIndex ( const QString &item) const { return ui.profilesComboBox->findText (item); } -void ContentSelectorView::ContentSelector::setProfileIndex(int index) -{ - if (index >=0 && index < ui.profilesComboBox->count()) - ui.profilesComboBox->setCurrentIndex(index); -} - void ContentSelectorView::ContentSelector::addProfile (const QString &item, bool setAsCurrent) { if (item.isEmpty()) return; + QString previous = ui.profilesComboBox->currentText(); + if (ui.profilesComboBox->findText(item) == -1) ui.profilesComboBox->addItem(item); if (setAsCurrent) - ui.profilesComboBox->setCurrentIndex(ui.profilesComboBox->findText(item)); + setProfile (ui.profilesComboBox->findText(item)); +} - enableProfilesComboBox(); +void ContentSelectorView::ContentSelector::setProfile(int index) +{ + //programmatic change requires second call to non-signalized "slot" since no signal responses + //occur for programmatic changes to the profilesComboBox. + if (index >= -1 && index < ui.profilesComboBox->count()) + { + QString previous = ui.profilesComboBox->itemText(ui.profilesComboBox->currentIndex()); + QString current = ui.profilesComboBox->itemText(index); + + ui.profilesComboBox->setCurrentIndex(index); + } } QString ContentSelectorView::ContentSelector::getProfileText() const @@ -285,36 +308,18 @@ QString ContentSelectorView::ContentSelector::getProfileText() const return ui.profilesComboBox->currentText(); } -void ContentSelectorView::ContentSelector::enableProfilesComboBox() +QAbstractItemModel *ContentSelectorView::ContentSelector::profilesModel() const { - if (!ui.profilesComboBox->isEnabled()) - ui.profilesComboBox->setEnabled(true); - - if (!ui.deleteProfileAction->isEnabled()) - ui.deleteProfileAction->setEnabled(true); - - ui.projectButtonBox->button(QDialogButtonBox::Open)->setEnabled (true); -} - -QStringList ContentSelectorView::ContentSelector::checkedItemsPaths() -{ - QStringList itemPaths; - - foreach( const ContentSelectorModel::EsmFile *file, mContentModel->checkedItems()) - itemPaths << file->path(); - - return itemPaths; + return ui.profilesComboBox->model(); } void ContentSelectorView::ContentSelector::slotCurrentProfileIndexChanged(int index) { //don't allow deleting "Default" profile - bool success = (ui.profilesComboBox->itemText(index) == "Default"); + bool success = (ui.profilesComboBox->itemText(index) != "Default"); ui.deleteProfileAction->setEnabled(success); ui.profilesComboBox->setEditEnabled(success); - - emit signalProfileChanged(index); } void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int index) @@ -370,10 +375,12 @@ void ContentSelectorView::ContentSelector::slotUpdateCreateButton(bool) ui.projectCreateButton->setEnabled(validGameFile && validFilename); } - void ContentSelectorView::ContentSelector::on_newProfileAction_triggered() { - emit signalProfileAdded(); + TextInputDialog newDialog (tr("New Profile"), tr("Profile name:"), this); + + if (newDialog.exec() == QDialog::Accepted) + emit signalAddNewProfile(newDialog.getText()); } void ContentSelectorView::ContentSelector::on_deleteProfileAction_triggered() @@ -402,11 +409,6 @@ void ContentSelectorView::ContentSelector::on_deleteProfileAction_triggered() //signal for removal from model emit signalProfileDeleted (profile); -} -/* -void ContentSelectorView::ContentSelector::slotUpdateOkButton(const QString &text) -{ - bool success = (ui.profilesComboBox->findText(text) == -1); - mNewDialog->setOkButtonEnabled(success); -}*/ + slotCurrentProfileIndexChanged(ui.profilesComboBox->currentIndex()); +} diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index caf9cc670e..64b9b37320 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -7,7 +7,6 @@ #include "../model/contentmodel.hpp" class QSortFilterProxyModel; -class TextInputDialog; namespace CSVDoc { @@ -29,6 +28,7 @@ namespace ContentSelectorView Q_OBJECT unsigned char mFlags; + bool mIgnoreProfileSignal; static ContentSelector *mInstance; static QStringList mFilePaths; @@ -36,8 +36,6 @@ namespace ContentSelectorView CSVDoc::FileWidget *mFileWidget; CSVDoc::AdjusterWidget *mAdjusterWidget; - TextInputDialog *mNewDialog; - protected: ContentSelectorModel::ContentModel *mContentModel; @@ -45,26 +43,28 @@ namespace ContentSelectorView QSortFilterProxyModel *mAddonProxyModel; public: + explicit ContentSelector(QWidget *parent = 0, unsigned char flags = Flag_Content); static void configure(QWidget *subject, unsigned char flags = Flag_Content); static ContentSelector &instance(); static void addFiles(const QString &path); + void clearCheckStates(); void setCheckStates (const QStringList &list); - QStringList checkedItemsPaths(); ContentSelectorModel::ContentFileList *CheckedItems(); - QString filename() const; + QString projectFilename() const; ContentSelectorModel::ContentFileList selectedFiles() const; - QStringList selectedFilePaths() const; + QAbstractItemModel *profilesModel() const; + void setGameFile (const QString &filename = ""); void addProfile (const QString &item, bool setAsCurrent = false); - void removeProfile (const QString &item); + void setProfile (int index); int getProfileIndex (const QString &item) const; - void setProfileIndex (int index); QString getProfileText() const; + private: Ui::DataFilesPage ui; @@ -77,22 +77,18 @@ namespace ContentSelectorView void buildLoadAddonView(); bool isFlagged(SelectorFlags flag) const; - QString getNewProfileName(); - void enableProfilesComboBox(); signals: + void accepted(); void rejected(); - void signalProfileChanged(int index); - void signalGameFileChanged(int value); - void signalCreateButtonClicked(); void signalProfileRenamed(QString,QString); - void signalProfileChanged(QString,QString); + void signalProfileChangedByUser(QString,QString); void signalProfileDeleted(QString); - void signalProfileAdded(); + void signalAddNewProfile(QString); private slots: @@ -102,7 +98,6 @@ namespace ContentSelectorView void slotAddonTableItemClicked(const QModelIndex &index); void slotUpdateCreateButton (bool); - // void slotUpdateOpenButton(const QStringList &items); // Action slots void on_newProfileAction_triggered(); diff --git a/components/contentselector/view/profilescombobox.cpp b/components/contentselector/view/profilescombobox.cpp index 29001189d0..0e9905df45 100644 --- a/components/contentselector/view/profilescombobox.cpp +++ b/components/contentselector/view/profilescombobox.cpp @@ -15,8 +15,8 @@ ContentSelectorView::ProfilesComboBox::ProfilesComboBox(QWidget *parent) : setValidator(mValidator); setCompleter(0); - connect(this, SIGNAL(currentIndexChanged(int)), this, - SLOT(slotIndexChanged(int))); + connect(this, SIGNAL(activated(int)), this, + SLOT(slotIndexChangedByUser(int))); setInsertPolicy(QComboBox::NoInsert); } @@ -85,13 +85,13 @@ void ContentSelectorView::ProfilesComboBox::slotEditingFinished() emit(profileRenamed(previous, current)); } -void ContentSelectorView::ProfilesComboBox::slotIndexChanged(int index) +void ContentSelectorView::ProfilesComboBox::slotIndexChangedByUser(int index) { if (index == -1) return; - emit(profileChanged(mOldProfile, currentText())); - mOldProfile = itemText(index); + emit (signalProfileChanged(mOldProfile, currentText())); + mOldProfile = currentText(); } void ContentSelectorView::ProfilesComboBox::paintEvent(QPaintEvent *) diff --git a/components/contentselector/view/profilescombobox.hpp b/components/contentselector/view/profilescombobox.hpp index 560c42c10f..fc87a94b40 100644 --- a/components/contentselector/view/profilescombobox.hpp +++ b/components/contentselector/view/profilescombobox.hpp @@ -14,17 +14,19 @@ namespace ContentSelectorView public: explicit ProfilesComboBox(QWidget *parent = 0); void setEditEnabled(bool editable); - void setPlaceholderText (const QString &text); + void setPlaceholderText(const QString &text); + // void indexChanged(int index); signals: - void signalProfileTextChanged (const QString &item); - void profileChanged(const QString &previous, const QString ¤t); + void signalProfileTextChanged(const QString &item); + void signalProfileChanged(const QString &previous, const QString ¤t); + void signalProfileChanged(int index); void profileRenamed(const QString &oldName, const QString &newName); private slots: void slotEditingFinished(); - void slotIndexChanged(int index); + void slotIndexChangedByUser(int index); void slotTextChanged(const QString &text); private: diff --git a/apps/launcher/utils/textinputdialog.cpp b/components/contentselector/view/textinputdialog.cpp similarity index 75% rename from apps/launcher/utils/textinputdialog.cpp rename to components/contentselector/view/textinputdialog.cpp index 51928c09a7..6bb92f113e 100644 --- a/apps/launcher/utils/textinputdialog.cpp +++ b/components/contentselector/view/textinputdialog.cpp @@ -16,6 +16,7 @@ TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWid mButtonBox = new QDialogButtonBox(this); mButtonBox->addButton(QDialogButtonBox::Ok); mButtonBox->addButton(QDialogButtonBox::Cancel); + mButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false); // Line edit QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore @@ -38,11 +39,11 @@ TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWid Q_UNUSED(title); #endif - setOkButtonEnabled(false); setModal(true); connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject())); + connect(mLineEdit, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateOkButton(QString))); } @@ -53,19 +54,23 @@ int TextInputDialog::exec() return QDialog::exec(); } -void TextInputDialog::setOkButtonEnabled(bool enabled) +QString TextInputDialog::getText() const { - QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok); - okButton->setEnabled(enabled); + return mLineEdit->text(); +} - QPalette *palette = new QPalette(); - palette->setColor(QPalette::Text,Qt::red); +void TextInputDialog::slotUpdateOkButton(QString text) +{ + bool enabled = !(text.isEmpty()); + mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(enabled); - if (enabled) { + if (enabled) mLineEdit->setPalette(QApplication::palette()); - } else { + else + { // Existing profile name, make the text red + QPalette *palette = new QPalette(); + palette->setColor(QPalette::Text,Qt::red); mLineEdit->setPalette(*palette); } - } diff --git a/apps/launcher/utils/textinputdialog.hpp b/components/contentselector/view/textinputdialog.hpp similarity index 78% rename from apps/launcher/utils/textinputdialog.hpp rename to components/contentselector/view/textinputdialog.hpp index de3a9fb723..a0b7e350a5 100644 --- a/apps/launcher/utils/textinputdialog.hpp +++ b/components/contentselector/view/textinputdialog.hpp @@ -13,18 +13,19 @@ namespace ContentSelectorView { class TextInputDialog : public QDialog { Q_OBJECT -public: - explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0); - inline ContentSelectorView::LineEdit *lineEdit() { return mLineEdit; } - void setOkButtonEnabled(bool enabled); ContentSelectorView::LineEdit *mLineEdit; + QDialogButtonBox *mButtonBox; + +public: + + explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0); + QString getText() const; int exec(); -private: - QDialogButtonBox *mButtonBox; - +private slots: + void slotUpdateOkButton(QString text); }; diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 73d7a4902e..0cafd606a8 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -7,7 +7,7 @@ 0 0 518 - 424 + 436 @@ -242,11 +242,6 @@ 0 - - - Default - -
From 4c72a9ffdfe592f154beeb4be2d05d3c1514fc01 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 6 Oct 2013 22:10:38 -0500 Subject: [PATCH 070/148] Fixed non-loading files --- apps/opencs/editor.cpp | 3 +++ apps/opencs/view/doc/filedialog.cpp | 2 +- components/contentselector/model/contentmodel.cpp | 11 ++++++----- components/contentselector/model/contentmodel.hpp | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 401f3839f1..ae10ec6422 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -128,6 +128,9 @@ void CS::Editor::openFiles() files.push_back(path.toStdString()); } + foreach (const boost::filesystem::path fp, files) + qDebug() << "loading files: " << fp.c_str(); + /// \todo Get the save path from the file dialogue CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), false); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index efa31100ab..2017ab1036 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -44,7 +44,7 @@ QStringList CSVDoc::FileDialog::selectedFilePaths() foreach (ContentSelectorModel::EsmFile *file, ContentSelectorView::ContentSelector:: instance().selectedFiles() ) { - filePaths.append(file->fileName()); + filePaths.append(file->path()); } return filePaths; } diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index a9796a1fbf..8bb052d3dc 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -15,10 +15,10 @@ ContentSelectorModel::ContentModel::ContentModel(QObject *parent) : mDefaultFlags (Qt::ItemIsDropEnabled | Qt::ItemIsSelectable), mDropActions (Qt::CopyAction | Qt::MoveAction) { - // setEncoding ("win1252"); + setEncoding ("win1252"); uncheckAll(); } -/* + void ContentSelectorModel::ContentModel::setEncoding(const QString &encoding) { if (encoding == QLatin1String("win1252")) @@ -33,7 +33,7 @@ void ContentSelectorModel::ContentModel::setEncoding(const QString &encoding) else return; // This should never happen; } -*/ + int ContentSelectorModel::ContentModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) @@ -420,8 +420,9 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) try { ESM::ESMReader fileReader; - ToUTF8::Utf8Encoder encoder(); //ToUTF8::calculateEncoding(QString(mCodec->name()).toStdString())); - //fileReader.setEncoder(&encoder); + ToUTF8::Utf8Encoder encoder = + ToUTF8::calculateEncoding(QString(mCodec->name()).toStdString()); + fileReader.setEncoder(&encoder); fileReader.open(dir.absoluteFilePath(path).toStdString()); foreach (const ESM::Header::MasterData &item, fileReader.getGameFiles()) diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index feea3643b3..0d6c52cc63 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -22,7 +22,7 @@ namespace ContentSelectorModel public: explicit ContentModel(QObject *parent = 0); - //void setEncoding(const QString &encoding); + void setEncoding(const QString &encoding); int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; From 3000386443acf0a8214994aaa14e59c68eb6d05f Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Wed, 16 Oct 2013 13:07:26 +0200 Subject: [PATCH 071/148] failed attempt on switch adding. --- apps/opencs/editor.cpp | 54 +++++++++++++++++++++++++++++++++++++++++- apps/opencs/editor.hpp | 5 +++- apps/opencs/main.cpp | 2 +- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index a430597953..4c75ed1cc9 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include "model/doc/document.hpp" #include "model/world/data.hpp" @@ -208,8 +210,13 @@ void CS::Editor::connectToIPCServer() mClientSocket->close(); } -int CS::Editor::run() +int CS::Editor::run(int argc, char** argv) { + if (!parseOptions(argc, argv) ) + { + return 0; + } + if (mLocal.empty()) return 1; @@ -219,3 +226,48 @@ int CS::Editor::run() return QApplication::exec(); } + +bool CS::Editor::parseOptions (int argc, char** argv) +{ + // Create a local alias for brevity + namespace bpo = boost::program_options; + typedef std::vector StringsVector; + + bpo::options_description desc("Syntax: openmw \nAllowed options"); + + desc.add_options() + ("help", "print help message") + + ("resources", bpo::value()->default_value("resources"), "set resources directory"); + + bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).options(desc).allow_unregistered().run(); + + bpo::variables_map variables; + + // Runtime options override settings from all configs + bpo::store(valid_opts, variables); + bpo::notify(variables); + +// cfgMgr.readConfiguration(variables, desc); + + bool run = true; + + if (variables.count ("help")) + { + std::cout << desc << std::endl; + run = false; + } + + if (!run) + return false; + + setResourceDir(variables["resources"].as()); + + return true; +} + +// Set resource dir +void CS::Editor::setResourceDir (const boost::filesystem::path& parResDir) +{ + mResDir = boost::filesystem::system_complete(parResDir); +} \ No newline at end of file diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 16f6b9516c..77ba0993e9 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -50,7 +50,7 @@ namespace CS bool makeIPCServer(); void connectToIPCServer(); - int run(); + int run(int argc, char** argv); ///< \return error status private slots: @@ -66,12 +66,15 @@ namespace CS void showStartup(); void showSettings(); + bool parseOptions (int argc, char** argv); + void setResourceDir (const boost::filesystem::path& parResDir); private: QString mIpcServerName; QLocalServer *mServer; QLocalSocket *mClientSocket; + boost::filesystem::path mResDir; }; } diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index e5e7514ce0..bec09bd4aa 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -45,5 +45,5 @@ int main(int argc, char *argv[]) // return 0; } - return editor.run(); + return editor.run(argc, argv); } From a7002e8a09602f4dfcbe3e1031ab6a4ef365cbad Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Thu, 17 Oct 2013 18:21:41 +0200 Subject: [PATCH 072/148] Implements switch (--help and --resources), and copying defaultfilters.omwaddon.project. Seems to work. --- apps/opencs/editor.cpp | 17 ++++------------- apps/opencs/editor.hpp | 2 -- apps/opencs/model/doc/document.cpp | 10 +++++----- apps/opencs/model/doc/document.hpp | 7 +++---- apps/opencs/model/doc/documentmanager.cpp | 7 ++++++- apps/opencs/model/doc/documentmanager.hpp | 7 +++++-- 6 files changed, 23 insertions(+), 27 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 4c75ed1cc9..d5888a3124 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -6,8 +6,6 @@ #include #include -#include - #include "model/doc/document.hpp" #include "model/world/data.hpp" @@ -59,7 +57,7 @@ void CS::Editor::setupDataFiles() if (!variables["data"].empty()) { dataDirs = Files::PathContainer(variables["data"].as()); } - + std::string local = variables["data-local"].as(); if (!local.empty()) { dataLocal.push_back(Files::PathContainer::value_type(local)); @@ -231,9 +229,8 @@ bool CS::Editor::parseOptions (int argc, char** argv) { // Create a local alias for brevity namespace bpo = boost::program_options; - typedef std::vector StringsVector; - bpo::options_description desc("Syntax: openmw \nAllowed options"); + bpo::options_description desc("Syntax: opencs \nAllowed options"); desc.add_options() ("help", "print help message") @@ -248,7 +245,7 @@ bool CS::Editor::parseOptions (int argc, char** argv) bpo::store(valid_opts, variables); bpo::notify(variables); -// cfgMgr.readConfiguration(variables, desc); + mCfgMgr.readConfiguration(variables, desc); bool run = true; @@ -261,13 +258,7 @@ bool CS::Editor::parseOptions (int argc, char** argv) if (!run) return false; - setResourceDir(variables["resources"].as()); + mDocumentManager.setResourceDir(variables["resources"].as()); return true; -} - -// Set resource dir -void CS::Editor::setResourceDir (const boost::filesystem::path& parResDir) -{ - mResDir = boost::filesystem::system_complete(parResDir); } \ No newline at end of file diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 77ba0993e9..763cde1002 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -67,14 +67,12 @@ namespace CS void showSettings(); bool parseOptions (int argc, char** argv); - void setResourceDir (const boost::filesystem::path& parResDir); private: QString mIpcServerName; QLocalServer *mServer; QLocalSocket *mClientSocket; - boost::filesystem::path mResDir; }; } diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 5c29d9f617..afb68b440f 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2146,10 +2146,8 @@ void CSMDoc::Document::createBase() } } -CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, - const std::vector& files, - const boost::filesystem::path& savePath, bool new_) -: mSavePath (savePath), mContentFiles (files), mTools (mData), +CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_) +: mSavePath (savePath), mContentFiles (files), mTools (mData), mResDir(resDir), mProjectPath ((configuration.getUserPath() / "projects") / (savePath.filename().string() + ".project")), mSaving (*this, mProjectPath) @@ -2183,7 +2181,9 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, } else { - /// \todo create new project file with default filters + boost::filesystem::path filters = mResDir; + filters /= "defaultfilters.omwaddon.project"; + boost::filesystem::copy_file(mResDir, mProjectPath); } } diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index d171dacaed..e6d1b0c872 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -43,13 +43,14 @@ namespace CSMDoc CSMTools::Tools mTools; boost::filesystem::path mProjectPath; Saving mSaving; + boost::filesystem::path mResDir; // It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is // using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late. QUndoStack mUndoStack; // not implemented - Document (const Document&); + Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, bool new_); Document& operator= (const Document&); void load (const std::vector::const_iterator& begin, @@ -70,9 +71,7 @@ namespace CSMDoc public: - Document (const Files::ConfigurationManager& configuration, - const std::vector& files, - const boost::filesystem::path& savePath, bool new_); + Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_); ~Document(); diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp index 1d6c88dccf..024c46beae 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -30,7 +30,7 @@ CSMDoc::DocumentManager::~DocumentManager() CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector& files, const boost::filesystem::path& savePath, bool new_) { - Document *document = new Document (mConfiguration, files, savePath, new_); + Document *document = new Document (mConfiguration, files, savePath, mResDir, new_); mDocuments.push_back (document); @@ -48,4 +48,9 @@ bool CSMDoc::DocumentManager::removeDocument (Document *document) delete document; return mDocuments.empty(); +} + +void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& parResDir) +{ + mResDir = boost::filesystem::system_complete(parResDir); } \ No newline at end of file diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp index 28a21216a6..b80a186429 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -29,8 +29,7 @@ namespace CSMDoc ~DocumentManager(); - Document *addDocument (const std::vector& files, - const boost::filesystem::path& savePath, bool new_); + Document *addDocument (const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, bool new_); ///< The ownership of the returned document is not transferred to the caller. /// /// \param new_ Do not load the last content file in \a files and instead create in an @@ -38,6 +37,10 @@ namespace CSMDoc bool removeDocument (Document *document); ///< \return last document removed? + void setResourceDir (const boost::filesystem::path& parResDir); + + private: + boost::filesystem::path mResDir; }; } From 4e26a61db3f217a8bf4bb6ce2f36e8a0a65b57c5 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Fri, 18 Oct 2013 22:11:14 +0200 Subject: [PATCH 073/148] Removed command line handling. Maybe zini will let me to implement it later. Implemented switch to handle resources directory. TODO: check for defaultfilters on data path. --- apps/opencs/editor.cpp | 55 ++++++------------------------------------ apps/opencs/editor.hpp | 3 +-- apps/opencs/main.cpp | 2 +- 3 files changed, 10 insertions(+), 50 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index d5888a3124..97c958f3da 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -41,13 +41,14 @@ CS::Editor::Editor() void CS::Editor::setupDataFiles() { boost::program_options::variables_map variables; - boost::program_options::options_description desc; - + boost::program_options::options_description desc("Syntax: opencs \nAllowed options"); + desc.add_options() ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()) ("data-local", boost::program_options::value()->default_value("")) ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) - ("encoding", boost::program_options::value()->default_value("win1252")); + ("encoding", boost::program_options::value()->default_value("win1252")) + ("resources", boost::program_options::value()->default_value("resources"), "set resources directory"); boost::program_options::notify(variables); @@ -86,6 +87,9 @@ void CS::Editor::setupDataFiles() //mFileDialog.setEncoding(encoding); dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); + +// Setting Resources directory. + mDocumentManager.setResourceDir(variables["resources"].as()); for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) { @@ -208,13 +212,8 @@ void CS::Editor::connectToIPCServer() mClientSocket->close(); } -int CS::Editor::run(int argc, char** argv) +int CS::Editor::run() { - if (!parseOptions(argc, argv) ) - { - return 0; - } - if (mLocal.empty()) return 1; @@ -223,42 +222,4 @@ int CS::Editor::run(int argc, char** argv) QApplication::setQuitOnLastWindowClosed (true); return QApplication::exec(); -} - -bool CS::Editor::parseOptions (int argc, char** argv) -{ - // Create a local alias for brevity - namespace bpo = boost::program_options; - - bpo::options_description desc("Syntax: opencs \nAllowed options"); - - desc.add_options() - ("help", "print help message") - - ("resources", bpo::value()->default_value("resources"), "set resources directory"); - - bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv).options(desc).allow_unregistered().run(); - - bpo::variables_map variables; - - // Runtime options override settings from all configs - bpo::store(valid_opts, variables); - bpo::notify(variables); - - mCfgMgr.readConfiguration(variables, desc); - - bool run = true; - - if (variables.count ("help")) - { - std::cout << desc << std::endl; - run = false; - } - - if (!run) - return false; - - mDocumentManager.setResourceDir(variables["resources"].as()); - - return true; } \ No newline at end of file diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 763cde1002..16f6b9516c 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -50,7 +50,7 @@ namespace CS bool makeIPCServer(); void connectToIPCServer(); - int run(int argc, char** argv); + int run(); ///< \return error status private slots: @@ -66,7 +66,6 @@ namespace CS void showStartup(); void showSettings(); - bool parseOptions (int argc, char** argv); private: diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index bec09bd4aa..e5e7514ce0 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -45,5 +45,5 @@ int main(int argc, char *argv[]) // return 0; } - return editor.run(argc, argv); + return editor.run(); } From 184456892b4abc42b4db73f66ecb82c21073011e Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 19 Oct 2013 18:43:47 +0200 Subject: [PATCH 074/148] Added check to load custom filters set when present. --- apps/opencs/editor.cpp | 21 +++++++++++---------- apps/opencs/model/doc/document.cpp | 26 +++++++++++++++++--------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 97c958f3da..9abc9ee780 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -8,10 +8,11 @@ #include "model/doc/document.hpp" #include "model/world/data.hpp" +#include CS::Editor::Editor() -: mDocumentManager (mCfgMgr), mViewManager (mDocumentManager) + : mDocumentManager (mCfgMgr), mViewManager (mDocumentManager) { mIpcServerName = "org.openmw.OpenCS"; @@ -32,23 +33,23 @@ CS::Editor::Editor() connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles())); connect (&mFileDialog, SIGNAL(createNewFile (const boost::filesystem::path&)), - this, SLOT(createNewFile (const boost::filesystem::path&))); + this, SLOT(createNewFile (const boost::filesystem::path&))); connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)), - this, SLOT (createNewGame (const boost::filesystem::path&))); + this, SLOT (createNewGame (const boost::filesystem::path&))); } void CS::Editor::setupDataFiles() { boost::program_options::variables_map variables; boost::program_options::options_description desc("Syntax: opencs \nAllowed options"); - + desc.add_options() ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()) ("data-local", boost::program_options::value()->default_value("")) ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) ("encoding", boost::program_options::value()->default_value("win1252")) - ("resources", boost::program_options::value()->default_value("resources"), "set resources directory"); + ("resources", boost::program_options::value()->default_value("resources")); boost::program_options::notify(variables); @@ -58,7 +59,7 @@ void CS::Editor::setupDataFiles() if (!variables["data"].empty()) { dataDirs = Files::PathContainer(variables["data"].as()); } - + std::string local = variables["data-local"].as(); if (!local.empty()) { dataLocal.push_back(Files::PathContainer::value_type(local)); @@ -83,12 +84,12 @@ void CS::Editor::setupDataFiles() } // Set the charset for reading the esm/esp files - // QString encoding = QString::fromStdString(variables["encoding"].as()); + // QString encoding = QString::fromStdString(variables["encoding"].as()); //mFileDialog.setEncoding(encoding); dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); - -// Setting Resources directory. + +// Adding Resources directory. First check if there is a file defaultfilters in the user path. mDocumentManager.setResourceDir(variables["resources"].as()); for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) @@ -222,4 +223,4 @@ int CS::Editor::run() QApplication::setQuitOnLastWindowClosed (true); return QApplication::exec(); -} \ No newline at end of file +} diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index afb68b440f..32c728f88c 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -9,7 +9,7 @@ #endif void CSMDoc::Document::load (const std::vector::const_iterator& begin, - const std::vector::const_iterator& end, bool lastAsModified) + const std::vector::const_iterator& end, bool lastAsModified) { assert (begin!=end); @@ -2147,10 +2147,10 @@ void CSMDoc::Document::createBase() } CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_) -: mSavePath (savePath), mContentFiles (files), mTools (mData), mResDir(resDir), - mProjectPath ((configuration.getUserPath() / "projects") / - (savePath.filename().string() + ".project")), - mSaving (*this, mProjectPath) + : mSavePath (savePath), mContentFiles (files), mTools (mData), mResDir(resDir), + mProjectPath ((configuration.getUserPath() / "projects") / + (savePath.filename().string() + ".project")), + mSaving (*this, mProjectPath) { if (files.empty()) throw std::runtime_error ("Empty content file sequence"); @@ -2181,9 +2181,17 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, co } else { - boost::filesystem::path filters = mResDir; - filters /= "defaultfilters.omwaddon.project"; - boost::filesystem::copy_file(mResDir, mProjectPath); + boost::filesystem::path locCustomFiltersPath (configuration.getUserPath()); + locCustomFiltersPath /= "defaultfilters.omwaddon.project"; + if (boost::filesystem::exists(locCustomFiltersPath)) + { + boost::filesystem::copy(locCustomFiltersPath, mProjectPath); + } else { + boost::filesystem::path filters(mResDir); + filters /= "defaultfilters.omwaddon.project"; + boost::filesystem::copy_file(filters, mProjectPath); + } + getData().loadFile (mProjectPath, false, true); } } @@ -2198,7 +2206,7 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, co connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); connect (&mSaving, SIGNAL (done (int)), this, SLOT (operationDone (int))); connect (&mSaving, SIGNAL (reportMessage (const QString&, int)), - this, SLOT (reportMessage (const QString&, int))); + this, SLOT (reportMessage (const QString&, int))); } CSMDoc::Document::~Document() From 762cbf62785d8f26ea2d58bcf24a65f32bedf64d Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sat, 19 Oct 2013 18:51:36 +0200 Subject: [PATCH 075/148] Changed gauntlets filter. It showes now both gauntlets and bracers. --- files/opencs/defaultfilters.omwaddon.project | Bin 0 -> 10544 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 files/opencs/defaultfilters.omwaddon.project diff --git a/files/opencs/defaultfilters.omwaddon.project b/files/opencs/defaultfilters.omwaddon.project new file mode 100644 index 0000000000000000000000000000000000000000..e58ed92786f2e0333519341d24f008e772828e34 GIT binary patch literal 10544 zcmWG>4K_AmWPk$?SC=3iMh1qov}N{i!BGa@5ODMK35i5f;pgb8D|>@38-C!2~K&0Czaqgob-@ zL26=Ydm* z8-=b70UHHN%2F`5LTo^BDT)#KV#`iogN!mDg%YJzD#W=)9tz=^DXFkztBKFex%tHJcCeffPr0dmqImi6xoIkX`|(y^6hU7YtSfu?wa50o4w%Xem-x z5!B5A)raufEiKt8AvP}0@)ZSW;SyMGds$q~r+d_F$2Rnvs;BpN-oB=bZeKjLf`r-1;-~(u-14 zGE?(PkW4}KM{Z^@s1cr+mzrN%jHC+c8K^@cWuK;wLSkMDyorr!P)=rg2C_8;`9&p( zNjaeYp%SRup`;0IR4c<$W@brhVs5cQNk(FcLULlBLQ<+iL1uDxYKlT>0VLA!rpM&` zypqJsyi{nn5qEloD}P#A*C zV3;s8x#y!<$cNk-OiE3Gl!TyeFw8H($wiq3B}zI9PN^yJ!6k`#DTzfX5KFXR{y`M- zkl}=+)D(qcus(&vl9I&ajMNlJ{%%CG9;JZ?8P&;1&B;xLv_=Uu?!oFcbrcYNd~oRs z@8WxaO@h_yAfJLm1Y!X+JjBpy3DgJx71qV+iKTfZIjJR(pgDp+|G?aYT#ul-$|p6g zM8T;jF*&serF=kD6$C0=k(9w=1638+EO)5;2%3ga2+aqe5)>RHa4#xoD|S)J%e5j8KRk#&Boh2;&AcpJB-w#Tl8YIVs?BfIvr$Krjb`4T6O; zJZC`6hvtkXH1~kI?TDfeoHLS3Gm8?7A^C#Xyox;`J3~yua1qo7NZb?QqV%HF#IjUs zxX2x15{8SQHb7hyixx(p)+juTlA*0NaD3q|w_%MsNRbN4S>P-Qs+wRXKrBP|K1u?C zDNRa+_T&k4Vs2>;v?BJRLd-dWje~^~FRW~b*aC?=ysZP6 zvx@SQAaO^PfXYvTcc8gpP6F8gaZoE-IAKlT#Tl7JkoF37Qd4FTd|w5J4ji@G=~xRRMXVAsQ{TK$DyBdZPe5&H$;ya911f z;RtZg0#xz9t%Mo?@mD&URiMr=+$zLy0mM4c*dnYpfcCDS0~1P~@R0@ROev^fgQL~>EG~hD2Po9QPJkJwkds+lQml}lrT{h=;t=!_8DP%xU0n#J^r4Vqa!A&n#$SX*O=8$@{ zXakkEm<~%z%}vaKGz4t$*CnW~as%tba1y)#g_w)voH8`$fO<=C=VYf=LJ|*w?hmd( zSI=UF>{QS?4U~~UcuNFiJS4Wz*JYrjWfarF5v7VVEu*M~L@}45_R20Em~ZlgaX9);^NGlGU(zI zOOl)e(TL(2P-O{Ds1P|+$KVYdcm^uYNi2gTQ(Pq<*35%wRDcbDay6kD2;PDLOG5Ka z4ZgsEy9b_YY$1VznC`%rml3%pEinf)=LB^Ke3%hrEK;Tc4Uxl~1Dbq6sdN%kQlL#K zP?-ZO<$Uu~GSf0sQ$ShM5u^fI#KNl_ur^3;@PV1lz`y`99wkpEB^E;h98}C=Gu z Date: Sun, 20 Oct 2013 09:44:10 +0200 Subject: [PATCH 076/148] Added missing light filter. --- files/opencs/defaultfilters.omwaddon.project | Bin 10544 -> 10962 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/files/opencs/defaultfilters.omwaddon.project b/files/opencs/defaultfilters.omwaddon.project index e58ed92786f2e0333519341d24f008e772828e34..c66990fdc2c26863fa33edefe0d336ef9ae8fa3b 100644 GIT binary patch delta 295 zcmdlGbSZR$f#&43jEX!tnduoN#SCtqJ|VJ`8+j#FeLw=53@)z0&VmdK48a-s42gLulMix<2S5zcQ7A4c%FIjGPzr!5 zRMJsUDk&;WRnmmmA`i1AKQE_JAum5q0cIqMo&A#;C1oTab{3Z;mSiS_ZFEA|=nu0o NFTaFzTXivP1prBPU8(>8 delta 7 OcmcZ Date: Sun, 20 Oct 2013 09:50:16 +0200 Subject: [PATCH 077/148] Added configrue file in cmake. Hopefully it will copy defaultfilters file. --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 625239aa06..a436d1fc3c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -319,6 +319,9 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg "${OpenMW_BINARY_DIR}/opencs.cfg") + +configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters.omwaddon.project + "${OpenMW_BINARY_DIR}/resources/defaultfilters.omwaddon.project") if (NOT WIN32 AND NOT APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop From 6b293961b4c7ee5152a282a6c245ec57c227b128 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 20 Oct 2013 10:02:33 +0200 Subject: [PATCH 078/148] This appears to work. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a436d1fc3c..2a902eb832 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -321,7 +321,7 @@ configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg "${OpenMW_BINARY_DIR}/opencs.cfg") configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters.omwaddon.project - "${OpenMW_BINARY_DIR}/resources/defaultfilters.omwaddon.project") + "${OpenMW_BINARY_DIR}/resources/defaultfilters.omwaddon.project" COPYONLY) if (NOT WIN32 AND NOT APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop From 96b6787255fb4596a0b02d3cecc438c086e29c73 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 20 Oct 2013 10:56:27 +0200 Subject: [PATCH 079/148] Getting rid of extension. Correcting tiny mistake in filters file. --- CMakeLists.txt | 2 +- apps/opencs/model/doc/document.cpp | 4 ++-- files/opencs/defaultfilters.omwaddon.project | Bin 10962 -> 10958 bytes 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a902eb832..df2986715b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -321,7 +321,7 @@ configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg "${OpenMW_BINARY_DIR}/opencs.cfg") configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters.omwaddon.project - "${OpenMW_BINARY_DIR}/resources/defaultfilters.omwaddon.project" COPYONLY) + "${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY) if (NOT WIN32 AND NOT APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 32c728f88c..fd2b328612 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2182,13 +2182,13 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, co else { boost::filesystem::path locCustomFiltersPath (configuration.getUserPath()); - locCustomFiltersPath /= "defaultfilters.omwaddon.project"; + locCustomFiltersPath /= "customfilters.omwaddon.project"; if (boost::filesystem::exists(locCustomFiltersPath)) { boost::filesystem::copy(locCustomFiltersPath, mProjectPath); } else { boost::filesystem::path filters(mResDir); - filters /= "defaultfilters.omwaddon.project"; + filters /= "defaultfilters"; boost::filesystem::copy_file(filters, mProjectPath); } getData().loadFile (mProjectPath, false, true); diff --git a/files/opencs/defaultfilters.omwaddon.project b/files/opencs/defaultfilters.omwaddon.project index c66990fdc2c26863fa33edefe0d336ef9ae8fa3b..0ac3c8db4bd939b8b9d559ab4af4056fc0b8d80e 100644 GIT binary patch delta 37 tcmcZXdMR|nEKSDV$rIINCU4h_oV;2~aI&n{MgWvH3W5Lt From ebf77329128ac86833586ce4e00ceec2916d3e1e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 20 Oct 2013 15:48:39 +0200 Subject: [PATCH 080/148] some cleanup --- apps/opencs/model/world/idcollection.hpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 04e65eea7b..72aafb91fe 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -14,14 +14,11 @@ namespace CSMWorld { public: - void load (ESM::ESMReader& reader, bool base, - UniversalId::Type type = UniversalId::Type_None); - ///< \param type Will be ignored, unless the collection supports multiple record types + void load (ESM::ESMReader& reader, bool base); }; template - void IdCollection::load (ESM::ESMReader& reader, bool base, - UniversalId::Type type) + void IdCollection::load (ESM::ESMReader& reader, bool base) { std::string id = reader.getHNOString ("NAME"); From adf3a41a835af0315adf53ab7a31f3a1c2356cfc Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 20 Oct 2013 17:13:31 +0200 Subject: [PATCH 081/148] added topic and journal tables --- apps/opencs/model/world/data.cpp | 58 ++++++++++++++++++++++++ apps/opencs/model/world/data.hpp | 11 +++++ apps/opencs/model/world/idcollection.hpp | 46 +++++++++++-------- apps/opencs/model/world/universalid.cpp | 4 ++ apps/opencs/model/world/universalid.hpp | 4 ++ apps/opencs/view/doc/view.cpp | 18 ++++++++ apps/opencs/view/doc/view.hpp | 4 ++ apps/opencs/view/world/subviews.cpp | 2 + components/esm/loaddial.cpp | 5 ++ components/esm/loaddial.hpp | 3 ++ 10 files changed, 136 insertions(+), 19 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 1e290d45f9..2af76cfa71 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -141,6 +141,12 @@ CSMWorld::Data::Data() : mRefs (mCells) mSpells.addColumn (new FlagColumn (Columns::ColumnId_StarterSpell, 0x2)); mSpells.addColumn (new FlagColumn (Columns::ColumnId_AlwaysSucceeds, 0x4)); + mTopics.addColumn (new StringIdColumn); + mTopics.addColumn (new RecordStateColumn); + + mJournals.addColumn (new StringIdColumn); + mJournals.addColumn (new RecordStateColumn); + mCells.addColumn (new StringIdColumn); mCells.addColumn (new RecordStateColumn); mCells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Cell)); @@ -196,6 +202,8 @@ CSMWorld::Data::Data() : mRefs (mCells) addModel (new IdTable (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region); addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign); addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell); + addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic); + addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal); addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell); addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, UniversalId::Type_Referenceable); @@ -319,6 +327,28 @@ CSMWorld::IdCollection& CSMWorld::Data::getSpells() return mSpells; } + +const CSMWorld::IdCollection& CSMWorld::Data::getTopcis() const +{ + return mTopics; +} + +CSMWorld::IdCollection& CSMWorld::Data::getTopcis() +{ + return mTopics; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getJournals() const +{ + return mJournals; +} + +CSMWorld::IdCollection& CSMWorld::Data::getJournals() +{ + return mJournals; +} + + const CSMWorld::IdCollection& CSMWorld::Data::getCells() const { return mCells; @@ -447,6 +477,30 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) case ESM::REC_STAT: mReferenceables.load (reader, base, UniversalId::Type_Static); break; case ESM::REC_WEAP: mReferenceables.load (reader, base, UniversalId::Type_Weapon); break; + case ESM::REC_DIAL: + { + std::string id = reader.getHNOString ("NAME"); + + ESM::Dialogue record; + record.mId = id; + record.load (reader); + + if (record.mType==ESM::Dialogue::Journal) + { + mJournals.load (record, base); + } + else if (record.mType==ESM::Dialogue::Deleted) + { + /// \todo handle deleted records + } + else + { + mTopics.load (record, base); + } + + break; + } + default: /// \todo throw an exception instead, once all records are implemented @@ -469,6 +523,8 @@ bool CSMWorld::Data::hasId (const std::string& id) const getRegions().searchId (id)!=-1 || getBirthsigns().searchId (id)!=-1 || getSpells().searchId (id)!=-1 || + getTopcis().searchId (id)!=-1 || + getJournals().searchId (id)!=-1 || getCells().searchId (id)!=-1 || getReferenceables().searchId (id)!=-1; } @@ -487,6 +543,8 @@ std::vector CSMWorld::Data::getIds (bool listDeleted) const appendIds (ids, mRegions, listDeleted); appendIds (ids, mBirthsigns, listDeleted); appendIds (ids, mSpells, listDeleted); + appendIds (ids, mTopics, listDeleted); + appendIds (ids, mJournals, listDeleted); appendIds (ids, mCells, listDeleted); appendIds (ids, mReferenceables, listDeleted); diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index e900bb10fb..74304e029b 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "../filter/filter.hpp" @@ -48,6 +49,8 @@ namespace CSMWorld IdCollection mRegions; IdCollection mBirthsigns; IdCollection mSpells; + IdCollection mTopics; + IdCollection mJournals; IdCollection mCells; RefIdCollection mReferenceables; RefCollection mRefs; @@ -116,6 +119,14 @@ namespace CSMWorld IdCollection& getSpells(); + const IdCollection& getTopcis() const; + + IdCollection& getTopcis(); + + const IdCollection& getJournals() const; + + IdCollection& getJournals(); + const IdCollection& getCells() const; IdCollection& getCells(); diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 72aafb91fe..b6ae04572c 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -15,6 +15,8 @@ namespace CSMWorld public: void load (ESM::ESMReader& reader, bool base); + + void load (const ESXRecordT& record, bool base); }; template @@ -53,29 +55,35 @@ namespace CSMWorld IdAccessorT().getId (record) = id; record.load (reader); - int index = this->searchId (IdAccessorT().getId (record)); + load (record, base); + } + } - if (index==-1) - { - // new record - Record record2; - record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; - (base ? record2.mBase : record2.mModified) = record; + template + void IdCollection::load (const ESXRecordT& record, bool base) + { + int index = this->searchId (IdAccessorT().getId (record)); - this->appendRecord (record2); - } + if (index==-1) + { + // new record + Record record2; + record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; + (base ? record2.mBase : record2.mModified) = record; + + this->appendRecord (record2); + } + else + { + // old record + Record record2 = Collection::getRecord (index); + + if (base) + record2.mBase = record; else - { - // old record - Record record2 = Collection::getRecord (index); + record2.setModified (record); - if (base) - record2.mBase = record; - else - record2.setModified (record); - - this->setRecord (index, record2); - } + this->setRecord (index, record2); } } } diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index c9edd0c162..6201a3cdaa 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -29,6 +29,8 @@ namespace { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, "Referenceables", 0 }, @@ -54,6 +56,8 @@ namespace { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", ":./land.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", ":./birthsign.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":./spell.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 2466407331..ffd99e572f 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -87,6 +87,10 @@ namespace CSMWorld Type_RegionMap, Type_Filter, Type_Filters, + Type_Topics, + Type_Topic, + Type_Journals, + Type_Journal, Type_Scene }; diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index b29250d204..5713449f23 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -163,6 +163,14 @@ void CSVDoc::View::setupMechanicsMenu() QAction *spells = new QAction (tr ("Spells"), this); connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView())); mechanics->addAction (spells); + + QAction *topics = new QAction (tr ("Topics"), this); + connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView())); + mechanics->addAction (topics); + + QAction *journals = new QAction (tr ("Journals"), this); + connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView())); + mechanics->addAction (journals); } void CSVDoc::View::setupAssetsMenu() @@ -412,6 +420,16 @@ void CSVDoc::View::addSceneSubView() addSubView (CSMWorld::UniversalId::Type_Scene); } +void CSVDoc::View::addTopicsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Topics); +} + +void CSVDoc::View::addJournalsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Journals); +} + void CSVDoc::View::abortOperation (int type) { mDocument->abortOperation (type); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 6f3c38daaa..2a31d9d807 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -166,6 +166,10 @@ namespace CSVDoc void addSceneSubView(); + void addTopicsSubView(); + + void addJournalsSubView(); + void toggleShowStatusBar (bool show); }; } diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 0e3465b388..417a7258fd 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -36,6 +36,8 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) CSMWorld::UniversalId::Type_Regions, CSMWorld::UniversalId::Type_Birthsigns, CSMWorld::UniversalId::Type_Spells, + CSMWorld::UniversalId::Type_Topics, + CSMWorld::UniversalId::Type_Journals, CSMWorld::UniversalId::Type_None // end marker }; diff --git a/components/esm/loaddial.cpp b/components/esm/loaddial.cpp index fb50d5e9f5..f6c63efd20 100644 --- a/components/esm/loaddial.cpp +++ b/components/esm/loaddial.cpp @@ -36,4 +36,9 @@ void Dialogue::save(ESMWriter &esm) } } + void Dialogue::blank() + { + mInfo.clear(); + } + } diff --git a/components/esm/loaddial.hpp b/components/esm/loaddial.hpp index 61f3f763de..0f4ceb6acf 100644 --- a/components/esm/loaddial.hpp +++ b/components/esm/loaddial.hpp @@ -35,6 +35,9 @@ struct Dialogue void load(ESMReader &esm); void save(ESMWriter &esm); + + void blank(); + ///< Set record to default state (does not touch the ID and does not change the type). }; } #endif From b138533bf307332852d907b181114f53b2bf4d91 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 20 Oct 2013 17:21:09 +0200 Subject: [PATCH 082/148] renamed defaultfilter.omwaddon.project to defaultfilters. --- CMakeLists.txt | 2 +- files/opencs/defaultfilters.omwaddon.project | Bin 10958 -> 0 bytes 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 files/opencs/defaultfilters.omwaddon.project diff --git a/CMakeLists.txt b/CMakeLists.txt index df2986715b..cfb682cdb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -320,7 +320,7 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg "${OpenMW_BINARY_DIR}/opencs.cfg") -configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters.omwaddon.project +configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters "${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY) if (NOT WIN32 AND NOT APPLE) diff --git a/files/opencs/defaultfilters.omwaddon.project b/files/opencs/defaultfilters.omwaddon.project deleted file mode 100644 index 0ac3c8db4bd939b8b9d559ab4af4056fc0b8d80e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10958 zcmWG>4K_AmWPk$?SC=3iMh1qov}N{i!BGa@5ODMK35i5f;pgb8D|>@38-C!2~K&0Czaqgob-@ zL26=Ydm* z8-=b70UHHN%2F`5LTo^BDT)#KV#`iogN!mDg%YJzD#W=)9tz=^DXFkztBKFex%tHJcCeffPr0dmqImi6xoIkX`|(y^6hU7YtSfu?wa50o4w%Xem-x z5!B5A)raufEiKt8AvP}0@)ZSW;SyMGds$q~r+d_F$2Rnvs;BpN-oB=bZeKjLf`r-1;-~(u-14 zGE?(PkW4}KM{Z^@s1cr+mzrN%jHC+c8K^@cWuK;wLSkMDyorr!P)=rg2C_8;`9&p( zNjaeYp%SRup`;0IR4c<$W@brhVs5cQNk(FcLULlBLQ<+iL1uDxYKlT>0VLA!rpM&` zypqJsyi{nn5qEloD}P#A*C zV3;s8x#y!<$cNk-OiE3Gl!TyeFw8H($wiq3B}zI9PN^yJ!6k`#DTzfX5KFXR{y`M- zkl}=+)D(qcus(&vl9I&ajMNlJ{%%CG9;JZ?8P&;1&B;xLv_=Uu?!oFcbrcYNd~oRs z@8WxaO@h_yAfJLm1Y!X+JjBpy3DgJx71qV+iKTfZIjJR(pgDp+|G?aYT#ul-$|p6g zM8T;jF*&serF=kD6$C0=k(9w=1638+EO)5;2%3ga2+aqe5)>RHa4#xoD|S)J%e5j8KRk#&Boh2;&AcpJB-w#Tl8YIVs?BfIvr$Krjb`4T6O; zJZC`6hvtkXH1~kI?TDfeoHLS3Gm8?7A^C#Xyox;`J3~yua1qo7NZb?QqV%HF#IjUs zxX2x15{8SQHb7hyixx(p)+juTlA*0NaD3q|w_%MsNRbN4S>P-Qs+wRXKrBP|K1u?C zDNRa+_T&k4Vs2>;v?BJRLd-dWje~^~FRW~b*aC?=ysZP6 zvx@SQAaO^PfXYvTcc8gpP6F8gaZoE-IAKlT#Tl7JkoF37Qd4FTd|w5J4ji@G=~xRRMXVAsQ{TK$DyBdZPe5&H$;ya911f z;RtZg0#xz9t%Mo?@mD&URiMr=+$zLy0mM4c*dnYpfcCDS0~1P~@R0@ROev^fgQL~>EG~hD2Po9QPJkJwkds+lQml}lrT{h=;t=!_8DP%xU0n#J^r4Vqa!A&n#$SX*O=8$@{ zXakkEm<~%z%}vaKGz4t$*CnW~as%tba1y)#g_w)voH8`$fO<=C=VYf=LJ|*w?hmd( zSI=UF>{QS?4U~~UcuNFiJS4Wz*JYrjWfarF5v7VVEu*M~L@}45_R20Em~ZlgaX9);^NGlGU(zI zOOl)e(TL(2P-O{Ds1P|+$KVYdcm^uYNi2gTQ(Pq<*35%wRDcbDay6kD2;PDLOG5Ka z4ZgsEy9b_YY$1VznC`%rml3%pEinf)=LB^Ke3%hrEK;Tc4Uxl~1Dbq6sdN%kQlL#K zP?-ZO<$Uu~GSf0sQ$ShM5u^fI#KNl_ur^3;@PV1lz`y`99wkpEB^E;h98}C=Gu zU zVL)(TfkFao2WV^pq8VZfdR2l_Oh6RECWpX&vV$Z&9K{60MnpXlfH|)Ob`B_d!OqFg v%c)d=S)c&158^QN1vsG61E3d(?F4ia2jYj1jBg%$4&3> From 75c5316ad779a2ac5e6b989aea03e752c8198e26 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 20 Oct 2013 17:26:09 +0200 Subject: [PATCH 083/148] added dialogue type column to topics table --- apps/opencs/model/world/columnbase.hpp | 3 ++- apps/opencs/model/world/columnimp.hpp | 27 ++++++++++++++++++++++++++ apps/opencs/model/world/columns.cpp | 7 +++++++ apps/opencs/model/world/columns.hpp | 1 + apps/opencs/model/world/data.cpp | 1 + apps/opencs/view/doc/viewmanager.cpp | 3 ++- 6 files changed, 40 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index c1b423c94c..9b8d7dafb8 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -43,7 +43,8 @@ namespace CSMWorld Display_CreatureType, Display_WeaponType, Display_RecordState, - Display_RefRecordType + Display_RefRecordType, + Display_DialogueType }; int mColumnId; diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index a13ac9a8a9..eec0a41e2a 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -1284,6 +1284,33 @@ namespace CSMWorld return true; } }; + + template + struct DialogueTypeColumn : public Column + { + DialogueTypeColumn() + : Column (Columns::ColumnId_DialogueType, ColumnBase::Display_DialogueType) + {} + + virtual QVariant get (const Record& record) const + { + return static_cast (record.get().mType); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mType = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return false; + } + }; } #endif diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index ca37840ad6..ae4136bd92 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -159,6 +159,7 @@ namespace CSMWorld { ColumnId_DoorPositionXRot, "Teleport Rot X" }, { ColumnId_DoorPositionYRot, "Teleport Rot Y" }, { ColumnId_DoorPositionZRot, "Teleport Rot Z" }, + { ColumnId_DialogueType, "Dialogue Type" }, { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, @@ -269,6 +270,11 @@ namespace "unknown", "none", "short", "integer", "long", "float", "string", 0 }; + static const char *sDialogueTypeEnums[] = + { + "Topic", "Voice", "Greeting", "Persuasion", 0 + }; + const char **getEnumNames (CSMWorld::Columns::ColumnId column) { switch (column) @@ -283,6 +289,7 @@ namespace case CSMWorld::Columns::ColumnId_WeaponType: return sWeaponTypes; case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums; case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums; + case CSMWorld::Columns::ColumnId_DialogueType: return sDialogueTypeEnums; default: return 0; } diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 9b26cac4c7..111931fa85 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -152,6 +152,7 @@ namespace CSMWorld ColumnId_DoorPositionXRot = 139, ColumnId_DoorPositionYRot = 140, ColumnId_DoorPositionZRot = 141, + ColumnId_DialogueType = 142, // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 2af76cfa71..284e756f06 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -143,6 +143,7 @@ CSMWorld::Data::Data() : mRefs (mCells) mTopics.addColumn (new StringIdColumn); mTopics.addColumn (new RecordStateColumn); + mTopics.addColumn (new DialogueTypeColumn); mJournals.addColumn (new StringIdColumn); mJournals.addColumn (new RecordStateColumn); diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 83cd93e5dd..a4849795b2 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -74,7 +74,8 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) { CSMWorld::ColumnBase::Display_ArmorType, CSMWorld::Columns::ColumnId_ArmorType, false }, { CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false }, { CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, false }, - { CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false } + { CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false }, + { CSMWorld::ColumnBase::Display_DialogueType, CSMWorld::Columns::ColumnId_DialogueType, false } }; for (std::size_t i=0; i Date: Mon, 21 Oct 2013 13:39:13 +0200 Subject: [PATCH 084/148] set dialogue type for newly created dialogue records --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/world/columnimp.hpp | 10 ++++-- apps/opencs/model/world/data.cpp | 1 + apps/opencs/view/world/dialoguecreator.cpp | 35 ++++++++++++++++++ apps/opencs/view/world/dialoguecreator.hpp | 41 ++++++++++++++++++++++ apps/opencs/view/world/subviews.cpp | 9 +++-- 6 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 apps/opencs/view/world/dialoguecreator.cpp create mode 100644 apps/opencs/view/world/dialoguecreator.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index f918cfebfb..541b3b5a2c 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -66,7 +66,7 @@ opencs_units (view/world opencs_units_noqt (view/world dialoguesubview subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate - scripthighlighter idvalidator + scripthighlighter idvalidator dialoguecreator ) diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index eec0a41e2a..23c529b3e3 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -1288,8 +1288,9 @@ namespace CSMWorld template struct DialogueTypeColumn : public Column { - DialogueTypeColumn() - : Column (Columns::ColumnId_DialogueType, ColumnBase::Display_DialogueType) + DialogueTypeColumn (bool hidden = false) + : Column (Columns::ColumnId_DialogueType, ColumnBase::Display_DialogueType, + hidden ? 0 : ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue) {} virtual QVariant get (const Record& record) const @@ -1307,6 +1308,11 @@ namespace CSMWorld } virtual bool isEditable() const + { + return true; + } + + virtual bool isUserEditable() const { return false; } diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 284e756f06..70e0e891db 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -147,6 +147,7 @@ CSMWorld::Data::Data() : mRefs (mCells) mJournals.addColumn (new StringIdColumn); mJournals.addColumn (new RecordStateColumn); + mJournals.addColumn (new DialogueTypeColumn (true)); mCells.addColumn (new StringIdColumn); mCells.addColumn (new RecordStateColumn); diff --git a/apps/opencs/view/world/dialoguecreator.cpp b/apps/opencs/view/world/dialoguecreator.cpp new file mode 100644 index 0000000000..c16214283e --- /dev/null +++ b/apps/opencs/view/world/dialoguecreator.cpp @@ -0,0 +1,35 @@ + +#include "dialoguecreator.hpp" + +#include + +#include "../../model/world/data.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/columns.hpp" +#include "../../model/world/idtable.hpp" + +void CSVWorld::DialogueCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const +{ + int index = + dynamic_cast (*getData().getTableModel (getCollectionId())). + findColumnIndex (CSMWorld::Columns::ColumnId_DialogueType); + + command.addValue (index, mType); +} + +CSVWorld::DialogueCreator::DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id, int type) +: GenericCreator (data, undoStack, id), mType (type) +{} + +CSVWorld::Creator *CSVWorld::TopicCreatorFactory::makeCreator (CSMWorld::Data& data, + QUndoStack& undoStack, const CSMWorld::UniversalId& id) const +{ + return new DialogueCreator (data, undoStack, id, ESM::Dialogue::Topic); +} + +CSVWorld::Creator *CSVWorld::JournalCreatorFactory::makeCreator (CSMWorld::Data& data, + QUndoStack& undoStack, const CSMWorld::UniversalId& id) const +{ + return new DialogueCreator (data, undoStack, id, ESM::Dialogue::Journal); +} \ No newline at end of file diff --git a/apps/opencs/view/world/dialoguecreator.hpp b/apps/opencs/view/world/dialoguecreator.hpp new file mode 100644 index 0000000000..26f866909a --- /dev/null +++ b/apps/opencs/view/world/dialoguecreator.hpp @@ -0,0 +1,41 @@ +#ifndef CSV_WORLD_DIALOGUECREATOR_H +#define CSV_WORLD_DIALOGUECREATOR_H + +#include "genericcreator.hpp" + +namespace CSVWorld +{ + class DialogueCreator : public GenericCreator + { + int mType; + + protected: + + virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + + public: + + DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id, int type); + }; + + class TopicCreatorFactory : public CreatorFactoryBase + { + public: + + virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) const; + ///< The ownership of the returned Creator is transferred to the caller. + }; + + class JournalCreatorFactory : public CreatorFactoryBase + { + public: + + virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) const; + ///< The ownership of the returned Creator is transferred to the caller. + }; +} + +#endif diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 417a7258fd..3d98cf73ce 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -14,6 +14,7 @@ #include "referenceablecreator.hpp" #include "referencecreator.hpp" #include "scenesubview.hpp" +#include "dialoguecreator.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { @@ -36,8 +37,6 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) CSMWorld::UniversalId::Type_Regions, CSMWorld::UniversalId::Type_Birthsigns, CSMWorld::UniversalId::Type_Spells, - CSMWorld::UniversalId::Type_Topics, - CSMWorld::UniversalId::Type_Journals, CSMWorld::UniversalId::Type_None // end marker }; @@ -55,6 +54,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_References, new CSVDoc::SubViewFactoryWithCreator >); + manager.add (CSMWorld::UniversalId::Type_Topics, + new CSVDoc::SubViewFactoryWithCreator); + + manager.add (CSMWorld::UniversalId::Type_Journal, + new CSVDoc::SubViewFactoryWithCreator); + // Subviews for editing/viewing individual records manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); From c0e550143108e81ffcb6ee896e1ea0dfaeffaff8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 21 Oct 2013 13:58:47 +0200 Subject: [PATCH 085/148] disallow the deletion of non-topic, non-journal dialogue records --- apps/opencs/view/world/table.cpp | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 6167c084a0..a58eb873f3 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -87,19 +87,33 @@ std::vector CSVWorld::Table::listDeletableSelectedIds() const { QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); + // check record state CSMWorld::RecordBase::State state = static_cast ( mModel->data (mModel->index (index.row(), 1)).toInt()); - if (state!=CSMWorld::RecordBase::State_Deleted) + if (state==CSMWorld::RecordBase::State_Deleted) + continue; + + // check other columns (only relevant for a subset of the tables) + int dialogueTypeIndex = + mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_DialogueType); + + if (dialogueTypeIndex!=-1) { - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + int type = mModel->data (mModel->index (index.row(), dialogueTypeIndex)).toInt(); - std::string id = mModel->data (mModel->index (index.row(), columnIndex)). - toString().toUtf8().constData(); - - deletableIds.push_back (id); + if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal) + continue; } + + // add the id to the collection + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + + std::string id = mModel->data (mModel->index (index.row(), columnIndex)). + toString().toUtf8().constData(); + + deletableIds.push_back (id); } } From dc12648a3efd8da82e148d2610536753434eff73 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 21 Oct 2013 14:26:54 +0200 Subject: [PATCH 086/148] add fixed dialogue records when creating a new omwgame file --- apps/opencs/model/doc/document.cpp | 73 ++++++++++++++++++++++++++++++ apps/opencs/model/world/data.cpp | 6 +-- apps/opencs/model/world/data.hpp | 4 +- 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index d7138f6714..64f627b5aa 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2137,6 +2137,79 @@ void CSMDoc::Document::createBase() getData().getSkills().add (record); } + + static const char *sVoice[] = + { + "Intruder", + "Attack", + "Hello", + "Thief", + "Alarm", + "Idle", + "Flee", + "Hit", + 0 + }; + + for (int i=0; sVoice[i]; ++i) + { + ESM::Dialogue record; + record.mId = sVoice[i]; + record.mType = ESM::Dialogue::Voice; + record.blank(); + + getData().getTopics().add (record); + } + + static const char *sGreetings[] = + { + "Greeting 0", + "Greeting 1", + "Greeting 2", + "Greeting 3", + "Greeting 4", + "Greeting 5", + "Greeting 6", + "Greeting 7", + "Greeting 8", + "Greeting 9", + 0 + }; + + for (int i=0; sGreetings[i]; ++i) + { + ESM::Dialogue record; + record.mId = sGreetings[i]; + record.mType = ESM::Dialogue::Greeting; + record.blank(); + + getData().getTopics().add (record); + } + + static const char *sPersuasion[] = + { + "Intimidate Success", + "Intimidate Fail", + "Service Refusal", + "Admire Success", + "Taunt Success", + "Bribe Success", + "Info Refusal", + "Admire Fail", + "Taunt Fail", + "Bribe Fail", + 0 + }; + + for (int i=0; sPersuasion[i]; ++i) + { + ESM::Dialogue record; + record.mId = sPersuasion[i]; + record.mType = ESM::Dialogue::Persuasion; + record.blank(); + + getData().getTopics().add (record); + } } CSMDoc::Document::Document (const std::vector& files, diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 70e0e891db..c854711b13 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -330,12 +330,12 @@ CSMWorld::IdCollection& CSMWorld::Data::getSpells() } -const CSMWorld::IdCollection& CSMWorld::Data::getTopcis() const +const CSMWorld::IdCollection& CSMWorld::Data::getTopics() const { return mTopics; } -CSMWorld::IdCollection& CSMWorld::Data::getTopcis() +CSMWorld::IdCollection& CSMWorld::Data::getTopics() { return mTopics; } @@ -525,7 +525,7 @@ bool CSMWorld::Data::hasId (const std::string& id) const getRegions().searchId (id)!=-1 || getBirthsigns().searchId (id)!=-1 || getSpells().searchId (id)!=-1 || - getTopcis().searchId (id)!=-1 || + getTopics().searchId (id)!=-1 || getJournals().searchId (id)!=-1 || getCells().searchId (id)!=-1 || getReferenceables().searchId (id)!=-1; diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 74304e029b..cf31c94947 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -119,9 +119,9 @@ namespace CSMWorld IdCollection& getSpells(); - const IdCollection& getTopcis() const; + const IdCollection& getTopics() const; - IdCollection& getTopcis(); + IdCollection& getTopics(); const IdCollection& getJournals() const; From 3b85d970872afc58954c1f5b9e3ec2951b5a2fb5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 21 Oct 2013 15:38:13 +0200 Subject: [PATCH 087/148] handle deleted dialogue records --- apps/opencs/model/world/data.cpp | 13 +++++++++- apps/opencs/model/world/idcollection.hpp | 32 +++++++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index c854711b13..130ce334f7 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -493,7 +493,18 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) } else if (record.mType==ESM::Dialogue::Deleted) { - /// \todo handle deleted records + if (mJournals.tryDelete (id)) + { + /// \todo handle info records + } + else if (mTopics.tryDelete (id)) + { + /// \todo handle info records + } + else + { + /// \todo report deletion of non-existing record + } } else { diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index b6ae04572c..0d723bef1e 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -7,7 +7,6 @@ namespace CSMWorld { - /// \brief Single type collection of top level records template > class IdCollection : public Collection @@ -17,6 +16,11 @@ namespace CSMWorld void load (ESM::ESMReader& reader, bool base); void load (const ESXRecordT& record, bool base); + + bool tryDelete (const std::string& id); + ///< Try deleting \a id. If the id does not exist or can't be deleted the call is ignored. + /// + /// \return Has the ID been deleted? }; template @@ -86,6 +90,32 @@ namespace CSMWorld this->setRecord (index, record2); } } + + template + bool IdCollection::tryDelete (const std::string& id) + { + int index = this->searchId (id); + + if (index==-1) + return false; + + Record record = Collection::getRecord (index); + + if (record.isDeleted()) + return false; + + if (record.mState==RecordBase::State_ModifiedOnly) + { + Collection::removeRows (index, 1); + } + else + { + record.mState = RecordBase::State_Deleted; + setRecord (index, record); + } + + return true; + } } #endif From b23df42817a0ed45a460e2680b98d02b2e19ab1d Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 21 Oct 2013 16:06:16 +0200 Subject: [PATCH 088/148] Removed old comment. Changed to set resources path correctly. --- apps/opencs/editor.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 9abc9ee780..94faa713b6 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -89,8 +89,7 @@ void CS::Editor::setupDataFiles() dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); -// Adding Resources directory. First check if there is a file defaultfilters in the user path. - mDocumentManager.setResourceDir(variables["resources"].as()); + mDocumentManager.setResourceDir(mCfgMgr.getGlobalDataPath()); for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) { From 743c6ea5b1bf1f10b6c429da6523f54d99c3eb0b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 21 Oct 2013 16:47:32 +0200 Subject: [PATCH 089/148] save dialogue records --- apps/opencs/model/doc/saving.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/opencs/model/doc/saving.cpp b/apps/opencs/model/doc/saving.cpp index 2b0056e721..b756a9dc19 100644 --- a/apps/opencs/model/doc/saving.cpp +++ b/apps/opencs/model/doc/saving.cpp @@ -58,6 +58,13 @@ CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& proje appendStage (new WriteCollectionStage > (mDocument.getData().getSpells(), mState)); + /// \todo deal with info records for topcis and journals + appendStage (new WriteCollectionStage > + (mDocument.getData().getTopics(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getJournals(), mState)); + appendStage (new WriteRefIdCollectionStage (mDocument, mState)); From 5e1bdd605b37e808e4785074cc675c9f8ca680d6 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 21 Oct 2013 18:15:26 +0200 Subject: [PATCH 090/148] Corrected formatting in document.hpp --- apps/opencs/model/doc/document.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index e6d1b0c872..fb929cca6d 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -43,7 +43,7 @@ namespace CSMDoc CSMTools::Tools mTools; boost::filesystem::path mProjectPath; Saving mSaving; - boost::filesystem::path mResDir; + boost::filesystem::path mResDir; // It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is // using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late. @@ -119,3 +119,4 @@ namespace CSMDoc } #endif + From 70602c2c36b74164112f2b5118ac620b08488db5 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 21 Oct 2013 18:24:16 +0200 Subject: [PATCH 091/148] Removed changes in the unimplemented copy ctor. --- apps/opencs/model/doc/document.cpp | 2 +- apps/opencs/model/doc/document.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index fd2b328612..b56460a032 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2182,7 +2182,7 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, co else { boost::filesystem::path locCustomFiltersPath (configuration.getUserPath()); - locCustomFiltersPath /= "customfilters.omwaddon.project"; + locCustomFiltersPath /= "defaultfilters"; if (boost::filesystem::exists(locCustomFiltersPath)) { boost::filesystem::copy(locCustomFiltersPath, mProjectPath); diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index fb929cca6d..437b0c5131 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -50,7 +50,7 @@ namespace CSMDoc QUndoStack mUndoStack; // not implemented - Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, bool new_); + Document (const Document&); Document& operator= (const Document&); void load (const std::vector::const_iterator& begin, From ce2c5582573118e19677690e2738d1913306db5d Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 21 Oct 2013 18:40:20 +0200 Subject: [PATCH 092/148] Missed a file. --- files/opencs/defaultfilters | Bin 0 -> 10958 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 files/opencs/defaultfilters diff --git a/files/opencs/defaultfilters b/files/opencs/defaultfilters new file mode 100644 index 0000000000000000000000000000000000000000..0ac3c8db4bd939b8b9d559ab4af4056fc0b8d80e GIT binary patch literal 10958 zcmWG>4K_AmWPk$?SC=3iMh1qov}N{i!BGa@5ODMK35i5f;pgb8D|>@38-C!2~K&0Czaqgob-@ zL26=Ydm* z8-=b70UHHN%2F`5LTo^BDT)#KV#`iogN!mDg%YJzD#W=)9tz=^DXFkztBKFex%tHJcCeffPr0dmqImi6xoIkX`|(y^6hU7YtSfu?wa50o4w%Xem-x z5!B5A)raufEiKt8AvP}0@)ZSW;SyMGds$q~r+d_F$2Rnvs;BpN-oB=bZeKjLf`r-1;-~(u-14 zGE?(PkW4}KM{Z^@s1cr+mzrN%jHC+c8K^@cWuK;wLSkMDyorr!P)=rg2C_8;`9&p( zNjaeYp%SRup`;0IR4c<$W@brhVs5cQNk(FcLULlBLQ<+iL1uDxYKlT>0VLA!rpM&` zypqJsyi{nn5qEloD}P#A*C zV3;s8x#y!<$cNk-OiE3Gl!TyeFw8H($wiq3B}zI9PN^yJ!6k`#DTzfX5KFXR{y`M- zkl}=+)D(qcus(&vl9I&ajMNlJ{%%CG9;JZ?8P&;1&B;xLv_=Uu?!oFcbrcYNd~oRs z@8WxaO@h_yAfJLm1Y!X+JjBpy3DgJx71qV+iKTfZIjJR(pgDp+|G?aYT#ul-$|p6g zM8T;jF*&serF=kD6$C0=k(9w=1638+EO)5;2%3ga2+aqe5)>RHa4#xoD|S)J%e5j8KRk#&Boh2;&AcpJB-w#Tl8YIVs?BfIvr$Krjb`4T6O; zJZC`6hvtkXH1~kI?TDfeoHLS3Gm8?7A^C#Xyox;`J3~yua1qo7NZb?QqV%HF#IjUs zxX2x15{8SQHb7hyixx(p)+juTlA*0NaD3q|w_%MsNRbN4S>P-Qs+wRXKrBP|K1u?C zDNRa+_T&k4Vs2>;v?BJRLd-dWje~^~FRW~b*aC?=ysZP6 zvx@SQAaO^PfXYvTcc8gpP6F8gaZoE-IAKlT#Tl7JkoF37Qd4FTd|w5J4ji@G=~xRRMXVAsQ{TK$DyBdZPe5&H$;ya911f z;RtZg0#xz9t%Mo?@mD&URiMr=+$zLy0mM4c*dnYpfcCDS0~1P~@R0@ROev^fgQL~>EG~hD2Po9QPJkJwkds+lQml}lrT{h=;t=!_8DP%xU0n#J^r4Vqa!A&n#$SX*O=8$@{ zXakkEm<~%z%}vaKGz4t$*CnW~as%tba1y)#g_w)voH8`$fO<=C=VYf=LJ|*w?hmd( zSI=UF>{QS?4U~~UcuNFiJS4Wz*JYrjWfarF5v7VVEu*M~L@}45_R20Em~ZlgaX9);^NGlGU(zI zOOl)e(TL(2P-O{Ds1P|+$KVYdcm^uYNi2gTQ(Pq<*35%wRDcbDay6kD2;PDLOG5Ka z4ZgsEy9b_YY$1VznC`%rml3%pEinf)=LB^Ke3%hrEK;Tc4Uxl~1Dbq6sdN%kQlL#K zP?-ZO<$Uu~GSf0sQ$ShM5u^fI#KNl_ur^3;@PV1lz`y`99wkpEB^E;h98}C=Gu zU zVL)(TfkFao2WV^pq8VZfdR2l_Oh6RECWpX&vV$Z&9K{60MnpXlfH|)Ob`B_d!OqFg v%c)d=S)c&158^QN1vsG61E3d(?F4ia2jYj1jBg%$4&3> literal 0 HcmV?d00001 From 3146af34d642a28b15b55f7eb9999d8ac50168a0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 21 Oct 2013 19:28:57 +0200 Subject: [PATCH 093/148] some fixes for the merged filter branch --- apps/opencs/editor.cpp | 2 +- apps/opencs/model/doc/document.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 94faa713b6..a05b515d3b 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -89,7 +89,7 @@ void CS::Editor::setupDataFiles() dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); - mDocumentManager.setResourceDir(mCfgMgr.getGlobalDataPath()); + mDocumentManager.setResourceDir (variables["resources"].as()); for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) { diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 34bdc056f8..cc886f9cc2 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2258,7 +2258,7 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, co locCustomFiltersPath /= "defaultfilters"; if (boost::filesystem::exists(locCustomFiltersPath)) { - boost::filesystem::copy(locCustomFiltersPath, mProjectPath); + boost::filesystem::copy_file (locCustomFiltersPath, mProjectPath); } else { boost::filesystem::path filters(mResDir); filters /= "defaultfilters"; From f9591ddda61fec57783514e4b2bd860b86e39f09 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Tue, 22 Oct 2013 21:52:35 -0500 Subject: [PATCH 094/148] Reimplemented constentselector view class --- apps/launcher/CMakeLists.txt | 11 + apps/launcher/datafilespage.cpp | 203 +++++++++---- apps/launcher/datafilespage.hpp | 33 ++- apps/launcher/maindialog.cpp | 2 +- .../launcher/utils/lineedit.cpp | 20 +- .../view => apps/launcher/utils}/lineedit.hpp | 33 ++- .../launcher/utils}/profilescombobox.cpp | 41 +-- apps/launcher/utils/profilescombobox.hpp | 40 +++ .../launcher/utils}/textinputdialog.cpp | 17 +- .../launcher/utils}/textinputdialog.hpp | 17 +- apps/opencs/CMakeLists.txt | 9 +- apps/opencs/editor.cpp | 6 +- .../opencs/view/doc}/adjusterwidget.cpp | 0 .../opencs/view/doc}/adjusterwidget.hpp | 0 apps/opencs/view/doc/filedialog.cpp | 111 ++++--- apps/opencs/view/doc/filedialog.hpp | 46 +-- .../opencs/view/doc}/filewidget.cpp | 0 .../opencs/view/doc}/filewidget.hpp | 0 apps/opencs/view/doc/newgame.cpp | 4 +- components/CMakeLists.txt | 8 +- .../contentselector/model/contentmodel.cpp | 10 + components/contentselector/view/combobox.cpp | 39 +++ components/contentselector/view/combobox.hpp | 30 ++ .../contentselector/view/comboboxlineedit.hpp | 37 --- .../contentselector/view/contentselector.cpp | 279 +----------------- .../contentselector/view/contentselector.hpp | 72 +---- components/contentselector/view/lineedit.cpp | 39 --- .../contentselector/view/profilescombobox.hpp | 42 --- files/ui/contentselector.ui | 111 +++++++ files/ui/datafilespage.ui | 203 +------------ files/ui/filedialog.ui | 73 +++++ 31 files changed, 695 insertions(+), 841 deletions(-) rename components/contentselector/view/comboboxlineedit.cpp => apps/launcher/utils/lineedit.cpp (58%) rename {components/contentselector/view => apps/launcher/utils}/lineedit.hpp (54%) rename {components/contentselector/view => apps/launcher/utils}/profilescombobox.cpp (60%) create mode 100644 apps/launcher/utils/profilescombobox.hpp rename {components/contentselector/view => apps/launcher/utils}/textinputdialog.cpp (77%) rename {components/contentselector/view => apps/launcher/utils}/textinputdialog.hpp (66%) rename {components/contentselector/view => apps/opencs/view/doc}/adjusterwidget.cpp (100%) rename {components/contentselector/view => apps/opencs/view/doc}/adjusterwidget.hpp (100%) rename {components/contentselector/view => apps/opencs/view/doc}/filewidget.cpp (100%) rename {components/contentselector/view => apps/opencs/view/doc}/filewidget.hpp (100%) create mode 100644 components/contentselector/view/combobox.cpp create mode 100644 components/contentselector/view/combobox.hpp delete mode 100644 components/contentselector/view/comboboxlineedit.hpp delete mode 100644 components/contentselector/view/lineedit.cpp delete mode 100644 components/contentselector/view/profilescombobox.hpp create mode 100644 files/ui/contentselector.ui create mode 100644 files/ui/filedialog.ui diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 49dedd8290..18c555a249 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -11,6 +11,9 @@ set(LAUNCHER settings/launchersettings.cpp utils/checkablemessagebox.cpp + utils/profilescombobox.cpp + utils/textinputdialog.cpp + utils/lineedit.cpp ${CMAKE_SOURCE_DIR}/files/launcher/launcher.rc ) @@ -31,6 +34,9 @@ set(LAUNCHER_HEADER settings/settingsbase.hpp utils/checkablemessagebox.hpp + utils/profilescombobox.hpp + utils/textinputdialog.hpp + utils/lineedit.hpp ) if(NOT WIN32) LIST(APPEND LAUNCHER_HEADER unshieldthread.hpp) @@ -45,7 +51,11 @@ set(LAUNCHER_HEADER_MOC playpage.hpp textslotmsgbox.hpp + utils/textinputdialog.hpp utils/checkablemessagebox.hpp + utils/profilescombobox.hpp + utils/lineedit.hpp + ) if(NOT WIN32) @@ -58,6 +68,7 @@ set(LAUNCHER_UI ${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui ${CMAKE_SOURCE_DIR}/files/ui/mainwindow.ui ${CMAKE_SOURCE_DIR}/files/ui/playpage.ui + ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui ) source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER}) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index a705ae37d8..2140caaf91 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -5,14 +5,16 @@ #include #include #include +#include #include #include -#include #include -#include + +#include "utils/textinputdialog.hpp" +#include "utils/profilescombobox.hpp" #include "settings/gamesettings.hpp" #include "settings/launchersettings.hpp" @@ -25,45 +27,30 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam , mLauncherSettings(launcherSettings) , QWidget(parent) { + ui.setupUi (this); + setObjectName ("DataFilesPage"); + mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); - unsigned char flags; - - flags = ContentSelectorView::Flag_Content | ContentSelectorView::Flag_Profile; - - ContentSelectorView::ContentSelector::configure(this, flags); - mSelector = &ContentSelectorView::ContentSelector::instance(); - + buildView(); setupDataFiles(); - - - connect (mSelector, SIGNAL (signalProfileRenamed (QString, QString)), - this, SLOT (slotProfileRenamed (QString, QString))); - - connect (mSelector, SIGNAL (signalProfileChangedByUser (QString, QString)), - this, SLOT (slotProfileChangedByUser (QString, QString))); - - connect (mSelector, SIGNAL (signalProfileDeleted (QString)), - this, SLOT (slotProfileDeleted (QString))); - - connect (mSelector, SIGNAL (signalAddNewProfile (QString)), - this, SLOT (slotAddNewProfile (QString))); } void DataFilesPage::loadSettings() { - QString profileName = mSelector->getProfileText(); + QString profileName = ui.profilesComboBox->currentText(); QStringList files = mLauncherSettings.values(QString("Profiles/") + profileName + QString("/game"), Qt::MatchExactly); QStringList addons = mLauncherSettings.values(QString("Profiles/") + profileName + QString("/addon"), Qt::MatchExactly); mSelector->clearCheckStates(); - if (files.size() > 0) - mSelector->setGameFile(files.at(0)); - else - mSelector->setGameFile(); + QString gameFile (""); + if (files.size()>0) + gameFile = files.at (0); + + mSelector->setGameFile(gameFile); mSelector->setCheckStates(addons); } @@ -72,7 +59,7 @@ void DataFilesPage::saveSettings(const QString &profile) QString profileName = profile; if (profileName.isEmpty()) - profileName = mSelector->getProfileText(); + profileName = ui.profilesComboBox->currentText(); //retrieve the files selected for the profile ContentSelectorModel::ContentFileList items = mSelector->selectedFiles(); @@ -83,7 +70,7 @@ void DataFilesPage::saveSettings(const QString &profile) mGameSettings.remove(QString("addon")); //set the value of the current profile (not necessarily the profile being saved!) - mLauncherSettings.setValue(QString("Profiles/currentprofile"), mSelector->getProfileText()); + mLauncherSettings.setValue(QString("Profiles/currentprofile"), ui.profilesComboBox->currentText()); foreach(const ContentSelectorModel::EsmFile *item, items) { @@ -98,39 +85,64 @@ void DataFilesPage::saveSettings(const QString &profile) } +void DataFilesPage::buildView() +{ + ui.verticalLayout->insertWidget (0, mSelector->uiWidget()); + + //tool buttons + ui.newProfileButton->setToolTip ("Create a new profile"); + ui.deleteProfileButton->setToolTip ("Delete an existing profile"); + + //combo box + ui.profilesComboBox->addItem ("Default"); + ui.profilesComboBox->setPlaceholderText (QString("Select a profile...")); + + // Add the actions to the toolbuttons + ui.newProfileButton->setDefaultAction (ui.newProfileAction); + ui.deleteProfileButton->setDefaultAction (ui.deleteProfileAction); + + //establish connections + connect (ui.profilesComboBox, SIGNAL (currentIndexChanged(int)), + this, SLOT (slotProfileChanged(int))); + + connect (ui.profilesComboBox, SIGNAL (profileRenamed(QString, QString)), + this, SLOT (slotProfileRenamed(QString, QString))); + + connect (ui.profilesComboBox, SIGNAL (signalProfileChanged(QString, QString)), + this, SLOT (slotProfileChangedByUser(QString, QString))); +} + void DataFilesPage::removeProfile(const QString &profile) { mLauncherSettings.remove(QString("Profiles/") + profile + QString("/game")); mLauncherSettings.remove(QString("Profiles/") + profile + QString("/addon")); } -void DataFilesPage::changeProfiles(const QString &previous, const QString ¤t, bool savePrevious) +void DataFilesPage::setProfile(int index, bool savePrevious) { - //abort if no change (typically a duplicate signal) - if (previous == current) - return; + if (index >= -1 && index < ui.profilesComboBox->count()) + { + QString previous = ui.profilesComboBox->itemText(ui.profilesComboBox->currentIndex()); + QString current = ui.profilesComboBox->itemText(index); - int index = -1; - - if (!previous.isEmpty()) - index = mSelector->getProfileIndex(previous); - - // Store the previous profile if it exists - if ( (index != -1) && savePrevious) - saveSettings(previous); - - loadSettings(); + setProfile (previous, current, savePrevious); + } } -void DataFilesPage::slotAddNewProfile(const QString &profile) +void DataFilesPage::setProfile (const QString &previous, const QString ¤t, bool savePrevious) { - saveSettings(); - mSelector->clearCheckStates(); - mSelector->addProfile(profile, true); - mSelector->setGameFile(); - saveSettings(); + //abort if no change (poss. duplicate signal) + if (previous == current) + return; - emit signalProfileChanged(mSelector->getProfileIndex(profile)); + if (!previous.isEmpty() && savePrevious) + saveSettings (previous); + + ui.profilesComboBox->setCurrentIndex (ui.profilesComboBox->findText (current)); + + loadSettings(); + + checkForDefaultProfile(); } void DataFilesPage::slotProfileDeleted (const QString &item) @@ -140,8 +152,8 @@ void DataFilesPage::slotProfileDeleted (const QString &item) void DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString ¤t) { - changeProfiles(previous, current); - emit signalProfileChanged(mSelector->getProfileIndex(current)); + setProfile(previous, current, true); + emit signalProfileChanged (ui.profilesComboBox->findText(current)); } void DataFilesPage::slotProfileRenamed(const QString &previous, const QString ¤t) @@ -160,7 +172,7 @@ void DataFilesPage::slotProfileRenamed(const QString &previous, const QString &c void DataFilesPage::slotProfileChanged(int index) { - mSelector->setProfile(index); + setProfile (index, true); } void DataFilesPage::setupDataFiles() @@ -178,21 +190,92 @@ void DataFilesPage::setupDataFiles() QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/")); QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); - foreach (const QString &item, profiles) - mSelector->addProfile (item); + addProfile (item, false); - mSelector->addProfile (profile, true); + addProfile (profile, true); loadSettings(); } -QAbstractItemModel *DataFilesPage::profilesModel() const +void DataFilesPage::on_newProfileAction_triggered() { - return mSelector->profilesModel(); + TextInputDialog newDialog (tr("New Profile"), tr("Profile name:"), this); + + if (newDialog.exec() != QDialog::Accepted) + return; + + QString profile = newDialog.getText(); + + if (profile.isEmpty()) + return; + + saveSettings(); + + mSelector->clearCheckStates(); + + addProfile(profile, true); + + mSelector->setGameFile(); + + saveSettings(); + + emit signalProfileChanged (ui.profilesComboBox->findText(profile)); } -int DataFilesPage::profilesIndex() const +void DataFilesPage::addProfile (const QString &profile, bool setAsCurrent) { - return mSelector->getProfileIndex(mSelector->getProfileText()); + if (profile.isEmpty()) + return; + + if (ui.profilesComboBox->findText (profile) != -1) + return; + + ui.profilesComboBox->addItem (profile); + + if (setAsCurrent) + setProfile (ui.profilesComboBox->findText (profile), false); +} + +void DataFilesPage::on_deleteProfileAction_triggered() +{ + QString profile = ui.profilesComboBox->currentText(); + + if (profile.isEmpty()) + return; + + if (!showDeleteMessageBox (profile)) + return; + + // Remove the profile from the combobox + ui.profilesComboBox->removeItem (ui.profilesComboBox->findText (profile)); + + loadSettings(); + + checkForDefaultProfile(); +} + +void DataFilesPage::checkForDefaultProfile() +{ + //don't allow deleting "Default" profile + bool success = (ui.profilesComboBox->currentText() != "Default"); + + ui.deleteProfileAction->setEnabled (success); + ui.profilesComboBox->setEditEnabled (success); +} + +bool DataFilesPage::showDeleteMessageBox (const QString &text) +{ + QMessageBox msgBox(this); + msgBox.setWindowTitle(tr("Delete Profile")); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setStandardButtons(QMessageBox::Cancel); + msgBox.setText(tr("Are you sure you want to delete %0?").arg(text)); + + QAbstractButton *deleteButton = + msgBox.addButton(tr("Delete"), QMessageBox::ActionRole); + + msgBox.exec(); + + return (msgBox.clickedButton() == deleteButton); } diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 47e28493d0..cc054a4e47 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -1,6 +1,7 @@ #ifndef DATAFILESPAGE_H #define DATAFILESPAGE_H +#include "ui_datafilespage.h" #include #include @@ -12,7 +13,7 @@ class QMenu; class TextInputDialog; class GameSettings; class LauncherSettings; - +class ProfilesComboBox; namespace Files { struct ConfigurationManager; } namespace ContentSelectorView { class ContentSelector; } @@ -22,32 +23,37 @@ class DataFilesPage : public QWidget Q_OBJECT ContentSelectorView::ContentSelector *mSelector; + Ui::DataFilesPage ui; public: - DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent = 0); + explicit DataFilesPage (Files::ConfigurationManager &cfg, GameSettings &gameSettings, + LauncherSettings &launcherSettings, QWidget *parent = 0); - QAbstractItemModel* profilesModel() const; - int profilesIndex() const; + QAbstractItemModel* profilesModel() const + { return ui.profilesComboBox->model(); } - void writeConfig(QString profile = QString()); + int profilesIndex() const + { return ui.profilesComboBox->currentIndex(); } + + //void writeConfig(QString profile = QString()); void saveSettings(const QString &profile = ""); void loadSettings(); signals: - void signalProfileChanged(int index); + void signalProfileChanged (int index); public slots: - //void showContextMenu(const QPoint &point); - + void slotProfileChanged (int index); private slots: - void slotAddNewProfile(const QString &profile); void slotProfileChangedByUser(const QString &previous, const QString ¤t); - void slotProfileChanged(int); void slotProfileRenamed(const QString &previous, const QString ¤t); void slotProfileDeleted(const QString &item); + void on_newProfileAction_triggered(); + void on_deleteProfileAction_triggered(); + private: QMenu *mContextMenu; @@ -59,11 +65,16 @@ private: void setPluginsCheckstates(Qt::CheckState state); + void buildView(); void setupDataFiles(); void setupConfig(); void readConfig(); + void setProfile (int index, bool savePrevious); + void setProfile (const QString &previous, const QString ¤t, bool savePrevious); void removeProfile (const QString &profile); - void changeProfiles(const QString &previous, const QString ¤t, bool savePrevious = true); + bool showDeleteMessageBox (const QString &text); + void addProfile (const QString &profile, bool setAsCurrent); + void checkForDefaultProfile(); }; #endif diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index fe9ca141ec..e514755fea 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -485,7 +485,7 @@ bool MainDialog::setupGameSettings() foreach (const QString path, mGameSettings.getDataDirs()) { QDir dir(path); QStringList filters; - filters << "*.esp" << "*.esm"; + filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon"; if (!dir.entryList(filters).isEmpty()) dataDirs.append(path); diff --git a/components/contentselector/view/comboboxlineedit.cpp b/apps/launcher/utils/lineedit.cpp similarity index 58% rename from components/contentselector/view/comboboxlineedit.cpp rename to apps/launcher/utils/lineedit.cpp index df647a4a09..3487075808 100644 --- a/components/contentselector/view/comboboxlineedit.cpp +++ b/apps/launcher/utils/lineedit.cpp @@ -1,10 +1,12 @@ -#include -#include +#include "lineedit.hpp" -#include "comboboxlineedit.hpp" - -ContentSelectorView::ComboBoxLineEdit::ComboBoxLineEdit(QWidget *parent) +LineEdit::LineEdit(QWidget *parent) : QLineEdit(parent) +{ + setupClearButton(); +} + +void LineEdit::setupClearButton() { mClearButton = new QToolButton(this); QPixmap pixmap(":images/clear.png"); @@ -15,13 +17,9 @@ ContentSelectorView::ComboBoxLineEdit::ComboBoxLineEdit(QWidget *parent) mClearButton->hide(); connect(mClearButton, SIGNAL(clicked()), this, SLOT(clear())); connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateClearButton(const QString&))); - int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); - - setObjectName(QString("ComboBoxLineEdit")); - setStyleSheet(QString("ComboBoxLineEdit { background-color: transparent; padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1)); } -void ContentSelectorView::ComboBoxLineEdit::resizeEvent(QResizeEvent *) +void LineEdit::resizeEvent(QResizeEvent *) { QSize sz = mClearButton->sizeHint(); int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); @@ -29,7 +27,7 @@ void ContentSelectorView::ComboBoxLineEdit::resizeEvent(QResizeEvent *) (rect().bottom() + 1 - sz.height())/2); } -void ContentSelectorView::ComboBoxLineEdit::updateClearButton(const QString& text) +void LineEdit::updateClearButton(const QString& text) { mClearButton->setVisible(!text.isEmpty()); } diff --git a/components/contentselector/view/lineedit.hpp b/apps/launcher/utils/lineedit.hpp similarity index 54% rename from components/contentselector/view/lineedit.hpp rename to apps/launcher/utils/lineedit.hpp index ab1c37203a..2dd7da32bd 100644 --- a/components/contentselector/view/lineedit.hpp +++ b/apps/launcher/utils/lineedit.hpp @@ -11,29 +11,32 @@ #define LINEEDIT_H #include +#include +#include +#include class QToolButton; -namespace ContentSelectorView +class LineEdit : public QLineEdit { - class LineEdit : public QLineEdit - { - Q_OBJECT + Q_OBJECT - QString mPlaceholderText; + QString mPlaceholderText; - public: - LineEdit(QWidget *parent = 0); +public: + LineEdit(QWidget *parent = 0); - protected: - void resizeEvent(QResizeEvent *); +protected: + void resizeEvent(QResizeEvent *); - private slots: - void updateClearButton(const QString &text); +private slots: + void updateClearButton(const QString &text); + +protected: + QToolButton *mClearButton; + + void setupClearButton(); +}; - private: - QToolButton *mClearButton; - }; -} #endif // LIENEDIT_H diff --git a/components/contentselector/view/profilescombobox.cpp b/apps/launcher/utils/profilescombobox.cpp similarity index 60% rename from components/contentselector/view/profilescombobox.cpp rename to apps/launcher/utils/profilescombobox.cpp index 0e9905df45..c143307249 100644 --- a/components/contentselector/view/profilescombobox.cpp +++ b/apps/launcher/utils/profilescombobox.cpp @@ -5,23 +5,17 @@ #include #include "profilescombobox.hpp" -#include "comboboxlineedit.hpp" -ContentSelectorView::ProfilesComboBox::ProfilesComboBox(QWidget *parent) : - QComboBox(parent) +ProfilesComboBox::ProfilesComboBox(QWidget *parent) : + ContentSelectorView::ComboBox(parent) { - mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore - setEditEnabled(true); - setValidator(mValidator); - setCompleter(0); - connect(this, SIGNAL(activated(int)), this, SLOT(slotIndexChangedByUser(int))); setInsertPolicy(QComboBox::NoInsert); } -void ContentSelectorView::ProfilesComboBox::setEditEnabled(bool editable) +void ProfilesComboBox::setEditEnabled(bool editable) { if (isEditable() == editable) return; @@ -37,6 +31,7 @@ void ContentSelectorView::ProfilesComboBox::setEditEnabled(bool editable) setValidator(mValidator); ComboBoxLineEdit *edit = new ComboBoxLineEdit(this); + setLineEdit(edit); setCompleter(0); @@ -50,7 +45,7 @@ void ContentSelectorView::ProfilesComboBox::setEditEnabled(bool editable) SIGNAL (signalProfileTextChanged (QString))); } -void ContentSelectorView::ProfilesComboBox::slotTextChanged(const QString &text) +void ProfilesComboBox::slotTextChanged(const QString &text) { QPalette *palette = new QPalette(); palette->setColor(QPalette::Text,Qt::red); @@ -64,7 +59,7 @@ void ContentSelectorView::ProfilesComboBox::slotTextChanged(const QString &text) } } -void ContentSelectorView::ProfilesComboBox::slotEditingFinished() +void ProfilesComboBox::slotEditingFinished() { QString current = currentText(); QString previous = itemText(currentIndex()); @@ -85,7 +80,7 @@ void ContentSelectorView::ProfilesComboBox::slotEditingFinished() emit(profileRenamed(previous, current)); } -void ContentSelectorView::ProfilesComboBox::slotIndexChangedByUser(int index) +void ProfilesComboBox::slotIndexChangedByUser(int index) { if (index == -1) return; @@ -94,23 +89,11 @@ void ContentSelectorView::ProfilesComboBox::slotIndexChangedByUser(int index) mOldProfile = currentText(); } -void ContentSelectorView::ProfilesComboBox::paintEvent(QPaintEvent *) +ProfilesComboBox::ComboBoxLineEdit::ComboBoxLineEdit (QWidget *parent) + : LineEdit (parent) { - QStylePainter painter(this); - painter.setPen(palette().color(QPalette::Text)); + int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); - // draw the combobox frame, focusrect and selected etc. - QStyleOptionComboBox opt; - initStyleOption(&opt); - painter.drawComplexControl(QStyle::CC_ComboBox, opt); - - // draw the icon and text - if (!opt.editable && currentIndex() == -1) // <<< we adjust the text displayed when nothing is selected - opt.currentText = mPlaceholderText; - painter.drawControl(QStyle::CE_ComboBoxLabel, opt); -} - -void ContentSelectorView::ProfilesComboBox::setPlaceholderText(const QString &text) -{ - mPlaceholderText = text; + setObjectName(QString("ComboBoxLineEdit")); + setStyleSheet(QString("ComboBoxLineEdit { background-color: transparent; padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1)); } diff --git a/apps/launcher/utils/profilescombobox.hpp b/apps/launcher/utils/profilescombobox.hpp new file mode 100644 index 0000000000..1e27f66a9c --- /dev/null +++ b/apps/launcher/utils/profilescombobox.hpp @@ -0,0 +1,40 @@ +#ifndef PROFILESCOMBOBOX_HPP +#define PROFILESCOMBOBOX_HPP + +#include "components/contentselector/view/combobox.hpp" +#include "lineedit.hpp" + +class QString; + +class ProfilesComboBox : public ContentSelectorView::ComboBox +{ + Q_OBJECT + +public: + class ComboBoxLineEdit : public LineEdit + { + public: + explicit ComboBoxLineEdit (QWidget *parent = 0); + }; + +public: + + explicit ProfilesComboBox(QWidget *parent = 0); + void setEditEnabled(bool editable); + +signals: + void signalProfileTextChanged(const QString &item); + void signalProfileChanged(const QString &previous, const QString ¤t); + void signalProfileChanged(int index); + void profileRenamed(const QString &oldName, const QString &newName); + +private slots: + + void slotEditingFinished(); + void slotIndexChangedByUser(int index); + void slotTextChanged(const QString &text); + +private: + QString mOldProfile; +}; +#endif // PROFILESCOMBOBOX_HPP diff --git a/components/contentselector/view/textinputdialog.cpp b/apps/launcher/utils/textinputdialog.cpp similarity index 77% rename from components/contentselector/view/textinputdialog.cpp rename to apps/launcher/utils/textinputdialog.cpp index 6bb92f113e..9957e7dda8 100644 --- a/components/contentselector/view/textinputdialog.cpp +++ b/apps/launcher/utils/textinputdialog.cpp @@ -7,8 +7,6 @@ #include #include -#include - TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) : QDialog(parent) { @@ -20,7 +18,7 @@ TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWid // Line edit QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore - mLineEdit = new ContentSelectorView::LineEdit(this); + mLineEdit = new DialogLineEdit(this); mLineEdit->setValidator(validator); mLineEdit->setCompleter(0); @@ -74,3 +72,16 @@ void TextInputDialog::slotUpdateOkButton(QString text) mLineEdit->setPalette(*palette); } } + +TextInputDialog::DialogLineEdit::DialogLineEdit (QWidget *parent) : + LineEdit (parent) +{ + int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + + setObjectName(QString("LineEdit")); + setStyleSheet(QString("LineEdit { padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1)); + QSize msz = minimumSizeHint(); + setMinimumSize(qMax(msz.width(), mClearButton->sizeHint().height() + frameWidth * 2 + 2), + qMax(msz.height(), mClearButton->sizeHint().height() + frameWidth * 2 + 2)); + +} diff --git a/components/contentselector/view/textinputdialog.hpp b/apps/launcher/utils/textinputdialog.hpp similarity index 66% rename from components/contentselector/view/textinputdialog.hpp rename to apps/launcher/utils/textinputdialog.hpp index a0b7e350a5..148bbd1522 100644 --- a/components/contentselector/view/textinputdialog.hpp +++ b/apps/launcher/utils/textinputdialog.hpp @@ -3,23 +3,30 @@ #include +#include "lineedit.hpp" + class QDialogButtonBox; -namespace ContentSelectorView { - class LineEdit; -} - +class LineEdit; class TextInputDialog : public QDialog { Q_OBJECT - ContentSelectorView::LineEdit *mLineEdit; + class DialogLineEdit : public LineEdit + { + public: + explicit DialogLineEdit (QWidget *parent = 0); + }; + + DialogLineEdit *mLineEdit; QDialogButtonBox *mButtonBox; public: explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0); + ~TextInputDialog () {} + QString getText() const; int exec(); diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 9ae12c7a73..bc78352d4e 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -44,6 +44,7 @@ opencs_units_noqt (model/tools opencs_units (view/doc viewmanager view operations operation subview startup filedialog newgame + filewidget adjusterwidget ) @@ -122,11 +123,13 @@ opencs_units (view/filter set (OPENCS_US ) -set (OPENCS_RES ../../files/opencs/resources.qrc - ../../files/launcher/launcher.qrc +set (OPENCS_RES ${CMAKE_SOURCE_DIR}/files/opencs/resources.qrc + ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc ) -set (OPENCS_UI ../../files/ui/datafilespage.ui +set (OPENCS_UI + ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui + ${CMAKE_SOURCE_DIR}/files/ui/filedialog.ui ) source_group (opencs FILES ${OPENCS_SRC} ${OPENCS_HDR}) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index ae10ec6422..e78357bc54 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -109,15 +109,13 @@ void CS::Editor::createGame() void CS::Editor::createAddon() { mStartup.hide(); - - mFileDialog.newFile(); + mFileDialog.showDialog (CSVDoc::FileDialog::DialogType_New); } void CS::Editor::loadDocument() { mStartup.hide(); - - mFileDialog.openFile(); + mFileDialog.showDialog (CSVDoc::FileDialog::DialogType_Open); } void CS::Editor::openFiles() diff --git a/components/contentselector/view/adjusterwidget.cpp b/apps/opencs/view/doc/adjusterwidget.cpp similarity index 100% rename from components/contentselector/view/adjusterwidget.cpp rename to apps/opencs/view/doc/adjusterwidget.cpp diff --git a/components/contentselector/view/adjusterwidget.hpp b/apps/opencs/view/doc/adjusterwidget.hpp similarity index 100% rename from components/contentselector/view/adjusterwidget.hpp rename to apps/opencs/view/doc/adjusterwidget.hpp diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 2017ab1036..49e90ec813 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -12,81 +12,120 @@ #include #include -#include -#include +#include "components/contentselector/model/esmfile.hpp" #include "components/contentselector/view/contentselector.hpp" +#include "filewidget.hpp" +#include "adjusterwidget.hpp" #include CSVDoc::FileDialog::FileDialog(QWidget *parent) : - QDialog(parent), - mOpenFileFlags (ContentSelectorView::Flag_Content | ContentSelectorView::Flag_LoadAddon), - mNewFileFlags (ContentSelectorView::Flag_Content | ContentSelectorView::Flag_NewAddon) - + QDialog(parent), mSelector (0) { + ui.setupUi (this); resize(400, 400); + + setObjectName ("FileDialog"); + mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); } void CSVDoc::FileDialog::addFiles(const QString &path) { - ContentSelectorView::ContentSelector::addFiles(path); -} - -QString CSVDoc::FileDialog::filename() -{ - return ContentSelectorView::ContentSelector::instance().projectFilename(); + mSelector->addFiles(path); } QStringList CSVDoc::FileDialog::selectedFilePaths() { QStringList filePaths; - foreach (ContentSelectorModel::EsmFile *file, ContentSelectorView::ContentSelector:: - instance().selectedFiles() ) - { + foreach (ContentSelectorModel::EsmFile *file, mSelector->selectedFiles() ) filePaths.append(file->path()); - } + return filePaths; } -void CSVDoc::FileDialog::showDialog() +void CSVDoc::FileDialog::showDialog(DialogType dialogType) { + mDialogType = dialogType; + + switch (mDialogType) + { + case DialogType_New: + buildNewFileView(); + break; + + case DialogType_Open: + buildOpenFileView(); + break; + default: + break; + } + show(); raise(); activateWindow(); } -void CSVDoc::FileDialog::openFile() +void CSVDoc::FileDialog::buildNewFileView() { - setWindowTitle(tr("Open")); + setWindowTitle(tr("Create a new addon")); - ContentSelectorView::ContentSelector::configure(this, mOpenFileFlags); + QPushButton* createButton = ui.projectButtonBox->button (QDialogButtonBox::Ok); + createButton->setText ("Create"); + createButton->setEnabled (false); - connect (&ContentSelectorView::ContentSelector::instance(), - SIGNAL (accepted()), this, SIGNAL (openFiles())); + mFileWidget = new FileWidget (this); - connect (&ContentSelectorView::ContentSelector::instance(), - SIGNAL (rejected()), this, SLOT (slotRejected())); + mFileWidget->setType (true); + mFileWidget->extensionLabelIsVisible(true); - showDialog(); + ui.projectGroupBoxLayout->insertWidget (0, mFileWidget); + + connect (mFileWidget, SIGNAL (nameChanged(const QString &, bool)), + this, SLOT (slotUpdateCreateButton(const QString &, bool))); + + connect (mSelector, SIGNAL (signalCurrentGamefileIndexChanged (int)), + this, SLOT (slotUpdateCreateButton (int))); + + connect (ui.projectButtonBox, SIGNAL (accepted()), this, SIGNAL (createNewFile())); + + connect (ui.projectButtonBox, SIGNAL (rejected()), this, SLOT (slotRejected())); } -void CSVDoc::FileDialog::newFile() +void CSVDoc::FileDialog::buildOpenFileView() { - setWindowTitle(tr("New")); + setWindowTitle(tr("Open")); + ui.projectGroupBox->setTitle (QString("")); - ContentSelectorView::ContentSelector::configure(this, mNewFileFlags); - - connect (&ContentSelectorView::ContentSelector::instance(), - SIGNAL (accepted()), this, SIGNAL (createNewFile())); - - connect (&ContentSelectorView::ContentSelector::instance(), - SIGNAL (rejected()), this, SLOT (slotRejected())); - - showDialog(); + connect (ui.projectButtonBox, SIGNAL (accepted()), this, SIGNAL (openFiles())); + connect (ui.projectButtonBox, SIGNAL (rejected()), this , SIGNAL (rejected())); } void CSVDoc::FileDialog::slotRejected() { close(); } + +void CSVDoc::FileDialog::slotUpdateCreateButton (int) +{ + slotUpdateCreateButton (mFileWidget->getName(), true); +} + +void CSVDoc::FileDialog::slotUpdateCreateButton(const QString &name, bool) +{ + if (!(mDialogType == DialogType_New)) + return; + + bool success = (!name.isEmpty() && mSelector->selectedFiles().size() > 0); + + ui.projectButtonBox->button (QDialogButtonBox::Ok)->setEnabled (success); +} + +QString CSVDoc::FileDialog::filename() const +{ + if (mDialogType == DialogType_New) + return mFileWidget->getName(); + + return QString (""); +} + diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index acc35189dc..78883791e4 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -3,49 +3,54 @@ #include #include -#include "../../../../components/contentselector/view/contentselector.hpp" -class QDialogButtonBox; -class QSortFilterProxyModel; -class QAbstractItemModel; -class QPushButton; -class QStringList; -class QString; -class QMenu; -class QLabel; +#include "ui_filedialog.h" class DataFilesModel; class PluginsProxyModel; - - namespace ContentSelectorView { - class LineEdit; + class ContentSelector; } namespace CSVDoc { + class FileWidget; + class FileDialog : public QDialog { Q_OBJECT - unsigned char mOpenFileFlags; - unsigned char mNewFileFlags; + public: + + enum DialogType + { + DialogType_New, + DialogType_Open + }; + + private: + + ContentSelectorView::ContentSelector *mSelector; + Ui::FileDialog ui; + DialogType mDialogType; + FileWidget *mFileWidget; public: - explicit FileDialog(QWidget *parent = 0); - void openFile(); - void newFile(); + explicit FileDialog(QWidget *parent = 0); + void showDialog (DialogType dialogType); + void addFiles (const QString &path); - QString filename(); + QString filename() const; QStringList selectedFilePaths(); private: - void showDialog(); + void buildNewFileView(); + void buildOpenFileView(); signals: @@ -58,6 +63,9 @@ namespace CSVDoc private slots: + void slotUpdateCreateButton (int); + void slotUpdateCreateButton (const QString &, bool); + }; } #endif // FILEDIALOG_HPP diff --git a/components/contentselector/view/filewidget.cpp b/apps/opencs/view/doc/filewidget.cpp similarity index 100% rename from components/contentselector/view/filewidget.cpp rename to apps/opencs/view/doc/filewidget.cpp diff --git a/components/contentselector/view/filewidget.hpp b/apps/opencs/view/doc/filewidget.hpp similarity index 100% rename from components/contentselector/view/filewidget.hpp rename to apps/opencs/view/doc/filewidget.hpp diff --git a/apps/opencs/view/doc/newgame.cpp b/apps/opencs/view/doc/newgame.cpp index 265b983056..98681c499d 100644 --- a/apps/opencs/view/doc/newgame.cpp +++ b/apps/opencs/view/doc/newgame.cpp @@ -7,8 +7,8 @@ #include #include -#include "components/contentselector/view/filewidget.hpp" -#include "components/contentselector/view/adjusterwidget.hpp" +#include "filewidget.hpp" +#include "adjusterwidget.hpp" CSVDoc::NewGameDialogue::NewGameDialogue() { diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 103c6f4126..acb70b04db 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -74,7 +74,7 @@ add_component_dir (loadinglistener loadinglistener ) -set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/datafilespage.ui +set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui ) find_package(Qt4 COMPONENTS QtCore QtGui) @@ -82,11 +82,7 @@ if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) add_component_qt_dir (contentselector model/modelitem model/esmfile model/naturalsort model/contentmodel - view/profilescombobox view/comboboxlineedit - view/lineedit view/contentselector - view/filewidget view/adjusterwidget - view/textinputdialog - + view/combobox view/contentselector ) include(${QT_USE_FILE}) diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 8bb052d3dc..accc149cbb 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -399,6 +399,10 @@ void ContentSelectorModel::ContentModel::addFile(EsmFile *file) beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); mFiles.append(file); endInsertRows(); + + QModelIndex idx = index (mFiles.size() - 2, 0, QModelIndex()); + + emit dataChanged (idx, idx); } void ContentSelectorModel::ContentModel::addFiles(const QString &path) @@ -466,6 +470,7 @@ void ContentSelectorModel::ContentModel::sortFiles() //iterate each file, obtaining a reference to it's gamefiles list for (int i = 0; i < fileCount; i++) { + QModelIndex idx1 = index (i, 0, QModelIndex()); const QStringList &gamefiles = mFiles.at(i)->gameFiles(); //iterate each file after the current file, verifying that none of it's //dependencies appear. @@ -474,6 +479,11 @@ void ContentSelectorModel::ContentModel::sortFiles() if (gamefiles.contains(mFiles.at(j)->fileName())) { mFiles.move(j, i); + + QModelIndex idx2 = index (j, 0, QModelIndex()); + + emit dataChanged (idx1, idx2); + movedFiles = true; } } diff --git a/components/contentselector/view/combobox.cpp b/components/contentselector/view/combobox.cpp new file mode 100644 index 0000000000..1d773b62dd --- /dev/null +++ b/components/contentselector/view/combobox.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include + +#include "combobox.hpp" + +ContentSelectorView::ComboBox::ComboBox(QWidget *parent) : + QComboBox(parent) +{ + mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore + setValidator(mValidator); + setCompleter(0); + setEnabled (true); + + setInsertPolicy(QComboBox::NoInsert); +} + +void ContentSelectorView::ComboBox::paintEvent(QPaintEvent *) +{ + QStylePainter painter(this); + painter.setPen(palette().color(QPalette::Text)); + + // draw the combobox frame, focusrect and selected etc. + QStyleOptionComboBox opt; + initStyleOption(&opt); + painter.drawComplexControl(QStyle::CC_ComboBox, opt); + + // draw the icon and text + if (!opt.editable && currentIndex() == -1) // <<< we adjust the text displayed when nothing is selected + opt.currentText = mPlaceholderText; + painter.drawControl(QStyle::CE_ComboBoxLabel, opt); +} + +void ContentSelectorView::ComboBox::setPlaceholderText(const QString &text) +{ + mPlaceholderText = text; +} diff --git a/components/contentselector/view/combobox.hpp b/components/contentselector/view/combobox.hpp new file mode 100644 index 0000000000..e3888af2c7 --- /dev/null +++ b/components/contentselector/view/combobox.hpp @@ -0,0 +1,30 @@ +#ifndef COMBOBOX_HPP +#define COMBOBOX_HPP + +#include +#include + +class QString; +class QRegExpValidator; + +namespace ContentSelectorView +{ + class ComboBox : public QComboBox + { + Q_OBJECT + + public: + explicit ComboBox (QWidget *parent = 0); + + void setPlaceholderText(const QString &text); + + private: + QString mPlaceholderText; + + protected: + void paintEvent(QPaintEvent *); + QRegExpValidator *mValidator; + }; +} + +#endif // COMBOBOX_HPP diff --git a/components/contentselector/view/comboboxlineedit.hpp b/components/contentselector/view/comboboxlineedit.hpp deleted file mode 100644 index 1aef2f57b6..0000000000 --- a/components/contentselector/view/comboboxlineedit.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/**************************************************************************** -** -** Copyright (c) 2007 Trolltech ASA -** -** Use, modification and distribution is allowed without limitation, -** warranty, liability or support of any kind. -** -****************************************************************************/ - -#ifndef LINEEDIT_H -#define LINEEDIT_H - -#include - -class QToolButton; - -namespace ContentSelectorView -{ - class ComboBoxLineEdit : public QLineEdit - { - Q_OBJECT - - public: - ComboBoxLineEdit(QWidget *parent = 0); - - protected: - void resizeEvent(QResizeEvent *); - - private slots: - void updateClearButton(const QString &text); - - private: - QToolButton *mClearButton; - }; -} -#endif // LIENEDIT_H - diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 6965c948ec..7a7e1fd9d5 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -1,7 +1,6 @@ #include "contentselector.hpp" #include "../model/esmfile.hpp" -#include "lineedit.hpp" #include @@ -11,45 +10,16 @@ #include #include -#include "filewidget.hpp" -#include "adjusterwidget.hpp" -#include "textinputdialog.hpp" - #include -ContentSelectorView::ContentSelector *ContentSelectorView::ContentSelector::mInstance = 0; -QStringList ContentSelectorView::ContentSelector::mFilePaths; - -void ContentSelectorView::ContentSelector::configure(QWidget *subject, unsigned char flags) +ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) : + QObject(parent) { - assert(!mInstance); - mInstance = new ContentSelector (subject, flags); -} - -ContentSelectorView::ContentSelector& ContentSelectorView::ContentSelector::instance() -{ - - assert(mInstance); - return *mInstance; -} - -ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent, unsigned char flags) : - QWidget(parent), mFlags (flags), - mAdjusterWidget (0), mFileWidget (0), - mIgnoreProfileSignal (false) -{ - - ui.setupUi (this); - - parent->setLayout(new QGridLayout()); - parent->layout()->addWidget(this); + ui.setupUi (parent); buildContentModel(); buildGameFileView(); buildAddonView(); - buildNewAddonView(); - buildLoadAddonView(); - buildProfilesView(); /* //mContentModel->sort(3); // Sort by date accessed @@ -57,16 +27,8 @@ ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent, unsigned */ } -bool ContentSelectorView::ContentSelector::isFlagged(SelectorFlags flag) const -{ - return (mFlags & flag); -} - void ContentSelectorView::ContentSelector::buildContentModel() { - if (!isFlagged (Flag_Content)) - return; - mContentModel = new ContentSelectorModel::ContentModel(); if (mFilePaths.size()>0) @@ -83,12 +45,6 @@ void ContentSelectorView::ContentSelector::buildContentModel() void ContentSelectorView::ContentSelector::buildGameFileView() { - if (!isFlagged (Flag_Content)) - { - ui.gameFileView->setVisible(false); - return; - } - ui.gameFileView->setVisible (true); mGameFileProxyModel = new QSortFilterProxyModel(this); @@ -99,19 +55,17 @@ void ContentSelectorView::ContentSelector::buildGameFileView() ui.gameFileView->setPlaceholderText(QString("Select a game file...")); ui.gameFileView->setModel(mGameFileProxyModel); - connect (ui.gameFileView, SIGNAL(currentIndexChanged(int)), this, SLOT (slotCurrentGameFileIndexChanged(int))); + connect (ui.gameFileView, SIGNAL(currentIndexChanged(int)), + this, SLOT (slotCurrentGameFileIndexChanged(int))); + + connect (ui.gameFileView, SIGNAL (currentIndexChanged (int)), + this, SIGNAL (signalCurrentGamefileIndexChanged (int))); ui.gameFileView->setCurrentIndex(-1); } void ContentSelectorView::ContentSelector::buildAddonView() { - if (!isFlagged (Flag_Content)) - { - ui.addonView->setVisible(false); - return; - } - ui.addonView->setVisible (true); mAddonProxyModel = new QSortFilterProxyModel(this); @@ -123,91 +77,9 @@ void ContentSelectorView::ContentSelector::buildAddonView() ui.addonView->setModel(mAddonProxyModel); connect(ui.addonView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotAddonTableItemClicked(const QModelIndex &))); -} -void ContentSelectorView::ContentSelector::buildProfilesView() -{ - if (!isFlagged (Flag_Profile)) - { - ui.profileGroupBox->setVisible(false); - return; - } - - ui.profileGroupBox->setVisible (true); - - // Add the actions to the toolbuttons - ui.newProfileButton->setDefaultAction (ui.newProfileAction); - ui.deleteProfileButton->setDefaultAction (ui.deleteProfileAction); - - //enable ui elements - ui.profilesComboBox->addItem ("Default"); - ui.profilesComboBox->setPlaceholderText (QString("Select a profile...")); - - if (!ui.profilesComboBox->isEnabled()) - ui.profilesComboBox->setEnabled(true); - - if (!ui.deleteProfileAction->isEnabled()) - ui.deleteProfileAction->setEnabled(true); - - //establish connections - connect (ui.profilesComboBox, SIGNAL (currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); - connect (ui.profilesComboBox, SIGNAL (profileRenamed(QString,QString)), this, SIGNAL(signalProfileRenamed(QString,QString))); - connect (ui.profilesComboBox, SIGNAL (signalProfileChanged(QString,QString)), this, SIGNAL(signalProfileChangedByUser(QString,QString))); - connect (ui.profilesComboBox, SIGNAL (signalProfileTextChanged(QString)), this, SLOT (slotProfileTextChanged (QString))); - - ui.profileGroupBox->setVisible (true); - ui.projectButtonBox->setVisible (false); -} - -void ContentSelectorView::ContentSelector::buildLoadAddonView() -{ - if (!isFlagged (Flag_LoadAddon)) - { - if (!isFlagged (Flag_NewAddon)) - ui.projectGroupBox->setVisible (false); - - return; - } - - ui.projectGroupBox->setVisible (true); - ui.projectCreateButton->setVisible (false); - ui.projectGroupBox->setTitle (""); - - connect(ui.projectButtonBox, SIGNAL(accepted()), this, SIGNAL(accepted())); - connect(ui.projectButtonBox, SIGNAL(rejected()), this, SIGNAL(rejected())); -} - -void ContentSelectorView::ContentSelector::buildNewAddonView() -{ - if (!isFlagged (Flag_NewAddon)) - { - if (!isFlagged (Flag_LoadAddon)) - ui.projectGroupBox->setVisible (false); - - return; - } - - ui.projectGroupBox->setVisible (true); - - mFileWidget = new CSVDoc::FileWidget (this); - mAdjusterWidget = new CSVDoc::AdjusterWidget (this); - - mFileWidget->setType(true); - mFileWidget->extensionLabelIsVisible(false); - - ui.fileWidgetFrame->layout()->addWidget(mFileWidget); - ui.adjusterWidgetFrame->layout()->addWidget(mAdjusterWidget); - - ui.projectButtonBox->setStandardButtons(QDialogButtonBox::Cancel); - ui.projectButtonBox->addButton(ui.projectCreateButton, QDialogButtonBox::ActionRole); - - connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)), - mAdjusterWidget, SLOT (setName (const QString&, bool))); - - connect (mAdjusterWidget, SIGNAL (stateChanged(bool)), this, SLOT (slotUpdateCreateButton(bool))); - - connect(ui.projectCreateButton, SIGNAL(clicked()), this, SIGNAL(accepted())); - connect(ui.projectButtonBox, SIGNAL(rejected()), this, SIGNAL(rejected())); + for (int i = 0; i < mAddonProxyModel->rowCount(); ++i) + qDebug() << mAddonProxyModel->data(mAddonProxyModel->index(i,0,QModelIndex())); } void ContentSelectorView::ContentSelector::setGameFile(const QString &filename) @@ -239,16 +111,6 @@ void ContentSelectorView::ContentSelector::setCheckStates(const QStringList &lis mContentModel->setCheckState(file, Qt::Checked); } -QString ContentSelectorView::ContentSelector::projectFilename() const -{ - QString filepath = ""; - - if (mAdjusterWidget) - filepath = QString::fromAscii(mAdjusterWidget->getPath().c_str()); - - return filepath; -} - ContentSelectorModel::ContentFileList ContentSelectorView::ContentSelector::selectedFiles() const { @@ -261,65 +123,10 @@ ContentSelectorModel::ContentFileList void ContentSelectorView::ContentSelector::addFiles(const QString &path) { - // if the model hasn't been instantiated, queue the path - if (!mInstance) - mFilePaths.append(path); - else - { - mInstance->mContentModel->addFiles(path); - mInstance->ui.gameFileView->setCurrentIndex(-1); - } -} + mContentModel->addFiles(path); -int ContentSelectorView::ContentSelector::getProfileIndex ( const QString &item) const -{ - return ui.profilesComboBox->findText (item); -} - -void ContentSelectorView::ContentSelector::addProfile (const QString &item, bool setAsCurrent) -{ - if (item.isEmpty()) - return; - - QString previous = ui.profilesComboBox->currentText(); - - if (ui.profilesComboBox->findText(item) == -1) - ui.profilesComboBox->addItem(item); - - if (setAsCurrent) - setProfile (ui.profilesComboBox->findText(item)); -} - -void ContentSelectorView::ContentSelector::setProfile(int index) -{ - //programmatic change requires second call to non-signalized "slot" since no signal responses - //occur for programmatic changes to the profilesComboBox. - if (index >= -1 && index < ui.profilesComboBox->count()) - { - QString previous = ui.profilesComboBox->itemText(ui.profilesComboBox->currentIndex()); - QString current = ui.profilesComboBox->itemText(index); - - ui.profilesComboBox->setCurrentIndex(index); - } -} - -QString ContentSelectorView::ContentSelector::getProfileText() const -{ - return ui.profilesComboBox->currentText(); -} - -QAbstractItemModel *ContentSelectorView::ContentSelector::profilesModel() const -{ - return ui.profilesComboBox->model(); -} - -void ContentSelectorView::ContentSelector::slotCurrentProfileIndexChanged(int index) -{ - //don't allow deleting "Default" profile - bool success = (ui.profilesComboBox->itemText(index) != "Default"); - - ui.deleteProfileAction->setEnabled(success); - ui.profilesComboBox->setEditEnabled(success); + if (ui.gameFileView->currentIndex() != -1) + ui.gameFileView->setCurrentIndex(-1); } void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int index) @@ -341,16 +148,6 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i if (proxy) proxy->setDynamicSortFilter(true); - - slotUpdateCreateButton(true); -} - -void ContentSelectorView::ContentSelector::slotProfileTextChanged(const QString &text) -{ - QPushButton *opnBtn = ui.projectButtonBox->button(QDialogButtonBox::Open); - - if (opnBtn->isEnabled()) - opnBtn->setEnabled (false); } void ContentSelectorView::ContentSelector::slotAddonTableItemClicked(const QModelIndex &index) @@ -362,53 +159,3 @@ void ContentSelectorView::ContentSelector::slotAddonTableItemClicked(const QMode else model->setData(index, Qt::Unchecked, Qt::CheckStateRole); } - -void ContentSelectorView::ContentSelector::slotUpdateCreateButton(bool) -{ - //enable only if a game file is selected and the adjuster widget is non-empty - bool validGameFile = (ui.gameFileView->currentIndex() != -1); - bool validFilename = false; - - if (mAdjusterWidget) - validFilename = mAdjusterWidget->isValid(); - - ui.projectCreateButton->setEnabled(validGameFile && validFilename); -} - -void ContentSelectorView::ContentSelector::on_newProfileAction_triggered() -{ - TextInputDialog newDialog (tr("New Profile"), tr("Profile name:"), this); - - if (newDialog.exec() == QDialog::Accepted) - emit signalAddNewProfile(newDialog.getText()); -} - -void ContentSelectorView::ContentSelector::on_deleteProfileAction_triggered() -{ - QString profile = ui.profilesComboBox->currentText(); - - if (profile.isEmpty()) - return; - - QMessageBox msgBox(this); - msgBox.setWindowTitle(tr("Delete Profile")); - msgBox.setIcon(QMessageBox::Warning); - msgBox.setStandardButtons(QMessageBox::Cancel); - msgBox.setText(tr("Are you sure you want to delete %0?").arg(profile)); - - QAbstractButton *deleteButton = - msgBox.addButton(tr("Delete"), QMessageBox::ActionRole); - - msgBox.exec(); - - if (msgBox.clickedButton() != deleteButton) - return; - - // Remove the profile from the combobox - ui.profilesComboBox->removeItem(ui.profilesComboBox->findText(profile)); - - //signal for removal from model - emit signalProfileDeleted (profile); - - slotCurrentProfileIndexChanged(ui.profilesComboBox->currentIndex()); -} diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index 64b9b37320..0882abfb77 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -3,38 +3,19 @@ #include -#include "ui_datafilespage.h" +#include "ui_contentselector.h" #include "../model/contentmodel.hpp" class QSortFilterProxyModel; -namespace CSVDoc -{ - class FileWidget; - class AdjusterWidget; -} namespace ContentSelectorView { - enum SelectorFlags - { - Flag_Content = 0x01, // gamefile combobox & addon list view - Flag_NewAddon = 0x02, // enable project button box (Create/Cancel) and file/adjuster widgets - Flag_LoadAddon = 0x04, // enable project button box (Open/Cancel) - Flag_Profile = 0x08 // enable profile combo box - }; - class ContentSelector : public QWidget + class ContentSelector : public QObject { Q_OBJECT - unsigned char mFlags; - bool mIgnoreProfileSignal; - - static ContentSelector *mInstance; - static QStringList mFilePaths; - - CSVDoc::FileWidget *mFileWidget; - CSVDoc::AdjusterWidget *mAdjusterWidget; + QStringList mFilePaths; protected: @@ -44,64 +25,39 @@ namespace ContentSelectorView public: - explicit ContentSelector(QWidget *parent = 0, unsigned char flags = Flag_Content); + explicit ContentSelector(QWidget *parent = 0); - static void configure(QWidget *subject, unsigned char flags = Flag_Content); - static ContentSelector &instance(); - static void addFiles(const QString &path); + void addFiles(const QString &path); void clearCheckStates(); void setCheckStates (const QStringList &list); - ContentSelectorModel::ContentFileList *CheckedItems(); - QString projectFilename() const; ContentSelectorModel::ContentFileList selectedFiles() const; - QAbstractItemModel *profilesModel() const; - void setGameFile (const QString &filename = ""); - void addProfile (const QString &item, bool setAsCurrent = false); - void setProfile (int index); - int getProfileIndex (const QString &item) const; - QString getProfileText() const; + void setGameFile (const QString &filename = QString("")); + + bool isGamefileSelected() const + { return ui.gameFileView->currentIndex() != -1; } + + QWidget *uiWidget() const + { return ui.contentGroupBox; } private: - Ui::DataFilesPage ui; + Ui::ContentSelector ui; void buildContentModel(); void buildGameFileView(); void buildAddonView(); - void buildProfilesView(); - void buildNewAddonView(); - void buildLoadAddonView(); - - bool isFlagged(SelectorFlags flag) const; signals: - - void accepted(); - void rejected(); - - void signalCreateButtonClicked(); - - void signalProfileRenamed(QString,QString); - void signalProfileChangedByUser(QString,QString); - void signalProfileDeleted(QString); - void signalAddNewProfile(QString); + void signalCurrentGamefileIndexChanged (int); private slots: - void slotProfileTextChanged (const QString &text); - void slotCurrentProfileIndexChanged(int index); void slotCurrentGameFileIndexChanged(int index); void slotAddonTableItemClicked(const QModelIndex &index); - - void slotUpdateCreateButton (bool); - - // Action slots - void on_newProfileAction_triggered(); - void on_deleteProfileAction_triggered(); }; } diff --git a/components/contentselector/view/lineedit.cpp b/components/contentselector/view/lineedit.cpp deleted file mode 100644 index b6fdfa805c..0000000000 --- a/components/contentselector/view/lineedit.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include -#include - -#include "lineedit.hpp" - -ContentSelectorView::LineEdit::LineEdit(QWidget *parent) - : QLineEdit(parent) -{ - mClearButton = new QToolButton(this); - QPixmap pixmap(":images/clear.png"); - mClearButton->setIcon(QIcon(pixmap)); - mClearButton->setIconSize(pixmap.size()); - mClearButton->setCursor(Qt::ArrowCursor); - mClearButton->setStyleSheet("QToolButton { border: none; padding: 0px; }"); - mClearButton->hide(); - connect(mClearButton, SIGNAL(clicked()), this, SLOT(clear())); - connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateClearButton(const QString&))); - int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); - - setObjectName(QString("LineEdit")); - setStyleSheet(QString("LineEdit { padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1)); - QSize msz = minimumSizeHint(); - setMinimumSize(qMax(msz.width(), mClearButton->sizeHint().height() + frameWidth * 2 + 2), - qMax(msz.height(), mClearButton->sizeHint().height() + frameWidth * 2 + 2)); -} - -void ContentSelectorView::LineEdit::resizeEvent(QResizeEvent *) -{ - QSize sz = mClearButton->sizeHint(); - int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); - mClearButton->move(rect().right() - frameWidth - sz.width(), - (rect().bottom() + 1 - sz.height())/2); -} - -void ContentSelectorView::LineEdit::updateClearButton(const QString& text) -{ - mClearButton->setVisible(!text.isEmpty()); -} diff --git a/components/contentselector/view/profilescombobox.hpp b/components/contentselector/view/profilescombobox.hpp deleted file mode 100644 index fc87a94b40..0000000000 --- a/components/contentselector/view/profilescombobox.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef PROFILESCOMBOBOX_HPP -#define PROFILESCOMBOBOX_HPP - -#include -#include -class QString; -class QRegExpValidator; - -namespace ContentSelectorView -{ - class ProfilesComboBox : public QComboBox - { - Q_OBJECT - public: - explicit ProfilesComboBox(QWidget *parent = 0); - void setEditEnabled(bool editable); - void setPlaceholderText(const QString &text); - // void indexChanged(int index); - - signals: - void signalProfileTextChanged(const QString &item); - void signalProfileChanged(const QString &previous, const QString ¤t); - void signalProfileChanged(int index); - void profileRenamed(const QString &oldName, const QString &newName); - - private slots: - - void slotEditingFinished(); - void slotIndexChangedByUser(int index); - void slotTextChanged(const QString &text); - - private: - QString mOldProfile; - QString mPlaceholderText; - QRegExpValidator *mValidator; - - protected: - void paintEvent(QPaintEvent *); - }; -} - -#endif // PROFILESCOMBOBOX_HPP diff --git a/files/ui/contentselector.ui b/files/ui/contentselector.ui new file mode 100644 index 0000000000..b9b5ba5a03 --- /dev/null +++ b/files/ui/contentselector.ui @@ -0,0 +1,111 @@ + + + ContentSelector + + + + 0 + 0 + 518 + 436 + + + + + 0 + 0 + + + + Qt::DefaultContextMenu + + + + 0 + + + + + Content + + + + 3 + + + + + false + + + + + + + + 0 + 0 + + + + Qt::DefaultContextMenu + + + true + + + QAbstractItemView::NoEditTriggers + + + true + + + false + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + Qt::ElideLeft + + + false + + + false + + + true + + + false + + + + + + + + + + + ContentSelectorView::ComboBox + QComboBox +
components/contentselector/view/combobox.hpp
+
+
+ + +
diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 0cafd606a8..eb5ebc61d6 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -7,7 +7,7 @@ 0 0 518 - 436 + 108 @@ -21,188 +21,7 @@ - - - Content - - - - 3 - - - - - - - false - - - - - - - - - - - - 0 - 0 - - - - Qt::DefaultContextMenu - - - true - - - QAbstractItemView::NoEditTriggers - - - true - - - false - - - QAbstractItemView::DragDrop - - - Qt::MoveAction - - - true - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - Qt::ElideLeft - - - false - - - false - - - true - - - false - - - - - - - - - - - - - - - 0 - 0 - - - - Project - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - 3 - - - 6 - - - 6 - - - 0 - - - - - - 0 - 0 - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - 0 - - - 0 - - - - - - - - false - - - - 0 - 0 - - - - Create - - - - - - - - - - - 1 - 0 - - - - QFrame::NoFrame - - - QFrame::Plain - - - 0 - - - - 0 - - - 0 - - - + @@ -232,9 +51,9 @@ 6 - + - false + true @@ -242,6 +61,9 @@ 0 + + Select a profiile + @@ -276,13 +98,6 @@
- - - - QDialogButtonBox::Cancel|QDialogButtonBox::Open - - -
@@ -332,9 +147,9 @@ - ContentSelectorView::ProfilesComboBox + ProfilesComboBox QComboBox -
components/contentselector/view/profilescombobox.hpp
+
apps/launcher/utils/profilescombobox.hpp
diff --git a/files/ui/filedialog.ui b/files/ui/filedialog.ui new file mode 100644 index 0000000000..114345e53b --- /dev/null +++ b/files/ui/filedialog.ui @@ -0,0 +1,73 @@ + + + FileDialog + + + + 0 + 0 + 518 + 108 + + + + + 0 + 0 + + + + Qt::DefaultContextMenu + + + + + + + + + Qt::NoFocus + + + Project Name + + + false + + + + 6 + + + 3 + + + 6 + + + 0 + + + 6 + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + + + + From ba3589bc763383ac03a2ebfb9710b20b86d178c8 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Tue, 22 Oct 2013 22:20:21 -0500 Subject: [PATCH 095/148] Revert "Implemented ContentSelector as a singleton "charm" modifier for" This reverts commit 24b167b7552ce8bf6e62933a535752a899c77473. Conflicts: apps/launcher/datafilespage.cpp apps/opencs/editor.cpp apps/opencs/view/doc/filedialog.cpp apps/opencs/view/doc/filedialog.hpp components/contentselector/view/contentselector.cpp components/contentselector/view/contentselector.hpp --- apps/launcher/datafilespage.cpp | 1 - apps/opencs/editor.cpp | 9 +++-- apps/opencs/view/doc/filedialog.cpp | 6 ++-- apps/opencs/view/doc/filedialog.hpp | 7 ++-- .../contentselector/view/contentselector.cpp | 33 +++++++------------ .../contentselector/view/contentselector.hpp | 1 - 6 files changed, 23 insertions(+), 34 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 2140caaf91..ca404c5d8b 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -28,7 +28,6 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam , QWidget(parent) { ui.setupUi (this); - setObjectName ("DataFilesPage"); mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index e78357bc54..284762812c 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -121,6 +121,7 @@ void CS::Editor::loadDocument() void CS::Editor::openFiles() { std::vector files; + QStringList paths = mFileDialog.checkedItemsPaths(); foreach (const QString &path, mFileDialog.selectedFilePaths()) { files.push_back(path.toStdString()); @@ -130,28 +131,30 @@ void CS::Editor::openFiles() qDebug() << "loading files: " << fp.c_str(); /// \todo Get the save path from the file dialogue + CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), false); mViewManager.addView (document); - mFileDialog.close(); + mFileDialog.hide(); } void CS::Editor::createNewFile() { std::vector files; + QStringList paths = mFileDialog.checkedItemsPaths(); foreach (const QString &path, mFileDialog.selectedFilePaths()) { files.push_back(path.toStdString()); } - files.push_back(mFileDialog.filename().toStdString()); + files.push_back(mFileDialog.fileName().toStdString()); /// \todo Get the save path from the file dialogue. CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), true); mViewManager.addView (document); - mFileDialog.close(); + mFileDialog.hide(); } void CS::Editor::createNewGame (const boost::filesystem::path& file) diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 49e90ec813..5438effffd 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -10,10 +10,10 @@ #include #include #include -#include #include "components/contentselector/model/esmfile.hpp" #include "components/contentselector/view/contentselector.hpp" + #include "filewidget.hpp" #include "adjusterwidget.hpp" @@ -101,9 +101,9 @@ void CSVDoc::FileDialog::buildOpenFileView() connect (ui.projectButtonBox, SIGNAL (rejected()), this , SIGNAL (rejected())); } -void CSVDoc::FileDialog::slotRejected() +void CSVDoc::FileDialog::slotGameFileSelected(int value) { - close(); + emit signalUpdateCreateButton(value > -1, 1); } void CSVDoc::FileDialog::slotUpdateCreateButton (int) diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 78883791e4..85582e95f4 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -53,19 +53,18 @@ namespace CSVDoc void buildOpenFileView(); signals: - void openFiles(); void createNewFile(); - public slots: + void signalUpdateCreateButton (bool, int); + void signalUpdateCreateButtonFlags(int); - void slotRejected(); + public slots: private slots: void slotUpdateCreateButton (int); void slotUpdateCreateButton (const QString &, bool); - }; } #endif // FILEDIALOG_HPP diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 7a7e1fd9d5..cb405f092a 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -6,6 +6,7 @@ #include #include + #include #include #include @@ -21,26 +22,13 @@ ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) : buildGameFileView(); buildAddonView(); - /* - //mContentModel->sort(3); // Sort by date accessed - -*/ + updateViews(); } void ContentSelectorView::ContentSelector::buildContentModel() { mContentModel = new ContentSelectorModel::ContentModel(); - - if (mFilePaths.size()>0) - { - foreach (const QString &path, mFilePaths) - mContentModel->addFiles(path); - - mFilePaths.clear(); - } - - ui.gameFileView->setCurrentIndex(-1); - mContentModel->uncheckAll(); + connect(mContentModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); } void ContentSelectorView::ContentSelector::buildGameFileView() @@ -52,8 +40,8 @@ void ContentSelectorView::ContentSelector::buildGameFileView() mGameFileProxyModel->setFilterRole (Qt::UserRole); mGameFileProxyModel->setSourceModel (mContentModel); - ui.gameFileView->setPlaceholderText(QString("Select a game file...")); - ui.gameFileView->setModel(mGameFileProxyModel); + gameFileView->setPlaceholderText(QString("Select a game file...")); + gameFileView->setModel(mGameFileProxyModel); connect (ui.gameFileView, SIGNAL(currentIndexChanged(int)), this, SLOT (slotCurrentGameFileIndexChanged(int))); @@ -61,7 +49,8 @@ void ContentSelectorView::ContentSelector::buildGameFileView() connect (ui.gameFileView, SIGNAL (currentIndexChanged (int)), this, SIGNAL (signalCurrentGamefileIndexChanged (int))); - ui.gameFileView->setCurrentIndex(-1); + gameFileView->setCurrentIndex(-1); + gameFileView->setCurrentIndex(0); } void ContentSelectorView::ContentSelector::buildAddonView() @@ -74,7 +63,7 @@ void ContentSelectorView::ContentSelector::buildAddonView() mAddonProxyModel->setDynamicSortFilter (true); mAddonProxyModel->setSourceModel (mContentModel); - ui.addonView->setModel(mAddonProxyModel); + addonView->setModel(mAddonProxyModel); connect(ui.addonView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotAddonTableItemClicked(const QModelIndex &))); @@ -120,7 +109,6 @@ ContentSelectorModel::ContentFileList return mContentModel->checkedItems(); } - void ContentSelectorView::ContentSelector::addFiles(const QString &path) { mContentModel->addFiles(path); @@ -133,7 +121,7 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i { static int oldIndex = -1; - QAbstractItemModel *const model = ui.gameFileView->model(); + QAbstractItemModel *const model = gameFileView->model(); QSortFilterProxyModel *proxy = dynamic_cast(model); if (proxy) @@ -152,7 +140,8 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i void ContentSelectorView::ContentSelector::slotAddonTableItemClicked(const QModelIndex &index) { - QAbstractItemModel *const model = ui.addonView->model(); + QAbstractItemModel *const model = addonView->model(); + //QSortFilterProxyModel *proxy = dynamic_cast(model); if (model->data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked) model->setData(index, Qt::Checked, Qt::CheckStateRole); diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index 0882abfb77..163b19855d 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -10,7 +10,6 @@ class QSortFilterProxyModel; namespace ContentSelectorView { - class ContentSelector : public QObject { Q_OBJECT From b48f066f33e0d54b5369e13194175ea2736203c7 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Wed, 23 Oct 2013 17:39:17 -0500 Subject: [PATCH 096/148] Reimplemented content selector for filedialog and datafilespage classes --- apps/opencs/editor.cpp | 4 +--- apps/opencs/view/doc/filedialog.cpp | 13 ++++++------- apps/opencs/view/doc/filedialog.hpp | 4 ++-- .../contentselector/view/contentselector.cpp | 18 ++++++++---------- 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 284762812c..1099226d2c 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -121,7 +121,6 @@ void CS::Editor::loadDocument() void CS::Editor::openFiles() { std::vector files; - QStringList paths = mFileDialog.checkedItemsPaths(); foreach (const QString &path, mFileDialog.selectedFilePaths()) { files.push_back(path.toStdString()); @@ -141,13 +140,12 @@ void CS::Editor::openFiles() void CS::Editor::createNewFile() { std::vector files; - QStringList paths = mFileDialog.checkedItemsPaths(); foreach (const QString &path, mFileDialog.selectedFilePaths()) { files.push_back(path.toStdString()); } - files.push_back(mFileDialog.fileName().toStdString()); + files.push_back(mFileDialog.filename().toStdString()); /// \todo Get the save path from the file dialogue. diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 5438effffd..1ac476cb29 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -88,7 +88,6 @@ void CSVDoc::FileDialog::buildNewFileView() this, SLOT (slotUpdateCreateButton (int))); connect (ui.projectButtonBox, SIGNAL (accepted()), this, SIGNAL (createNewFile())); - connect (ui.projectButtonBox, SIGNAL (rejected()), this, SLOT (slotRejected())); } @@ -98,12 +97,7 @@ void CSVDoc::FileDialog::buildOpenFileView() ui.projectGroupBox->setTitle (QString("")); connect (ui.projectButtonBox, SIGNAL (accepted()), this, SIGNAL (openFiles())); - connect (ui.projectButtonBox, SIGNAL (rejected()), this , SIGNAL (rejected())); -} - -void CSVDoc::FileDialog::slotGameFileSelected(int value) -{ - emit signalUpdateCreateButton(value > -1, 1); + connect (ui.projectButtonBox, SIGNAL (rejected()), this, SLOT (slotRejected())); } void CSVDoc::FileDialog::slotUpdateCreateButton (int) @@ -129,3 +123,8 @@ QString CSVDoc::FileDialog::filename() const return QString (""); } +void CSVDoc::FileDialog::slotRejected() +{ + emit rejected(); + close(); +} diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 85582e95f4..3b93f42a86 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -53,18 +53,18 @@ namespace CSVDoc void buildOpenFileView(); signals: + void openFiles(); void createNewFile(); void signalUpdateCreateButton (bool, int); void signalUpdateCreateButtonFlags(int); - public slots: - private slots: void slotUpdateCreateButton (int); void slotUpdateCreateButton (const QString &, bool); + void slotRejected(); }; } #endif // FILEDIALOG_HPP diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index cb405f092a..3d615906d8 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -21,14 +21,12 @@ ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) : buildContentModel(); buildGameFileView(); buildAddonView(); - - updateViews(); } void ContentSelectorView::ContentSelector::buildContentModel() { mContentModel = new ContentSelectorModel::ContentModel(); - connect(mContentModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); + //connect(mContentModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); } void ContentSelectorView::ContentSelector::buildGameFileView() @@ -40,8 +38,8 @@ void ContentSelectorView::ContentSelector::buildGameFileView() mGameFileProxyModel->setFilterRole (Qt::UserRole); mGameFileProxyModel->setSourceModel (mContentModel); - gameFileView->setPlaceholderText(QString("Select a game file...")); - gameFileView->setModel(mGameFileProxyModel); + ui.gameFileView->setPlaceholderText(QString("Select a game file...")); + ui.gameFileView->setModel(mGameFileProxyModel); connect (ui.gameFileView, SIGNAL(currentIndexChanged(int)), this, SLOT (slotCurrentGameFileIndexChanged(int))); @@ -49,8 +47,8 @@ void ContentSelectorView::ContentSelector::buildGameFileView() connect (ui.gameFileView, SIGNAL (currentIndexChanged (int)), this, SIGNAL (signalCurrentGamefileIndexChanged (int))); - gameFileView->setCurrentIndex(-1); - gameFileView->setCurrentIndex(0); + ui.gameFileView->setCurrentIndex(-1); + ui.gameFileView->setCurrentIndex(0); } void ContentSelectorView::ContentSelector::buildAddonView() @@ -63,7 +61,7 @@ void ContentSelectorView::ContentSelector::buildAddonView() mAddonProxyModel->setDynamicSortFilter (true); mAddonProxyModel->setSourceModel (mContentModel); - addonView->setModel(mAddonProxyModel); + ui.addonView->setModel(mAddonProxyModel); connect(ui.addonView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotAddonTableItemClicked(const QModelIndex &))); @@ -121,7 +119,7 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i { static int oldIndex = -1; - QAbstractItemModel *const model = gameFileView->model(); + QAbstractItemModel *const model = ui.gameFileView->model(); QSortFilterProxyModel *proxy = dynamic_cast(model); if (proxy) @@ -140,7 +138,7 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i void ContentSelectorView::ContentSelector::slotAddonTableItemClicked(const QModelIndex &index) { - QAbstractItemModel *const model = addonView->model(); + QAbstractItemModel *const model = ui.addonView->model(); //QSortFilterProxyModel *proxy = dynamic_cast(model); if (model->data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked) From 2ca7f247317ea2270df6f816c1a209ac1360efef Mon Sep 17 00:00:00 2001 From: graffy76 Date: Thu, 24 Oct 2013 17:33:28 -0500 Subject: [PATCH 097/148] Fixed filedialog new / edit content path issue --- apps/opencs/editor.cpp | 11 +++--- apps/opencs/view/doc/filedialog.cpp | 2 +- .../contentselector/model/contentmodel.cpp | 1 - .../contentselector/view/contentselector.cpp | 34 ++++++++++++------- .../contentselector/view/contentselector.hpp | 2 ++ 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 1099226d2c..6396563f23 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -126,12 +126,9 @@ void CS::Editor::openFiles() files.push_back(path.toStdString()); } - foreach (const boost::filesystem::path fp, files) - qDebug() << "loading files: " << fp.c_str(); + boost::filesystem::path savePath = mFileDialog.filename().toStdString(); - /// \todo Get the save path from the file dialogue - - CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), false); + CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, false); mViewManager.addView (document); mFileDialog.hide(); @@ -147,9 +144,9 @@ void CS::Editor::createNewFile() files.push_back(mFileDialog.filename().toStdString()); - /// \todo Get the save path from the file dialogue. + boost::filesystem::path savePath = mFileDialog.filename().toStdString(); - CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), true); + CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, true); mViewManager.addView (document); mFileDialog.hide(); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 1ac476cb29..e82cc30cbf 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -120,7 +120,7 @@ QString CSVDoc::FileDialog::filename() const if (mDialogType == DialogType_New) return mFileWidget->getName(); - return QString (""); + return mSelector->currentFile(); } void CSVDoc::FileDialog::slotRejected() diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index accc149cbb..0674642eed 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -503,7 +503,6 @@ bool ContentSelectorModel::ContentModel::isChecked(const QString& name) const void ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool checkState) { - if (name.isEmpty()) return; diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 3d615906d8..33b31b00c5 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -9,10 +9,9 @@ #include #include +#include #include -#include - ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) : QObject(parent) { @@ -26,7 +25,6 @@ ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) : void ContentSelectorView::ContentSelector::buildContentModel() { mContentModel = new ContentSelectorModel::ContentModel(); - //connect(mContentModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); } void ContentSelectorView::ContentSelector::buildGameFileView() @@ -41,7 +39,7 @@ void ContentSelectorView::ContentSelector::buildGameFileView() ui.gameFileView->setPlaceholderText(QString("Select a game file...")); ui.gameFileView->setModel(mGameFileProxyModel); - connect (ui.gameFileView, SIGNAL(currentIndexChanged(int)), + connect (ui.gameFileView, SIGNAL (currentIndexChanged(int)), this, SLOT (slotCurrentGameFileIndexChanged(int))); connect (ui.gameFileView, SIGNAL (currentIndexChanged (int)), @@ -64,9 +62,6 @@ void ContentSelectorView::ContentSelector::buildAddonView() ui.addonView->setModel(mAddonProxyModel); connect(ui.addonView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotAddonTableItemClicked(const QModelIndex &))); - - for (int i = 0; i < mAddonProxyModel->rowCount(); ++i) - qDebug() << mAddonProxyModel->data(mAddonProxyModel->index(i,0,QModelIndex())); } void ContentSelectorView::ContentSelector::setGameFile(const QString &filename) @@ -113,6 +108,19 @@ void ContentSelectorView::ContentSelector::addFiles(const QString &path) if (ui.gameFileView->currentIndex() != -1) ui.gameFileView->setCurrentIndex(-1); + + mContentModel->uncheckAll(); +} + +QString ContentSelectorView::ContentSelector::currentFile() const +{ + QModelIndex currentIdx = ui.addonView->currentIndex(); + + if (!currentIdx.isValid()) + return ui.gameFileView->currentText(); + + QModelIndex idx = mContentModel->index(mAddonProxyModel->mapToSource(currentIdx).row(), 0, QModelIndex()); + return mContentModel->data(idx, Qt::DisplayRole).toString(); } void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int index) @@ -125,12 +133,15 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i if (proxy) proxy->setDynamicSortFilter(false); - if (oldIndex > -1) - model->setData(model->index(oldIndex, 0), false, Qt::UserRole + 1); + if (index != oldIndex) + { + if (oldIndex > -1) + model->setData(model->index(oldIndex, 0), false, Qt::UserRole + 1); - oldIndex = index; + oldIndex = index; - model->setData(model->index(index, 0), true, Qt::UserRole + 1); + model->setData(model->index(index, 0), true, Qt::UserRole + 1); + } if (proxy) proxy->setDynamicSortFilter(true); @@ -139,7 +150,6 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i void ContentSelectorView::ContentSelector::slotAddonTableItemClicked(const QModelIndex &index) { QAbstractItemModel *const model = ui.addonView->model(); - //QSortFilterProxyModel *proxy = dynamic_cast(model); if (model->data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked) model->setData(index, Qt::Checked, Qt::CheckStateRole); diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index 163b19855d..1e24a5523e 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -26,6 +26,8 @@ namespace ContentSelectorView explicit ContentSelector(QWidget *parent = 0); + QString currentFile() const; + void addFiles(const QString &path); void clearCheckStates(); From 1a23cccce3a067943f27b2a2a00b18f175f867c7 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Fri, 25 Oct 2013 11:17:26 -0500 Subject: [PATCH 098/148] Implemented Launcher namespace --- apps/launcher/datafilespage.cpp | 44 +++--- apps/launcher/datafilespage.hpp | 94 ++++++------- apps/launcher/graphicspage.cpp | 24 ++-- apps/launcher/graphicspage.hpp | 67 ++++----- apps/launcher/main.cpp | 3 +- apps/launcher/maindialog.cpp | 41 +++--- apps/launcher/maindialog.hpp | 100 +++++++------- apps/launcher/playpage.cpp | 8 +- apps/launcher/playpage.hpp | 30 ++-- apps/launcher/settings/gamesettings.cpp | 12 +- apps/launcher/settings/gamesettings.hpp | 76 +++++----- apps/launcher/settings/graphicssettings.cpp | 6 +- apps/launcher/settings/graphicssettings.hpp | 16 ++- apps/launcher/settings/launchersettings.cpp | 10 +- apps/launcher/settings/launchersettings.hpp | 20 +-- apps/launcher/settings/settingsbase.hpp | 146 ++++++++++---------- apps/launcher/textslotmsgbox.cpp | 2 +- apps/launcher/textslotmsgbox.hpp | 14 +- apps/launcher/unshieldthread.cpp | 26 ++-- apps/launcher/unshieldthread.hpp | 66 ++++----- apps/launcher/utils/checkablemessagebox.cpp | 139 +++++++++---------- apps/launcher/utils/checkablemessagebox.hpp | 110 ++++++++------- apps/launcher/utils/textinputdialog.cpp | 10 +- apps/launcher/utils/textinputdialog.hpp | 49 +++---- 24 files changed, 573 insertions(+), 540 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index ca404c5d8b..e246b45154 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -21,7 +21,7 @@ #include "components/contentselector/view/contentselector.hpp" -DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent) +Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent) : mCfgMgr(cfg) , mGameSettings(gameSettings) , mLauncherSettings(launcherSettings) @@ -35,7 +35,7 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gam setupDataFiles(); } -void DataFilesPage::loadSettings() +void Launcher::DataFilesPage::loadSettings() { QString profileName = ui.profilesComboBox->currentText(); @@ -53,7 +53,7 @@ void DataFilesPage::loadSettings() mSelector->setCheckStates(addons); } -void DataFilesPage::saveSettings(const QString &profile) +void Launcher::DataFilesPage::saveSettings(const QString &profile) { QString profileName = profile; @@ -84,7 +84,7 @@ void DataFilesPage::saveSettings(const QString &profile) } -void DataFilesPage::buildView() +void Launcher::DataFilesPage::buildView() { ui.verticalLayout->insertWidget (0, mSelector->uiWidget()); @@ -111,13 +111,23 @@ void DataFilesPage::buildView() this, SLOT (slotProfileChangedByUser(QString, QString))); } -void DataFilesPage::removeProfile(const QString &profile) +void Launcher::DataFilesPage::removeProfile(const QString &profile) { mLauncherSettings.remove(QString("Profiles/") + profile + QString("/game")); mLauncherSettings.remove(QString("Profiles/") + profile + QString("/addon")); } -void DataFilesPage::setProfile(int index, bool savePrevious) +QAbstractItemModel *Launcher::DataFilesPage::profilesModel() const +{ + return ui.profilesComboBox->model(); +} + +int Launcher::DataFilesPage::profilesIndex() const +{ + return ui.profilesComboBox->currentIndex(); +} + +void Launcher::DataFilesPage::setProfile(int index, bool savePrevious) { if (index >= -1 && index < ui.profilesComboBox->count()) { @@ -128,7 +138,7 @@ void DataFilesPage::setProfile(int index, bool savePrevious) } } -void DataFilesPage::setProfile (const QString &previous, const QString ¤t, bool savePrevious) +void Launcher::DataFilesPage::setProfile (const QString &previous, const QString ¤t, bool savePrevious) { //abort if no change (poss. duplicate signal) if (previous == current) @@ -144,18 +154,18 @@ void DataFilesPage::setProfile (const QString &previous, const QString ¤t, checkForDefaultProfile(); } -void DataFilesPage::slotProfileDeleted (const QString &item) +void Launcher::DataFilesPage::slotProfileDeleted (const QString &item) { removeProfile (item); } -void DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString ¤t) +void Launcher::DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString ¤t) { setProfile(previous, current, true); emit signalProfileChanged (ui.profilesComboBox->findText(current)); } -void DataFilesPage::slotProfileRenamed(const QString &previous, const QString ¤t) +void Launcher::DataFilesPage::slotProfileRenamed(const QString &previous, const QString ¤t) { if (previous.isEmpty()) return; @@ -169,12 +179,12 @@ void DataFilesPage::slotProfileRenamed(const QString &previous, const QString &c loadSettings(); } -void DataFilesPage::slotProfileChanged(int index) +void Launcher::DataFilesPage::slotProfileChanged(int index) { setProfile (index, true); } -void DataFilesPage::setupDataFiles() +void Launcher::DataFilesPage::setupDataFiles() { QStringList paths = mGameSettings.getDataDirs(); @@ -197,7 +207,7 @@ void DataFilesPage::setupDataFiles() loadSettings(); } -void DataFilesPage::on_newProfileAction_triggered() +void Launcher::DataFilesPage::on_newProfileAction_triggered() { TextInputDialog newDialog (tr("New Profile"), tr("Profile name:"), this); @@ -222,7 +232,7 @@ void DataFilesPage::on_newProfileAction_triggered() emit signalProfileChanged (ui.profilesComboBox->findText(profile)); } -void DataFilesPage::addProfile (const QString &profile, bool setAsCurrent) +void Launcher::DataFilesPage::addProfile (const QString &profile, bool setAsCurrent) { if (profile.isEmpty()) return; @@ -236,7 +246,7 @@ void DataFilesPage::addProfile (const QString &profile, bool setAsCurrent) setProfile (ui.profilesComboBox->findText (profile), false); } -void DataFilesPage::on_deleteProfileAction_triggered() +void Launcher::DataFilesPage::on_deleteProfileAction_triggered() { QString profile = ui.profilesComboBox->currentText(); @@ -254,7 +264,7 @@ void DataFilesPage::on_deleteProfileAction_triggered() checkForDefaultProfile(); } -void DataFilesPage::checkForDefaultProfile() +void Launcher::DataFilesPage::checkForDefaultProfile() { //don't allow deleting "Default" profile bool success = (ui.profilesComboBox->currentText() != "Default"); @@ -263,7 +273,7 @@ void DataFilesPage::checkForDefaultProfile() ui.profilesComboBox->setEditEnabled (success); } -bool DataFilesPage::showDeleteMessageBox (const QString &text) +bool Launcher::DataFilesPage::showDeleteMessageBox (const QString &text) { QMessageBox msgBox(this); msgBox.setWindowTitle(tr("Delete Profile")); diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index cc054a4e47..e394e6f41f 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -3,78 +3,76 @@ #include "ui_datafilespage.h" #include -#include class QSortFilterProxyModel; class QAbstractItemModel; -class QAction; class QMenu; -class TextInputDialog; -class GameSettings; -class LauncherSettings; -class ProfilesComboBox; - namespace Files { struct ConfigurationManager; } namespace ContentSelectorView { class ContentSelector; } -class DataFilesPage : public QWidget +namespace Launcher { - Q_OBJECT + class TextInputDialog; + class GameSettings; + class LauncherSettings; + class ProfilesComboBox; - ContentSelectorView::ContentSelector *mSelector; - Ui::DataFilesPage ui; + class DataFilesPage : public QWidget + { + Q_OBJECT -public: - explicit DataFilesPage (Files::ConfigurationManager &cfg, GameSettings &gameSettings, - LauncherSettings &launcherSettings, QWidget *parent = 0); + ContentSelectorView::ContentSelector *mSelector; + Ui::DataFilesPage ui; - QAbstractItemModel* profilesModel() const - { return ui.profilesComboBox->model(); } + public: + explicit DataFilesPage (Files::ConfigurationManager &cfg, GameSettings &gameSettings, + LauncherSettings &launcherSettings, QWidget *parent = 0); - int profilesIndex() const - { return ui.profilesComboBox->currentIndex(); } + QAbstractItemModel* profilesModel() const; - //void writeConfig(QString profile = QString()); - void saveSettings(const QString &profile = ""); - void loadSettings(); + int profilesIndex() const; -signals: - void signalProfileChanged (int index); + //void writeConfig(QString profile = QString()); + void saveSettings(const QString &profile = ""); + void loadSettings(); -public slots: - void slotProfileChanged (int index); + signals: + void signalProfileChanged (int index); -private slots: + public slots: + void slotProfileChanged (int index); - void slotProfileChangedByUser(const QString &previous, const QString ¤t); - void slotProfileRenamed(const QString &previous, const QString ¤t); - void slotProfileDeleted(const QString &item); + private slots: - void on_newProfileAction_triggered(); - void on_deleteProfileAction_triggered(); + void slotProfileChangedByUser(const QString &previous, const QString ¤t); + void slotProfileRenamed(const QString &previous, const QString ¤t); + void slotProfileDeleted(const QString &item); -private: + void on_newProfileAction_triggered(); + void on_deleteProfileAction_triggered(); - QMenu *mContextMenu; + private: - Files::ConfigurationManager &mCfgMgr; + QMenu *mContextMenu; - GameSettings &mGameSettings; - LauncherSettings &mLauncherSettings; + Files::ConfigurationManager &mCfgMgr; - void setPluginsCheckstates(Qt::CheckState state); + GameSettings &mGameSettings; + LauncherSettings &mLauncherSettings; - void buildView(); - void setupDataFiles(); - void setupConfig(); - void readConfig(); - void setProfile (int index, bool savePrevious); - void setProfile (const QString &previous, const QString ¤t, bool savePrevious); - void removeProfile (const QString &profile); - bool showDeleteMessageBox (const QString &text); - void addProfile (const QString &profile, bool setAsCurrent); - void checkForDefaultProfile(); -}; + void setPluginsCheckstates(Qt::CheckState state); + void buildView(); + void setupDataFiles(); + void setupConfig(); + void readConfig(); + void setProfile (int index, bool savePrevious); + void setProfile (const QString &previous, const QString ¤t, bool savePrevious); + void removeProfile (const QString &profile); + bool showDeleteMessageBox (const QString &text); + void addProfile (const QString &profile, bool setAsCurrent); + void checkForDefaultProfile(); + }; +} #endif diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 2c6c711ea9..1ed1abaebf 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -33,7 +33,7 @@ QString getAspect(int x, int y) return QString(QString::number(xaspect) + ":" + QString::number(yaspect)); } -GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSetting, QWidget *parent) +Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSetting, QWidget *parent) : mCfgMgr(cfg) , mGraphicsSettings(graphicsSetting) , QWidget(parent) @@ -53,7 +53,7 @@ GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &g } -bool GraphicsPage::setupOgre() +bool Launcher::GraphicsPage::setupOgre() { // Create a log manager so we can surpress debug text to stdout/stderr Ogre::LogManager* logMgr = OGRE_NEW Ogre::LogManager; @@ -157,7 +157,7 @@ bool GraphicsPage::setupOgre() return true; } -bool GraphicsPage::setupSDL() +bool Launcher::GraphicsPage::setupSDL() { int displays = SDL_GetNumVideoDisplays(); @@ -180,7 +180,7 @@ bool GraphicsPage::setupSDL() return true; } -bool GraphicsPage::loadSettings() +bool Launcher::GraphicsPage::loadSettings() { if (!setupSDL()) return false; @@ -219,7 +219,7 @@ bool GraphicsPage::loadSettings() return true; } -void GraphicsPage::saveSettings() +void Launcher::GraphicsPage::saveSettings() { vSyncCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/vsync"), QString("true")) : mGraphicsSettings.setValue(QString("Video/vsync"), QString("false")); @@ -246,7 +246,7 @@ void GraphicsPage::saveSettings() mGraphicsSettings.setValue(QString("Video/screen"), QString::number(screenComboBox->currentIndex())); } -QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer) +QStringList Launcher::GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer) { QStringList result; @@ -279,7 +279,7 @@ QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSy return result; } -QStringList GraphicsPage::getAvailableResolutions(int screen) +QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen) { QStringList result; SDL_DisplayMode mode; @@ -326,7 +326,7 @@ QStringList GraphicsPage::getAvailableResolutions(int screen) return result; } -QRect GraphicsPage::getMaximumResolution() +QRect Launcher::GraphicsPage::getMaximumResolution() { QRect max; int screens = QApplication::desktop()->screenCount(); @@ -341,7 +341,7 @@ QRect GraphicsPage::getMaximumResolution() return max; } -void GraphicsPage::rendererChanged(const QString &renderer) +void Launcher::GraphicsPage::rendererChanged(const QString &renderer) { mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString()); @@ -350,7 +350,7 @@ void GraphicsPage::rendererChanged(const QString &renderer) antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem)); } -void GraphicsPage::screenChanged(int screen) +void Launcher::GraphicsPage::screenChanged(int screen) { if (screen >= 0) { resolutionComboBox->clear(); @@ -358,7 +358,7 @@ void GraphicsPage::screenChanged(int screen) } } -void GraphicsPage::slotFullScreenChanged(int state) +void Launcher::GraphicsPage::slotFullScreenChanged(int state) { if (state == Qt::Checked) { standardRadioButton->toggle(); @@ -372,7 +372,7 @@ void GraphicsPage::slotFullScreenChanged(int state) } } -void GraphicsPage::slotStandardToggled(bool checked) +void Launcher::GraphicsPage::slotStandardToggled(bool checked) { if (checked) { resolutionComboBox->setEnabled(true); diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index d233ea12e2..7f5dcae1ee 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -18,49 +18,52 @@ #include "ui_graphicspage.h" -class GraphicsSettings; namespace Files { struct ConfigurationManager; } -class GraphicsPage : public QWidget, private Ui::GraphicsPage +namespace Launcher { - Q_OBJECT + class GraphicsSettings; -public: - GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0); + class GraphicsPage : public QWidget, private Ui::GraphicsPage + { + Q_OBJECT - void saveSettings(); - bool loadSettings(); + public: + GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0); -public slots: - void rendererChanged(const QString &renderer); - void screenChanged(int screen); + void saveSettings(); + bool loadSettings(); -private slots: - void slotFullScreenChanged(int state); - void slotStandardToggled(bool checked); + public slots: + void rendererChanged(const QString &renderer); + void screenChanged(int screen); -private: - Ogre::Root *mOgre; - Ogre::RenderSystem *mSelectedRenderSystem; - Ogre::RenderSystem *mOpenGLRenderSystem; - Ogre::RenderSystem *mDirect3DRenderSystem; - #ifdef ENABLE_PLUGIN_GL - Ogre::GLPlugin* mGLPlugin; - #endif - #ifdef ENABLE_PLUGIN_Direct3D9 - Ogre::D3D9Plugin* mD3D9Plugin; - #endif + private slots: + void slotFullScreenChanged(int state); + void slotStandardToggled(bool checked); - Files::ConfigurationManager &mCfgMgr; - GraphicsSettings &mGraphicsSettings; + private: + Ogre::Root *mOgre; + Ogre::RenderSystem *mSelectedRenderSystem; + Ogre::RenderSystem *mOpenGLRenderSystem; + Ogre::RenderSystem *mDirect3DRenderSystem; + #ifdef ENABLE_PLUGIN_GL + Ogre::GLPlugin* mGLPlugin; + #endif + #ifdef ENABLE_PLUGIN_Direct3D9 + Ogre::D3D9Plugin* mD3D9Plugin; + #endif - QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer); - QStringList getAvailableResolutions(int screen); - QRect getMaximumResolution(); + Files::ConfigurationManager &mCfgMgr; + GraphicsSettings &mGraphicsSettings; - bool setupOgre(); - bool setupSDL(); -}; + QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer); + QStringList getAvailableResolutions(int screen); + QRect getMaximumResolution(); + bool setupOgre(); + bool setupSDL(); + }; +} #endif diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index f67f5edcff..0b5e62a66f 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -49,7 +49,7 @@ int main(int argc, char *argv[]) // Support non-latin characters QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); - MainDialog mainWin; + Launcher::MainDialog mainWin; if (mainWin.setup()) { mainWin.show(); @@ -61,4 +61,3 @@ int main(int argc, char *argv[]) SDL_Quit(); return returnValue; } - diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index e514755fea..dca9720ac7 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -1,5 +1,6 @@ #include "maindialog.hpp" +#include #include #include #include @@ -23,8 +24,8 @@ #include "graphicspage.hpp" #include "datafilespage.hpp" -MainDialog::MainDialog() - : mGameSettings(mCfgMgr) +Launcher::MainDialog::MainDialog(QWidget *parent) + : mGameSettings(mCfgMgr), QMainWindow (parent) { // Install the stylesheet font QFile file; @@ -69,7 +70,7 @@ MainDialog::MainDialog() createIcons(); } -void MainDialog::createIcons() +void Launcher::MainDialog::createIcons() { if (!QIcon::hasThemeIcon("document-new")) QIcon::setThemeName("tango"); @@ -101,7 +102,7 @@ void MainDialog::createIcons() } -void MainDialog::createPages() +void Launcher::MainDialog::createPages() { mPlayPage = new PlayPage(this); mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this); @@ -126,7 +127,7 @@ void MainDialog::createPages() } -bool MainDialog::showFirstRunDialog() +bool Launcher::MainDialog::showFirstRunDialog() { QStringList iniPaths; @@ -282,7 +283,7 @@ bool MainDialog::showFirstRunDialog() return true; } -bool MainDialog::setup() +bool Launcher::MainDialog::setup() { if (!setupLauncherSettings()) return false; @@ -311,7 +312,7 @@ bool MainDialog::setup() return true; } -void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) +void Launcher::MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) { if (!current) current = previous; @@ -337,7 +338,7 @@ void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) } } -bool MainDialog::setupLauncherSettings() +bool Launcher::MainDialog::setupLauncherSettings() { mLauncherSettings.setMultiValueEnabled(true); @@ -374,7 +375,7 @@ bool MainDialog::setupLauncherSettings() } #ifndef WIN32 -bool expansions(UnshieldThread& cd) +bool Launcher::expansions(Launcher::UnshieldThread& cd) { if(cd.BloodmoonDone()) { @@ -385,7 +386,7 @@ bool expansions(UnshieldThread& cd) QMessageBox expansionsBox; expansionsBox.setText(QObject::tr("
Would you like to install expansions now ? (make sure you have the disc)
\ If you want to install both Bloodmoon and Tribunal, you have to install Tribunal first.
")); - + QAbstractButton* tribunalButton = NULL; if(!cd.TribunalDone()) tribunalButton = expansionsBox.addButton(QObject::tr("&Tribunal"), QMessageBox::ActionRole); @@ -404,7 +405,7 @@ bool expansions(UnshieldThread& cd) { TextSlotMsgBox cdbox; - cdbox.setStandardButtons(QMessageBox::Cancel); + cdbox.setStandardButtons(QMessageBox::Cancel); QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&))); QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject())); @@ -423,7 +424,7 @@ bool expansions(UnshieldThread& cd) { TextSlotMsgBox cdbox; - cdbox.setStandardButtons(QMessageBox::Cancel); + cdbox.setStandardButtons(QMessageBox::Cancel); QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&))); QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject())); @@ -445,7 +446,7 @@ bool expansions(UnshieldThread& cd) } #endif // WIN32 -bool MainDialog::setupGameSettings() +bool Launcher::MainDialog::setupGameSettings() { QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); @@ -568,7 +569,7 @@ bool MainDialog::setupGameSettings() return true; } -bool MainDialog::setupGraphicsSettings() +bool Launcher::MainDialog::setupGraphicsSettings() { mGraphicsSettings.setMultiValueEnabled(false); @@ -622,7 +623,7 @@ bool MainDialog::setupGraphicsSettings() return true; } -void MainDialog::loadSettings() +void Launcher::MainDialog::loadSettings() { int width = mLauncherSettings.value(QString("General/MainWindow/width")).toInt(); int height = mLauncherSettings.value(QString("General/MainWindow/height")).toInt(); @@ -634,7 +635,7 @@ void MainDialog::loadSettings() move(posX, posY); } -void MainDialog::saveSettings() +void Launcher::MainDialog::saveSettings() { QString width = QString::number(this->width()); QString height = QString::number(this->height()); @@ -652,7 +653,7 @@ void MainDialog::saveSettings() } -bool MainDialog::writeSettings() +bool Launcher::MainDialog::writeSettings() { // Now write all config files saveSettings(); @@ -745,13 +746,13 @@ bool MainDialog::writeSettings() return true; } -void MainDialog::closeEvent(QCloseEvent *event) +void Launcher::MainDialog::closeEvent(QCloseEvent *event) { writeSettings(); event->accept(); } -void MainDialog::play() +void Launcher::MainDialog::play() { if (!writeSettings()) { qApp->quit(); @@ -774,7 +775,7 @@ void MainDialog::play() qApp->quit(); } -bool MainDialog::startProgram(const QString &name, const QStringList &arguments, bool detached) +bool Launcher::MainDialog::startProgram(const QString &name, const QStringList &arguments, bool detached) { QString path = name; #ifdef Q_OS_WIN diff --git a/apps/launcher/maindialog.hpp b/apps/launcher/maindialog.hpp index 824dff6e82..5b8e4908e7 100644 --- a/apps/launcher/maindialog.hpp +++ b/apps/launcher/maindialog.hpp @@ -11,57 +11,59 @@ #include "ui_mainwindow.h" -class QListWidget; class QListWidgetItem; -class QStackedWidget; -class QStringList; -class QStringListModel; -class QString; -class PlayPage; -class GraphicsPage; -class DataFilesPage; - -class MainDialog : public QMainWindow, private Ui::MainWindow +namespace Launcher { - Q_OBJECT - -public: - MainDialog(); - bool setup(); - bool showFirstRunDialog(); - -public slots: - void changePage(QListWidgetItem *current, QListWidgetItem *previous); - void play(); - -private: - void createIcons(); - void createPages(); - - bool setupLauncherSettings(); - bool setupGameSettings(); - bool setupGraphicsSettings(); - - void loadSettings(); - void saveSettings(); - bool writeSettings(); - - inline bool startProgram(const QString &name, bool detached = false) { return startProgram(name, QStringList(), detached); } - bool startProgram(const QString &name, const QStringList &arguments, bool detached = false); - - void closeEvent(QCloseEvent *event); - - PlayPage *mPlayPage; - GraphicsPage *mGraphicsPage; - DataFilesPage *mDataFilesPage; - - Files::ConfigurationManager mCfgMgr; - - GameSettings mGameSettings; - GraphicsSettings mGraphicsSettings; - LauncherSettings mLauncherSettings; - -}; + class PlayPage; + class GraphicsPage; + class DataFilesPage; + class UnshieldThread; +#ifndef WIN32 + bool expansions(Launcher::UnshieldThread& cd); +#endif + + class MainDialog : public QMainWindow, private Ui::MainWindow + { + Q_OBJECT + + public: + explicit MainDialog(QWidget *parent = 0); + bool setup(); + bool showFirstRunDialog(); + + public slots: + void changePage(QListWidgetItem *current, QListWidgetItem *previous); + void play(); + + private: + void createIcons(); + void createPages(); + + bool setupLauncherSettings(); + bool setupGameSettings(); + bool setupGraphicsSettings(); + + void loadSettings(); + void saveSettings(); + bool writeSettings(); + + inline bool startProgram(const QString &name, bool detached = false) { return startProgram(name, QStringList(), detached); } + bool startProgram(const QString &name, const QStringList &arguments, bool detached = false); + + void closeEvent(QCloseEvent *event); + + PlayPage *mPlayPage; + GraphicsPage *mGraphicsPage; + DataFilesPage *mDataFilesPage; + + Files::ConfigurationManager mCfgMgr; + + GameSettings mGameSettings; + GraphicsSettings mGraphicsSettings; + LauncherSettings mLauncherSettings; + + }; +} #endif diff --git a/apps/launcher/playpage.cpp b/apps/launcher/playpage.cpp index fc1ed1c69b..6cfb9686fa 100644 --- a/apps/launcher/playpage.cpp +++ b/apps/launcher/playpage.cpp @@ -6,7 +6,7 @@ #include #endif -PlayPage::PlayPage(QWidget *parent) : QWidget(parent) +Launcher::PlayPage::PlayPage(QWidget *parent) : QWidget(parent) { setObjectName ("PlayPage"); setupUi(this); @@ -23,17 +23,17 @@ PlayPage::PlayPage(QWidget *parent) : QWidget(parent) } -void PlayPage::setProfilesModel(QAbstractItemModel *model) +void Launcher::PlayPage::setProfilesModel(QAbstractItemModel *model) { profilesComboBox->setModel(model); } -void PlayPage::setProfilesIndex(int index) +void Launcher::PlayPage::setProfilesIndex(int index) { profilesComboBox->setCurrentIndex(index); } -void PlayPage::slotPlayClicked() +void Launcher::PlayPage::slotPlayClicked() { emit playButtonClicked(); } diff --git a/apps/launcher/playpage.hpp b/apps/launcher/playpage.hpp index 42edfadb18..1dc5bb0fe0 100644 --- a/apps/launcher/playpage.hpp +++ b/apps/launcher/playpage.hpp @@ -9,26 +9,28 @@ class QComboBox; class QPushButton; class QAbstractItemModel; -class PlayPage : public QWidget, private Ui::PlayPage +namespace Launcher { - Q_OBJECT + class PlayPage : public QWidget, private Ui::PlayPage + { + Q_OBJECT -public: - PlayPage(QWidget *parent = 0); - void setProfilesModel(QAbstractItemModel *model); + public: + PlayPage(QWidget *parent = 0); + void setProfilesModel(QAbstractItemModel *model); -signals: - void signalProfileChanged(int index); - void playButtonClicked(); + signals: + void signalProfileChanged(int index); + void playButtonClicked(); -public slots: - void setProfilesIndex(int index); + public slots: + void setProfilesIndex(int index); -private slots: - void slotPlayClicked(); + private slots: + void slotPlayClicked(); -}; - + }; +} #endif diff --git a/apps/launcher/settings/gamesettings.cpp b/apps/launcher/settings/gamesettings.cpp index 205879bc37..5231753f2d 100644 --- a/apps/launcher/settings/gamesettings.cpp +++ b/apps/launcher/settings/gamesettings.cpp @@ -26,16 +26,16 @@ namespace boost #endif /* (BOOST_VERSION <= 104600) */ -GameSettings::GameSettings(Files::ConfigurationManager &cfg) +Launcher::GameSettings::GameSettings(Files::ConfigurationManager &cfg) : mCfgMgr(cfg) { } -GameSettings::~GameSettings() +Launcher::GameSettings::~GameSettings() { } -void GameSettings::validatePaths() +void Launcher::GameSettings::validatePaths() { if (mSettings.isEmpty() || !mDataDirs.isEmpty()) return; // Don't re-validate paths if they are already parsed @@ -81,14 +81,14 @@ void GameSettings::validatePaths() } } -QStringList GameSettings::values(const QString &key, const QStringList &defaultValues) +QStringList Launcher::GameSettings::values(const QString &key, const QStringList &defaultValues) { if (!mSettings.values(key).isEmpty()) return mSettings.values(key); return defaultValues; } -bool GameSettings::readFile(QTextStream &stream) +bool Launcher::GameSettings::readFile(QTextStream &stream) { QMap cache; QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); @@ -130,7 +130,7 @@ bool GameSettings::readFile(QTextStream &stream) return true; } -bool GameSettings::writeFile(QTextStream &stream) +bool Launcher::GameSettings::writeFile(QTextStream &stream) { // Iterate in reverse order to preserve insertion order QMapIterator i(mSettings); diff --git a/apps/launcher/settings/gamesettings.hpp b/apps/launcher/settings/gamesettings.hpp index 55b2107e2a..11f06b027c 100644 --- a/apps/launcher/settings/gamesettings.hpp +++ b/apps/launcher/settings/gamesettings.hpp @@ -11,52 +11,54 @@ namespace Files { typedef std::vector PathContainer; struct ConfigurationManager;} -class GameSettings +namespace Launcher { -public: - GameSettings(Files::ConfigurationManager &cfg); - ~GameSettings(); - - inline QString value(const QString &key, const QString &defaultValue = QString()) + class GameSettings { - return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key); - } + public: + GameSettings(Files::ConfigurationManager &cfg); + ~GameSettings(); + + inline QString value(const QString &key, const QString &defaultValue = QString()) + { + return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key); + } - inline void setValue(const QString &key, const QString &value) - { - mSettings.insert(key, value); - } + inline void setValue(const QString &key, const QString &value) + { + mSettings.insert(key, value); + } - inline void setMultiValue(const QString &key, const QString &value) - { - QStringList values = mSettings.values(key); - if (!values.contains(value)) - mSettings.insertMulti(key, value); - } + inline void setMultiValue(const QString &key, const QString &value) + { + QStringList values = mSettings.values(key); + if (!values.contains(value)) + mSettings.insertMulti(key, value); + } - inline void remove(const QString &key) - { - mSettings.remove(key); - } + inline void remove(const QString &key) + { + mSettings.remove(key); + } - inline QStringList getDataDirs() { return mDataDirs; } - inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); } - inline QString getDataLocal() {return mDataLocal; } - inline bool hasMaster() { return mSettings.count(QString("master")) > 0; } + inline QStringList getDataDirs() { return mDataDirs; } + inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); } + inline QString getDataLocal() {return mDataLocal; } + inline bool hasMaster() { return mSettings.count(QString("master")) > 0; } - QStringList values(const QString &key, const QStringList &defaultValues = QStringList()); - bool readFile(QTextStream &stream); - bool writeFile(QTextStream &stream); + QStringList values(const QString &key, const QStringList &defaultValues = QStringList()); + bool readFile(QTextStream &stream); + bool writeFile(QTextStream &stream); -private: - Files::ConfigurationManager &mCfgMgr; + private: + Files::ConfigurationManager &mCfgMgr; - void validatePaths(); - QMap mSettings; - - QStringList mDataDirs; - QString mDataLocal; -}; + void validatePaths(); + QMap mSettings; + QStringList mDataDirs; + QString mDataLocal; + }; +} #endif // GAMESETTINGS_HPP diff --git a/apps/launcher/settings/graphicssettings.cpp b/apps/launcher/settings/graphicssettings.cpp index 0c55800917..9dad3dee6b 100644 --- a/apps/launcher/settings/graphicssettings.cpp +++ b/apps/launcher/settings/graphicssettings.cpp @@ -5,15 +5,15 @@ #include #include -GraphicsSettings::GraphicsSettings() +Launcher::GraphicsSettings::GraphicsSettings() { } -GraphicsSettings::~GraphicsSettings() +Launcher::GraphicsSettings::~GraphicsSettings() { } -bool GraphicsSettings::writeFile(QTextStream &stream) +bool Launcher::GraphicsSettings::writeFile(QTextStream &stream) { QString sectionPrefix; QRegExp sectionRe("([^/]+)/(.+)$"); diff --git a/apps/launcher/settings/graphicssettings.hpp b/apps/launcher/settings/graphicssettings.hpp index 3e8617849e..6f7c135473 100644 --- a/apps/launcher/settings/graphicssettings.hpp +++ b/apps/launcher/settings/graphicssettings.hpp @@ -3,14 +3,16 @@ #include "settingsbase.hpp" -class GraphicsSettings : public SettingsBase > +namespace Launcher { -public: - GraphicsSettings(); - ~GraphicsSettings(); + class GraphicsSettings : public SettingsBase > + { + public: + GraphicsSettings(); + ~GraphicsSettings(); - bool writeFile(QTextStream &stream); - -}; + bool writeFile(QTextStream &stream); + }; +} #endif // GRAPHICSSETTINGS_HPP diff --git a/apps/launcher/settings/launchersettings.cpp b/apps/launcher/settings/launchersettings.cpp index 5d298e814e..7c97144eaf 100644 --- a/apps/launcher/settings/launchersettings.cpp +++ b/apps/launcher/settings/launchersettings.cpp @@ -5,15 +5,15 @@ #include #include -LauncherSettings::LauncherSettings() +Launcher::LauncherSettings::LauncherSettings() { } -LauncherSettings::~LauncherSettings() +Launcher::LauncherSettings::~LauncherSettings() { } -QStringList LauncherSettings::values(const QString &key, Qt::MatchFlags flags) +QStringList Launcher::LauncherSettings::values(const QString &key, Qt::MatchFlags flags) { QMap settings = SettingsBase::getSettings(); @@ -34,7 +34,7 @@ QStringList LauncherSettings::values(const QString &key, Qt::MatchFlags flags) return result; } -QStringList LauncherSettings::subKeys(const QString &key) +QStringList Launcher::LauncherSettings::subKeys(const QString &key) { QMap settings = SettingsBase::getSettings(); QStringList keys = settings.uniqueKeys(); @@ -61,7 +61,7 @@ QStringList LauncherSettings::subKeys(const QString &key) return result; } -bool LauncherSettings::writeFile(QTextStream &stream) +bool Launcher::LauncherSettings::writeFile(QTextStream &stream) { QString sectionPrefix; QRegExp sectionRe("([^/]+)/(.+)$"); diff --git a/apps/launcher/settings/launchersettings.hpp b/apps/launcher/settings/launchersettings.hpp index 60c6f86bc7..8acc389a9d 100644 --- a/apps/launcher/settings/launchersettings.hpp +++ b/apps/launcher/settings/launchersettings.hpp @@ -3,17 +3,19 @@ #include "settingsbase.hpp" -class LauncherSettings : public SettingsBase > +namespace Launcher { -public: - LauncherSettings(); - ~LauncherSettings(); + class LauncherSettings : public SettingsBase > + { + public: + LauncherSettings(); + ~LauncherSettings(); - QStringList subKeys(const QString &key); - QStringList values(const QString &key, Qt::MatchFlags flags = Qt::MatchExactly); + QStringList subKeys(const QString &key); + QStringList values(const QString &key, Qt::MatchFlags flags = Qt::MatchExactly); - bool writeFile(QTextStream &stream); - -}; + bool writeFile(QTextStream &stream); + }; +} #endif // LAUNCHERSETTINGS_HPP diff --git a/apps/launcher/settings/settingsbase.hpp b/apps/launcher/settings/settingsbase.hpp index ed8ada56c3..3a1cf8e30e 100644 --- a/apps/launcher/settings/settingsbase.hpp +++ b/apps/launcher/settings/settingsbase.hpp @@ -7,103 +7,105 @@ #include #include -template -class SettingsBase +namespace Launcher { - -public: - SettingsBase() { mMultiValue = false; } - ~SettingsBase() {} - - inline QString value(const QString &key, const QString &defaultValue = QString()) + template + class SettingsBase { - return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key); - } - inline void setValue(const QString &key, const QString &value) - { - QStringList values = mSettings.values(key); - if (!values.contains(value)) - mSettings.insert(key, value); - } + public: + SettingsBase() { mMultiValue = false; } + ~SettingsBase() {} - inline void setMultiValue(const QString &key, const QString &value) - { - QStringList values = mSettings.values(key); - if (!values.contains(value)) - mSettings.insertMulti(key, value); - } + inline QString value(const QString &key, const QString &defaultValue = QString()) + { + return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key); + } - inline void setMultiValueEnabled(bool enable) - { - mMultiValue = enable; - } + inline void setValue(const QString &key, const QString &value) + { + QStringList values = mSettings.values(key); + if (!values.contains(value)) + mSettings.insert(key, value); + } - inline void remove(const QString &key) - { - mSettings.remove(key); - } + inline void setMultiValue(const QString &key, const QString &value) + { + QStringList values = mSettings.values(key); + if (!values.contains(value)) + mSettings.insertMulti(key, value); + } - Map getSettings() {return mSettings;} + inline void setMultiValueEnabled(bool enable) + { + mMultiValue = enable; + } - bool readFile(QTextStream &stream) - { - mCache.clear(); + inline void remove(const QString &key) + { + mSettings.remove(key); + } - QString sectionPrefix; + Map getSettings() {return mSettings;} - QRegExp sectionRe("^\\[([^]]+)\\]"); - QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); + bool readFile(QTextStream &stream) + { + mCache.clear(); - while (!stream.atEnd()) { - QString line = stream.readLine(); + QString sectionPrefix; - if (line.isEmpty() || line.startsWith("#")) - continue; + QRegExp sectionRe("^\\[([^]]+)\\]"); + QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); - if (sectionRe.exactMatch(line)) { - sectionPrefix = sectionRe.cap(1); - sectionPrefix.append("/"); - continue; - } + while (!stream.atEnd()) { + QString line = stream.readLine(); - if (keyRe.indexIn(line) != -1) { + if (line.isEmpty() || line.startsWith("#")) + continue; - QString key = keyRe.cap(1).trimmed(); - QString value = keyRe.cap(2).trimmed(); + if (sectionRe.exactMatch(line)) { + sectionPrefix = sectionRe.cap(1); + sectionPrefix.append("/"); + continue; + } - if (!sectionPrefix.isEmpty()) - key.prepend(sectionPrefix); + if (keyRe.indexIn(line) != -1) { - mSettings.remove(key); + QString key = keyRe.cap(1).trimmed(); + QString value = keyRe.cap(2).trimmed(); - QStringList values = mCache.values(key); + if (!sectionPrefix.isEmpty()) + key.prepend(sectionPrefix); - if (!values.contains(value)) { - if (mMultiValue) { - mCache.insertMulti(key, value); - } else { - mCache.insert(key, value); + mSettings.remove(key); + + QStringList values = mCache.values(key); + + if (!values.contains(value)) { + if (mMultiValue) { + mCache.insertMulti(key, value); + } else { + mCache.insert(key, value); + } } } } - } - if (mSettings.isEmpty()) { - mSettings = mCache; // This is the first time we read a file + if (mSettings.isEmpty()) { + mSettings = mCache; // This is the first time we read a file + return true; + } + + // Merge the changed keys with those which didn't + mSettings.unite(mCache); return true; } - // Merge the changed keys with those which didn't - mSettings.unite(mCache); - return true; - } - -private: - Map mSettings; - Map mCache; - - bool mMultiValue; -}; + private: + Map mSettings; + Map mCache; + bool mMultiValue; + }; +} #endif // SETTINGSBASE_HPP diff --git a/apps/launcher/textslotmsgbox.cpp b/apps/launcher/textslotmsgbox.cpp index 0607d1cc6e..62d9cf5761 100644 --- a/apps/launcher/textslotmsgbox.cpp +++ b/apps/launcher/textslotmsgbox.cpp @@ -1,6 +1,6 @@ #include "textslotmsgbox.hpp" -void TextSlotMsgBox::setTextSlot(const QString& string) +void Launcher::TextSlotMsgBox::setTextSlot(const QString& string) { setText(string); } diff --git a/apps/launcher/textslotmsgbox.hpp b/apps/launcher/textslotmsgbox.hpp index a29e2c3543..a0fefaa253 100644 --- a/apps/launcher/textslotmsgbox.hpp +++ b/apps/launcher/textslotmsgbox.hpp @@ -3,11 +3,13 @@ #include -class TextSlotMsgBox : public QMessageBox +namespace Launcher { -Q_OBJECT - public slots: - void setTextSlot(const QString& string); -}; - + class TextSlotMsgBox : public QMessageBox + { + Q_OBJECT + public slots: + void setTextSlot(const QString& string); + }; +} #endif diff --git a/apps/launcher/unshieldthread.cpp b/apps/launcher/unshieldthread.cpp index 69b241365c..d0dbeb1bdb 100644 --- a/apps/launcher/unshieldthread.cpp +++ b/apps/launcher/unshieldthread.cpp @@ -292,30 +292,30 @@ namespace } -bool UnshieldThread::SetMorrowindPath(const std::string& path) +bool Launcher::UnshieldThread::SetMorrowindPath(const std::string& path) { mMorrowindPath = path; return true; } -bool UnshieldThread::SetTribunalPath(const std::string& path) +bool Launcher::UnshieldThread::SetTribunalPath(const std::string& path) { mTribunalPath = path; return true; } -bool UnshieldThread::SetBloodmoonPath(const std::string& path) +bool Launcher::UnshieldThread::SetBloodmoonPath(const std::string& path) { mBloodmoonPath = path; return true; } -void UnshieldThread::SetOutputPath(const std::string& path) +void Launcher::UnshieldThread::SetOutputPath(const std::string& path) { mOutputPath = path; } -bool UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, const char* prefix, int index) +bool Launcher::UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, const char* prefix, int index) { bool success; bfs::path dirname; @@ -349,7 +349,7 @@ bool UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, cons return success; } -void UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_dir, bool extract_ini) +void Launcher::UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_dir, bool extract_ini) { Unshield * unshield; unshield = unshield_open(cab.c_str()); @@ -369,7 +369,7 @@ void UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_d } -bool UnshieldThread::extract() +bool Launcher::UnshieldThread::extract() { bfs::path outputDataFilesDir = mOutputPath; outputDataFilesDir /= "Data Files"; @@ -475,7 +475,7 @@ bool UnshieldThread::extract() return true; } -void UnshieldThread::Done() +void Launcher::UnshieldThread::Done() { // Get rid of unnecessary files bfs::remove_all(mOutputPath / "extract-temp"); @@ -491,28 +491,28 @@ void UnshieldThread::Done() bfs::last_write_time(findFile(mOutputPath, "bloodmoon.esm"), getTime("3 June 2003")); } -std::string UnshieldThread::GetMWEsmPath() +std::string Launcher::UnshieldThread::GetMWEsmPath() { return findFile(mOutputPath / "Data Files", "morrowind.esm").string(); } -bool UnshieldThread::TribunalDone() +bool Launcher::UnshieldThread::TribunalDone() { return mTribunalDone; } -bool UnshieldThread::BloodmoonDone() +bool Launcher::UnshieldThread::BloodmoonDone() { return mBloodmoonDone; } -void UnshieldThread::run() +void Launcher::UnshieldThread::run() { extract(); emit close(); } -UnshieldThread::UnshieldThread() +Launcher::UnshieldThread::UnshieldThread() { unshield_set_log_level(0); mMorrowindDone = false; diff --git a/apps/launcher/unshieldthread.hpp b/apps/launcher/unshieldthread.hpp index 655cb5b536..de6a32b442 100644 --- a/apps/launcher/unshieldthread.hpp +++ b/apps/launcher/unshieldthread.hpp @@ -7,50 +7,52 @@ #include -class UnshieldThread : public QThread +namespace Launcher { - Q_OBJECT + class UnshieldThread : public QThread + { + Q_OBJECT - public: - bool SetMorrowindPath(const std::string& path); - bool SetTribunalPath(const std::string& path); - bool SetBloodmoonPath(const std::string& path); + public: + bool SetMorrowindPath(const std::string& path); + bool SetTribunalPath(const std::string& path); + bool SetBloodmoonPath(const std::string& path); - void SetOutputPath(const std::string& path); - - bool extract(); + void SetOutputPath(const std::string& path); - bool TribunalDone(); - bool BloodmoonDone(); + bool extract(); - void Done(); + bool TribunalDone(); + bool BloodmoonDone(); - std::string GetMWEsmPath(); + void Done(); - UnshieldThread(); + std::string GetMWEsmPath(); - private: + UnshieldThread(); - void extract_cab(const boost::filesystem::path& cab, const boost::filesystem::path& output_dir, bool extract_ini = false); - bool extract_file(Unshield* unshield, boost::filesystem::path output_dir, const char* prefix, int index); - - boost::filesystem::path mMorrowindPath; - boost::filesystem::path mTribunalPath; - boost::filesystem::path mBloodmoonPath; + private: - bool mMorrowindDone; - bool mTribunalDone; - bool mBloodmoonDone; + void extract_cab(const boost::filesystem::path& cab, const boost::filesystem::path& output_dir, bool extract_ini = false); + bool extract_file(Unshield* unshield, boost::filesystem::path output_dir, const char* prefix, int index); - boost::filesystem::path mOutputPath; + boost::filesystem::path mMorrowindPath; + boost::filesystem::path mTribunalPath; + boost::filesystem::path mBloodmoonPath; + + bool mMorrowindDone; + bool mTribunalDone; + bool mBloodmoonDone; + + boost::filesystem::path mOutputPath; - protected: - virtual void run(); - - signals: - void signalGUI(QString); - void close(); -}; + protected: + virtual void run(); + signals: + void signalGUI(QString); + void close(); + }; +} #endif diff --git a/apps/launcher/utils/checkablemessagebox.cpp b/apps/launcher/utils/checkablemessagebox.cpp index 41207a8ded..2f775af57a 100644 --- a/apps/launcher/utils/checkablemessagebox.cpp +++ b/apps/launcher/utils/checkablemessagebox.cpp @@ -54,72 +54,61 @@ Emulates the QMessageBox API with static conveniences. The message label can open external URLs. */ - -class CheckableMessageBoxPrivate -{ -public: - CheckableMessageBoxPrivate(QDialog *q) +Launcher::CheckableMessageBoxPrivate::CheckableMessageBoxPrivate(QDialog *q) : clickedButton(0) - { - QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); +{ + QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); - pixmapLabel = new QLabel(q); - sizePolicy.setHorizontalStretch(0); - sizePolicy.setVerticalStretch(0); - sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth()); - pixmapLabel->setSizePolicy(sizePolicy); - pixmapLabel->setVisible(false); + pixmapLabel = new QLabel(q); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth()); + pixmapLabel->setSizePolicy(sizePolicy); + pixmapLabel->setVisible(false); - QSpacerItem *pixmapSpacer = - new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); + QSpacerItem *pixmapSpacer = + new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); - messageLabel = new QLabel(q); - messageLabel->setMinimumSize(QSize(300, 0)); - messageLabel->setWordWrap(true); - messageLabel->setOpenExternalLinks(true); - messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse); + messageLabel = new QLabel(q); + messageLabel->setMinimumSize(QSize(300, 0)); + messageLabel->setWordWrap(true); + messageLabel->setOpenExternalLinks(true); + messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse); - QSpacerItem *checkBoxRightSpacer = - new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); - QSpacerItem *buttonSpacer = - new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum); + QSpacerItem *checkBoxRightSpacer = + new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); + QSpacerItem *buttonSpacer = + new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum); - checkBox = new QCheckBox(q); - checkBox->setText(CheckableMessageBox::tr("Do not ask again")); + checkBox = new QCheckBox(q); + checkBox->setText(Launcher::CheckableMessageBox::tr("Do not ask again")); - buttonBox = new QDialogButtonBox(q); - buttonBox->setOrientation(Qt::Horizontal); - buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + buttonBox = new QDialogButtonBox(q); + buttonBox->setOrientation(Qt::Horizontal); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); - QVBoxLayout *verticalLayout = new QVBoxLayout(); - verticalLayout->addWidget(pixmapLabel); - verticalLayout->addItem(pixmapSpacer); + QVBoxLayout *verticalLayout = new QVBoxLayout(); + verticalLayout->addWidget(pixmapLabel); + verticalLayout->addItem(pixmapSpacer); - QHBoxLayout *horizontalLayout_2 = new QHBoxLayout(); - horizontalLayout_2->addLayout(verticalLayout); - horizontalLayout_2->addWidget(messageLabel); + QHBoxLayout *horizontalLayout_2 = new QHBoxLayout(); + horizontalLayout_2->addLayout(verticalLayout); + horizontalLayout_2->addWidget(messageLabel); - QHBoxLayout *horizontalLayout = new QHBoxLayout(); - horizontalLayout->addWidget(checkBox); - horizontalLayout->addItem(checkBoxRightSpacer); + QHBoxLayout *horizontalLayout = new QHBoxLayout(); + horizontalLayout->addWidget(checkBox); + horizontalLayout->addItem(checkBoxRightSpacer); - QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q); - verticalLayout_2->addLayout(horizontalLayout_2); - verticalLayout_2->addLayout(horizontalLayout); - verticalLayout_2->addItem(buttonSpacer); - verticalLayout_2->addWidget(buttonBox); - } + QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q); + verticalLayout_2->addLayout(horizontalLayout_2); + verticalLayout_2->addLayout(horizontalLayout); + verticalLayout_2->addItem(buttonSpacer); + verticalLayout_2->addWidget(buttonBox); +} - QLabel *pixmapLabel; - QLabel *messageLabel; - QCheckBox *checkBox; - QDialogButtonBox *buttonBox; - QAbstractButton *clickedButton; -}; - -CheckableMessageBox::CheckableMessageBox(QWidget *parent) : +Launcher::CheckableMessageBox::CheckableMessageBox(QWidget *parent) : QDialog(parent), - d(new CheckableMessageBoxPrivate(this)) + d(new Launcher::CheckableMessageBoxPrivate(this)) { setModal(true); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); @@ -129,102 +118,102 @@ CheckableMessageBox::CheckableMessageBox(QWidget *parent) : SLOT(slotClicked(QAbstractButton*))); } -CheckableMessageBox::~CheckableMessageBox() +Launcher::CheckableMessageBox::~CheckableMessageBox() { delete d; } -void CheckableMessageBox::slotClicked(QAbstractButton *b) +void Launcher::CheckableMessageBox::slotClicked(QAbstractButton *b) { d->clickedButton = b; } -QAbstractButton *CheckableMessageBox::clickedButton() const +QAbstractButton *Launcher::CheckableMessageBox::clickedButton() const { return d->clickedButton; } -QDialogButtonBox::StandardButton CheckableMessageBox::clickedStandardButton() const +QDialogButtonBox::StandardButton Launcher::CheckableMessageBox::clickedStandardButton() const { if (d->clickedButton) return d->buttonBox->standardButton(d->clickedButton); return QDialogButtonBox::NoButton; } -QString CheckableMessageBox::text() const +QString Launcher::CheckableMessageBox::text() const { return d->messageLabel->text(); } -void CheckableMessageBox::setText(const QString &t) +void Launcher::CheckableMessageBox::setText(const QString &t) { d->messageLabel->setText(t); } -QPixmap CheckableMessageBox::iconPixmap() const +QPixmap Launcher::CheckableMessageBox::iconPixmap() const { if (const QPixmap *p = d->pixmapLabel->pixmap()) return QPixmap(*p); return QPixmap(); } -void CheckableMessageBox::setIconPixmap(const QPixmap &p) +void Launcher::CheckableMessageBox::setIconPixmap(const QPixmap &p) { d->pixmapLabel->setPixmap(p); d->pixmapLabel->setVisible(!p.isNull()); } -bool CheckableMessageBox::isChecked() const +bool Launcher::CheckableMessageBox::isChecked() const { return d->checkBox->isChecked(); } -void CheckableMessageBox::setChecked(bool s) +void Launcher::CheckableMessageBox::setChecked(bool s) { d->checkBox->setChecked(s); } -QString CheckableMessageBox::checkBoxText() const +QString Launcher::CheckableMessageBox::checkBoxText() const { return d->checkBox->text(); } -void CheckableMessageBox::setCheckBoxText(const QString &t) +void Launcher::CheckableMessageBox::setCheckBoxText(const QString &t) { d->checkBox->setText(t); } -bool CheckableMessageBox::isCheckBoxVisible() const +bool Launcher::CheckableMessageBox::isCheckBoxVisible() const { return d->checkBox->isVisible(); } -void CheckableMessageBox::setCheckBoxVisible(bool v) +void Launcher::CheckableMessageBox::setCheckBoxVisible(bool v) { d->checkBox->setVisible(v); } -QDialogButtonBox::StandardButtons CheckableMessageBox::standardButtons() const +QDialogButtonBox::StandardButtons Launcher::CheckableMessageBox::standardButtons() const { return d->buttonBox->standardButtons(); } -void CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s) +void Launcher::CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s) { d->buttonBox->setStandardButtons(s); } -QPushButton *CheckableMessageBox::button(QDialogButtonBox::StandardButton b) const +QPushButton *Launcher::CheckableMessageBox::button(QDialogButtonBox::StandardButton b) const { return d->buttonBox->button(b); } -QPushButton *CheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role) +QPushButton *Launcher::CheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role) { return d->buttonBox->addButton(text, role); } -QDialogButtonBox::StandardButton CheckableMessageBox::defaultButton() const +QDialogButtonBox::StandardButton Launcher::CheckableMessageBox::defaultButton() const { foreach (QAbstractButton *b, d->buttonBox->buttons()) if (QPushButton *pb = qobject_cast(b)) @@ -233,7 +222,7 @@ QDialogButtonBox::StandardButton CheckableMessageBox::defaultButton() const return QDialogButtonBox::NoButton; } -void CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s) +void Launcher::CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s) { if (QPushButton *b = d->buttonBox->button(s)) { b->setDefault(true); @@ -242,7 +231,7 @@ void CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s) } QDialogButtonBox::StandardButton -CheckableMessageBox::question(QWidget *parent, +Launcher::CheckableMessageBox::question(QWidget *parent, const QString &title, const QString &question, const QString &checkBoxText, @@ -263,7 +252,7 @@ CheckableMessageBox::question(QWidget *parent, return mb.clickedStandardButton(); } -QMessageBox::StandardButton CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db) +QMessageBox::StandardButton Launcher::CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db) { return static_cast(int(db)); } diff --git a/apps/launcher/utils/checkablemessagebox.hpp b/apps/launcher/utils/checkablemessagebox.hpp index 93fd43fe1f..09a501b9c2 100644 --- a/apps/launcher/utils/checkablemessagebox.hpp +++ b/apps/launcher/utils/checkablemessagebox.hpp @@ -34,67 +34,83 @@ #include #include -class CheckableMessageBoxPrivate; +class QCheckBox; -class CheckableMessageBox : public QDialog +namespace Launcher { - Q_OBJECT - Q_PROPERTY(QString text READ text WRITE setText) - Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap) - Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked) - Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText) - Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons) - Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton) + class CheckableMessageBoxPrivate + { + public: -public: - explicit CheckableMessageBox(QWidget *parent); - virtual ~CheckableMessageBox(); + QLabel *pixmapLabel; + QLabel *messageLabel; + QCheckBox *checkBox; + QDialogButtonBox *buttonBox; + QAbstractButton *clickedButton; - static QDialogButtonBox::StandardButton - question(QWidget *parent, - const QString &title, - const QString &question, - const QString &checkBoxText, - bool *checkBoxSetting, - QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No, - QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No); + public: + CheckableMessageBoxPrivate(QDialog *q); + }; - QString text() const; - void setText(const QString &); + class CheckableMessageBox : public QDialog + { + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap) + Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked) + Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText) + Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons) + Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton) - bool isChecked() const; - void setChecked(bool s); + public: + explicit CheckableMessageBox(QWidget *parent); + virtual ~CheckableMessageBox(); - QString checkBoxText() const; - void setCheckBoxText(const QString &); + static QDialogButtonBox::StandardButton + question(QWidget *parent, + const QString &title, + const QString &question, + const QString &checkBoxText, + bool *checkBoxSetting, + QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No, + QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No); - bool isCheckBoxVisible() const; - void setCheckBoxVisible(bool); + QString text() const; + void setText(const QString &); - QDialogButtonBox::StandardButtons standardButtons() const; - void setStandardButtons(QDialogButtonBox::StandardButtons s); - QPushButton *button(QDialogButtonBox::StandardButton b) const; - QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role); + bool isChecked() const; + void setChecked(bool s); - QDialogButtonBox::StandardButton defaultButton() const; - void setDefaultButton(QDialogButtonBox::StandardButton s); + QString checkBoxText() const; + void setCheckBoxText(const QString &); - // See static QMessageBox::standardPixmap() - QPixmap iconPixmap() const; - void setIconPixmap (const QPixmap &p); + bool isCheckBoxVisible() const; + void setCheckBoxVisible(bool); - // Query the result - QAbstractButton *clickedButton() const; - QDialogButtonBox::StandardButton clickedStandardButton() const; + QDialogButtonBox::StandardButtons standardButtons() const; + void setStandardButtons(QDialogButtonBox::StandardButtons s); + QPushButton *button(QDialogButtonBox::StandardButton b) const; + QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role); - // Conversion convenience - static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton); + QDialogButtonBox::StandardButton defaultButton() const; + void setDefaultButton(QDialogButtonBox::StandardButton s); -private slots: - void slotClicked(QAbstractButton *b); + // See static QMessageBox::standardPixmap() + QPixmap iconPixmap() const; + void setIconPixmap (const QPixmap &p); -private: - CheckableMessageBoxPrivate *d; -}; + // Query the result + QAbstractButton *clickedButton() const; + QDialogButtonBox::StandardButton clickedStandardButton() const; + // Conversion convenience + static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton); + + private slots: + void slotClicked(QAbstractButton *b); + + private: + CheckableMessageBoxPrivate *d; + }; +} #endif // CHECKABLEMESSAGEBOX_HPP diff --git a/apps/launcher/utils/textinputdialog.cpp b/apps/launcher/utils/textinputdialog.cpp index 9957e7dda8..76cbe32d01 100644 --- a/apps/launcher/utils/textinputdialog.cpp +++ b/apps/launcher/utils/textinputdialog.cpp @@ -7,7 +7,7 @@ #include #include -TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) : +Launcher::TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) : QDialog(parent) { setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); @@ -45,19 +45,19 @@ TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWid } -int TextInputDialog::exec() +int Launcher::TextInputDialog::exec() { mLineEdit->clear(); mLineEdit->setFocus(); return QDialog::exec(); } -QString TextInputDialog::getText() const +QString Launcher::TextInputDialog::getText() const { return mLineEdit->text(); } -void TextInputDialog::slotUpdateOkButton(QString text) +void Launcher::TextInputDialog::slotUpdateOkButton(QString text) { bool enabled = !(text.isEmpty()); mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(enabled); @@ -73,7 +73,7 @@ void TextInputDialog::slotUpdateOkButton(QString text) } } -TextInputDialog::DialogLineEdit::DialogLineEdit (QWidget *parent) : +Launcher::TextInputDialog::DialogLineEdit::DialogLineEdit (QWidget *parent) : LineEdit (parent) { int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); diff --git a/apps/launcher/utils/textinputdialog.hpp b/apps/launcher/utils/textinputdialog.hpp index 148bbd1522..bb01778be3 100644 --- a/apps/launcher/utils/textinputdialog.hpp +++ b/apps/launcher/utils/textinputdialog.hpp @@ -7,33 +7,34 @@ class QDialogButtonBox; -class LineEdit; - -class TextInputDialog : public QDialog +namespace Launcher { - Q_OBJECT - - class DialogLineEdit : public LineEdit + class TextInputDialog : public QDialog { + Q_OBJECT + + class DialogLineEdit : public LineEdit + { + public: + explicit DialogLineEdit (QWidget *parent = 0); + }; + + DialogLineEdit *mLineEdit; + QDialogButtonBox *mButtonBox; + public: - explicit DialogLineEdit (QWidget *parent = 0); + + explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0); + ~TextInputDialog () {} + + QString getText() const; + + int exec(); + + private slots: + void slotUpdateOkButton(QString text); + }; - - DialogLineEdit *mLineEdit; - QDialogButtonBox *mButtonBox; - -public: - - explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0); - ~TextInputDialog () {} - - QString getText() const; - - int exec(); - -private slots: - void slotUpdateOkButton(QString text); - -}; +} #endif // TEXTINPUTDIALOG_HPP From ba365ff49ed9be07af0244389077c3fc71b36d7a Mon Sep 17 00:00:00 2001 From: graffy76 Date: Fri, 25 Oct 2013 19:23:03 -0500 Subject: [PATCH 099/148] Fixed merge conflicts with saving branch --- apps/launcher/datafilespage.cpp | 34 ------------------- apps/opencs/editor.cpp | 8 ++--- apps/opencs/editor.hpp | 2 +- apps/opencs/model/doc/document.cpp | 2 +- apps/opencs/model/world/idcollection.hpp | 2 +- apps/opencs/view/doc/filedialog.cpp | 42 +++++++++++------------- apps/opencs/view/doc/filedialog.hpp | 9 +++-- 7 files changed, 31 insertions(+), 68 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index d36ab65e93..e246b45154 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -161,42 +161,8 @@ void Launcher::DataFilesPage::slotProfileDeleted (const QString &item) void Launcher::DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString ¤t) { -<<<<<<< HEAD setProfile(previous, current, true); emit signalProfileChanged (ui.profilesComboBox->findText(current)); -======= - if (mContentModel->rowCount() < 1) - return; - - QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); - - if (profile.isEmpty()) { - profile = profilesComboBox->currentText(); - mLauncherSettings.setValue(QString("Profiles/currentprofile"), profile); - } - - mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master")); - mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin")); - - mGameSettings.remove(QString("master")); - mGameSettings.remove(QString("plugins")); - mGameSettings.remove(QString("content")); - - ContentSelectorModel::ContentFileList items = mContentModel->checkedItems(); - - foreach(const ContentSelectorModel::EsmFile *item, items) { - - if (item->gameFiles().size() == 0) { - mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/master"), item->fileName()); - mGameSettings.setMultiValue(QString("content"), item->fileName()); - - } else { - mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/plugin"), item->fileName()); - mGameSettings.setMultiValue(QString("content"), item->fileName()); - } - } - ->>>>>>> 3146af34d642a28b15b55f7eb9999d8ac50168a0 } void Launcher::DataFilesPage::slotProfileRenamed(const QString &previous, const QString ¤t) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 03758b16e5..51cc490c73 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -10,6 +10,7 @@ #include "model/world/data.hpp" #include +#include CS::Editor::Editor() : mDocumentManager (mCfgMgr), mViewManager (mDocumentManager) @@ -19,7 +20,6 @@ CS::Editor::Editor() setupDataFiles(); mNewGame.setLocalData (mLocal); - mFileDialog.setLocalData (mLocal); connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ())); connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ())); @@ -32,8 +32,8 @@ CS::Editor::Editor() connect (&mStartup, SIGNAL (editConfig()), this, SLOT (showSettings ())); connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles())); - connect (&mFileDialog, SIGNAL(createNewFile (const boost::filesystem::path&)), - this, SLOT(createNewFile (const boost::filesystem::path&))); + connect (&mFileDialog, SIGNAL(createNewFile ()), + this, SLOT(createNewFile ())); connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)), this, SLOT (createNewGame (const boost::filesystem::path&))); @@ -141,7 +141,7 @@ void CS::Editor::openFiles() mFileDialog.hide(); } -void CS::Editor::createNewFile (const boost::filesystem::path& savePath) +void CS::Editor::createNewFile () { std::vector files; diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 16f6b9516c..ef013bc8f9 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -60,7 +60,7 @@ namespace CS void loadDocument(); void openFiles(); - void createNewFile (const boost::filesystem::path& savePath); + void createNewFile (); void createNewGame (const boost::filesystem::path& file); void showStartup(); diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index cc886f9cc2..590a19439c 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2246,7 +2246,7 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, co mData.setAuthor (""); } /// \todo un-outcomment the else, once loading an existing content file works properly again. -// else + else { if (boost::filesystem::exists (mProjectPath)) { diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 0d723bef1e..a7b37be5bc 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -111,7 +111,7 @@ namespace CSMWorld else { record.mState = RecordBase::State_Deleted; - setRecord (index, record); + this->setRecord (index, record); } return true; diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 0bf7c6951b..fb6c0a0e89 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -17,8 +17,6 @@ #include "filewidget.hpp" #include "adjusterwidget.hpp" -#include - CSVDoc::FileDialog::FileDialog(QWidget *parent) : QDialog(parent), mSelector (0) { @@ -61,6 +59,13 @@ void CSVDoc::FileDialog::showDialog(DialogType dialogType) break; } + //connections common to both dialog view flavors + connect (mSelector, SIGNAL (signalCurrentGamefileIndexChanged (int)), + this, SLOT (slotUpdateAcceptButton (int))); + + connect (ui.projectButtonBox, SIGNAL (accepted()), this, SIGNAL (createNewFile())); + connect (ui.projectButtonBox, SIGNAL (rejected()), this, SLOT (slotRejected())); + show(); raise(); activateWindow(); @@ -82,13 +87,7 @@ void CSVDoc::FileDialog::buildNewFileView() ui.projectGroupBoxLayout->insertWidget (0, mFileWidget); connect (mFileWidget, SIGNAL (nameChanged(const QString &, bool)), - this, SLOT (slotUpdateCreateButton(const QString &, bool))); - - connect (mSelector, SIGNAL (signalCurrentGamefileIndexChanged (int)), - this, SLOT (slotUpdateCreateButton (int))); - - connect (ui.projectButtonBox, SIGNAL (accepted()), this, SIGNAL (createNewFile())); - connect (ui.projectButtonBox, SIGNAL (rejected()), this, SLOT (slotRejected())); + this, SLOT (slotUpdateAcceptButton(const QString &, bool))); } void CSVDoc::FileDialog::buildOpenFileView() @@ -96,21 +95,25 @@ void CSVDoc::FileDialog::buildOpenFileView() setWindowTitle(tr("Open")); ui.projectGroupBox->setTitle (QString("")); - connect (ui.projectButtonBox, SIGNAL (accepted()), this, SIGNAL (openFiles())); - connect (ui.projectButtonBox, SIGNAL (rejected()), this, SLOT (slotRejected())); + ui.projectButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false); } -void CSVDoc::FileDialog::slotUpdateCreateButton (int) +void CSVDoc::FileDialog::slotUpdateAcceptButton (int) { - slotUpdateCreateButton (mFileWidget->getName(), true); + QString name = ""; + + if (mDialogType == DialogType_New) + name = mFileWidget->getName(); + + slotUpdateAcceptButton (name, true); } -void CSVDoc::FileDialog::slotUpdateCreateButton(const QString &name, bool) +void CSVDoc::FileDialog::slotUpdateAcceptButton(const QString &name, bool) { - if (!(mDialogType == DialogType_New)) - return; + bool success = (mSelector->selectedFiles().size() > 0); - bool success = (!name.isEmpty() && mSelector->selectedFiles().size() > 0); + if (mDialogType == DialogType_New) + success = success && !(name.isEmpty()); ui.projectButtonBox->button (QDialogButtonBox::Ok)->setEnabled (success); } @@ -128,8 +131,3 @@ void CSVDoc::FileDialog::slotRejected() emit rejected(); close(); } - -void CSVDoc::FileDialog::createNewFile() -{ - emit createNewFile (mAdjusterWidget->getPath()); -} diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index be268f3720..30f2f5d56d 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -55,15 +55,14 @@ namespace CSVDoc signals: void openFiles(); - void createNewFile (const boost::filesystem::path& savePath); + void createNewFile (); - void signalUpdateCreateButton (bool, int); - void signalUpdateCreateButtonFlags(int); + void signalUpdateAcceptButton (bool, int); private slots: - void slotUpdateCreateButton (int); - void slotUpdateCreateButton (const QString &, bool); + void slotUpdateAcceptButton (int); + void slotUpdateAcceptButton (const QString &, bool); void slotRejected(); }; } From 9b483c3ae31f0ce11ea9a47fbada16441cf2c4ae Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sat, 26 Oct 2013 22:55:44 -0500 Subject: [PATCH 100/148] Fix for file path issues --- apps/opencs/editor.cpp | 20 +++++++-------- apps/opencs/editor.hpp | 4 +-- apps/opencs/view/doc/filedialog.cpp | 39 ++++++++++++++++++++++++++--- apps/opencs/view/doc/filedialog.hpp | 17 +++++++++++-- files/ui/filedialog.ui | 4 +-- 5 files changed, 64 insertions(+), 20 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 51cc490c73..a8006d2b41 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -20,6 +20,7 @@ CS::Editor::Editor() setupDataFiles(); mNewGame.setLocalData (mLocal); + mFileDialog.setLocalData (mLocal); connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ())); connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ())); @@ -31,9 +32,11 @@ CS::Editor::Editor() connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ())); connect (&mStartup, SIGNAL (editConfig()), this, SLOT (showSettings ())); - connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles())); - connect (&mFileDialog, SIGNAL(createNewFile ()), - this, SLOT(createNewFile ())); + connect (&mFileDialog, SIGNAL(signalOpenFiles (const boost::filesystem::path&)), + this, SLOT(openFiles (const boost::filesystem::path&))); + + connect (&mFileDialog, SIGNAL(signalCreateNewFile (const boost::filesystem::path&)), + this, SLOT(createNewFile (const boost::filesystem::path&))); connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)), this, SLOT (createNewGame (const boost::filesystem::path&))); @@ -125,15 +128,12 @@ void CS::Editor::loadDocument() mFileDialog.showDialog (CSVDoc::FileDialog::DialogType_Open); } -void CS::Editor::openFiles() +void CS::Editor::openFiles (const boost::filesystem::path &savePath) { std::vector files; - foreach (const QString &path, mFileDialog.selectedFilePaths()) { + foreach (const QString &path, mFileDialog.selectedFilePaths()) files.push_back(path.toStdString()); - } - - boost::filesystem::path savePath = mFileDialog.filename().toStdString(); CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, false); @@ -141,7 +141,7 @@ void CS::Editor::openFiles() mFileDialog.hide(); } -void CS::Editor::createNewFile () +void CS::Editor::createNewFile (const boost::filesystem::path &savePath) { std::vector files; @@ -151,8 +151,6 @@ void CS::Editor::createNewFile () files.push_back(mFileDialog.filename().toStdString()); - boost::filesystem::path savePath = mFileDialog.filename().toStdString(); - CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, true); mViewManager.addView (document); diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index ef013bc8f9..930aa9d643 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -59,8 +59,8 @@ namespace CS void createAddon(); void loadDocument(); - void openFiles(); - void createNewFile (); + void openFiles (const boost::filesystem::path &path); + void createNewFile (const boost::filesystem::path& path); void createNewGame (const boost::filesystem::path& file); void showStartup(); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index fb6c0a0e89..eb94aa5f4f 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -17,14 +17,17 @@ #include "filewidget.hpp" #include "adjusterwidget.hpp" +#include + CSVDoc::FileDialog::FileDialog(QWidget *parent) : - QDialog(parent), mSelector (0) + QDialog(parent), mSelector (0), mFileWidget (0), mAdjusterWidget (0) { ui.setupUi (this); resize(400, 400); setObjectName ("FileDialog"); mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); + mAdjusterWidget = new AdjusterWidget (this); } void CSVDoc::FileDialog::addFiles(const QString &path) @@ -42,6 +45,14 @@ QStringList CSVDoc::FileDialog::selectedFilePaths() return filePaths; } +void CSVDoc::FileDialog::setLocalData (const boost::filesystem::path& localData) +{ + if (mDialogType != DialogType_New) + return; + + mAdjusterWidget->setLocalData (localData); +} + void CSVDoc::FileDialog::showDialog(DialogType dialogType) { mDialogType = dialogType; @@ -63,7 +74,6 @@ void CSVDoc::FileDialog::showDialog(DialogType dialogType) connect (mSelector, SIGNAL (signalCurrentGamefileIndexChanged (int)), this, SLOT (slotUpdateAcceptButton (int))); - connect (ui.projectButtonBox, SIGNAL (accepted()), this, SIGNAL (createNewFile())); connect (ui.projectButtonBox, SIGNAL (rejected()), this, SLOT (slotRejected())); show(); @@ -85,17 +95,26 @@ void CSVDoc::FileDialog::buildNewFileView() mFileWidget->extensionLabelIsVisible(true); ui.projectGroupBoxLayout->insertWidget (0, mFileWidget); + ui.projectGroupBoxLayout->insertWidget (1, mAdjusterWidget); + + connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)), + mAdjusterWidget, SLOT (setName (const QString&, bool))); connect (mFileWidget, SIGNAL (nameChanged(const QString &, bool)), this, SLOT (slotUpdateAcceptButton(const QString &, bool))); + + connect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotNewFile())); } void CSVDoc::FileDialog::buildOpenFileView() { setWindowTitle(tr("Open")); ui.projectGroupBox->setTitle (QString("")); + ui.projectGroupBox->layout()->addWidget (mAdjusterWidget); ui.projectButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false); + + connect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotOpenFile())); } void CSVDoc::FileDialog::slotUpdateAcceptButton (int) @@ -121,7 +140,7 @@ void CSVDoc::FileDialog::slotUpdateAcceptButton(const QString &name, bool) QString CSVDoc::FileDialog::filename() const { if (mDialogType == DialogType_New) - return mFileWidget->getName(); + return ""; return mSelector->currentFile(); } @@ -131,3 +150,17 @@ void CSVDoc::FileDialog::slotRejected() emit rejected(); close(); } + +void CSVDoc::FileDialog::slotNewFile() +{ + emit signalCreateNewFile (mAdjusterWidget->getPath()); +} + +void CSVDoc::FileDialog::slotOpenFile() +{ + ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back(); + + mAdjusterWidget->setName (file->fileName(), !file->isGameFile()); + + emit signalOpenFiles (mAdjusterWidget->getPath()); +} diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 30f2f5d56d..777ee31e4c 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -4,6 +4,13 @@ #include #include +#include + +#ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED +#define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED +Q_DECLARE_METATYPE (boost::filesystem::path) +#endif + #include "ui_filedialog.h" class DataFilesModel; @@ -17,6 +24,7 @@ namespace ContentSelectorView namespace CSVDoc { class FileWidget; + class AdjusterWidget; class FileDialog : public QDialog { @@ -36,6 +44,7 @@ namespace CSVDoc Ui::FileDialog ui; DialogType mDialogType; FileWidget *mFileWidget; + AdjusterWidget *mAdjusterWidget; public: @@ -47,6 +56,8 @@ namespace CSVDoc QString filename() const; QStringList selectedFilePaths(); + void setLocalData (const boost::filesystem::path& localData); + private: void buildNewFileView(); @@ -54,13 +65,15 @@ namespace CSVDoc signals: - void openFiles(); - void createNewFile (); + void signalOpenFiles (const boost::filesystem::path &path); + void signalCreateNewFile (const boost::filesystem::path &path); void signalUpdateAcceptButton (bool, int); private slots: + void slotNewFile(); + void slotOpenFile(); void slotUpdateAcceptButton (int); void slotUpdateAcceptButton (const QString &, bool); void slotRejected(); diff --git a/files/ui/filedialog.ui b/files/ui/filedialog.ui index 114345e53b..b3af166dab 100644 --- a/files/ui/filedialog.ui +++ b/files/ui/filedialog.ui @@ -7,7 +7,7 @@ 0 0 518 - 108 + 109
@@ -52,7 +52,7 @@ - + From 5e123d3f52bafc42d63455f91d30d7b2677a9389 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sat, 26 Oct 2013 22:57:22 -0500 Subject: [PATCH 101/148] Hide adjusterwidget for open files view --- apps/opencs/view/doc/filedialog.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index eb94aa5f4f..903e78cf16 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -112,6 +112,8 @@ void CSVDoc::FileDialog::buildOpenFileView() ui.projectGroupBox->setTitle (QString("")); ui.projectGroupBox->layout()->addWidget (mAdjusterWidget); + mAdjusterWidget->setVisible (false); + ui.projectButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false); connect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotOpenFile())); From ea7a8eb2a4e7367530831c8172e159d6a9acf663 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sat, 26 Oct 2013 23:04:39 -0500 Subject: [PATCH 102/148] last commit --- apps/opencs/editor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index a8006d2b41..1c861ed67e 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -135,6 +135,7 @@ void CS::Editor::openFiles (const boost::filesystem::path &savePath) foreach (const QString &path, mFileDialog.selectedFilePaths()) files.push_back(path.toStdString()); + qDebug() << "save file path: " << savePath.c_str(); CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, false); mViewManager.addView (document); From 489475a32f14e24e4554b393261280abc947c194 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 27 Oct 2013 19:57:05 +0100 Subject: [PATCH 103/148] Corrected compilation error (g++ 4.8.2) triggered by not found declaration in argument-dependent lookup at the point of instantiation. Signed-off-by: Lukasz Gromanowski --- apps/opencs/model/world/idcollection.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 0d723bef1e..a7b37be5bc 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -111,7 +111,7 @@ namespace CSMWorld else { record.mState = RecordBase::State_Deleted; - setRecord (index, record); + this->setRecord (index, record); } return true; From d51c9b64dd2c4df61b03ce5dadfb724bcb6dc07f Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 27 Oct 2013 20:03:12 +0100 Subject: [PATCH 104/148] Issue #913: Merge --master and --plugin switches Launcher part of master/plugin switches merge. Signed-off-by: Lukasz Gromanowski --- apps/launcher/datafilespage.cpp | 28 +++-------------- apps/launcher/maindialog.cpp | 16 +++------- apps/launcher/settings/gamesettings.cpp | 31 ++++++++++++------- apps/launcher/settings/gamesettings.hpp | 10 ++++-- .../contentselector/model/contentmodel.cpp | 10 +++++- 5 files changed, 45 insertions(+), 50 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 43f09d1687..5e19ba9c9d 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -185,9 +185,6 @@ void DataFilesPage::loadSettings() // mContentSelector. mContentModel->uncheckAll(); - - QStringList gameFiles = mLauncherSettings.values(QString("Profiles/") + profile + QString("/master"), Qt::MatchExactly); - QStringList addons = mLauncherSettings.values(QString("Profiles/") + profile + QString("/plugin"), Qt::MatchExactly); } void DataFilesPage::saveSettings() @@ -202,27 +199,14 @@ void DataFilesPage::saveSettings() mLauncherSettings.setValue(QString("Profiles/currentprofile"), profile); } - mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master")); - mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin")); - - mGameSettings.remove(QString("master")); - mGameSettings.remove(QString("plugins")); + mLauncherSettings.remove(QString("Profiles/") + profile + QString("/content")); mGameSettings.remove(QString("content")); ContentSelectorModel::ContentFileList items = mContentModel->checkedItems(); - foreach(const ContentSelectorModel::EsmFile *item, items) { - - if (item->gameFiles().size() == 0) { - mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/master"), item->fileName()); - mGameSettings.setMultiValue(QString("content"), item->fileName()); - - } else { - mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/plugin"), item->fileName()); - mGameSettings.setMultiValue(QString("content"), item->fileName()); - } + mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/content"), item->fileName()); + mGameSettings.setMultiValue(QString("content"), item->fileName()); } - } void DataFilesPage::updateOkButton(const QString &text) @@ -281,8 +265,7 @@ void DataFilesPage::on_deleteProfileAction_triggered() msgBox.exec(); if (msgBox.clickedButton() == deleteButton) { - mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master")); - mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin")); + mLauncherSettings.remove(QString("Profiles/") + profile + QString("/content")); // Remove the profile from the combobox profilesComboBox->removeItem(profilesComboBox->findText(profile)); @@ -348,8 +331,7 @@ void DataFilesPage::profileRenamed(const QString &previous, const QString &curre saveSettings(); // Remove the old one - mLauncherSettings.remove(QString("Profiles/") + previous + QString("/master")); - mLauncherSettings.remove(QString("Profiles/") + previous + QString("/plugin")); + mLauncherSettings.remove(QString("Profiles/") + previous + QString("/content")); // Remove the profile from the combobox profilesComboBox->removeItem(profilesComboBox->findText(previous)); diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 032f70916f..0f59b3aaf0 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -261,19 +261,11 @@ bool MainDialog::showFirstRunDialog() // Add a new profile if (msgBox.isChecked()) { mLauncherSettings.setValue(QString("Profiles/currentprofile"), QString("Imported")); + mLauncherSettings.remove(QString("Profiles/Imported/content")); - mLauncherSettings.remove(QString("Profiles/Imported/master")); - mLauncherSettings.remove(QString("Profiles/Imported/plugin")); - - QStringList masters = mGameSettings.values(QString("master")); - QStringList plugins = mGameSettings.values(QString("plugin")); - - foreach (const QString &master, masters) { - mLauncherSettings.setMultiValue(QString("Profiles/Imported/master"), master); - } - - foreach (const QString &plugin, plugins) { - mLauncherSettings.setMultiValue(QString("Profiles/Imported/plugin"), plugin); + QStringList contents = mGameSettings.values(QString("content")); + foreach (const QString &content, contents) { + mLauncherSettings.setMultiValue(QString("Profiles/Imported/content"), content); } } diff --git a/apps/launcher/settings/gamesettings.cpp b/apps/launcher/settings/gamesettings.cpp index 7b2356cd08..211e319279 100644 --- a/apps/launcher/settings/gamesettings.cpp +++ b/apps/launcher/settings/gamesettings.cpp @@ -139,13 +139,13 @@ bool GameSettings::writeFile(QTextStream &stream) while (i.hasPrevious()) { i.previous(); - if (i.key() == QLatin1String("master") || i.key() == QLatin1String("plugin")) + if (i.key() == QLatin1String("content")) continue; // Quote paths with spaces if (i.key() == QLatin1String("data") - || i.key() == QLatin1String("data-local") - || i.key() == QLatin1String("resources")) + || i.key() == QLatin1String("data-local") + || i.key() == QLatin1String("resources")) { if (i.value().contains(QChar(' '))) { @@ -161,15 +161,24 @@ bool GameSettings::writeFile(QTextStream &stream) } - QStringList masters = mSettings.values(QString("master")); - for (int i = masters.count(); i--;) { - stream << "content=" << masters.at(i) << "\n"; - } - - QStringList plugins = mSettings.values(QString("plugin")); - for (int i = plugins.count(); i--;) { - stream << "content=" << plugins.at(i) << "\n"; + QStringList content = mSettings.values(QString("content")); + for (int i = content.count(); i--;) { + stream << "content=" << content.at(i) << "\n"; } return true; } + +bool GameSettings::hasMaster() +{ + bool result = false; + QStringList content = mSettings.values(QString("content")); + for (int i = 0; i < content.count(); ++i) { + if (content.at(i).contains(".omwgame") || content.at(i).contains(".esm")) { + result = true; + break; + } + } + + return result; +} diff --git a/apps/launcher/settings/gamesettings.hpp b/apps/launcher/settings/gamesettings.hpp index 55b2107e2a..c009c30ed9 100644 --- a/apps/launcher/settings/gamesettings.hpp +++ b/apps/launcher/settings/gamesettings.hpp @@ -8,8 +8,11 @@ #include -namespace Files { typedef std::vector PathContainer; - struct ConfigurationManager;} +namespace Files +{ + typedef std::vector PathContainer; + struct ConfigurationManager; +} class GameSettings { @@ -43,7 +46,8 @@ public: inline QStringList getDataDirs() { return mDataDirs; } inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); } inline QString getDataLocal() {return mDataLocal; } - inline bool hasMaster() { return mSettings.count(QString("master")) > 0; } + + bool hasMaster(); QStringList values(const QString &key, const QStringList &defaultValues = QStringList()); bool readFile(QTextStream &stream); diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index b85da25c67..fb011198ca 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -461,9 +461,17 @@ ContentSelectorModel::ContentFileList ContentSelectorModel::ContentModel::checke { ContentFileList list; + // First search for game files and next addons, + // so we get more or less correct game files vs addons order. foreach (EsmFile *file, mFiles) { - if (isChecked(file->fileName())) + if (isChecked(file->fileName()) && file->isGameFile()) + list << file; + } + + foreach (EsmFile *file, mFiles) + { + if (isChecked(file->fileName()) && !file->isGameFile()) list << file; } From b51bef0d98687c95d35f69a8b02b5b2fac6d48a5 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 27 Oct 2013 20:21:19 -0500 Subject: [PATCH 105/148] fixed missing adjuster widget in file dialog open view --- apps/opencs/view/doc/adjusterwidget.cpp | 13 +++++++++- apps/opencs/view/doc/adjusterwidget.hpp | 11 +++++++++ apps/opencs/view/doc/filedialog.cpp | 24 ++++++++++++------- apps/opencs/view/doc/filedialog.hpp | 14 +++-------- .../contentselector/view/contentselector.cpp | 12 ++++++++-- .../contentselector/view/contentselector.hpp | 2 ++ 6 files changed, 53 insertions(+), 23 deletions(-) diff --git a/apps/opencs/view/doc/adjusterwidget.cpp b/apps/opencs/view/doc/adjusterwidget.cpp index b1eec63c37..332d460758 100644 --- a/apps/opencs/view/doc/adjusterwidget.cpp +++ b/apps/opencs/view/doc/adjusterwidget.cpp @@ -11,7 +11,7 @@ #include CSVDoc::AdjusterWidget::AdjusterWidget (QWidget *parent) -: QWidget (parent), mValid (false) + : QWidget (parent), mValid (false), mAction (ContentAction_Undefined) { QHBoxLayout *layout = new QHBoxLayout (this); @@ -30,6 +30,11 @@ CSVDoc::AdjusterWidget::AdjusterWidget (QWidget *parent) setLayout (layout); } +void CSVDoc::AdjusterWidget::setAction (ContentAction action) +{ + mAction = action; +} + void CSVDoc::AdjusterWidget::setLocalData (const boost::filesystem::path& localData) { mLocalData = localData; @@ -51,6 +56,12 @@ void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon) { QString message; + if (mAction == ContentAction_Undefined) + { + throw std::runtime_error("ContentAction_Undefined when AdjusterWidget::setName() called."); + return; + } + if (name.isEmpty()) { mValid = false; diff --git a/apps/opencs/view/doc/adjusterwidget.hpp b/apps/opencs/view/doc/adjusterwidget.hpp index 461cfb3452..627f89c1f8 100644 --- a/apps/opencs/view/doc/adjusterwidget.hpp +++ b/apps/opencs/view/doc/adjusterwidget.hpp @@ -9,21 +9,32 @@ class QLabel; namespace CSVDoc { + enum ContentAction + { + ContentAction_New, + ContentAction_Edit, + ContentAction_Undefined + }; + class AdjusterWidget : public QWidget { Q_OBJECT + public: + boost::filesystem::path mLocalData; QLabel *mMessage; QLabel *mIcon; bool mValid; boost::filesystem::path mResultPath; + ContentAction mAction; public: AdjusterWidget (QWidget *parent = 0); void setLocalData (const boost::filesystem::path& localData); + void setAction (ContentAction action); bool isValid() const; diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 903e78cf16..57b61ff8b2 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -53,17 +53,19 @@ void CSVDoc::FileDialog::setLocalData (const boost::filesystem::path& localData) mAdjusterWidget->setLocalData (localData); } -void CSVDoc::FileDialog::showDialog(DialogType dialogType) +void CSVDoc::FileDialog::showDialog (ContentAction action) { - mDialogType = dialogType; + mAction = action; - switch (mDialogType) + ui.projectGroupBoxLayout->insertWidget (0, mAdjusterWidget); + + switch (mAction) { - case DialogType_New: + case ContentAction_New: buildNewFileView(); break; - case DialogType_Open: + case ContentAction_Edit: buildOpenFileView(); break; default: @@ -95,7 +97,6 @@ void CSVDoc::FileDialog::buildNewFileView() mFileWidget->extensionLabelIsVisible(true); ui.projectGroupBoxLayout->insertWidget (0, mFileWidget); - ui.projectGroupBoxLayout->insertWidget (1, mAdjusterWidget); connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)), mAdjusterWidget, SLOT (setName (const QString&, bool))); @@ -110,12 +111,12 @@ void CSVDoc::FileDialog::buildOpenFileView() { setWindowTitle(tr("Open")); ui.projectGroupBox->setTitle (QString("")); - ui.projectGroupBox->layout()->addWidget (mAdjusterWidget); - - mAdjusterWidget->setVisible (false); ui.projectButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false); + connect (mSelector, SIGNAL (signalAddonFileSelected (int)), this, SLOT (slotUpdateAcceptButton (int))); + connect (mSelector, SIGNAL (signalAddonFileUnselected (int)), this, SLOT (slotUpdateAcceptButton (int))); + connect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotOpenFile())); } @@ -135,6 +136,11 @@ void CSVDoc::FileDialog::slotUpdateAcceptButton(const QString &name, bool) if (mDialogType == DialogType_New) success = success && !(name.isEmpty()); + else + { + ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back(); + mAdjusterWidget->setName (file->fileName(), !file->isGameFile()); + } ui.projectButtonBox->button (QDialogButtonBox::Ok)->setEnabled (success); } diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 777ee31e4c..d9fd569435 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -5,6 +5,7 @@ #include #include +#include "adjusterwidget.hpp" #ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED #define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED @@ -24,32 +25,23 @@ namespace ContentSelectorView namespace CSVDoc { class FileWidget; - class AdjusterWidget; class FileDialog : public QDialog { Q_OBJECT - public: - - enum DialogType - { - DialogType_New, - DialogType_Open - }; - private: ContentSelectorView::ContentSelector *mSelector; Ui::FileDialog ui; - DialogType mDialogType; + ContentAction mAction; FileWidget *mFileWidget; AdjusterWidget *mAdjusterWidget; public: explicit FileDialog(QWidget *parent = 0); - void showDialog (DialogType dialogType); + void showDialog (ContentAction action); void addFiles (const QString &path); diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 33b31b00c5..b9e5189312 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -151,8 +151,16 @@ void ContentSelectorView::ContentSelector::slotAddonTableItemClicked(const QMode { QAbstractItemModel *const model = ui.addonView->model(); + Qt::CheckState checkState = Qt::Unchecked; + if (model->data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked) - model->setData(index, Qt::Checked, Qt::CheckStateRole); + checkState = Qt::Checked; + + model->setData(index, checkState, Qt::CheckStateRole); + + if (checkState == Qt::Checked) + emit signalAddonFileSelected (index.row()); else - model->setData(index, Qt::Unchecked, Qt::CheckStateRole); + emit signalAddonFileUnselected (index.row()); + } diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index 1e24a5523e..da1c39973d 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -54,6 +54,8 @@ namespace ContentSelectorView signals: void signalCurrentGamefileIndexChanged (int); + void signalAddonFileSelected (int); + void signalAddonFileUnselected (int); private slots: From 29fce6d11f741924cf5e9d780548d4b75808d480 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 28 Oct 2013 08:45:56 +0100 Subject: [PATCH 106/148] increased version number --- CMakeLists.txt | 2 +- readme.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 625239aa06..d0de9f7712 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ include (OpenMWMacros) # Version set (OPENMW_VERSION_MAJOR 0) -set (OPENMW_VERSION_MINOR 26) +set (OPENMW_VERSION_MINOR 27) set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") diff --git a/readme.txt b/readme.txt index 7865f8dba2..c4d9d27461 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.26.0 +Version: 0.27.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org From 632a53ead4f175e5b2769db4649b9ead93131019 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Tue, 29 Oct 2013 13:28:43 +0100 Subject: [PATCH 107/148] Support packing the OpenCS into windows builds --- CMakeLists.txt | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 625239aa06..5a87a1a404 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -458,10 +458,20 @@ if(WIN32) "${OpenMW_SOURCE_DIR}/Daedric Font License.txt" "${OpenMW_BINARY_DIR}/settings-default.cfg" "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" - "${OpenMW_BINARY_DIR}/Release/mwiniimport.exe" - "${OpenMW_BINARY_DIR}/Release/omwlauncher.exe" "${OpenMW_BINARY_DIR}/Release/openmw.exe" DESTINATION ".") + + IF(BUILD_LAUNCHER) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/omwlauncher.exe" DESTINATION ".") + ENDIF(BUILD_LAUNCHER) + IF(BUILD_MWINIIMPORTER) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/mwiniimport.exe" DESTINATION ".") + ENDIF(BUILD_MWINIIMPORTER) + IF(BUILD_OPENCS) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/opencs.exe" DESTINATION ".") + INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.cfg" DESTINATION ".") + ENDIF(BUILD_OPENCS) + INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".") SET(CPACK_GENERATOR "NSIS") @@ -471,7 +481,13 @@ if(WIN32) SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR}) SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) - SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW;omwlauncher;OpenMW Launcher") + SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW") + IF(BUILD_LAUNCHER) + SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};omwlauncher;OpenMW Launcher") + ENDIF(BUILD_LAUNCHER) + IF(BUILD_OPENCS) + SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};opencs;OpenMW Construction Set") + ENDIF(BUILD_OPENCS) SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'") SET(CPACK_NSIS_DELETE_ICONS_EXTRA " !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP From 636d399c7f89b854db2621499f54b17042d94b77 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 30 Oct 2013 14:04:33 +0100 Subject: [PATCH 108/148] Refactored Ogre initialisation into a component --- CMakeLists.txt | 1 - apps/launcher/graphicspage.cpp | 43 +---- apps/launcher/graphicspage.hpp | 20 +-- apps/launcher/main.cpp | 2 - apps/openmw/engine.cpp | 3 +- components/CMakeLists.txt | 8 +- .../ogre => components/nifogre}/particles.cpp | 0 .../ogre => components/nifogre}/particles.hpp | 0 components/ogreinit/ogreinit.cpp | 165 ++++++++++++++++++ components/ogreinit/ogreinit.hpp | 75 ++++++++ components/{files => ogreinit}/ogreplugin.cpp | 0 components/{files => ogreinit}/ogreplugin.hpp | 0 libs/openengine/ogre/renderer.cpp | 149 +--------------- libs/openengine/ogre/renderer.hpp | 62 +------ 14 files changed, 267 insertions(+), 261 deletions(-) rename {libs/openengine/ogre => components/nifogre}/particles.cpp (100%) rename {libs/openengine/ogre => components/nifogre}/particles.hpp (100%) create mode 100644 components/ogreinit/ogreinit.cpp create mode 100644 components/ogreinit/ogreinit.hpp rename components/{files => ogreinit}/ogreplugin.cpp (100%) rename components/{files => ogreinit}/ogreplugin.hpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 625239aa06..6fd64aa3fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,7 +79,6 @@ set(OENGINE_OGRE ${LIBDIR}/openengine/ogre/renderer.cpp ${LIBDIR}/openengine/ogre/fader.cpp ${LIBDIR}/openengine/ogre/lights.cpp - ${LIBDIR}/openengine/ogre/particles.cpp ${LIBDIR}/openengine/ogre/selectionbuffer.cpp ${LIBDIR}/openengine/ogre/imagerotate.cpp ) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 97a24d54bd..b7f59c781c 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -10,12 +10,9 @@ #endif #include -#include - #include #include -#include #include @@ -54,13 +51,9 @@ GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &g bool GraphicsPage::setupOgre() { - // Create a log manager so we can surpress debug text to stdout/stderr - Ogre::LogManager* logMgr = OGRE_NEW Ogre::LogManager; - logMgr->createLog((mCfgMgr.getLogPath().string() + "/launcherOgre.log"), true, false, false); - try { - mOgre = new Ogre::Root("", "", "./launcherOgre.log"); + mOgre = mOgreInit.init(mCfgMgr.getLogPath().string() + "/launcherOgre.log"); } catch(Ogre::Exception &ex) { @@ -78,40 +71,6 @@ bool GraphicsPage::setupOgre() return false; } - - std::string pluginDir; - const char* pluginEnv = getenv("OPENMW_OGRE_PLUGIN_DIR"); - if (pluginEnv) - pluginDir = pluginEnv; - else - { -#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 - pluginDir = ".\\"; -#endif -#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE - pluginDir = OGRE_PLUGIN_DIR; -#endif -#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX - pluginDir = OGRE_PLUGIN_DIR_REL; -#endif - } - - QDir dir(QString::fromStdString(pluginDir)); - pluginDir = dir.absolutePath().toStdString(); - - Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mOgre); - Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mOgre); - Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mOgre); - -#ifdef ENABLE_PLUGIN_GL - mGLPlugin = new Ogre::GLPlugin(); - mOgre->installPlugin(mGLPlugin); -#endif -#ifdef ENABLE_PLUGIN_Direct3D9 - mD3D9Plugin = new Ogre::D3D9Plugin(); - mOgre->installPlugin(mD3D9Plugin); -#endif - // Get the available renderers and put them in the combobox const Ogre::RenderSystemList &renderers = mOgre->getAvailableRenderers(); diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index d233ea12e2..29a95c36a5 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -5,16 +5,9 @@ #include #include -//#include -//#include -// Static plugin headers -#ifdef ENABLE_PLUGIN_GL -# include "OgreGLPlugin.h" -#endif -#ifdef ENABLE_PLUGIN_Direct3D9 -# include "OgreD3D9Plugin.h" -#endif +#include + #include "ui_graphicspage.h" @@ -41,16 +34,13 @@ private slots: void slotStandardToggled(bool checked); private: + OgreInit::OgreInit mOgreInit; + Ogre::Root *mOgre; Ogre::RenderSystem *mSelectedRenderSystem; Ogre::RenderSystem *mOpenGLRenderSystem; Ogre::RenderSystem *mDirect3DRenderSystem; - #ifdef ENABLE_PLUGIN_GL - Ogre::GLPlugin* mGLPlugin; - #endif - #ifdef ENABLE_PLUGIN_Direct3D9 - Ogre::D3D9Plugin* mD3D9Plugin; - #endif + Files::ConfigurationManager &mCfgMgr; GraphicsSettings &mGraphicsSettings; diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index f67f5edcff..cb1a51d8c1 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -10,8 +10,6 @@ #include #include "maindialog.hpp" -// SDL workaround -#include "graphicspage.hpp" int main(int argc, char *argv[]) { diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 7e344c4dba..020283f2d5 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -357,8 +357,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mOgre->configure( mCfgMgr.getLogPath().string(), renderSystem, - Settings::Manager::getString("opengl rtt mode", "Video"), - false); + Settings::Manager::getString("opengl rtt mode", "Video")); // This has to be added BEFORE MyGUI is initialized, as it needs // to find core.xml here. diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 04423dc6f0..ef1556038b 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -19,7 +19,7 @@ add_component_dir (nif ) add_component_dir (nifogre - ogrenifloader skeleton material mesh + ogrenifloader skeleton material mesh particles ) add_component_dir (nifbullet @@ -48,7 +48,7 @@ add_component_dir (misc add_component_dir (files linuxpath windowspath macospath fixedpath multidircollection collections fileops configurationmanager - filelibrary ogreplugin constrainedfiledatastream lowlevelfile + filelibrary constrainedfiledatastream lowlevelfile ) add_component_dir (compiler @@ -74,6 +74,10 @@ add_component_dir (loadinglistener loadinglistener ) +add_component_dir (ogreinit + ogreinit ogreplugin + ) + find_package(Qt4 COMPONENTS QtCore QtGui) if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) diff --git a/libs/openengine/ogre/particles.cpp b/components/nifogre/particles.cpp similarity index 100% rename from libs/openengine/ogre/particles.cpp rename to components/nifogre/particles.cpp diff --git a/libs/openengine/ogre/particles.hpp b/components/nifogre/particles.hpp similarity index 100% rename from libs/openengine/ogre/particles.hpp rename to components/nifogre/particles.hpp diff --git a/components/ogreinit/ogreinit.cpp b/components/ogreinit/ogreinit.cpp new file mode 100644 index 0000000000..92a6ed0123 --- /dev/null +++ b/components/ogreinit/ogreinit.cpp @@ -0,0 +1,165 @@ +#include "ogreinit.hpp" + +#include + +#include +#include +#include + +#include + +#include "ogreplugin.hpp" + +namespace OgreInit +{ + + OgreInit::OgreInit() + : mRoot(NULL) + #ifdef ENABLE_PLUGIN_CgProgramManager + , mCgPlugin(NULL) + #endif + #ifdef ENABLE_PLUGIN_OctreeSceneManager + , mOctreePlugin(NULL) + #endif + #ifdef ENABLE_PLUGIN_ParticleFX + , mParticleFXPlugin(NULL) + #endif + #ifdef ENABLE_PLUGIN_GL + , mGLPlugin(NULL) + #endif + #ifdef ENABLE_PLUGIN_Direct3D9 + , mD3D9Plugin(NULL) + #endif + {} + + Ogre::Root* OgreInit::init(const std::string &logPath) + { + // Set up logging first + new Ogre::LogManager; + Ogre::Log *log = Ogre::LogManager::getSingleton().createLog(logPath + std::string("Ogre.log")); + + // Disable logging to cout/cerr + log->setDebugOutputEnabled(false); + + mRoot = new Ogre::Root("", "", ""); + + #if defined(ENABLE_PLUGIN_GL) || defined(ENABLE_PLUGIN_Direct3D9) || defined(ENABLE_PLUGIN_CgProgramManager) || defined(ENABLE_PLUGIN_OctreeSceneManager) || defined(ENABLE_PLUGIN_ParticleFX) + loadStaticPlugins(); + #else + loadPlugins(); + #endif + + loadParticleFactories(); + + return mRoot; + } + + OgreInit::~OgreInit() + { + delete mRoot; + + std::vector::iterator ei; + for(ei = mEmitterFactories.begin();ei != mEmitterFactories.end();++ei) + OGRE_DELETE (*ei); + mEmitterFactories.clear(); + + std::vector::iterator ai; + for(ai = mAffectorFactories.begin();ai != mAffectorFactories.end();++ai) + OGRE_DELETE (*ai); + mAffectorFactories.clear(); + + #ifdef ENABLE_PLUGIN_GL + delete mGLPlugin; + mGLPlugin = NULL; + #endif + #ifdef ENABLE_PLUGIN_Direct3D9 + delete mD3D9Plugin; + mD3D9Plugin = NULL; + #endif + #ifdef ENABLE_PLUGIN_CgProgramManager + delete mCgPlugin; + mCgPlugin = NULL; + #endif + #ifdef ENABLE_PLUGIN_OctreeSceneManager + delete mOctreePlugin; + mOctreePlugin = NULL; + #endif + #ifdef ENABLE_PLUGIN_ParticleFX + delete mParticleFXPlugin; + mParticleFXPlugin = NULL; + #endif + } + + void OgreInit::loadStaticPlugins() + { + #ifdef ENABLE_PLUGIN_GL + mGLPlugin = new Ogre::GLPlugin(); + mRoot->installPlugin(mGLPlugin); + #endif + #ifdef ENABLE_PLUGIN_Direct3D9 + mD3D9Plugin = new Ogre::D3D9Plugin(); + mRoot->installPlugin(mD3D9Plugin); + #endif + #ifdef ENABLE_PLUGIN_CgProgramManager + mCgPlugin = new Ogre::CgPlugin(); + mRoot->installPlugin(mCgPlugin); + #endif + #ifdef ENABLE_PLUGIN_OctreeSceneManager + mOctreePlugin = new Ogre::OctreePlugin(); + mRoot->installPlugin(mOctreePlugin); + #endif + #ifdef ENABLE_PLUGIN_ParticleFX + mParticleFXPlugin = new Ogre::ParticleFXPlugin(); + mRoot->installPlugin(mParticleFXPlugin); + #endif + } + + void OgreInit::loadPlugins() + { + std::string pluginDir; + const char* pluginEnv = getenv("OPENMW_OGRE_PLUGIN_DIR"); + if (pluginEnv) + pluginDir = pluginEnv; + else + { + #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 + pluginDir = ".\\"; + #endif + #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE + pluginDir = OGRE_PLUGIN_DIR; + #endif + #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX + pluginDir = OGRE_PLUGIN_DIR_REL; + #endif + } + + boost::filesystem::path absPluginPath = boost::filesystem::absolute(boost::filesystem::path(pluginDir)); + + pluginDir = absPluginPath.string(); + + Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mRoot); + Files::loadOgrePlugin(pluginDir, "RenderSystem_GLES2", *mRoot); + Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mRoot); + Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mRoot); + Files::loadOgrePlugin(pluginDir, "Plugin_CgProgramManager", *mRoot); + Files::loadOgrePlugin(pluginDir, "Plugin_ParticleFX", *mRoot); + } + + void OgreInit::loadParticleFactories() + { + Ogre::ParticleEmitterFactory *emitter; + emitter = OGRE_NEW NifEmitterFactory(); + Ogre::ParticleSystemManager::getSingleton().addEmitterFactory(emitter); + mEmitterFactories.push_back(emitter); + + Ogre::ParticleAffectorFactory *affector; + affector = OGRE_NEW GrowFadeAffectorFactory(); + Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector); + mAffectorFactories.push_back(affector); + + affector = OGRE_NEW GravityAffectorFactory(); + Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector); + mAffectorFactories.push_back(affector); + } + +} diff --git a/components/ogreinit/ogreinit.hpp b/components/ogreinit/ogreinit.hpp new file mode 100644 index 0000000000..b6fe4631a8 --- /dev/null +++ b/components/ogreinit/ogreinit.hpp @@ -0,0 +1,75 @@ +#ifndef OPENMW_COMPONENTS_OGREINIT_H +#define OPENMW_COMPONENTS_OGREINIT_H + +#include +#include + +// Static plugin headers +#ifdef ENABLE_PLUGIN_CgProgramManager +# include "OgreCgPlugin.h" +#endif +#ifdef ENABLE_PLUGIN_OctreeSceneManager +# include "OgreOctreePlugin.h" +#endif +#ifdef ENABLE_PLUGIN_ParticleFX +# include "OgreParticleFXPlugin.h" +#endif +#ifdef ENABLE_PLUGIN_GL +# include "OgreGLPlugin.h" +#endif +#ifdef ENABLE_PLUGIN_Direct3D9 +# include "OgreD3D9Plugin.h" +#endif + +namespace Ogre +{ + class ParticleEmitterFactory; + class ParticleAffectorFactory; + class Root; +} + +namespace OgreInit +{ + /** + * @brief Starts Ogre::Root and loads required plugins and NIF particle factories + */ + class OgreInit + { + public: + OgreInit(); + + Ogre::Root* init(const std::string &logPath // Path to directory where to store log files + ); + + ~OgreInit(); + + private: + std::vector mEmitterFactories; + std::vector mAffectorFactories; + Ogre::Root* mRoot; + + void loadStaticPlugins(); + void loadPlugins(); + void loadParticleFactories(); + + + #ifdef ENABLE_PLUGIN_CgProgramManager + Ogre::CgPlugin* mCgPlugin; + #endif + #ifdef ENABLE_PLUGIN_OctreeSceneManager + Ogre::OctreePlugin* mOctreePlugin; + #endif + #ifdef ENABLE_PLUGIN_ParticleFX + Ogre::ParticleFXPlugin* mParticleFXPlugin; + #endif + #ifdef ENABLE_PLUGIN_GL + Ogre::GLPlugin* mGLPlugin; + #endif + #ifdef ENABLE_PLUGIN_Direct3D9 + Ogre::D3D9Plugin* mD3D9Plugin; + #endif + + }; +} + +#endif diff --git a/components/files/ogreplugin.cpp b/components/ogreinit/ogreplugin.cpp similarity index 100% rename from components/files/ogreplugin.cpp rename to components/ogreinit/ogreplugin.cpp diff --git a/components/files/ogreplugin.hpp b/components/ogreinit/ogreplugin.hpp similarity index 100% rename from components/files/ogreplugin.hpp rename to components/ogreinit/ogreplugin.hpp diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index d078e3c61e..2a438e1fef 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -1,27 +1,17 @@ #include "renderer.hpp" #include "fader.hpp" -#include "particles.hpp" #include -#include "OgreRoot.h" -#include "OgreRenderWindow.h" -#include "OgreLogManager.h" -#include "OgreLog.h" -#include "OgreTextureManager.h" -#include "OgreTexture.h" -#include "OgreHardwarePixelBuffer.h" -#include -#include "OgreParticleAffectorFactory.h" - -#include - -#include +#include +#include +#include +#include +#include #include #include -#include #include using namespace Ogre; @@ -33,74 +23,11 @@ void OgreRenderer::cleanup() delete mFader; mFader = NULL; - delete mRoot; - mRoot = NULL; - // If we don't do this, the desktop resolution is not restored on exit SDL_SetWindowFullscreen(mSDLWindow, 0); SDL_DestroyWindow(mSDLWindow); mSDLWindow = NULL; - - unloadPlugins(); -} - -void OgreRenderer::loadPlugins() -{ - #ifdef ENABLE_PLUGIN_GL - mGLPlugin = new Ogre::GLPlugin(); - mRoot->installPlugin(mGLPlugin); - #endif - #ifdef ENABLE_PLUGIN_Direct3D9 - mD3D9Plugin = new Ogre::D3D9Plugin(); - mRoot->installPlugin(mD3D9Plugin); - #endif - #ifdef ENABLE_PLUGIN_CgProgramManager - mCgPlugin = new Ogre::CgPlugin(); - mRoot->installPlugin(mCgPlugin); - #endif - #ifdef ENABLE_PLUGIN_OctreeSceneManager - mOctreePlugin = new Ogre::OctreePlugin(); - mRoot->installPlugin(mOctreePlugin); - #endif - #ifdef ENABLE_PLUGIN_ParticleFX - mParticleFXPlugin = new Ogre::ParticleFXPlugin(); - mRoot->installPlugin(mParticleFXPlugin); - #endif -} - -void OgreRenderer::unloadPlugins() -{ - std::vector::iterator ei; - for(ei = mEmitterFactories.begin();ei != mEmitterFactories.end();++ei) - OGRE_DELETE (*ei); - mEmitterFactories.clear(); - - std::vector::iterator ai; - for(ai = mAffectorFactories.begin();ai != mAffectorFactories.end();++ai) - OGRE_DELETE (*ai); - mAffectorFactories.clear(); - - #ifdef ENABLE_PLUGIN_GL - delete mGLPlugin; - mGLPlugin = NULL; - #endif - #ifdef ENABLE_PLUGIN_Direct3D9 - delete mD3D9Plugin; - mD3D9Plugin = NULL; - #endif - #ifdef ENABLE_PLUGIN_CgProgramManager - delete mCgPlugin; - mCgPlugin = NULL; - #endif - #ifdef ENABLE_PLUGIN_OctreeSceneManager - delete mOctreePlugin; - mOctreePlugin = NULL; - #endif - #ifdef ENABLE_PLUGIN_ParticleFX - delete mParticleFXPlugin; - mParticleFXPlugin = NULL; - #endif } void OgreRenderer::update(float dt) @@ -120,70 +47,10 @@ float OgreRenderer::getFPS() void OgreRenderer::configure(const std::string &logPath, const std::string& renderSystem, - const std::string& rttMode, - bool _logging) + const std::string& rttMode + ) { - // Set up logging first - new LogManager; - Log *log = LogManager::getSingleton().createLog(logPath + std::string("Ogre.log")); - - if(_logging) - // Full log detail - log->setLogDetail(LL_BOREME); - else - // Disable logging - log->setDebugOutputEnabled(false); - - mRoot = new Root("", "", ""); - - #if defined(ENABLE_PLUGIN_GL) || defined(ENABLE_PLUGIN_Direct3D9) || defined(ENABLE_PLUGIN_CgProgramManager) || defined(ENABLE_PLUGIN_OctreeSceneManager) || defined(ENABLE_PLUGIN_ParticleFX) - loadPlugins(); - #endif - - std::string pluginDir; - const char* pluginEnv = getenv("OPENMW_OGRE_PLUGIN_DIR"); - if (pluginEnv) - pluginDir = pluginEnv; - else - { -#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 - pluginDir = ".\\"; -#endif -#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE - pluginDir = OGRE_PLUGIN_DIR; -#endif -#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX - pluginDir = OGRE_PLUGIN_DIR_REL; -#endif - } - - boost::filesystem::path absPluginPath = boost::filesystem::absolute(boost::filesystem::path(pluginDir)); - - pluginDir = absPluginPath.string(); - - Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mRoot); - Files::loadOgrePlugin(pluginDir, "RenderSystem_GLES2", *mRoot); - Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mRoot); - Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mRoot); - Files::loadOgrePlugin(pluginDir, "Plugin_CgProgramManager", *mRoot); - Files::loadOgrePlugin(pluginDir, "Plugin_ParticleFX", *mRoot); - - - Ogre::ParticleEmitterFactory *emitter; - emitter = OGRE_NEW NifEmitterFactory(); - Ogre::ParticleSystemManager::getSingleton().addEmitterFactory(emitter); - mEmitterFactories.push_back(emitter); - - - Ogre::ParticleAffectorFactory *affector; - affector = OGRE_NEW GrowFadeAffectorFactory(); - Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector); - mAffectorFactories.push_back(affector); - - affector = OGRE_NEW GravityAffectorFactory(); - Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector); - mAffectorFactories.push_back(affector); - + mRoot = mOgreInit.init(logPath); RenderSystem* rs = mRoot->getRenderSystemByName(renderSystem); if (rs == 0) diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index c6a838805e..e4af0bf77c 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -7,25 +7,9 @@ #include -// Static plugin headers -#ifdef ENABLE_PLUGIN_CgProgramManager -# include "OgreCgPlugin.h" -#endif -#ifdef ENABLE_PLUGIN_OctreeSceneManager -# include "OgreOctreePlugin.h" -#endif -#ifdef ENABLE_PLUGIN_ParticleFX -# include "OgreParticleFXPlugin.h" -#endif -#ifdef ENABLE_PLUGIN_GL -# include "OgreGLPlugin.h" -#endif -#ifdef ENABLE_PLUGIN_Direct3D9 -# include "OgreD3D9Plugin.h" -#endif +#include -#include "OgreTexture.h" -#include +#include struct SDL_Window; @@ -72,24 +56,10 @@ namespace OEngine Ogre::SceneManager *mScene; Ogre::Camera *mCamera; Ogre::Viewport *mView; - #ifdef ENABLE_PLUGIN_CgProgramManager - Ogre::CgPlugin* mCgPlugin; - #endif - #ifdef ENABLE_PLUGIN_OctreeSceneManager - Ogre::OctreePlugin* mOctreePlugin; - #endif - #ifdef ENABLE_PLUGIN_ParticleFX - Ogre::ParticleFXPlugin* mParticleFXPlugin; - #endif - #ifdef ENABLE_PLUGIN_GL - Ogre::GLPlugin* mGLPlugin; - #endif - #ifdef ENABLE_PLUGIN_Direct3D9 - Ogre::D3D9Plugin* mD3D9Plugin; - #endif + + OgreInit::OgreInit mOgreInit; + Fader* mFader; - std::vector mEmitterFactories; - std::vector mAffectorFactories; WindowSizeListener* mWindowListener; @@ -102,21 +72,6 @@ namespace OEngine , mCamera(NULL) , mView(NULL) , mWindowListener(NULL) - #ifdef ENABLE_PLUGIN_CgProgramManager - , mCgPlugin(NULL) - #endif - #ifdef ENABLE_PLUGIN_OctreeSceneManager - , mOctreePlugin(NULL) - #endif - #ifdef ENABLE_PLUGIN_ParticleFX - , mParticleFXPlugin(NULL) - #endif - #ifdef ENABLE_PLUGIN_GL - , mGLPlugin(NULL) - #endif - #ifdef ENABLE_PLUGIN_Direct3D9 - , mD3D9Plugin(NULL) - #endif , mFader(NULL) { } @@ -128,8 +83,7 @@ namespace OEngine void configure( const std::string &logPath, // Path to directory where to store log files const std::string &renderSystem, - const std::string &rttMode, - bool _logging); // Enable or disable logging + const std::string &rttMode); // Enable or disable logging /// Create a window with the given title void createWindow(const std::string &title, const WindowSettings& settings); @@ -145,10 +99,6 @@ namespace OEngine /// Kill the renderer. void cleanup(); - void loadPlugins(); - - void unloadPlugins(); - void update(float dt); /// Write a screenshot to file From a85dffe644b039a9badbdb6126c5609bc4479eb2 Mon Sep 17 00:00:00 2001 From: Nikolay Kasyanov Date: Thu, 31 Oct 2013 20:28:01 +0300 Subject: [PATCH 109/148] Delete README_Mac.md Use updated manual from wiki: https://wiki.openmw.org/index.php?title=Development_Environment_Setup#Mac_OS_X --- README_Mac.md | 160 -------------------------------------------------- 1 file changed, 160 deletions(-) delete mode 100644 README_Mac.md diff --git a/README_Mac.md b/README_Mac.md deleted file mode 100644 index dc39183680..0000000000 --- a/README_Mac.md +++ /dev/null @@ -1,160 +0,0 @@ -#Getting OpenMW Working on OS X - -## Initial setup -First of all, clone OpenMW repo. - - $ git clone github.com/zinnschlag/openmw - -Or use your github url if you forked. - -About dependencies: I prefer not to install them globally (i. e. in /usr/local/), so I'm installing them in directory in my home directory. If OpenMW sources is in $HOME/path/openmw, I'm using $HOME/path/libs/root as prefix for boost and other libs. - -It's useful to create env var for lib install prefix: - - $ export OMW_LIB_PREFIX=$HOME/path/libs/root` - -Most of libs can be installed from [Homebrew][homebrew]. Only mpg123 needs to be installed from source (due to lack of universal compilation support). I think that some of libs can be installed from MacPorts or Fink too. - -As OpenMW currently only supports i386 architecture on OS X, denendencies also should support it. Set some env vars in current terminal: - - $ export CFLAGS="-arch i386" - $ export CXXFLAGS="-arch i386" - $ export LDFLAGS="-arch i386" - -If you close your terminal, you should set env vars again before pcoceeding to next steps! - -## Boost -Download [boost][boost] and install it with the following command: - - $ cd /path/to/boost/source - $ ./bootstrap.sh --prefix=$OMW_LIB_PREFIX - $ ./bjam --build-dir=build --layout=versioned \ - --toolset=darwin architecture=x86 address-model=32 \ - --link-shared,static --prefix=$OMW_LIB_PREFIX install - - -Alternatively you can install boost with homebrew: - - $ brew install boost --universal - -I think MacPorts also should support universal build for boost. - -## Ogre -Download [Ogre][] SDK (tested with 1.7.3), unpack it somewhere and move -`lib/Release/Ogre.framework` into `/Library/Frameworks`. - -## OIS -Download patched [OIS][] and use the XCode project provided. Be sure to set your build architecture to - `i386`. Once it built, locate built OIS.framework with Xcode and move it to `/Library/Frameworks`. - -## mpg123 -Download [MPG 123][mpg123] and build it: - - $ cd /path/to/mpg123/source - $ ./configure --prefix=$OMW_LIB_PREFIX --disable-debug \ - --disable-dependency-tracking \ - --with-optimization=4 \ - --with-audio=dummy \ - --with-default-audio=dummy \ - --with-cpu=sse_alone \ - $ make install - -## libsndfile -Download [libsndfile][] and build it: - - $ cd /path/to/libsndfile/source - $ ./configure --prefix=$OMW_LIB_PREFIX \ - --disable-dependency-tracking - $ make install - -or install with homebrew: - - $ brew install libsndfile --universal - -## Bullet -Download [Bullet][] and build it: - - $ cd /path/to/bullet/source - $ mkdir build - $ cd build - $ cmake -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=$OMW_LIB_PREFIX \ - -DBUILD_EXTRAS=OFF \ - -DBUILD_DEMOS=OFF \ - -DCMAKE_OSX_ARCHITECTURES=i386 \ - -DCMAKE_INSTALL_NAME_DIR=$OMW_LIB_RPEFIX/lib \ - -G"Unix Makefiles" ../ - $ make install - -or install with homebrew: - - $ brew install bullet --HEAD --universal - -I prefer head because 2.79 has some issue which causes OpenMW to lag. Also you can edit formula and install 2.77, which is stable and haven't mentioned issue. - -## Qt -Install [Qt][qt]. Qt SDK distributed by Nokia is not an option because it's 64 bit only, and OpenMW currently doesn't build for 64 bit on OS X. I'm installing it from Homebrew: - - $ brew install qt --universal - -## Run CMake -Generate the Makefile for OpenMW as follows and build OpenMW: - - $ mkdir /path/to/openmw/build/dir - $ cd /path/to/open/build/dir - $ cmake \ - -D CMAKE_OSX_ARCHITECTURES=i386 \ - -D OGRE_SDK=/path/to/ogre/sdk \ - -D BOOST_INCLUDEDIR=$OMW_LIB_PREFIX/include/boost-1_45 \ - -D BOOST_LIBRARYDIR=$OMW_LIB_PREFIX/lib \ - -D SNDFILE_INCLUDE_DIR=$OMW_LIB_PREFIX/include \ - -D SNDFILE_LIBRARY=$OMW_LIB_PREFIX/lib/libsndfile.a \ - -D MPG123_LIBRARY=$OMW_LIB_PREFIX/lib/libmpg123.a \ - -D MPG123_INCLUDE_DIR=$OMW_LIB_PREFIX/include \ - -D BULLET_DYNAMICS_LIBRARY=$OMW_LIB_PREFIX/lib/libBulletDynamics.a \ - -D BULLET_COLLISION_LIBRARY=$OMW_LIB_PREFIX/lib/libBulletCollision.a \ - -D BULLET_MATH_LIBRARY=$OMW_LIB_PREFIX/lib/libLinearMath.a \ - -D BULLET_SOFTBODY_LIBRARY=$OMW_LIB_PREFIX/lib/libBulletSoftBody.a \ - -D BULLET_INCLUDE_DIR=$OMW_LIB_PREFIX/include/bullet/ \ - -G "Unix Makefiles" /path/to/openmw/source/dir - $ make - -You can use `-G"Xcode"` if you prefer Xcode, or -G"Eclipse CDT4 - Unix Makefiles" -if you prefer Eclipse. You also can specify `-D CMAKE_BUILD_TYPE=Debug` for debug -build. As for CMake 2.8.7 and Xcode 4.3, Xcode generator is broken. Sadly Eclipse CDT also cannot import generated project at least on my machine. - -If all libs installed via homebrew (excluding mpg123), then command would be even simplier: - - $ cmake \ - -D CMAKE_OSX_ARCHITECTURES="i386" \ - -D OGRE_SDK=/path/to/ogre/sdk \ - -D MPG123_LIBRARY=$OMW_LIB_PREFIX/lib/libmpg123.a \ - -D MPG123_INCLUDE_DIR=$OMW_LIB_PREFIX/include \ - -G "Unix Makefiles" /path/to/openmw/source/dir - $ make - -Note for users with recent Xcode versions: you must explicitly specify what set of compilers do you use! If not, gcc will be used for C and Clang for C++. Just add this two -D's to command: `-D CMAKE_C_COMPILER=/usr/bin/clang` and `-D CMAKE_CXX_COMPILER=/usr/bin/clang` - -Note for Xcode 4.3 users: you should specify full path to used SDK, because current CMake (2.8.7) couldn't find SDKs inside Xcode app bundle: - - -D CMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk" - -# Run -From your build directory run: - - $ OpenMW.app/Contents/MacOS/openmw -or: - - $ open OpenMW.app -Enjoy! - -[homebrew]: https://github.com/mxcl/homebrew -[boost]: http://www.boost.org -[Ogre]: http://www.ogre3d.org -[Bullet]: http://bulletphysics.org -[OIS]: https://github.com/corristo/ois-fork -[mpg123]: http://www.mpg123.de -[libsndfile]: http://www.mega-nerd.com/libsndfile -[official website]: http://openmw.com -[Will Thimbleby's Ogre Framework]: http://www.thimbleby.net/ogre/ -[qt]: http://qt.nokia.com/ \ No newline at end of file From 0cb591e4f61a3f2caeb84cfc73c0b68ff187a244 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Thu, 31 Oct 2013 18:12:13 -0500 Subject: [PATCH 110/148] Fixed path problem with adjuster widget and local data path. --- apps/opencs/editor.cpp | 7 ++--- apps/opencs/view/doc/adjusterwidget.cpp | 35 ++++++++++++++++--------- apps/opencs/view/doc/adjusterwidget.hpp | 2 ++ apps/opencs/view/doc/filedialog.cpp | 18 ++++++------- 4 files changed, 35 insertions(+), 27 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 1c861ed67e..63afe7a9dd 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -10,8 +10,6 @@ #include "model/world/data.hpp" #include -#include - CS::Editor::Editor() : mDocumentManager (mCfgMgr), mViewManager (mDocumentManager) { @@ -119,13 +117,13 @@ void CS::Editor::createGame() void CS::Editor::createAddon() { mStartup.hide(); - mFileDialog.showDialog (CSVDoc::FileDialog::DialogType_New); + mFileDialog.showDialog (CSVDoc::ContentAction_New); } void CS::Editor::loadDocument() { mStartup.hide(); - mFileDialog.showDialog (CSVDoc::FileDialog::DialogType_Open); + mFileDialog.showDialog (CSVDoc::ContentAction_Edit); } void CS::Editor::openFiles (const boost::filesystem::path &savePath) @@ -135,7 +133,6 @@ void CS::Editor::openFiles (const boost::filesystem::path &savePath) foreach (const QString &path, mFileDialog.selectedFilePaths()) files.push_back(path.toStdString()); - qDebug() << "save file path: " << savePath.c_str(); CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, false); mViewManager.addView (document); diff --git a/apps/opencs/view/doc/adjusterwidget.cpp b/apps/opencs/view/doc/adjusterwidget.cpp index 332d460758..09e58690fc 100644 --- a/apps/opencs/view/doc/adjusterwidget.cpp +++ b/apps/opencs/view/doc/adjusterwidget.cpp @@ -52,47 +52,56 @@ bool CSVDoc::AdjusterWidget::isValid() const { return mValid; } + +void CSVDoc::AdjusterWidget::setFilenameCheck (bool doCheck) +{ + mDoFilenameCheck = doCheck; +} + void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon) { QString message; - if (mAction == ContentAction_Undefined) - { - throw std::runtime_error("ContentAction_Undefined when AdjusterWidget::setName() called."); - return; - } + mValid = (!name.isEmpty()); - if (name.isEmpty()) + if (!mValid) { - mValid = false; message = "No name."; } else { boost::filesystem::path path (name.toUtf8().data()); - path.replace_extension (addon ? ".omwaddon" : ".omwgame"); + bool isLegacyPath = (path.extension() == ".esm" || + path.extension() == ".esp"); - if (path.parent_path().string()==mLocalData.string()) + bool isFilePathChanged = (path.parent_path().string() != mLocalData.string()); + + if (isLegacyPath) + path.replace_extension (addon ? ".omwaddon" : ".omwgame"); + + //if the file came from data-local and is not a legacy file to be converted, + //don't worry about doing a file check. + if (!isFilePathChanged && !isLegacyPath) { // path already points to the local data directory message = QString::fromUtf8 (("Will be saved as: " + path.string()).c_str()); mResultPath = path; - mValid = true; } + //in all other cases, ensure the path points to data-local and do an existing file check else { // path points somewhere else or is a leaf name. - path = mLocalData / path.filename(); + if (isFilePathChanged) + path = mLocalData / path.filename(); message = QString::fromUtf8 (("Will be saved as: " + path.string()).c_str()); mResultPath = path; - mValid = true; if (boost::filesystem::exists (path)) { /// \todo add an user setting to make this an error. - message += "

But a file with the same name already exists. If you continue, it will be overwritten."; + message += "

A file with the same name already exists. If you continue, it will be overwritten."; } } } diff --git a/apps/opencs/view/doc/adjusterwidget.hpp b/apps/opencs/view/doc/adjusterwidget.hpp index 627f89c1f8..91e308236f 100644 --- a/apps/opencs/view/doc/adjusterwidget.hpp +++ b/apps/opencs/view/doc/adjusterwidget.hpp @@ -28,6 +28,7 @@ namespace CSVDoc bool mValid; boost::filesystem::path mResultPath; ContentAction mAction; + bool mDoFilenameCheck; public: @@ -36,6 +37,7 @@ namespace CSVDoc void setLocalData (const boost::filesystem::path& localData); void setAction (ContentAction action); + void setFilenameCheck (bool doCheck); bool isValid() const; boost::filesystem::path getPath() const; diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 57b61ff8b2..c0cda26dc9 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -17,8 +17,6 @@ #include "filewidget.hpp" #include "adjusterwidget.hpp" -#include - CSVDoc::FileDialog::FileDialog(QWidget *parent) : QDialog(parent), mSelector (0), mFileWidget (0), mAdjusterWidget (0) { @@ -47,9 +45,6 @@ QStringList CSVDoc::FileDialog::selectedFilePaths() void CSVDoc::FileDialog::setLocalData (const boost::filesystem::path& localData) { - if (mDialogType != DialogType_New) - return; - mAdjusterWidget->setLocalData (localData); } @@ -68,10 +63,13 @@ void CSVDoc::FileDialog::showDialog (ContentAction action) case ContentAction_Edit: buildOpenFileView(); break; + default: break; } + mAdjusterWidget->setFilenameCheck (mAction == ContentAction_New); + //connections common to both dialog view flavors connect (mSelector, SIGNAL (signalCurrentGamefileIndexChanged (int)), this, SLOT (slotUpdateAcceptButton (int))); @@ -124,7 +122,7 @@ void CSVDoc::FileDialog::slotUpdateAcceptButton (int) { QString name = ""; - if (mDialogType == DialogType_New) + if (mAction == ContentAction_New) name = mFileWidget->getName(); slotUpdateAcceptButton (name, true); @@ -134,11 +132,13 @@ void CSVDoc::FileDialog::slotUpdateAcceptButton(const QString &name, bool) { bool success = (mSelector->selectedFiles().size() > 0); - if (mDialogType == DialogType_New) + bool isNew = (mAction == ContentAction_New); + + if (isNew) success = success && !(name.isEmpty()); else { - ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back(); + ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back();; mAdjusterWidget->setName (file->fileName(), !file->isGameFile()); } @@ -147,7 +147,7 @@ void CSVDoc::FileDialog::slotUpdateAcceptButton(const QString &name, bool) QString CSVDoc::FileDialog::filename() const { - if (mDialogType == DialogType_New) + if (mAction == ContentAction_New) return ""; return mSelector->currentFile(); From e6960d915a1399229538c761d39cf9f7e418b8ff Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 2 Nov 2013 02:48:30 +0100 Subject: [PATCH 111/148] Add simple Ogre widget --- apps/opencs/CMakeLists.txt | 4 + apps/opencs/editor.cpp | 17 ++++ apps/opencs/main.cpp | 8 ++ apps/opencs/view/render/scenewidget.cpp | 128 ++++++++++++++++++++++++ apps/opencs/view/render/scenewidget.hpp | 40 ++++++++ apps/opencs/view/world/scenesubview.cpp | 15 ++- extern/sdl4ogre/sdlinputwrapper.cpp | 4 +- 7 files changed, 205 insertions(+), 11 deletions(-) create mode 100644 apps/opencs/view/render/scenewidget.cpp create mode 100644 apps/opencs/view/render/scenewidget.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index f918cfebfb..ed6a6b29e0 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -63,6 +63,10 @@ opencs_units (view/world scenetoolmode ) +opencs_units (view/render + scenewidget + ) + opencs_units_noqt (view/world dialoguesubview subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 9a6832ec00..31cbc3dbaa 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -9,6 +9,9 @@ #include "model/doc/document.hpp" #include "model/world/data.hpp" +#include +#include + CS::Editor::Editor() : mViewManager (mDocumentManager) { @@ -212,6 +215,20 @@ int CS::Editor::run() if (mLocal.empty()) return 1; + // TODO: setting + Ogre::Root::getSingleton().setRenderSystem(Ogre::Root::getSingleton().getRenderSystemByName("OpenGL Rendering Subsystem")); + + Ogre::Root::getSingleton().initialise(false); + + // Create a hidden background window to keep resources + Ogre::NameValuePairList params; + params.insert(std::make_pair("title", "")); + params.insert(std::make_pair("FSAA", "0")); + params.insert(std::make_pair("vsync", "false")); + params.insert(std::make_pair("hidden", "true")); + Ogre::RenderWindow* hiddenWindow = Ogre::Root::getSingleton().createRenderWindow("InactiveHidden", 1, 1, false, ¶ms); + hiddenWindow->setActive(false); + mStartup.show(); QApplication::setQuitOnLastWindowClosed (true); diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index ef7123c204..9f55f6004e 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -7,6 +7,8 @@ #include #include +#include + class Application : public QApplication { private: @@ -33,6 +35,12 @@ class Application : public QApplication int main(int argc, char *argv[]) { Q_INIT_RESOURCE (resources); + + // TODO: Ogre startup shouldn't be here, but it currently has to: + // SceneWidget destructor will delete the created render window, which would be called _after_ Root has shut down :( + OgreInit::OgreInit ogreInit; + ogreInit.init("./opencsOgre.log"); // TODO log path? + Application mApplication (argc, argv); mApplication.setWindowIcon (QIcon (":./opencs.png")); diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp new file mode 100644 index 0000000000..c8b37e9bb0 --- /dev/null +++ b/apps/opencs/view/render/scenewidget.cpp @@ -0,0 +1,128 @@ +#include "scenewidget.hpp" + +#include +#include + +#include +#include +#include + +namespace CSVRender +{ + + SceneWidget::SceneWidget(QWidget *parent) + : QWidget(parent) + , mWindow(NULL) + , mCamera(NULL) + , mSceneMgr(NULL) + { + setAttribute(Qt::WA_PaintOnScreen); + setAttribute(Qt::WA_NoSystemBackground); + + mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); + + // Throw in a random color just to make sure multiple scenes work + Ogre::Real r = Ogre::Math::RangeRandom(0, 1); + Ogre::Real g = Ogre::Math::RangeRandom(0, 1); + Ogre::Real b = Ogre::Math::RangeRandom(0, 1); + mSceneMgr->setAmbientLight(Ogre::ColourValue(r,g,b,1)); + + Ogre::Light* l = mSceneMgr->createLight(); + l->setType (Ogre::Light::LT_DIRECTIONAL); + l->setDirection (Ogre::Vector3(-0.4, -0.7, 0.3)); + l->setDiffuseColour (Ogre::ColourValue(0.7,0.7,0.7)); + + mCamera = mSceneMgr->createCamera("foo"); + + Ogre::Entity* ent = mSceneMgr->createEntity("cube", Ogre::SceneManager::PT_CUBE); + ent->setMaterialName("BaseWhite"); + + mSceneMgr->getRootSceneNode()->attachObject(ent); + + mCamera->setPosition(300,300,300); + mCamera->lookAt(0,0,0); + mCamera->setNearClipDistance(0.1); + mCamera->setFarClipDistance(3000); + } + + void SceneWidget::updateOgreWindow() + { + if (mWindow) + { + Ogre::Root::getSingleton().destroyRenderTarget(mWindow); + mWindow = NULL; + } + + std::stringstream windowHandle; + windowHandle << this->winId(); + + std::stringstream windowTitle; + static int count=0; + windowTitle << ++count; + + Ogre::NameValuePairList params; + + params.insert(std::make_pair("externalWindowHandle", windowHandle.str())); + params.insert(std::make_pair("title", windowTitle.str())); + params.insert(std::make_pair("FSAA", "0")); // TODO setting + params.insert(std::make_pair("vsync", "false")); // TODO setting + + mWindow = Ogre::Root::getSingleton().createRenderWindow(windowTitle.str(), this->width(), this->height(), false, ¶ms); + mWindow->addViewport(mCamera)->setBackgroundColour(Ogre::ColourValue(0.3,0.3,0.3,1)); + + Ogre::Real aspectRatio = Ogre::Real(width()) / Ogre::Real(height()); + mCamera->setAspectRatio(aspectRatio); + } + + SceneWidget::~SceneWidget() + { + Ogre::Root::getSingleton().destroyRenderTarget(mWindow); + } + + void SceneWidget::paintEvent(QPaintEvent* e) + { + if (!mWindow) + updateOgreWindow(); + + mWindow->update(); + e->accept(); + } + + + QPaintEngine* SceneWidget::paintEngine() const + { + // We don't want another paint engine to get in the way. + // So we return nothing. + return NULL; + } + + void SceneWidget::resizeEvent(QResizeEvent *e) + { + if (!mWindow) + return; + + const QSize &newSize = e->size(); + + // TODO: Fix Ogre to handle this more consistently (fixed in 1.9) +#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX + mWindow->resize(newSize.width(), newSize.height()); +#else + mWindow->windowMovedOrResized(); +#endif + + Ogre::Real aspectRatio = Ogre::Real(newSize.width()) / Ogre::Real(newSize.height()); + mCamera->setAspectRatio(aspectRatio); + } + + bool SceneWidget::event(QEvent *e) + { + if (e->type() == QEvent::WinIdChange) + { + // I haven't actually seen this happen yet. + if (mWindow) + updateOgreWindow(); + } + return QWidget::event(e); + } + +} diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp new file mode 100644 index 0000000000..355a6331e9 --- /dev/null +++ b/apps/opencs/view/render/scenewidget.hpp @@ -0,0 +1,40 @@ +#ifndef OPENCS_VIEW_SCENEWIDGET_H +#define OPENCS_VIEW_SCENEWIDGET_H + +#include + +namespace Ogre +{ + class Camera; + class SceneManager; + class RenderWindow; +} + +namespace CSVRender +{ + + class SceneWidget : public QWidget + { + Q_OBJECT + + public: + SceneWidget(QWidget *parent); + virtual ~SceneWidget(void); + + QPaintEngine* paintEngine() const; + + private: + void paintEvent(QPaintEvent* e); + void resizeEvent(QResizeEvent* e); + bool event(QEvent* e); + + void updateOgreWindow(); + + Ogre::Camera* mCamera; + Ogre::SceneManager* mSceneMgr; + Ogre::RenderWindow* mWindow; + }; + +} + +#endif diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index e3618c5493..83b30be133 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -9,6 +9,8 @@ #include "../filter/filterbox.hpp" +#include "../render/scenewidget.hpp" + #include "tablebottombox.hpp" #include "creator.hpp" #include "scenetoolbar.hpp" @@ -41,15 +43,10 @@ toolbar->addTool (new SceneToolMode (toolbar)); toolbar->addTool (new SceneToolMode (toolbar)); layout2->addWidget (toolbar, 0); - /// \todo replace with rendering widget - QPalette palette2 (palette()); - palette2.setColor (QPalette::Background, Qt::white); - QLabel *placeholder = new QLabel ("Here goes the 3D scene", this); - placeholder->setAutoFillBackground (true); - placeholder->setPalette (palette2); - placeholder->setAlignment (Qt::AlignHCenter); - layout2->addWidget (placeholder, 1); + CSVRender::SceneWidget* sceneWidget = new CSVRender::SceneWidget(this); + + layout2->addWidget (sceneWidget, 1); layout->insertLayout (0, layout2, 1); @@ -79,4 +76,4 @@ void CSVWorld::SceneSubView::updateEditorSetting(const QString &settingName, con void CSVWorld::SceneSubView::setStatusBar (bool show) { mBottom->setStatusBar (show); -} \ No newline at end of file +} diff --git a/extern/sdl4ogre/sdlinputwrapper.cpp b/extern/sdl4ogre/sdlinputwrapper.cpp index df74bba3b6..e71ed909e5 100644 --- a/extern/sdl4ogre/sdlinputwrapper.cpp +++ b/extern/sdl4ogre/sdlinputwrapper.cpp @@ -126,7 +126,7 @@ namespace SFO case SDL_WINDOWEVENT_SIZE_CHANGED: int w,h; SDL_GetWindowSize(mSDLWindow, &w, &h); - // TODO: Fix Ogre to handle this more consistently + // TODO: Fix Ogre to handle this more consistently (fixed in 1.9) #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX mOgreWindow->resize(w, h); #else @@ -137,7 +137,7 @@ namespace SFO break; case SDL_WINDOWEVENT_RESIZED: - // TODO: Fix Ogre to handle this more consistently + // TODO: Fix Ogre to handle this more consistently (fixed in 1.9) #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX mOgreWindow->resize(evt.window.data1, evt.window.data2); #else From cddece4f9e84de70e7861c24373c62a37d4356ad Mon Sep 17 00:00:00 2001 From: graffy76 Date: Fri, 1 Nov 2013 21:47:26 -0500 Subject: [PATCH 112/148] Another stab at fixing the pathing problem... --- apps/opencs/view/doc/adjusterwidget.cpp | 6 ++++- apps/opencs/view/doc/filedialog.cpp | 9 ++++--- .../contentselector/model/contentmodel.cpp | 27 ++++++++++++------- components/contentselector/model/esmfile.cpp | 6 ++--- components/contentselector/model/esmfile.hpp | 6 ++--- .../contentselector/view/contentselector.cpp | 5 ++-- 6 files changed, 37 insertions(+), 22 deletions(-) diff --git a/apps/opencs/view/doc/adjusterwidget.cpp b/apps/opencs/view/doc/adjusterwidget.cpp index 09e58690fc..5ebfacd035 100644 --- a/apps/opencs/view/doc/adjusterwidget.cpp +++ b/apps/opencs/view/doc/adjusterwidget.cpp @@ -10,6 +10,9 @@ #include #include +#include + + CSVDoc::AdjusterWidget::AdjusterWidget (QWidget *parent) : QWidget (parent), mValid (false), mAction (ContentAction_Undefined) { @@ -76,7 +79,8 @@ void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon) path.extension() == ".esp"); bool isFilePathChanged = (path.parent_path().string() != mLocalData.string()); - + qDebug() << "current path: " << path.parent_path().c_str(); + qDebug() << "data-local: " << mLocalData.c_str(); if (isLegacyPath) path.replace_extension (addon ? ".omwaddon" : ".omwgame"); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index c0cda26dc9..ce7d7dfb0b 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -17,6 +17,8 @@ #include "filewidget.hpp" #include "adjusterwidget.hpp" +#include + CSVDoc::FileDialog::FileDialog(QWidget *parent) : QDialog(parent), mSelector (0), mFileWidget (0), mAdjusterWidget (0) { @@ -38,7 +40,7 @@ QStringList CSVDoc::FileDialog::selectedFilePaths() QStringList filePaths; foreach (ContentSelectorModel::EsmFile *file, mSelector->selectedFiles() ) - filePaths.append(file->path()); + filePaths.append(file->filePath()); return filePaths; } @@ -139,7 +141,8 @@ void CSVDoc::FileDialog::slotUpdateAcceptButton(const QString &name, bool) else { ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back();; - mAdjusterWidget->setName (file->fileName(), !file->isGameFile()); + mAdjusterWidget->setName (file->filePath(), !file->isGameFile()); + qDebug() << "setting filepath " << file->filePath(); } ui.projectButtonBox->button (QDialogButtonBox::Ok)->setEnabled (success); @@ -168,7 +171,7 @@ void CSVDoc::FileDialog::slotOpenFile() { ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back(); - mAdjusterWidget->setName (file->fileName(), !file->isGameFile()); + mAdjusterWidget->setName (file->filePath(), !file->isGameFile()); emit signalOpenFiles (mAdjusterWidget->getPath()); } diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 0674642eed..ea389c2bf6 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -69,7 +69,7 @@ const ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(co { foreach (const EsmFile *file, mFiles) { - if (name == file->fileName()) + if (name == file->filePath()) return file; } return 0; @@ -158,7 +158,7 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int case Qt::CheckStateRole: { if (!file->isGameFile()) - return isChecked(file->fileName()); + return isChecked(file->filePath()); break; } @@ -174,7 +174,7 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int } case Qt::UserRole + 1: - return isChecked(file->fileName()); + return isChecked(file->filePath()); break; } return QVariant(); @@ -186,7 +186,7 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex &index, const return false; EsmFile *file = item(index.row()); - QString fileName = file->fileName(); + QString fileName = file->filePath(); bool success = false; switch(role) @@ -396,6 +396,7 @@ bool ContentSelectorModel::ContentModel::canBeChecked(const EsmFile *file) const void ContentSelectorModel::ContentModel::addFile(EsmFile *file) { + qDebug() << "adding file: " << file->filePath(); beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); mFiles.append(file); endInsertRows(); @@ -417,6 +418,8 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) // Create a decoder for non-latin characters in esx metadata QTextDecoder *decoder = codec->makeDecoder(); + qDebug() << "searching path: " << path << " files found: " << dir.entryList().size(); + foreach (const QString &path, dir.entryList()) { QFileInfo info(dir.absoluteFilePath(path)); @@ -430,18 +433,24 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) fileReader.open(dir.absoluteFilePath(path).toStdString()); foreach (const ESM::Header::MasterData &item, fileReader.getGameFiles()) + { + qDebug() << "adding gamefile: " << item.name.c_str(); file->addGameFile(QString::fromStdString(item.name)); + } file->setAuthor (decoder->toUnicode(fileReader.getAuthor().c_str())); file->setDate (info.lastModified()); file->setFormat (fileReader.getFormat()); - file->setPath (info.absoluteFilePath()); + file->setFilePath (info.absoluteFilePath()); file->setDescription(decoder->toUnicode(fileReader.getDesc().c_str())); // Put the file in the table - if (item(path) == 0) + if (item(file->filePath()) == 0) + { + qDebug () << "adding file " << file->filePath(); addFile(file); + } } catch(std::runtime_error &e) { // An error occurred while reading the .esp @@ -543,8 +552,8 @@ void ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool { if (downstreamFile->gameFiles().contains(name)) { - if (mCheckStates.contains(downstreamFile->fileName())) - mCheckStates[downstreamFile->fileName()] = Qt::Unchecked; + if (mCheckStates.contains(downstreamFile->filePath())) + mCheckStates[downstreamFile->filePath()] = Qt::Unchecked; emit dataChanged(indexFromItem(downstreamFile), indexFromItem(downstreamFile)); } @@ -558,7 +567,7 @@ ContentSelectorModel::ContentFileList ContentSelectorModel::ContentModel::checke foreach (EsmFile *file, mFiles) { - if (isChecked(file->fileName())) + if (isChecked(file->filePath())) list << file; } diff --git a/components/contentselector/model/esmfile.cpp b/components/contentselector/model/esmfile.cpp index 9dfe49ebaf..35f78386ce 100644 --- a/components/contentselector/model/esmfile.cpp +++ b/components/contentselector/model/esmfile.cpp @@ -34,7 +34,7 @@ void ContentSelectorModel::EsmFile::setFormat(int format) mFormat = format; } -void ContentSelectorModel::EsmFile::setPath(const QString &path) +void ContentSelectorModel::EsmFile::setFilePath(const QString &path) { mPath = path; } @@ -81,7 +81,7 @@ QVariant ContentSelectorModel::EsmFile::fileProperty(const FileProperty prop) co return mModified.toString(Qt::ISODate); break; - case FileProperty_Path: + case FileProperty_FilePath: return mPath; break; @@ -118,7 +118,7 @@ void ContentSelectorModel::EsmFile::setFileProperty (const FileProperty prop, co mModified = QDateTime::fromString(value); break; - case FileProperty_Path: + case FileProperty_FilePath: mPath = value; break; diff --git a/components/contentselector/model/esmfile.hpp b/components/contentselector/model/esmfile.hpp index 743b1c5a68..fc0cca8a2b 100644 --- a/components/contentselector/model/esmfile.hpp +++ b/components/contentselector/model/esmfile.hpp @@ -23,7 +23,7 @@ namespace ContentSelectorModel FileProperty_Author = 1, FileProperty_Format = 2, FileProperty_DateModified = 3, - FileProperty_Path = 4, + FileProperty_FilePath = 4, FileProperty_Description = 5, FileProperty_GameFile = 6 }; @@ -41,7 +41,7 @@ namespace ContentSelectorModel void setSize(const int size); void setDate(const QDateTime &modified); void setFormat(const int format); - void setPath(const QString &path); + void setFilePath(const QString &path); void setGameFiles(const QStringList &gameFiles); void setDescription(const QString &description); @@ -52,7 +52,7 @@ namespace ContentSelectorModel inline QString author() const { return mAuthor; } inline QDateTime modified() const { return mModified; } inline float format() const { return mFormat; } - inline QString path() const { return mPath; } + inline QString filePath() const { return mPath; } inline const QStringList &gameFiles() const { return mGameFiles; } inline QString description() const { return mDescription; } inline QString toolTip() const { return sToolTip.arg(mAuthor) diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index b9e5189312..d12c8cb242 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -42,9 +42,6 @@ void ContentSelectorView::ContentSelector::buildGameFileView() connect (ui.gameFileView, SIGNAL (currentIndexChanged(int)), this, SLOT (slotCurrentGameFileIndexChanged(int))); - connect (ui.gameFileView, SIGNAL (currentIndexChanged (int)), - this, SIGNAL (signalCurrentGamefileIndexChanged (int))); - ui.gameFileView->setCurrentIndex(-1); ui.gameFileView->setCurrentIndex(0); } @@ -145,6 +142,8 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i if (proxy) proxy->setDynamicSortFilter(true); + + emit signalCurrentGamefileIndexChanged (index); } void ContentSelectorView::ContentSelector::slotAddonTableItemClicked(const QModelIndex &index) From afce10cf37d261cf749faeed509e5c2fd63f5131 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sat, 2 Nov 2013 16:20:40 +0100 Subject: [PATCH 113/148] Fixes #597: Assertion `dialogue->mId == id' failed in esmstore.cpp It seems that assertion was unnecessary, after removing it, dialogs related to moon-and-star in "Path of the Incarnate" quest were correctly loaded (dumped DialInfo records were correct). Signed-off-by: Lukasz Gromanowski --- apps/openmw/mwworld/esmstore.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 7703f2d233..5cee7efd97 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -100,11 +100,7 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) it->second->load(esm, id); if (n.val==ESM::REC_DIAL) { - // dirty hack, but it is better than non-const search() - // or friends - //dialogue = &mDialogs.mStatic.back(); dialogue = const_cast(mDialogs.find(id)); - assert (dialogue->mId == id); } else { dialogue = 0; } From 973803eb2f6cdbdaded0649250092e7adcc526bc Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 3 Nov 2013 00:02:46 -0500 Subject: [PATCH 114/148] Fixed pathing issues in launcher --- apps/launcher/datafilespage.cpp | 44 +++++++++++-- apps/launcher/datafilespage.hpp | 58 ++++++++++++++++ apps/launcher/settings/gamesettings.cpp | 2 +- apps/launcher/settings/gamesettings.hpp | 5 -- apps/opencs/view/doc/adjusterwidget.cpp | 6 +- apps/opencs/view/doc/filedialog.cpp | 3 - .../contentselector/model/contentmodel.cpp | 66 +++++++++---------- .../contentselector/model/contentmodel.hpp | 3 +- components/contentselector/model/esmfile.cpp | 5 +- components/contentselector/model/esmfile.hpp | 1 + .../contentselector/view/contentselector.cpp | 12 ++-- 11 files changed, 143 insertions(+), 62 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index e246b45154..71d072599a 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include @@ -37,6 +36,10 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, GameSet void Launcher::DataFilesPage::loadSettings() { + QStringList paths = mGameSettings.getDataDirs(); + paths.insert (0, mDataLocal); + PathIterator pathIterator (paths); + QString profileName = ui.profilesComboBox->currentText(); QStringList files = mLauncherSettings.values(QString("Profiles/") + profileName + QString("/game"), Qt::MatchExactly); @@ -47,10 +50,37 @@ void Launcher::DataFilesPage::loadSettings() QString gameFile (""); if (files.size()>0) - gameFile = files.at (0); + { + gameFile = pathIterator.findFirstPath (files.at(0)); - mSelector->setGameFile(gameFile); - mSelector->setCheckStates(addons); + if (!gameFile.isEmpty()) + mSelector->setGameFile (gameFile); +/* else + { + //throw gamefile error here. + }*/ + } + + QStringList missingFiles; + QStringList foundFiles; + + foreach (const QString &addon, addons) + { + QString filePath = pathIterator.findFirstPath (addon); + + if (filePath.isEmpty()) + missingFiles << addon; + else + foundFiles << filePath; + } +/* + if (missingFiles.size() > 0) + { + //throw addons error here. + } +*/ + if (foundFiles.size() > 0) + mSelector->setCheckStates (foundFiles); } void Launcher::DataFilesPage::saveSettings(const QString &profile) @@ -191,10 +221,10 @@ void Launcher::DataFilesPage::setupDataFiles() foreach (const QString &path, paths) mSelector->addFiles(path); - QString dataLocal = mGameSettings.getDataLocal(); + mDataLocal = mGameSettings.getDataLocal(); - if (!dataLocal.isEmpty()) - mSelector->addFiles(dataLocal); + if (!mDataLocal.isEmpty()) + mSelector->addFiles(mDataLocal); QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/")); QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index e394e6f41f..37603a2106 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -4,6 +4,10 @@ #include "ui_datafilespage.h" #include + +#include +#include + class QSortFilterProxyModel; class QAbstractItemModel; class QMenu; @@ -61,6 +65,8 @@ namespace Launcher GameSettings &mGameSettings; LauncherSettings &mLauncherSettings; + QString mDataLocal; + void setPluginsCheckstates(Qt::CheckState state); void buildView(); @@ -73,6 +79,58 @@ namespace Launcher bool showDeleteMessageBox (const QString &text); void addProfile (const QString &profile, bool setAsCurrent); void checkForDefaultProfile(); + + class PathIterator + { + QStringList::ConstIterator mCitEnd; + QStringList::ConstIterator mCitCurrent; + QStringList::ConstIterator mCitBegin; + QString mFile; + QString mFilePath; + + public: + PathIterator (const QStringList &list) + { + mCitBegin = list.constBegin(); + mCitCurrent = mCitBegin; + mCitEnd = list.constEnd(); + } + + QString findFirstPath (const QString &file) + { + mCitCurrent = mCitBegin; + mFile = file; + return path(); + } + + QString findNextPath () { return path(); } + + private: + + QString path () + { + bool success = false; + QDir dir; + QFileInfo file; + + while (!success) + { + if (mCitCurrent == mCitEnd) + break; + + dir.setPath (*(mCitCurrent++)); + file.setFile (dir.absoluteFilePath (mFile)); + + success = file.exists(); + } + + if (success) + return file.absoluteFilePath(); + + return ""; + } + + }; }; } #endif diff --git a/apps/launcher/settings/gamesettings.cpp b/apps/launcher/settings/gamesettings.cpp index 32eb2071f7..83c0995291 100644 --- a/apps/launcher/settings/gamesettings.cpp +++ b/apps/launcher/settings/gamesettings.cpp @@ -169,7 +169,7 @@ bool Launcher::GameSettings::writeFile(QTextStream &stream) return true; } -bool GameSettings::hasMaster() +bool Launcher::GameSettings::hasMaster() { bool result = false; QStringList content = mSettings.values(QString("content")); diff --git a/apps/launcher/settings/gamesettings.hpp b/apps/launcher/settings/gamesettings.hpp index 56917a79fd..60236200a9 100644 --- a/apps/launcher/settings/gamesettings.hpp +++ b/apps/launcher/settings/gamesettings.hpp @@ -51,11 +51,6 @@ namespace Launcher bool hasMaster(); - inline QStringList getDataDirs() { return mDataDirs; } - inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); } - inline QString getDataLocal() {return mDataLocal; } - inline bool hasMaster() { return mSettings.count(QString("master")) > 0; } - QStringList values(const QString &key, const QStringList &defaultValues = QStringList()); bool readFile(QTextStream &stream); bool writeFile(QTextStream &stream); diff --git a/apps/opencs/view/doc/adjusterwidget.cpp b/apps/opencs/view/doc/adjusterwidget.cpp index 5ebfacd035..09e58690fc 100644 --- a/apps/opencs/view/doc/adjusterwidget.cpp +++ b/apps/opencs/view/doc/adjusterwidget.cpp @@ -10,9 +10,6 @@ #include #include -#include - - CSVDoc::AdjusterWidget::AdjusterWidget (QWidget *parent) : QWidget (parent), mValid (false), mAction (ContentAction_Undefined) { @@ -79,8 +76,7 @@ void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon) path.extension() == ".esp"); bool isFilePathChanged = (path.parent_path().string() != mLocalData.string()); - qDebug() << "current path: " << path.parent_path().c_str(); - qDebug() << "data-local: " << mLocalData.c_str(); + if (isLegacyPath) path.replace_extension (addon ? ".omwaddon" : ".omwgame"); diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index ce7d7dfb0b..80b3066ddd 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -17,8 +17,6 @@ #include "filewidget.hpp" #include "adjusterwidget.hpp" -#include - CSVDoc::FileDialog::FileDialog(QWidget *parent) : QDialog(parent), mSelector (0), mFileWidget (0), mAdjusterWidget (0) { @@ -142,7 +140,6 @@ void CSVDoc::FileDialog::slotUpdateAcceptButton(const QString &name, bool) { ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back();; mAdjusterWidget->setName (file->filePath(), !file->isGameFile()); - qDebug() << "setting filepath " << file->filePath(); } ui.projectButtonBox->button (QDialogButtonBox::Ok)->setEnabled (success); diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 065b9c4974..7661f96083 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -3,9 +3,11 @@ #include #include -#include +#include #include +#include "components/esm/esmreader.hpp" + ContentSelectorModel::ContentModel::ContentModel(QObject *parent) : QAbstractTableModel(parent), mMimeType ("application/omwcontent"), @@ -380,15 +382,18 @@ bool ContentSelectorModel::ContentModel::canBeChecked(const EsmFile *file) const //addon can be checked if its gamefile is foreach (const QString &fileName, file->gameFiles()) { - const EsmFile *dependency = item(fileName); - - if (!dependency) - continue; - - if (dependency->isGameFile()) + foreach (EsmFile *dependency, mFiles) { - if (isChecked(fileName)) - return true; + //compare filenames only. Multiple instances + //of the filename (with different paths) is not relevant here. + if (!(dependency->fileName() == fileName)) + continue; + + if (dependency->isGameFile()) + { + if (isChecked(dependency->filePath())) + return true; + } } } return false; @@ -396,7 +401,6 @@ bool ContentSelectorModel::ContentModel::canBeChecked(const EsmFile *file) const void ContentSelectorModel::ContentModel::addFile(EsmFile *file) { - qDebug() << "adding file: " << file->filePath(); beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); mFiles.append(file); endInsertRows(); @@ -418,8 +422,6 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) // Create a decoder for non-latin characters in esx metadata QTextDecoder *decoder = codec->makeDecoder(); - qDebug() << "searching path: " << path << " files found: " << dir.entryList().size(); - foreach (const QString &path, dir.entryList()) { QFileInfo info(dir.absoluteFilePath(path)); @@ -433,10 +435,7 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) fileReader.open(dir.absoluteFilePath(path).toStdString()); foreach (const ESM::Header::MasterData &item, fileReader.getGameFiles()) - { - qDebug() << "adding gamefile: " << item.name.c_str(); file->addGameFile(QString::fromStdString(item.name)); - } file->setAuthor (decoder->toUnicode(fileReader.getAuthor().c_str())); file->setDate (info.lastModified()); @@ -447,10 +446,7 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path) // Put the file in the table if (item(file->filePath()) == 0) - { - qDebug () << "adding file " << file->filePath(); addFile(file); - } } catch(std::runtime_error &e) { // An error occurred while reading the .esp @@ -510,10 +506,23 @@ bool ContentSelectorModel::ContentModel::isChecked(const QString& name) const return false; } -void ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool checkState) +void ContentSelectorModel::ContentModel::setCheckStates (const QStringList &fileList, bool isChecked) +{ + foreach (const QString &file, fileList) + { + setCheckState (file, isChecked); + } +} + +bool ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool checkState) { if (name.isEmpty()) - return; + return false; + + const EsmFile *file = item(name); + + if (!file) + return false; Qt::CheckState state = Qt::Unchecked; @@ -523,8 +532,6 @@ void ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool mCheckStates[name] = state; emit dataChanged(indexFromItem(item(name)), indexFromItem(item(name))); - const EsmFile *file = item(name); - if (file->isGameFile()) emit dataChanged (index(0,0), index(rowCount()-1,0)); @@ -559,29 +566,20 @@ void ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool } } } + + return true; } ContentSelectorModel::ContentFileList ContentSelectorModel::ContentModel::checkedItems() const { ContentFileList list; + // TODO: // First search for game files and next addons, // so we get more or less correct game files vs addons order. foreach (EsmFile *file, mFiles) - { -<<<<<<< HEAD if (isChecked(file->filePath())) -======= - if (isChecked(file->fileName()) && file->isGameFile()) list << file; - } - - foreach (EsmFile *file, mFiles) - { - if (isChecked(file->fileName()) && !file->isGameFile()) ->>>>>>> f5fbe7361fad698e8dd3330e9820a157800be8ae - list << file; - } return list; } diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index 0d6c52cc63..ae49dd27da 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -45,7 +45,8 @@ namespace ContentSelectorModel const EsmFile *item(const QString &name) const; bool isChecked(const QString &name) const; - void setCheckState(const QString &name, bool isChecked); + bool setCheckState(const QString &name, bool isChecked); + void setCheckStates (const QStringList &fileList, bool isChecked); ContentFileList checkedItems() const; void uncheckAll(); diff --git a/components/contentselector/model/esmfile.cpp b/components/contentselector/model/esmfile.cpp index 35f78386ce..a0a09105a7 100644 --- a/components/contentselector/model/esmfile.cpp +++ b/components/contentselector/model/esmfile.cpp @@ -6,8 +6,9 @@ int ContentSelectorModel::EsmFile::sPropertyCount = 7; QString ContentSelectorModel::EsmFile::sToolTip = QString("Author: %1
\ Version: %2
\ -
Description:
%3
\ -
Dependencies: %4
"); + Path:
%3
\ +
Description:
%4
\ +
Dependencies: %5
"); ContentSelectorModel::EsmFile::EsmFile(QString fileName, ModelItem *parent) diff --git a/components/contentselector/model/esmfile.hpp b/components/contentselector/model/esmfile.hpp index fc0cca8a2b..ca24b52d11 100644 --- a/components/contentselector/model/esmfile.hpp +++ b/components/contentselector/model/esmfile.hpp @@ -57,6 +57,7 @@ namespace ContentSelectorModel inline QString description() const { return mDescription; } inline QString toolTip() const { return sToolTip.arg(mAuthor) .arg(mFormat) + .arg(mPath) .arg(mDescription) .arg(mGameFiles.join(", ")); } diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index d12c8cb242..4758dd5a05 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -67,10 +67,15 @@ void ContentSelectorView::ContentSelector::setGameFile(const QString &filename) if (!filename.isEmpty()) { - index = ui.gameFileView->findText(filename); + const ContentSelectorModel::EsmFile *file = mContentModel->item (filename); + index = ui.gameFileView->findText (file->fileName()); //verify that the current index is also checked in the model - mContentModel->setCheckState(filename, true); + if (!mContentModel->setCheckState(filename, true)) + { + //throw error in case file not found? + return; + } } ui.gameFileView->setCurrentIndex(index); @@ -86,8 +91,7 @@ void ContentSelectorView::ContentSelector::setCheckStates(const QStringList &lis if (list.isEmpty()) return; - foreach (const QString &file, list) - mContentModel->setCheckState(file, Qt::Checked); + mContentModel->setCheckStates (list, true); } ContentSelectorModel::ContentFileList From 12c06a56152bd0452c0239b07acf8610a0f6a6b8 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 3 Nov 2013 06:21:28 -0600 Subject: [PATCH 115/148] Fixed broken dependency check --- apps/opencs/view/doc/filedialog.cpp | 2 +- components/contentselector/model/contentmodel.cpp | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 80b3066ddd..ab56415a14 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -138,7 +138,7 @@ void CSVDoc::FileDialog::slotUpdateAcceptButton(const QString &name, bool) success = success && !(name.isEmpty()); else { - ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back();; + ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back(); mAdjusterWidget->setName (file->filePath(), !file->isGameFile()); } diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 7661f96083..0fb1b42161 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include "components/esm/esmreader.hpp" @@ -69,9 +68,14 @@ ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(int row) } const ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(const QString &name) const { + EsmFile::FileProperty fp = EsmFile::FileProperty_FileName; + + if (name.contains ('/')) + fp = EsmFile::FileProperty_FilePath; + foreach (const EsmFile *file, mFiles) { - if (name == file->filePath()) + if (name == file->fileProperty (fp).toString()) return file; } return 0; @@ -538,15 +542,15 @@ bool ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool //if we're checking an item, ensure all "upstream" files (dependencies) are checked as well. if (state == Qt::Checked) { - foreach (const QString &upstreamName, file->gameFiles()) + foreach (QString upstreamName, file->gameFiles()) { const EsmFile *upstreamFile = item(upstreamName); if (!upstreamFile) continue; - if (!isChecked(upstreamName)) - mCheckStates[upstreamName] = Qt::Checked; + if (!isChecked(upstreamFile->filePath())) + mCheckStates[upstreamFile->filePath()] = Qt::Checked; emit dataChanged(indexFromItem(upstreamFile), indexFromItem(upstreamFile)); From 1d4b5a2425c8a627b6489db5af7c2b63798542e8 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 3 Nov 2013 14:02:41 -0600 Subject: [PATCH 116/148] Fix broken launcher content file display / selection scheme Disable selection of content files with missing dependencies (grayed out) --- apps/launcher/datafilespage.cpp | 11 +- apps/launcher/utils/profilescombobox.hpp | 7 ++ .../contentselector/model/contentmodel.cpp | 104 +++++++++++------- .../contentselector/model/contentmodel.hpp | 5 +- .../contentselector/view/contentselector.cpp | 20 +++- 5 files changed, 93 insertions(+), 54 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 71d072599a..5ae850566f 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -79,8 +79,7 @@ void Launcher::DataFilesPage::loadSettings() //throw addons error here. } */ - if (foundFiles.size() > 0) - mSelector->setCheckStates (foundFiles); + mSelector->setCheckStates (foundFiles); } void Launcher::DataFilesPage::saveSettings(const QString &profile) @@ -177,7 +176,7 @@ void Launcher::DataFilesPage::setProfile (const QString &previous, const QString if (!previous.isEmpty() && savePrevious) saveSettings (previous); - ui.profilesComboBox->setCurrentIndex (ui.profilesComboBox->findText (current)); + ui.profilesComboBox->setCurrentProfile (ui.profilesComboBox->findText (current)); loadSettings(); @@ -232,7 +231,7 @@ void Launcher::DataFilesPage::setupDataFiles() foreach (const QString &item, profiles) addProfile (item, false); - addProfile (profile, true); + setProfile (ui.profilesComboBox->findText(profile), false); loadSettings(); } @@ -289,6 +288,10 @@ void Launcher::DataFilesPage::on_deleteProfileAction_triggered() // Remove the profile from the combobox ui.profilesComboBox->removeItem (ui.profilesComboBox->findText (profile)); + removeProfile(profile); + + saveSettings(); + loadSettings(); checkForDefaultProfile(); diff --git a/apps/launcher/utils/profilescombobox.hpp b/apps/launcher/utils/profilescombobox.hpp index 1e27f66a9c..7b83c41b2f 100644 --- a/apps/launcher/utils/profilescombobox.hpp +++ b/apps/launcher/utils/profilescombobox.hpp @@ -4,6 +4,8 @@ #include "components/contentselector/view/combobox.hpp" #include "lineedit.hpp" +#include + class QString; class ProfilesComboBox : public ContentSelectorView::ComboBox @@ -21,6 +23,11 @@ public: explicit ProfilesComboBox(QWidget *parent = 0); void setEditEnabled(bool editable); + void setCurrentProfile(int index) + { + ComboBox::setCurrentIndex(index); + mOldProfile = currentText(); + } signals: void signalProfileTextChanged(const QString &item); diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index 0fb1b42161..5f3575eb40 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -13,7 +13,6 @@ ContentSelectorModel::ContentModel::ContentModel(QObject *parent) : mMimeTypes (QStringList() << mMimeType), mColumnCount (1), mDragDropFlags (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled), - mDefaultFlags (Qt::ItemIsDropEnabled | Qt::ItemIsSelectable), mDropActions (Qt::CopyAction | Qt::MoveAction) { setEncoding ("win1252"); @@ -102,10 +101,53 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index if (!file) return Qt::NoItemFlags; - if (canBeChecked(file)) - return Qt::ItemIsEnabled | mDragDropFlags | mDefaultFlags; + //game files can always be checked + if (file->isGameFile()) + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; - return Qt::NoItemFlags; + Qt::ItemFlags returnFlags; + bool allDependenciesFound = true; + bool gamefileChecked = false; + + //addon can be checked if its gamefile is and all other dependencies exist + foreach (const QString &fileName, file->gameFiles()) + { + bool depFound = false; + foreach (EsmFile *dependency, mFiles) + { + //compare filenames only. Multiple instances + //of the filename (with different paths) is not relevant here. + depFound = (dependency->fileName() == fileName); + + if (!depFound) + continue; + + if (!gamefileChecked) + { + if (isChecked (dependency->filePath())) + gamefileChecked = (dependency->isGameFile()); + } + + // force it to iterate all files in cases where the current + // dependency is a game file to ensure that a later duplicate + // game file is / is not checked. + // (i.e., break only if it's not a gamefile or the game file has been checked previously) + if (gamefileChecked || !(dependency->isGameFile())) + break; + } + + allDependenciesFound = allDependenciesFound && depFound; + } + + if (gamefileChecked) + { + if (allDependenciesFound) + returnFlags = returnFlags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | mDragDropFlags; + else + returnFlags = Qt::ItemIsSelectable; + } + + return returnFlags; } QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int role) const @@ -173,7 +215,7 @@ QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int if (file->isGameFile()) return ContentType_GameFile; else - if (flags(index) & mDefaultFlags) + if (flags(index)) return ContentType_Addon; break; @@ -215,19 +257,13 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex &index, const case Qt::UserRole+1: { - setCheckState(fileName, value.toBool()); + success = (flags (index) & Qt::ItemIsEnabled); - emit dataChanged(index, index); - - foreach (EsmFile *file, mFiles) + if (success) { - if (file->gameFiles().contains(fileName)) - { - QModelIndex idx = indexFromItem(file); - emit dataChanged(idx, idx); - } + success = setCheckState(fileName, value.toBool()); + emit dataChanged(index, index); } - success = true; } break; @@ -377,32 +413,6 @@ bool ContentSelectorModel::ContentModel::dropMimeData(const QMimeData *data, Qt: return true; } -bool ContentSelectorModel::ContentModel::canBeChecked(const EsmFile *file) const -{ - //game files can always be checked - if (file->isGameFile()) - return true; - - //addon can be checked if its gamefile is - foreach (const QString &fileName, file->gameFiles()) - { - foreach (EsmFile *dependency, mFiles) - { - //compare filenames only. Multiple instances - //of the filename (with different paths) is not relevant here. - if (!(dependency->fileName() == fileName)) - continue; - - if (dependency->isGameFile()) - { - if (isChecked(dependency->filePath())) - return true; - } - } - } - return false; -} - void ContentSelectorModel::ContentModel::addFile(EsmFile *file) { beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); @@ -510,6 +520,11 @@ bool ContentSelectorModel::ContentModel::isChecked(const QString& name) const return false; } +bool ContentSelectorModel::ContentModel::isEnabled (QModelIndex index) const +{ + return (flags(index) & Qt::ItemIsEnabled); +} + void ContentSelectorModel::ContentModel::setCheckStates (const QStringList &fileList, bool isChecked) { foreach (const QString &file, fileList) @@ -518,6 +533,11 @@ void ContentSelectorModel::ContentModel::setCheckStates (const QStringList &file } } +void ContentSelectorModel::ContentModel::refreshModel() +{ + emit dataChanged (index(0,0), index(rowCount()-1,0)); +} + bool ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool checkState) { if (name.isEmpty()) @@ -537,7 +557,7 @@ bool ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool emit dataChanged(indexFromItem(item(name)), indexFromItem(item(name))); if (file->isGameFile()) - emit dataChanged (index(0,0), index(rowCount()-1,0)); + refreshModel(); //if we're checking an item, ensure all "upstream" files (dependencies) are checked as well. if (state == Qt::Checked) diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp index ae49dd27da..8c8c2124bc 100644 --- a/components/contentselector/model/contentmodel.hpp +++ b/components/contentselector/model/contentmodel.hpp @@ -44,19 +44,21 @@ namespace ContentSelectorModel QModelIndex indexFromItem(const EsmFile *item) const; const EsmFile *item(const QString &name) const; + bool isEnabled (QModelIndex index) const; bool isChecked(const QString &name) const; bool setCheckState(const QString &name, bool isChecked); void setCheckStates (const QStringList &fileList, bool isChecked); ContentFileList checkedItems() const; void uncheckAll(); + void refreshModel(); + private: void addFile(EsmFile *file); const EsmFile *item(int row) const; EsmFile *item(int row); - bool canBeChecked(const EsmFile *file) const; void sortFiles(); ContentFileList mFiles; @@ -69,7 +71,6 @@ namespace ContentSelectorModel QStringList mMimeTypes; int mColumnCount; Qt::ItemFlags mDragDropFlags; - Qt::ItemFlags mDefaultFlags; Qt::DropActions mDropActions; }; } diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index 4758dd5a05..b962fa9ce6 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -12,6 +12,8 @@ #include #include +#include + ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) : QObject(parent) { @@ -89,9 +91,12 @@ void ContentSelectorView::ContentSelector::clearCheckStates() void ContentSelectorView::ContentSelector::setCheckStates(const QStringList &list) { if (list.isEmpty()) - return; - - mContentModel->setCheckStates (list, true); + { + qDebug() << "refreshing model"; + slotCurrentGameFileIndexChanged (ui.gameFileView->currentIndex()); + } + else + mContentModel->setCheckStates (list, true); } ContentSelectorModel::ContentFileList @@ -152,14 +157,17 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i void ContentSelectorView::ContentSelector::slotAddonTableItemClicked(const QModelIndex &index) { - QAbstractItemModel *const model = ui.addonView->model(); + QModelIndex sourceIndex = mAddonProxyModel->mapToSource (index); + + if (!mContentModel->isEnabled (sourceIndex)) + return; Qt::CheckState checkState = Qt::Unchecked; - if (model->data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked) + if (mContentModel->data(sourceIndex, Qt::CheckStateRole).toInt() == Qt::Unchecked) checkState = Qt::Checked; - model->setData(index, checkState, Qt::CheckStateRole); + mContentModel->setData(sourceIndex, checkState, Qt::CheckStateRole); if (checkState == Qt::Checked) emit signalAddonFileSelected (index.row()); From 3ac58b387b1cbc07ec76a5d82fe77008dc6fd4b2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 3 Nov 2013 22:08:38 +0100 Subject: [PATCH 117/148] fixed wording of an error message --- apps/launcher/maindialog.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 273494ff3a..4012a1fbd5 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -496,12 +496,12 @@ bool Launcher::MainDialog::setupGameSettings() QAbstractButton *dirSelectButton = msgBox.addButton(QObject::tr("Browse to &Install..."), QMessageBox::ActionRole); - + #ifndef WIN32 - QAbstractButton *cdSelectButton = + QAbstractButton *cdSelectButton = msgBox.addButton(QObject::tr("Browse to &CD..."), QMessageBox::ActionRole); #endif - + msgBox.exec(); @@ -516,14 +516,14 @@ bool Launcher::MainDialog::setupGameSettings() #ifndef WIN32 else if(msgBox.clickedButton() == cdSelectButton) { UnshieldThread cd; - + { TextSlotMsgBox cdbox; - cdbox.setStandardButtons(QMessageBox::Cancel); + cdbox.setStandardButtons(QMessageBox::Cancel); QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&))); QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject())); - + cd.SetMorrowindPath( QFileDialog::getOpenFileName( NULL, @@ -537,11 +537,11 @@ bool Launcher::MainDialog::setupGameSettings() QObject::tr("Select where to extract files to"), QDir::currentPath(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks).toUtf8().constData()); - + cd.start(); cdbox.exec(); } - + while(expansions(cd)); selectedFile = QString::fromStdString(cd.GetMWEsmPath()); @@ -753,11 +753,11 @@ void Launcher::MainDialog::play() if(!mGameSettings.hasMaster()) { QMessageBox msgBox; - msgBox.setWindowTitle(tr("No master file selected")); + msgBox.setWindowTitle(tr("No game file selected")); msgBox.setIcon(QMessageBox::Warning); msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("
You do not have any master files selected.

\ - OpenMW will not start without a master file selected.
")); + msgBox.setText(tr("
You do not have no game file selected.

\ + OpenMW will not start without a game file selected.
")); msgBox.exec(); return; } From dace9044900a94eadddeea0442182fad7b152452 Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 3 Nov 2013 16:45:18 -0600 Subject: [PATCH 118/148] changed game/addon to content for writing to openmw.cfg from launcher --- apps/launcher/datafilespage.cpp | 4 ++-- apps/opencs/editor.cpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 5ae850566f..5452b73985 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -104,10 +104,10 @@ void Launcher::DataFilesPage::saveSettings(const QString &profile) if (item->gameFiles().size() == 0) { mLauncherSettings.setMultiValue(QString("Profiles/") + profileName + QString("/game"), item->fileName()); - mGameSettings.setMultiValue(QString("game"), item->fileName()); + mGameSettings.setMultiValue(QString("content"), item->fileName()); } else { mLauncherSettings.setMultiValue(QString("Profiles/") + profileName + QString("/addon"), item->fileName()); - mGameSettings.setMultiValue(QString("addon"), item->fileName()); + mGameSettings.setMultiValue(QString("content"), item->fileName()); } } diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 63afe7a9dd..e879cb25e8 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -94,6 +94,7 @@ void CS::Editor::setupDataFiles() for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) { + QString path = QString::fromStdString(iter->string()); mFileDialog.addFiles(path); } From ed913936f8544d7d1b646e9454e9c26b5f860e3f Mon Sep 17 00:00:00 2001 From: graffy76 Date: Sun, 3 Nov 2013 21:36:41 -0600 Subject: [PATCH 119/148] Eliminated game & addon keys from profile configuration --- apps/launcher/datafilespage.cpp | 65 ++++++++----------- apps/launcher/settings/gamesettings.cpp | 1 + apps/launcher/settings/launchersettings.cpp | 5 +- .../contentselector/view/contentselector.cpp | 32 ++++++++- .../contentselector/view/contentselector.hpp | 1 + 5 files changed, 59 insertions(+), 45 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 5452b73985..734277b97d 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -42,44 +42,19 @@ void Launcher::DataFilesPage::loadSettings() QString profileName = ui.profilesComboBox->currentText(); - QStringList files = mLauncherSettings.values(QString("Profiles/") + profileName + QString("/game"), Qt::MatchExactly); - QStringList addons = mLauncherSettings.values(QString("Profiles/") + profileName + QString("/addon"), Qt::MatchExactly); + QStringList files = mLauncherSettings.values(QString("Profiles/") + profileName, Qt::MatchExactly); - mSelector->clearCheckStates(); + QStringList filepaths; - QString gameFile (""); - - if (files.size()>0) + foreach (const QString &file, files) { - gameFile = pathIterator.findFirstPath (files.at(0)); + QString filepath = pathIterator.findFirstPath (file); - if (!gameFile.isEmpty()) - mSelector->setGameFile (gameFile); -/* else - { - //throw gamefile error here. - }*/ + if (!filepath.isEmpty()) + filepaths << filepath; } - QStringList missingFiles; - QStringList foundFiles; - - foreach (const QString &addon, addons) - { - QString filePath = pathIterator.findFirstPath (addon); - - if (filePath.isEmpty()) - missingFiles << addon; - else - foundFiles << filePath; - } -/* - if (missingFiles.size() > 0) - { - //throw addons error here. - } -*/ - mSelector->setCheckStates (foundFiles); + mSelector->setProfileContent (filepaths); } void Launcher::DataFilesPage::saveSettings(const QString &profile) @@ -94,8 +69,7 @@ void Launcher::DataFilesPage::saveSettings(const QString &profile) removeProfile (profileName); - mGameSettings.remove(QString("game")); - mGameSettings.remove(QString("addon")); + mGameSettings.remove(QString("content")); //set the value of the current profile (not necessarily the profile being saved!) mLauncherSettings.setValue(QString("Profiles/currentprofile"), ui.profilesComboBox->currentText()); @@ -103,10 +77,10 @@ void Launcher::DataFilesPage::saveSettings(const QString &profile) foreach(const ContentSelectorModel::EsmFile *item, items) { if (item->gameFiles().size() == 0) { - mLauncherSettings.setMultiValue(QString("Profiles/") + profileName + QString("/game"), item->fileName()); + mLauncherSettings.setMultiValue(QString("Profiles/") + profileName, item->fileName()); mGameSettings.setMultiValue(QString("content"), item->fileName()); } else { - mLauncherSettings.setMultiValue(QString("Profiles/") + profileName + QString("/addon"), item->fileName()); + mLauncherSettings.setMultiValue(QString("Profiles/") + profileName, item->fileName()); mGameSettings.setMultiValue(QString("content"), item->fileName()); } } @@ -225,13 +199,26 @@ void Launcher::DataFilesPage::setupDataFiles() if (!mDataLocal.isEmpty()) mSelector->addFiles(mDataLocal); - QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/")); - QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); + QStringList profiles; + QString currentProfile = mLauncherSettings.getSettings().value("Profiles/currentprofile"); + + foreach (QString key, mLauncherSettings.getSettings().keys()) + { + if (key.contains("Profiles/")) + { + QString profile = key.mid (9); + if (profile != "currentprofile") + { + if (!profiles.contains(profile)) + profiles << profile; + } + } + } foreach (const QString &item, profiles) addProfile (item, false); - setProfile (ui.profilesComboBox->findText(profile), false); + setProfile (ui.profilesComboBox->findText(currentProfile), false); loadSettings(); } diff --git a/apps/launcher/settings/gamesettings.cpp b/apps/launcher/settings/gamesettings.cpp index 83c0995291..41113c35aa 100644 --- a/apps/launcher/settings/gamesettings.cpp +++ b/apps/launcher/settings/gamesettings.cpp @@ -9,6 +9,7 @@ #include #include + /** * Workaround for problems with whitespaces in paths in older versions of Boost library */ diff --git a/apps/launcher/settings/launchersettings.cpp b/apps/launcher/settings/launchersettings.cpp index 7c97144eaf..705453555d 100644 --- a/apps/launcher/settings/launchersettings.cpp +++ b/apps/launcher/settings/launchersettings.cpp @@ -5,6 +5,8 @@ #include #include +#include + Launcher::LauncherSettings::LauncherSettings() { } @@ -44,12 +46,9 @@ QStringList Launcher::LauncherSettings::subKeys(const QString &key) QStringList result; foreach (const QString ¤tKey, keys) { - if (keyRe.indexIn(currentKey) != -1) { - QString prefixedKey = keyRe.cap(1); if(prefixedKey.startsWith(key)) { - QString subKey = prefixedKey.remove(key); if (!subKey.isEmpty()) result.append(subKey); diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp index b962fa9ce6..e9599de498 100644 --- a/components/contentselector/view/contentselector.cpp +++ b/components/contentselector/view/contentselector.cpp @@ -12,8 +12,6 @@ #include #include -#include - ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) : QObject(parent) { @@ -63,6 +61,35 @@ void ContentSelectorView::ContentSelector::buildAddonView() connect(ui.addonView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotAddonTableItemClicked(const QModelIndex &))); } +void ContentSelectorView::ContentSelector::setProfileContent(const QStringList &fileList) +{ + clearCheckStates(); + bool foundGamefile = false; + + foreach (const QString &filepath, fileList) + { + if (!foundGamefile) + { + const ContentSelectorModel::EsmFile *file = mContentModel->item(filepath); + + foundGamefile = (file->isGameFile()); + + if (foundGamefile) + { + setGameFile (filepath); + break; + } + } + } + +/* if (!foundGameFile) + { + //throw gamefile error here. + }*/ + + setCheckStates (fileList); +} + void ContentSelectorView::ContentSelector::setGameFile(const QString &filename) { int index = -1; @@ -92,7 +119,6 @@ void ContentSelectorView::ContentSelector::setCheckStates(const QStringList &lis { if (list.isEmpty()) { - qDebug() << "refreshing model"; slotCurrentGameFileIndexChanged (ui.gameFileView->currentIndex()); } else diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp index da1c39973d..a25eb20ae3 100644 --- a/components/contentselector/view/contentselector.hpp +++ b/components/contentselector/view/contentselector.hpp @@ -29,6 +29,7 @@ namespace ContentSelectorView QString currentFile() const; void addFiles(const QString &path); + void setProfileContent (const QStringList &fileList); void clearCheckStates(); void setCheckStates (const QStringList &list); From 8e439c290d616b1f6af2488bf9e3be4cb54f42e5 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 4 Nov 2013 09:22:23 +0100 Subject: [PATCH 120/148] updated changelog --- readme.txt | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/readme.txt b/readme.txt index c4d9d27461..a790ca9e35 100644 --- a/readme.txt +++ b/readme.txt @@ -82,6 +82,31 @@ Allowed options: CHANGELOG +0.27.0 + +Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp +Bug #794: incorrect display of decimal numbers +Bug #840: First-person sneaking camera height +Bug #887: Ambient sounds playing while paused +Bug #902: Problems with Polish character encoding +Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key +Bug #917: Quick character creation plugin does not work +Bug #918: Fatigue does not refill +Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2) +Feature #57: Acrobatics Skill +Feature #462: Editor: Start Dialogue +Feature #546: Modify ESX selector to handle new content file scheme +Feature #588: Editor: Adjust name/path of edited content files +Feature #644: Editor: Save +Feature #710: Editor: Configure script compiler context +Feature #790: God Mode +Feature #881: Editor: Allow only one instance of OpenCS +Feature #889: Editor: Record filtering +Feature #895: Extinguish torches +Feature #898: Breath meter enhancements +Feature #901: Editor: Default record filter +Feature #913: Merge --master and --plugin switches + 0.26.0 Bug #274: Inconsistencies in the terrain From 468e8e3635d6532fb334d17cabd5b7bf52a5ecc1 Mon Sep 17 00:00:00 2001 From: "Alexander \"Ace\" Olofsson" Date: Mon, 4 Nov 2013 10:36:22 +0100 Subject: [PATCH 121/148] Missing iostream include --- apps/openmw/mwworld/contentloader.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/openmw/mwworld/contentloader.hpp b/apps/openmw/mwworld/contentloader.hpp index c57935c907..46bd7d3f96 100644 --- a/apps/openmw/mwworld/contentloader.hpp +++ b/apps/openmw/mwworld/contentloader.hpp @@ -2,6 +2,7 @@ #define CONTENTLOADER_HPP #include +#include #include #include "components/loadinglistener/loadinglistener.hpp" From 8bacdc8ab8910f613fa7de32849998e31218bad7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 4 Nov 2013 14:19:58 +0100 Subject: [PATCH 122/148] updated changelog --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index a790ca9e35..afcfadea3d 100644 --- a/readme.txt +++ b/readme.txt @@ -90,6 +90,7 @@ Bug #840: First-person sneaking camera height Bug #887: Ambient sounds playing while paused Bug #902: Problems with Polish character encoding Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key +Bug #910: Some CDs not working correctly with Unshield installer Bug #917: Quick character creation plugin does not work Bug #918: Fatigue does not refill Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2) From 3a827d9c12d2cdedf7eee03b17a348fbbaa96669 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 5 Nov 2013 00:32:06 +0100 Subject: [PATCH 123/148] Deleted Obliviontt.zip --- Daedric Font License.txt | 10 ---------- apps/openmw/engine.cpp | 1 - files/mygui/CMakeLists.txt | 1 - files/mygui/Obliviontt.zip | Bin 138502 -> 0 bytes 4 files changed, 12 deletions(-) delete mode 100644 Daedric Font License.txt delete mode 100644 files/mygui/Obliviontt.zip diff --git a/Daedric Font License.txt b/Daedric Font License.txt deleted file mode 100644 index a1553d0b04..0000000000 --- a/Daedric Font License.txt +++ /dev/null @@ -1,10 +0,0 @@ -Dongle's Oblivion Daedric font set -http://www.uesp.net/wiki/Lore:Daedric_Alphabet#Daedric_Font - ---------------------------------------------------- - -This was done entirely as a personal project. Bethesda Softworks graciously granted me the permission for it. I am not connected with them in any way. -You may freely use these fonts to create anything you'd like. You may re-distribute the fonts freely, over the Internet, or by any other means. Always keep the .zip file intact, and this read me included. -Please do not modify and redistribute the fonts without my permission. -You may NOT sell any of these fonts under any circumstances. This includes putting them on compilation font CDs for sale, putting them in a "members only" pay-area of a website, or any other means of financial gain connected in ANY way with the redistribution of any of these fonts. -You have my permission to create and sell any artwork made with these fonts, however you may need to contact Bethesda Softworks before doing so. diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index e1fd3a0af3..35ff6593b3 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -370,7 +370,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) addResourcesDirectory(mResDir / "mygui"); addResourcesDirectory(mResDir / "water"); addResourcesDirectory(mResDir / "shadows"); - addZipResource(mResDir / "mygui" / "Obliviontt.zip"); OEngine::Render::WindowSettings windowSettings; windowSettings.fullscreen = settings.getBool("fullscreen", "Video"); diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 1ec1e08cb5..21153a4740 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -9,7 +9,6 @@ set(MYGUI_FILES core.skin core.xml EBGaramond-Regular.ttf - Obliviontt.zip openmw_alchemy_window.layout openmw_book.layout openmw_box.skin.xml diff --git a/files/mygui/Obliviontt.zip b/files/mygui/Obliviontt.zip deleted file mode 100644 index af4f809fd846cff444d5b1f09c7e86df1b6207d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138502 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

|M_Nl?xkEwCsnEW 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 From e94da61ff03f77897a7a9100864d6c5d165735a0 Mon Sep 17 00:00:00 2001 From: Jordan Ayers Date: Tue, 5 Nov 2013 19:38:45 -0600 Subject: [PATCH 124/148] Chargen cleanup. Move common gui state pop out of if checks. Move health update before gui state push (used as input for some windows). --- apps/openmw/mwgui/charactercreation.cpp | 43 +++++++++---------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 816f42e3d1..9b742742d8 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -332,24 +332,22 @@ namespace MWGui mPickClassDialog = 0; } + updatePlayerHealth(); + //TODO This bit gets repeated a few times; wrap it in a function + MWBase::Environment::get().getWindowManager()->popGuiMode(); if (mCreationStage == CSE_ReviewNext) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else if (mCreationStage >= CSE_ClassChosen) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); } else { mCreationStage = CSE_ClassChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); } - - updatePlayerHealth(); } void CharacterCreation::onPickClassDialogBack() @@ -403,20 +401,18 @@ namespace MWGui mNameDialog = 0; } + MWBase::Environment::get().getWindowManager()->popGuiMode(); if (mCreationStage == CSE_ReviewNext) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else if (mCreationStage >= CSE_NameChosen) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); } else { mCreationStage = CSE_NameChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); } } @@ -462,23 +458,21 @@ namespace MWGui mRaceDialog = 0; } + updatePlayerHealth(); + + MWBase::Environment::get().getWindowManager()->popGuiMode(); if (mCreationStage == CSE_ReviewNext) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else if (mCreationStage >= CSE_RaceChosen) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); } else { mCreationStage = CSE_RaceChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); } - - updatePlayerHealth(); } void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) @@ -492,18 +486,17 @@ namespace MWGui mBirthSignDialog = 0; } + updatePlayerHealth(); + + MWBase::Environment::get().getWindowManager()->popGuiMode(); if (mCreationStage >= CSE_BirthSignChosen) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else { mCreationStage = CSE_BirthSignChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); } - - updatePlayerHealth(); } void CharacterCreation::onBirthSignDialogBack() @@ -552,23 +545,21 @@ namespace MWGui mCreateClassDialog = 0; } + updatePlayerHealth(); + + MWBase::Environment::get().getWindowManager()->popGuiMode(); if (mCreationStage == CSE_ReviewNext) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else if (mCreationStage >= CSE_ClassChosen) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); } else { mCreationStage = CSE_ClassChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); } - - updatePlayerHealth(); } void CharacterCreation::onCreateClassDialogBack() @@ -722,23 +713,21 @@ namespace MWGui mPlayerClass = *klass; MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); + updatePlayerHealth(); + + MWBase::Environment::get().getWindowManager()->popGuiMode(); if (mCreationStage == CSE_ReviewNext) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else if (mCreationStage >= CSE_ClassChosen) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); } else { mCreationStage = CSE_ClassChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); } - - updatePlayerHealth(); } CharacterCreation::~CharacterCreation() From d48cc27a8938279c22aa6408a9d4a245b532c9bf Mon Sep 17 00:00:00 2001 From: Jordan Ayers Date: Tue, 5 Nov 2013 19:39:43 -0600 Subject: [PATCH 125/148] Chargen fix: Back sequence control. Related to bug #894. Eliminate double windows when using 'Back' from the review screen. Force consistent back behavior (handling was changing after a individual change button had been used). --- apps/openmw/mwgui/charactercreation.cpp | 2 ++ apps/openmw/mwgui/charactercreation.hpp | 1 + 2 files changed, 3 insertions(+) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 9b742742d8..eb087ea07b 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -285,7 +285,9 @@ namespace MWGui { MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); mReviewDialog = 0; + mCreationStage = CSE_ReviewBack; + MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); } diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index bd88266776..bb2a69a7a3 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -104,6 +104,7 @@ namespace MWGui CSE_RaceChosen, CSE_ClassChosen, CSE_BirthSignChosen, + CSE_ReviewBack, CSE_ReviewNext }; From 3fbf918751c0e6ea5f945069a3099e4df388421d Mon Sep 17 00:00:00 2001 From: Jordan Ayers Date: Tue, 5 Nov 2013 19:42:55 -0600 Subject: [PATCH 126/148] Chargen Review Dialog: Init fix. Load the starting Health/Magicka/Fatigue from the player stats when creating the Review Dialog, and remove the extra copy of these stats. In some cases, the old stat values were never updated from 0/0. --- apps/openmw/mwgui/charactercreation.cpp | 26 ++++++++----------------- apps/openmw/mwgui/charactercreation.hpp | 9 --------- apps/openmw/mwgui/windowmanagerimp.cpp | 5 +---- 3 files changed, 9 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index eb087ea07b..b829f219d7 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -219,9 +219,14 @@ namespace MWGui mReviewDialog->setClass(mPlayerClass); mReviewDialog->setBirthSign(mPlayerBirthSignId); - mReviewDialog->setHealth(mPlayerHealth); - mReviewDialog->setMagicka(mPlayerMagicka); - mReviewDialog->setFatigue(mPlayerFatigue); + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::CreatureStats stats = MWWorld::Class::get(player).getCreatureStats(player); + + mReviewDialog->setHealth ( stats.getHealth() ); + mReviewDialog->setMagicka( stats.getMagicka() ); + mReviewDialog->setFatigue( stats.getFatigue() ); + } { std::map > attributes = MWBase::Environment::get().getWindowManager()->getPlayerAttributeValues(); @@ -258,21 +263,6 @@ namespace MWGui mRaceDialog->doRenderUpdate(); } - void CharacterCreation::setPlayerHealth (const MWMechanics::DynamicStat& value) - { - mPlayerHealth = value; - } - - void CharacterCreation::setPlayerMagicka (const MWMechanics::DynamicStat& value) - { - mPlayerMagicka = value; - } - - void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& value) - { - mPlayerFatigue = value; - } - void CharacterCreation::onReviewDialogDone(WindowBase* parWindow) { MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index bb2a69a7a3..b80aaae41c 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -31,12 +31,6 @@ namespace MWGui //Show a dialog void spawnDialog(const char id); - void setPlayerHealth (const MWMechanics::DynamicStat& value); - - void setPlayerMagicka (const MWMechanics::DynamicStat& value); - - void setPlayerFatigue (const MWMechanics::DynamicStat& value); - void setValue (const std::string& id, const MWMechanics::Stat& value); void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); @@ -60,9 +54,6 @@ namespace MWGui std::string mPlayerRaceId; std::string mPlayerBirthSignId; ESM::Class mPlayerClass; - MWMechanics::DynamicStat mPlayerHealth; - MWMechanics::DynamicStat mPlayerMagicka; - MWMechanics::DynamicStat mPlayerFatigue; //Class generation vars unsigned mGenerateClassStep; // Keeps track of current step in Generate Class dialog diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 4b4d2dfd19..76ae65eb9b 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -581,17 +581,14 @@ namespace MWGui if (id == "HBar") { mPlayerHealth = value; - mCharGen->setPlayerHealth (value); } else if (id == "MBar") { mPlayerMagicka = value; - mCharGen->setPlayerMagicka (value); } else if (id == "FBar") { mPlayerFatigue = value; - mCharGen->setPlayerFatigue (value); } } @@ -599,7 +596,7 @@ namespace MWGui MWMechanics::DynamicStat WindowManager::getValue(const std::string& id) { if(id == "HBar") - return layerHealth; + return mPlayerHealth; else if (id == "MBar") return mPlayerMagicka; else if (id == "FBar") From 36efe9605f1e2ac13db6f870cab384e83edfdf76 Mon Sep 17 00:00:00 2001 From: Bret Curtis Date: Thu, 7 Nov 2013 23:05:45 +0100 Subject: [PATCH 127/148] necessary dpkg rules to get opencs building and packaged on dpkg systems --- CMakeLists.txt | 2 +- apps/opencs/CMakeLists.txt | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d45264aed..efa2f47642 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -430,7 +430,7 @@ IF(NOT WIN32 AND NOT APPLE) Data files from the original game is required to run it.") SET(CPACK_DEBIAN_PACKAGE_NAME "openmw") SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}") - SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW bsatool;Bsatool esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter") + SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW opencs;OpenCS bsatool;Bsatool esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter") SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)") SET(CPACK_DEBIAN_PACKAGE_SECTION "Games") diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index d317331e51..7498044ab5 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -160,3 +160,8 @@ target_link_libraries(opencs ${QT_LIBRARIES} components ) + +if(DPKG_PROGRAM) + INSTALL(TARGETS opencs RUNTIME DESTINATION games COMPONENT opencs) +endif() + From 6b931f566dedff256b1b429e5d9c2b8b0809acaa Mon Sep 17 00:00:00 2001 From: eroen Date: Fri, 8 Nov 2013 04:24:36 +0100 Subject: [PATCH 128/148] Stop installing "Daedric Font License.txt" It was removed in 3a827d9c12 --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d45264aed..9e6f4eb9c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -382,7 +382,6 @@ IF(NOT WIN32 AND NOT APPLE) # Install licenses INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" ) - INSTALL(FILES "Daedric Font License.txt" DESTINATION "${LICDIR}" ) INSTALL(FILES "OFL.txt" DESTINATION "${LICDIR}" ) INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" ) ENDIF (DPKG_PROGRAM) @@ -457,7 +456,6 @@ if(WIN32) "${OpenMW_SOURCE_DIR}/GPL3.txt" "${OpenMW_SOURCE_DIR}/OFL.txt" "${OpenMW_SOURCE_DIR}/DejaVu Font License.txt" - "${OpenMW_SOURCE_DIR}/Daedric Font License.txt" "${OpenMW_BINARY_DIR}/settings-default.cfg" "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" "${OpenMW_BINARY_DIR}/Release/openmw.exe" From 23b8206bdc86770e5f4dd68908936ec541ee4563 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 13 Aug 2013 01:19:33 +0200 Subject: [PATCH 129/148] Add remove methods to MWWorld::ContainerStore --- apps/openmw/mwworld/containerstore.cpp | 34 ++++++++++++++++++++++++++ apps/openmw/mwworld/containerstore.hpp | 10 ++++++++ apps/openmw/mwworld/inventorystore.cpp | 5 ++++ apps/openmw/mwworld/inventorystore.hpp | 6 +++++ 4 files changed, 55 insertions(+) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index c6768f5fdd..acc4d4c308 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -198,6 +198,40 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImpl (const Ptr& ptr return it; } +int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor) +{ + int toRemove = count; + + for (ContainerStoreIterator iter(begin()); iter != end() && toRemove > 0; ++iter) + if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, itemId)) + toRemove -= remove(*iter, toRemove, actor); + + // number of removed items + return count - toRemove; +} + +int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor) +{ + assert(this == item.getContainerStore()); + + int toRemove = count; + RefData& itemRef = item.getRefData(); + + if (itemRef.getCount() <= toRemove) + { + toRemove -= itemRef.getCount(); + itemRef.setCount(0); + } + else + { + itemRef.setCount(itemRef.getCount() - toRemove); + toRemove = 0; + } + + // number of removed items + return count - toRemove; +} + void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner, const MWWorld::ESMStore& store) { for (std::vector::const_iterator iter (items.mList.begin()); iter!=items.mList.end(); diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 9a11f1603b..d19f1ad50f 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -75,6 +75,16 @@ namespace MWWorld /// /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item. + int remove(const std::string& itemId, int count, const Ptr& actor); + ///< Remove \a count item(s) designated by \a itemId from this container. + /// + /// @return the number of items actually removed + + virtual int remove(const Ptr& item, int count, const Ptr& actor); + ///< Remove \a count item(s) designated by \a item from this inventory. + /// + /// @return the number of items actually removed + protected: ContainerStoreIterator addImpl (const Ptr& ptr); ///< Add the item to this container (no stacking) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 98cb6d347b..2aba0b5489 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -325,3 +325,8 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSelectedEnchantItem( { return mSelectedEnchantItem; } + +int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor) +{ + return ContainerStore::remove(item, count, actor); +} diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index f0cba0f9fe..f1168dda1a 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -108,6 +108,12 @@ namespace MWWorld ///< @return true if the two specified objects can stack with each other /// @note ptr1 is the item that is already in this container + virtual int remove(const Ptr& item, int count, const Ptr& actor); + ///< Remove \a count item(s) designated by \a item from this inventory. + /// + /// \todo check if the item is equipped and do stuff + /// + /// @return the number of items actually removed }; } From 10abb9d297fbaab2ea9021f256ce15e15d19343f Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 13 Aug 2013 01:19:33 +0200 Subject: [PATCH 130/148] Call ContainerStore::remove() to remove items from inventory Make placeObject() and dropObjectOnGround() in MWWorld to copy objects (and indicate it clearly). Enchanting an item now unequips it. --- apps/openmw/mwbase/world.hpp | 11 ++-- apps/openmw/mwclass/potion.cpp | 6 ++- apps/openmw/mwgui/containeritemmodel.cpp | 4 +- apps/openmw/mwgui/hud.cpp | 9 +--- apps/openmw/mwgui/inventoryitemmodel.cpp | 16 ++---- apps/openmw/mwgui/tradewindow.cpp | 22 ++------ apps/openmw/mwmechanics/actors.cpp | 4 +- apps/openmw/mwmechanics/alchemy.cpp | 3 +- apps/openmw/mwmechanics/enchanting.cpp | 43 ++++++--------- apps/openmw/mwmechanics/enchanting.hpp | 1 - apps/openmw/mwmechanics/repair.cpp | 7 +-- apps/openmw/mwmechanics/security.cpp | 5 +- apps/openmw/mwscript/containerextensions.cpp | 33 ++---------- apps/openmw/mwscript/miscextensions.cpp | 57 ++++++-------------- apps/openmw/mwworld/actioneat.cpp | 6 ++- apps/openmw/mwworld/worldimp.cpp | 26 ++++----- apps/openmw/mwworld/worldimp.hpp | 11 ++-- 17 files changed, 101 insertions(+), 163 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 8ae563e124..4eb9d30880 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -309,14 +309,19 @@ namespace MWBase virtual void update (float duration, bool paused) = 0; - virtual bool placeObject(const MWWorld::Ptr& object, float cursorX, float cursorY) = 0; - ///< place an object into the gameworld at the specified cursor position + virtual bool placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) = 0; + ///< copy and place an object into the gameworld at the specified cursor position /// @param object /// @param cursor X (relative 0-1) /// @param cursor Y (relative 0-1) + /// @param number of objects to place /// @return true if the object was placed, or false if it was rejected because the position is too far away - virtual void dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object) = 0; + virtual void dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount) = 0; + ///< copy and place an object into the gameworld at the given actor's position + /// @param actor giving the dropped object position + /// @param object + /// @param number of objects to place virtual bool canPlaceObject (float cursorX, float cursorY) = 0; ///< @return true if it is possible to place on object at specified cursor location diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 08683a6684..2f9e63d138 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -11,6 +11,7 @@ #include "../mwworld/actiontake.hpp" #include "../mwworld/actionapply.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/containerstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/player.hpp" #include "../mwworld/nullaction.hpp" @@ -164,10 +165,11 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - ptr.getRefData().setCount (ptr.getRefData().getCount()-1); - MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + // remove used potion (assume it is present in inventory) + ptr.getContainerStore()->remove(ptr, 1, actor); + boost::shared_ptr action ( new MWWorld::ActionApply (actor, ref->mBase->mId)); diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp index eff8fbcc1b..6b0fbd8903 100644 --- a/apps/openmw/mwgui/containeritemmodel.cpp +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -94,9 +94,7 @@ void ContainerItemModel::removeItem (const ItemStack& item, size_t count) { if (stacks(*it, item.mBase)) { - int refCount = it->getRefData().getCount(); - it->getRefData().setCount(std::max(0, refCount - toRemove)); - toRemove -= refCount; + toRemove -= store.remove(*it, toRemove, *source); if (toRemove <= 0) return; } diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index e7b9f9c015..f0843834d5 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -243,21 +243,16 @@ namespace MWGui float mouseX = cursorPosition.left / float(viewSize.width); float mouseY = cursorPosition.top / float(viewSize.height); - int origCount = object.getRefData().getCount(); - object.getRefData().setCount(mDragAndDrop->mDraggedCount); - if (world->canPlaceObject(mouseX, mouseY)) - world->placeObject(object, mouseX, mouseY); + world->placeObject(object, mouseX, mouseY, mDragAndDrop->mDraggedCount); else - world->dropObjectOnGround(world->getPlayer().getPlayer(), object); + world->dropObjectOnGround(world->getPlayer().getPlayer(), object, mDragAndDrop->mDraggedCount); MWBase::Environment::get().getWindowManager()->changePointer("arrow"); std::string sound = MWWorld::Class::get(object).getDownSoundId(object); MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - object.getRefData().setCount(origCount); - // remove object from the container it was coming from mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount); mDragAndDrop->finish(); diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index 62a5a75f06..712e1b6c64 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -52,18 +52,12 @@ void InventoryItemModel::copyItem (const ItemStack& item, size_t count) void InventoryItemModel::removeItem (const ItemStack& item, size_t count) { MWWorld::ContainerStore& store = MWWorld::Class::get(mActor).getContainerStore(mActor); + int removed = store.remove(item.mBase, count, mActor); - for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) - { - if (*it == item.mBase) - { - if (it->getRefData().getCount() < static_cast(count)) - throw std::runtime_error("Not enough items in the stack to remove"); - it->getRefData().setCount(it->getRefData().getCount() - count); - return; - } - } - throw std::runtime_error("Item to remove not found in container store"); + if (removed == 0) + throw std::runtime_error("Item to remove not found in container store"); + else if (removed < count) + throw std::runtime_error("Not enough items in the stack to remove"); } void InventoryItemModel::update() diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 94141b1a0a..b6b49b3551 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -202,31 +202,19 @@ namespace MWGui void TradeWindow::addOrRemoveGold(int amount) { - bool goldFound = false; - MWWorld::Ptr gold; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::ContainerStore& playerStore = MWWorld::Class::get(player).getContainerStore(player); - for (MWWorld::ContainerStoreIterator it = playerStore.begin(); - it != playerStore.end(); ++it) + if (amount > 0) { - if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, "gold_001")) - { - goldFound = true; - gold = *it; - } - } - if (goldFound) - { - gold.getRefData().setCount(gold.getRefData().getCount() + amount); - } - else - { - assert(amount > 0); MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), "Gold_001"); ref.getPtr().getRefData().setCount(amount); playerStore.add(ref.getPtr(), player); } + else + { + playerStore.remove("gold_001", - amount, player); + } } void TradeWindow::onFrame(float frameDuration) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 39276e9645..175d334628 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -244,7 +244,7 @@ namespace MWMechanics heldIter->getClass().setRemainingUsageTime(*heldIter, timeRemaining); else { - heldIter->getRefData().setCount(0); // remove it + inventoryStore.remove(*heldIter, 1, ptr); // remove it return; } } @@ -253,7 +253,7 @@ namespace MWMechanics // Both NPC and player lights extinguish in water. if(MWBase::Environment::get().getWorld()->isSwimming(ptr)) { - heldIter->getRefData().setCount(0); // remove it + inventoryStore.remove(*heldIter, 1, ptr); // remove it // ...But, only the player makes a sound. if(isPlayer) diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index adfd6d5fce..ab35dbdb1f 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -240,7 +240,8 @@ void MWMechanics::Alchemy::removeIngredients() for (TIngredientsContainer::iterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter) if (!iter->isEmpty()) { - iter->getRefData().setCount (iter->getRefData().getCount()-1); + iter->getContainerStore()->remove(*iter, 1, mAlchemist); + if (iter->getRefData().getCount()<1) { needsUpdate = true; diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 4e26b5027c..7a8a8f5f0b 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -14,7 +14,6 @@ namespace MWMechanics Enchanting::Enchanting() : mCastStyle(ESM::Enchantment::CastOnce) , mSelfEnchanting(false) - , mOldItemCount(0) {} void Enchanting::setOldItem(MWWorld::Ptr oldItem) @@ -24,7 +23,6 @@ namespace MWMechanics { mObjectType = mOldItemPtr.getTypeName(); mOldItemId = mOldItemPtr.getCellRef().mRefID; - mOldItemCount = mOldItemPtr.getRefData().getCount(); } else { @@ -55,17 +53,18 @@ namespace MWMechanics bool Enchanting::create() { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); ESM::Enchantment enchantment; enchantment.mData.mCharge = getGemCharge(); - mSoulGemPtr.getRefData().setCount (mSoulGemPtr.getRefData().getCount()-1); + store.remove(mSoulGemPtr, 1, player); //Exception for Azura Star, new one will be added after enchanting if(boost::iequals(mSoulGemPtr.get()->mBase->mId, "Misc_SoulGem_Azura")) { MWWorld::ManualRef azura (MWBase::Environment::get().getWorld()->getStore(), "Misc_SoulGem_Azura"); - MWWorld::Class::get (player).getContainerStore (player).add (azura.getPtr(), player); + store.add(azura.getPtr(), player); } if(mSelfEnchanting) @@ -84,16 +83,19 @@ namespace MWMechanics enchantment.mData.mCost = getEnchantPoints(); enchantment.mEffects = mEffectList; - const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment); - - MWWorld::Class::get(mOldItemPtr).applyEnchantment(mOldItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName); - - mOldItemPtr.getRefData().setCount(1); - + // Create a new item MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), mOldItemId); - ref.getPtr().getRefData().setCount (mOldItemCount-1); + const MWWorld::Ptr& newItemPtr = ref.getPtr(); + newItemPtr.getRefData().setCount(1); + + // Apply the enchantment + const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment); + MWWorld::Class::get(newItemPtr).applyEnchantment(newItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName); + + // Add the new item to player inventory and remove the old one + store.add(newItemPtr, player); + store.remove(mOldItemPtr, 1, player); - MWWorld::Class::get (player).getContainerStore (player).add (ref.getPtr(), player); if(!mSelfEnchanting) payForEnchantment(); @@ -299,20 +301,9 @@ namespace MWMechanics void Enchanting::payForEnchantment() const { - MWWorld::Ptr gold; - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); - for (MWWorld::ContainerStoreIterator it = store.begin(); - it != store.end(); ++it) - { - if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, "gold_001")) - { - gold = *it; - } - } - - gold.getRefData().setCount(gold.getRefData().getCount() - getEnchantPrice()); + store.remove("gold_001", getEnchantPrice(), player); } } diff --git a/apps/openmw/mwmechanics/enchanting.hpp b/apps/openmw/mwmechanics/enchanting.hpp index a25fd43abc..988ce41fc7 100644 --- a/apps/openmw/mwmechanics/enchanting.hpp +++ b/apps/openmw/mwmechanics/enchanting.hpp @@ -22,7 +22,6 @@ namespace MWMechanics std::string mNewItemName; std::string mObjectType; std::string mOldItemId; - int mOldItemCount; public: Enchanting(); diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index 66c492bf8b..38b2a48d7b 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -85,7 +85,10 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) // tool used up? if (mTool.getCellRef().mCharge == 0) { - mTool.getRefData().setCount(0); + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); + + store.remove(mTool, 1, player); std::string message = MWBase::Environment::get().getWorld()->getStore().get() .find("sNotifyMessage51")->getString(); @@ -93,8 +96,6 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) MWBase::Environment::get().getWindowManager()->messageBox((boost::format(message) % MWWorld::Class::get(mTool).getName(mTool)).str()); // try to find a new tool of the same ID - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) { diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index d19da6e2af..c373e83f51 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -2,6 +2,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/containerstore.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -61,7 +62,7 @@ namespace MWMechanics lockpick.getCellRef().mCharge = lockpick.get()->mBase->mData.mUses; --lockpick.getCellRef().mCharge; if (!lockpick.getCellRef().mCharge) - lockpick.getRefData().setCount(0); + lockpick.getContainerStore()->remove(lockpick, 1, mActor); } void Security::probeTrap(const MWWorld::Ptr &trap, const MWWorld::Ptr &probe, @@ -103,7 +104,7 @@ namespace MWMechanics probe.getCellRef().mCharge = probe.get()->mBase->mData.mUses; --probe.getCellRef().mCharge; if (!probe.getCellRef().mCharge) - probe.getRefData().setCount(0); + probe.getContainerStore()->remove(probe, 1, mActor); } } diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 2f3ef2d792..a5cdfc5e29 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -136,41 +136,18 @@ namespace MWScript return; MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); - + std::string itemName = ""; - // originalCount holds the total number of items to remove, count holds the remaining number of items to remove - Interpreter::Type_Integer originalCount = count; + int numRemoved = store.remove(item, count, ptr); - for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end() && count; - ++iter) - { - if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item)) - { - itemName = MWWorld::Class::get(*iter).getName(*iter); - - if (iter->getRefData().getCount()<=count) - { - count -= iter->getRefData().getCount(); - iter->getRefData().setCount (0); - } - else - { - iter->getRefData().setCount (iter->getRefData().getCount()-count); - count = 0; - } - } - } - // Spawn a messagebox (only for items removed from player's inventory) - if (ptr == MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer()) + if ((numRemoved > 0) + && (ptr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer())) { // The two GMST entries below expand to strings informing the player of what, and how many of it has been removed from their inventory std::string msgBox; - int numRemoved = (originalCount - count); - if (numRemoved == 0) - return; - + if(numRemoved > 1) { msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage63}"); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 4ae1136e22..1a2de650fc 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -374,18 +374,7 @@ namespace MWScript MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); - - for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) - { - if (::Misc::StringUtils::ciEqual(iter->getCellRef().mSoul, soul)) - { - if (iter->getRefData().getCount() <= 1) - iter->getRefData().setCount (0); - else - iter->getRefData().setCount (iter->getRefData().getCount() - 1); - break; - } - } + store.remove(soul, 1, ptr); } }; @@ -415,24 +404,18 @@ namespace MWScript MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); + int toRemove = amount; for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) { if (::Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item)) { - if(iter->getRefData().getCount() <= amount) - { - MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter); - iter->getRefData().setCount(0); - } - else - { - int original = iter->getRefData().getCount(); - iter->getRefData().setCount(amount); - MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter); - iter->getRefData().setCount(original - amount); - } + int removed = store.remove(*iter, toRemove, ptr); + MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, removed); - break; + toRemove -= removed; + + if (toRemove <= 0) + break; } } } @@ -458,20 +441,8 @@ namespace MWScript { if (::Misc::StringUtils::ciEqual(iter->getCellRef().mSoul, soul)) { - - if(iter->getRefData().getCount() <= 1) - { - MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter); - iter->getRefData().setCount(0); - } - else - { - int original = iter->getRefData().getCount(); - iter->getRefData().setCount(1); - MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter); - iter->getRefData().setCount(original - 1); - } - + MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, 1); + store.remove(*iter, 1, ptr); break; } } @@ -545,7 +516,13 @@ namespace MWScript if (ptr.isInCell()) MWBase::Environment::get().getWorld()->deleteObject (ptr); else - ptr.getRefData().setCount(0); + { + MWWorld::ContainerStore* store = ptr.getContainerStore(); + if (store != NULL) + store->remove(ptr, ptr.getRefData().getCount(), ptr); + else + ptr.getRefData().setCount(0); + } } } }; diff --git a/apps/openmw/mwworld/actioneat.cpp b/apps/openmw/mwworld/actioneat.cpp index 63efff738e..470eeda2b3 100644 --- a/apps/openmw/mwworld/actioneat.cpp +++ b/apps/openmw/mwworld/actioneat.cpp @@ -11,6 +11,8 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwworld/containerstore.hpp" + #include "esmstore.hpp" #include "class.hpp" @@ -18,8 +20,8 @@ namespace MWWorld { void ActionEat::executeImp (const Ptr& actor) { - // remove used item - getTarget().getRefData().setCount (getTarget().getRefData().getCount()-1); + // remove used item (assume the item is present in inventory) + getTarget().getContainerStore()->remove(getTarget(), 1, actor); // check for success const MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get (actor).getCreatureStats (actor); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 3ea9f72825..e24a7ed0ad 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1477,7 +1477,7 @@ namespace MWWorld item.getRefData().getLocals().setVarByInt(script, "onpcdrop", 1); } - bool World::placeObject (const Ptr& object, float cursorX, float cursorY) + bool World::placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) { std::pair result = mPhysics->castRay(cursorX, cursorY); @@ -1502,9 +1502,14 @@ namespace MWWorld pos.rot[0] = 0; pos.rot[1] = 0; + // copy the object and set its count + int origCount = object.getRefData().getCount(); + object.getRefData().setCount(amount); Ptr dropped = copyObjectToCell(object, *cell, pos); + object.getRefData().setCount(origCount); + + // only the player place items in the world, so no need to check actor PCDropped(dropped); - object.getRefData().setCount(0); return true; } @@ -1549,7 +1554,7 @@ namespace MWWorld return dropped; } - void World::dropObjectOnGround (const Ptr& actor, const Ptr& object) + void World::dropObjectOnGround (const Ptr& actor, const Ptr& object, int amount) { MWWorld::Ptr::CellStore* cell = actor.getCell(); @@ -1570,10 +1575,14 @@ namespace MWWorld mPhysics->castRay(orig, dir, len); pos.pos[2] = hit.second.z; + // copy the object and set its count + int origCount = object.getRefData().getCount(); + object.getRefData().setCount(amount); Ptr dropped = copyObjectToCell(object, *cell, pos); + object.getRefData().setCount(origCount); + if(actor == mPlayer->getPlayer()) // Only call if dropped by player PCDropped(dropped); - object.getRefData().setCount(0); } void World::processChangedSettings(const Settings::CategorySettingVector& settings) @@ -1955,14 +1964,7 @@ namespace MWWorld } else { - ContainerStore &store = Class::get(actor).getContainerStore(actor); - - const std::string item = "WerewolfRobe"; - for(ContainerStoreIterator iter(store.begin());iter != store.end();++iter) - { - if(Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item)) - iter->getRefData().setCount(0); - } + Class::get(actor).getContainerStore(actor).remove("WerewolfRobe", 1, actor); } if(actor.getRefData().getHandle() == "player") diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 49f26998de..cd982aceee 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -354,14 +354,19 @@ namespace MWWorld virtual void update (float duration, bool paused); - virtual bool placeObject (const Ptr& object, float cursorX, float cursorY); - ///< place an object into the gameworld at the specified cursor position + virtual bool placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount); + ///< copy and place an object into the gameworld at the specified cursor position /// @param object /// @param cursor X (relative 0-1) /// @param cursor Y (relative 0-1) + /// @param number of objects to place /// @return true if the object was placed, or false if it was rejected because the position is too far away - virtual void dropObjectOnGround (const Ptr& actor, const Ptr& object); + virtual void dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount); + ///< copy and place an object into the gameworld at the given actor's position + /// @param actor giving the dropped object position + /// @param object + /// @param number of objects to place virtual bool canPlaceObject(float cursorX, float cursorY); ///< @return true if it is possible to place on object at specified cursor location From d05baa8c22e5aa3e06824f6fb80d1bfa9206a9f1 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Fri, 1 Nov 2013 00:54:54 +0100 Subject: [PATCH 131/148] Add method InventoryStore::unequipSlot() This will permit to do run a treatment when an item is unequipped. --- apps/openmw/mwworld/inventorystore.cpp | 37 +++++++++++++++++--------- apps/openmw/mwworld/inventorystore.hpp | 4 +-- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 2aba0b5489..4bda723183 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -129,18 +129,7 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite void MWWorld::InventoryStore::unequipAll(const MWWorld::Ptr& actor) { for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) - { - MWWorld::ContainerStoreIterator it = getSlot(slot); - if (it != end()) - { - equip(slot, end()); - std::string script = MWWorld::Class::get(*it).getScript(*it); - - // Unset OnPCEquip Variable on item's script, if it has a script with that variable declared - if((actor.getRefData().getHandle() == "player") && (script != "")) - (*it).getRefData().getLocals().setVarByInt(script, "onpcequip", 0); - } - } + unequipSlot(slot, actor); } MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) @@ -328,5 +317,29 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSelectedEnchantItem( int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor) { + for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) + { + if (mSlots[slot] == end()) + continue; + + if (*mSlots[slot] == item) + { + unequipSlot(slot, actorPtr); + break; + } + } + return ContainerStore::remove(item, count, actor); } + +void MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor) +{ + ContainerStoreIterator it = getSlot(slot); + if (it != end()) + { + equip(slot, end()); + + + /// \todo update actor model + } +} diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index f1168dda1a..fd9f3fa7a5 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -111,9 +111,9 @@ namespace MWWorld virtual int remove(const Ptr& item, int count, const Ptr& actor); ///< Remove \a count item(s) designated by \a item from this inventory. /// - /// \todo check if the item is equipped and do stuff - /// /// @return the number of items actually removed + + void unequipSlot(int slot, const Ptr& actor); }; } From 52cef199821fdf710ba57217198099b9fe1ea977 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Fri, 1 Nov 2013 00:55:24 +0100 Subject: [PATCH 132/148] Update weapon/magic icons when items are removed from player inventory --- apps/openmw/mwworld/inventorystore.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 4bda723183..63cd46b628 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwmechanics/npcstats.hpp" @@ -339,6 +340,26 @@ void MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor) { equip(slot, end()); + if (actor.getRefData().getHandle() == "player") + { + // Unset OnPCEquip Variable on item's script, if it has a script with that variable declared + const std::string& script = Class::get(*it).getScript(*it); + if (script != "") + (*it).getRefData().getLocals().setVarByInt(script, "onpcequip", 0); + + // Update HUD icon when removing player weapon or selected enchanted item. + // We have to check for both as the weapon could also be the enchanted item. + if (slot == MWWorld::InventoryStore::Slot_CarriedRight) + { + // weapon + MWBase::Environment::get().getWindowManager()->unsetSelectedWeapon(); + } + if ((mSelectedEnchantItem != end()) && (mSelectedEnchantItem == it)) + { + // enchanted item + MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); + } + } /// \todo update actor model } From 2786530430086fefd03a68201f25d2334ce58b8c Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Tue, 13 Aug 2013 02:06:46 +0200 Subject: [PATCH 133/148] =?UTF-8?q?Edit=20InventoryStore::equip()=20to=20c?= =?UTF-8?q?all=20the=20new=20unequipSlot=20function=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …to unequip previously equipped item. --- apps/openmw/mwgui/inventorywindow.cpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 2 +- apps/openmw/mwworld/actionequip.cpp | 8 ++--- apps/openmw/mwworld/inventorystore.cpp | 35 ++++++++++--------- apps/openmw/mwworld/inventorystore.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 2 +- 6 files changed, 26 insertions(+), 25 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 4f616b3126..5c8cd2cdc4 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -384,7 +384,7 @@ namespace MWGui MWWorld::ContainerStoreIterator it = invStore.getSlot(slot); if (it != invStore.end() && *it == item) { - invStore.equip(slot, invStore.end()); + invStore.equip(slot, invStore.end(), mPtr); std::string script = MWWorld::Class::get(*it).getScript(*it); // Unset OnPCEquip Variable on item's script, if it has a script with that variable declared diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 8c13db7900..54b02fb529 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -159,7 +159,7 @@ namespace MWMechanics // auto-equip again. we need this for when the race is changed to a beast race MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore(ptr); for (int i=0; i=static_cast (mSlots.size())) throw std::runtime_error ("slot number out of range"); @@ -98,19 +98,8 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite throw std::runtime_error ("invalid slot"); } - // restack item previously in this slot (if required) if (mSlots[slot] != end()) - { - for (MWWorld::ContainerStoreIterator iter (begin()); iter!=end(); ++iter) - { - if (stacks(*iter, *mSlots[slot])) - { - iter->getRefData().setCount( iter->getRefData().getCount() + mSlots[slot]->getRefData().getCount() ); - mSlots[slot]->getRefData().setCount(0); - break; - } - } - } + unequipSlot(slot, actor); // unstack item pointed to by iterator if required if (iterator!=end() && !slots.second && iterator->getRefData().getCount() > 1) // if slots.second is true, item can stay stacked when equipped @@ -214,10 +203,10 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc) case 0: continue; case 2: - invStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, invStore.end()); + invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedLeft, npc); break; case 3: - invStore.equip(MWWorld::InventoryStore::Slot_CarriedRight, invStore.end()); + invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, npc); break; } @@ -325,7 +314,7 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor if (*mSlots[slot] == item) { - unequipSlot(slot, actorPtr); + unequipSlot(slot, actor); break; } } @@ -338,7 +327,19 @@ void MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor) ContainerStoreIterator it = getSlot(slot); if (it != end()) { - equip(slot, end()); + // restack item previously in this slot + for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) + { + if (stacks(*iter, *mSlots[slot])) + { + iter->getRefData().setCount(iter->getRefData().getCount() + mSlots[slot]->getRefData().getCount()); + mSlots[slot]->getRefData().setCount(0); + break; + } + } + + // empty this slot + mSlots[slot] = end(); if (actor.getRefData().getHandle() == "player") { diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index fd9f3fa7a5..9468f928f1 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -76,7 +76,7 @@ namespace MWWorld /// /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item. - void equip (int slot, const ContainerStoreIterator& iterator); + void equip (int slot, const ContainerStoreIterator& iterator, const Ptr& actor); ///< \note \a iterator can be an end-iterator void setSelectedEnchantItem(const ContainerStoreIterator& iterator); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e24a7ed0ad..144901fc57 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1960,7 +1960,7 @@ namespace MWWorld // Not sure this is right InventoryStore &inv = Class::get(actor).getInventoryStore(actor); - inv.equip(InventoryStore::Slot_Robe, inv.add(ref.getPtr(), actor)); + inv.equip(InventoryStore::Slot_Robe, inv.add(ref.getPtr(), actor), actor); } else { From 8ff747fbefe67381d2b9ec77f06e22052ad476a5 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Wed, 16 Oct 2013 18:39:29 +0200 Subject: [PATCH 134/148] Move some deleteObject logic from OpDelete to MWWorld::deleteObject --- apps/openmw/mwscript/miscextensions.cpp | 13 +------------ apps/openmw/mwworld/worldimp.cpp | 9 +++++---- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 1a2de650fc..66354513ea 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -512,18 +512,7 @@ namespace MWScript runtime.pop(); if (parameter == 1) - { - if (ptr.isInCell()) - MWBase::Environment::get().getWorld()->deleteObject (ptr); - else - { - MWWorld::ContainerStore* store = ptr.getContainerStore(); - if (store != NULL) - store->remove(ptr, ptr.getRefData().getCount(), ptr); - else - ptr.getRefData().setCount(0); - } - } + MWBase::Environment::get().getWorld()->deleteObject(ptr); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 144901fc57..affcf03878 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -837,12 +837,13 @@ namespace MWWorld void World::deleteObject (const Ptr& ptr) { - if (ptr.getRefData().getCount()>0) + if (ptr.getRefData().getCount() > 0) { - ptr.getRefData().setCount (0); + ptr.getRefData().setCount(0); - if (mWorldScene->getActiveCells().find (ptr.getCell())!=mWorldScene->getActiveCells().end() && - ptr.getRefData().isEnabled()) + if (ptr.isInCell() + && mWorldScene->getActiveCells().find(ptr.getCell()) != mWorldScene->getActiveCells().end() + && ptr.getRefData().isEnabled()) { mWorldScene->removeObjectFromScene (ptr); mLocalScripts.remove (ptr); From f4f2586e8ce334f5141348ad120f9959d0caa85e Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Mon, 14 Oct 2013 21:49:21 +0200 Subject: [PATCH 135/148] Remove duplicate code for PlaceAtMe/PlaceAtPC using a template --- .../mwscript/transformationextensions.cpp | 69 ++++--------------- 1 file changed, 12 insertions(+), 57 deletions(-) diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 6246daee22..f2d1af5c26 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -450,62 +450,16 @@ namespace MWScript } }; - template - class OpPlaceAtPc : public Interpreter::Opcode0 + template + class OpPlaceAt : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { - std::string itemID = runtime.getStringLiteral (runtime[0].mInteger); - runtime.pop(); - - Interpreter::Type_Integer count = runtime[0].mInteger; - runtime.pop(); - Interpreter::Type_Float distance = runtime[0].mFloat; - runtime.pop(); - Interpreter::Type_Integer direction = runtime[0].mInteger; - runtime.pop(); - - if (count<0) - throw std::runtime_error ("count must be non-negative"); - - // no-op - if (count == 0) - return; - - ESM::Position ipos = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getRefData().getPosition(); - Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]); - Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); - if(direction == 0) pos = pos + distance*rot.yAxis(); - else if(direction == 1) pos = pos - distance*rot.yAxis(); - else if(direction == 2) pos = pos - distance*rot.xAxis(); - else if(direction == 3) pos = pos + distance*rot.xAxis(); - else throw std::runtime_error ("direction must be 0,1,2 or 3"); - - ipos.pos[0] = pos.x; - ipos.pos[1] = pos.y; - ipos.pos[2] = pos.z; - ipos.rot[0] = 0; - ipos.rot[1] = 0; - ipos.rot[2] = 0; - - MWWorld::CellStore* store = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); - MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); - ref.getPtr().getCellRef().mPos = ipos; - ref.getPtr().getRefData().setCount(count); - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos); - } - }; - - template - class OpPlaceAtMe : public Interpreter::Opcode0 - { - public: - - virtual void execute (Interpreter::Runtime& runtime) - { - MWWorld::Ptr me = R()(runtime); + MWWorld::Ptr actor = pc + ? MWBase::Environment::get().getWorld()->getPlayer().getPlayer() + : R()(runtime); std::string itemID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); @@ -524,7 +478,7 @@ namespace MWScript if (count == 0) return; - ESM::Position ipos = me.getRefData().getPosition(); + ESM::Position ipos = actor.getRefData().getPosition(); Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]); Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); if(direction == 0) pos = pos + distance*rot.yAxis(); @@ -540,12 +494,13 @@ namespace MWScript ipos.rot[1] = 0; ipos.rot[2] = 0; - MWWorld::CellStore* store = me.getCell(); + // create item + MWWorld::CellStore* store = actor.getCell(); MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); ref.getPtr().getCellRef().mPos = ipos; ref.getPtr().getRefData().setCount(count); - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos); + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos); } }; @@ -730,9 +685,9 @@ namespace MWScript interpreter.installSegment5(Compiler::Transformation::opcodePositionCellExplicit,new OpPositionCell); interpreter.installSegment5(Compiler::Transformation::opcodePlaceItemCell,new OpPlaceItemCell); interpreter.installSegment5(Compiler::Transformation::opcodePlaceItem,new OpPlaceItem); - interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtPc,new OpPlaceAtPc); - interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMe,new OpPlaceAtMe); - interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMeExplicit,new OpPlaceAtMe); + interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtPc,new OpPlaceAt); + interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMe,new OpPlaceAt); + interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMeExplicit,new OpPlaceAt); interpreter.installSegment5(Compiler::Transformation::opcodeModScale,new OpModScale); interpreter.installSegment5(Compiler::Transformation::opcodeModScaleExplicit,new OpModScale); interpreter.installSegment5(Compiler::Transformation::opcodeRotate,new OpRotate); From 0691978603e42ab05c42845b41ffc4076784b57f Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sun, 20 Oct 2013 15:49:33 +0200 Subject: [PATCH 136/148] Add item count to ManualRef constructor as optional argument --- apps/openmw/mwworld/manualref.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 6616165fae..1cdcd8484e 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -37,7 +37,7 @@ namespace MWWorld public: - ManualRef (const MWWorld::ESMStore& store, const std::string& name) + ManualRef (const MWWorld::ESMStore& store, const std::string& name, const int count=1) { // create if (!create (store.get(), name) && @@ -74,6 +74,7 @@ namespace MWWorld cellRef.mTeleport = false; cellRef.mLockLevel = 0; cellRef.mReferenceBlocked = 0; + mPtr.getRefData().setCount(count); } const Ptr& getPtr() const From aefa54d72daa71c8fea030c7d637f212d868ff89 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sun, 20 Oct 2013 15:49:43 +0200 Subject: [PATCH 137/148] Pass item count to ManualRef constructor This remove the need to call setCount in multiple places. --- apps/openmw/mwclass/misc.cpp | 1 - apps/openmw/mwgui/tradewindow.cpp | 3 +-- apps/openmw/mwmechanics/enchanting.cpp | 3 +-- apps/openmw/mwscript/containerextensions.cpp | 4 +--- apps/openmw/mwscript/miscextensions.cpp | 4 +--- apps/openmw/mwscript/transformationextensions.cpp | 3 +-- apps/openmw/mwworld/containerstore.cpp | 7 ++----- apps/openmw/mwworld/worldimp.cpp | 1 - 8 files changed, 7 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 6247191a92..67f79c40ba 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -219,7 +219,6 @@ namespace MWClass MWWorld::LiveCellRef *ref = newRef.getPtr().get(); newPtr = MWWorld::Ptr(&cell.mMiscItems.insert(*ref), &cell); - newPtr.getRefData ().setCount(1); newPtr.getCellRef().mGoldValue = goldAmount; } else { MWWorld::LiveCellRef *ref = diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index b6b49b3551..c179236087 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -207,8 +207,7 @@ namespace MWGui if (amount > 0) { - MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), "Gold_001"); - ref.getPtr().getRefData().setCount(amount); + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), "Gold_001", amount); playerStore.add(ref.getPtr(), player); } else diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 7a8a8f5f0b..5d148d1106 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -84,9 +84,8 @@ namespace MWMechanics enchantment.mEffects = mEffectList; // Create a new item - MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), mOldItemId); + MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), mOldItemId, 1); const MWWorld::Ptr& newItemPtr = ref.getPtr(); - newItemPtr.getRefData().setCount(1); // Apply the enchantment const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment); diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index a5cdfc5e29..d3ed50838b 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -53,10 +53,8 @@ namespace MWScript if (count == 0) return; - MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), item); + MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), item, count); - ref.getPtr().getRefData().setCount (count); - // Configure item's script variables std::string script = MWWorld::Class::get(ref.getPtr()).getScript(ref.getPtr()); if (script != "") diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 66354513ea..35f7a40447 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -348,9 +348,7 @@ namespace MWScript const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); store.get().find(creature); // This line throws an exception if it can't find the creature - MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), gem); - - ref.getPtr().getRefData().setCount (1); + MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), gem, 1); ref.getPtr().getCellRef().mSoul = creature; diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index f2d1af5c26..4b60f47ce2 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -496,9 +496,8 @@ namespace MWScript // create item MWWorld::CellStore* store = actor.getCell(); - MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), itemID, count); ref.getPtr().getCellRef().mPos = ipos; - ref.getPtr().getRefData().setCount(count); MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos); } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index acc4d4c308..d25b29397a 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -140,11 +140,9 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr) || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_025") || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_100")) { - MWWorld::ManualRef ref(esmStore, "Gold_001"); - int count = MWWorld::Class::get(ptr).getValue(ptr) * ptr.getRefData().getCount(); + MWWorld::ManualRef ref(esmStore, "Gold_001", count); - ref.getPtr().getRefData().setCount(count); for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) { if (Misc::StringUtils::ciEqual((*iter).get()->mRef.mRefID, "gold_001")) @@ -250,7 +248,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: try { - ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id); + ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) { @@ -300,7 +298,6 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: } else { - ref.getPtr().getRefData().setCount (count); ref.getPtr().getCellRef().mOwner = owner; addImp (ref.getPtr()); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index affcf03878..e1a2fb096d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1949,7 +1949,6 @@ namespace MWWorld if(werewolf) { ManualRef ref(getStore(), "WerewolfRobe"); - ref.getPtr().getRefData().setCount(1); // Configure item's script variables std::string script = Class::get(ref.getPtr()).getScript(ref.getPtr()); From 26e4ccb8c20218c8848279894a7fcbddc5898f5a Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Mon, 21 Oct 2013 00:33:47 +0200 Subject: [PATCH 138/148] Cosmetic changes Rename ContainerStore::addImpl to addNewStack (it was confusing, since ContainerStore had methods named 'addImp' and 'addImpl'). --- apps/openmw/mwworld/containerstore.cpp | 10 +++++----- apps/openmw/mwworld/containerstore.hpp | 4 ++-- apps/openmw/mwworld/inventorystore.cpp | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index d25b29397a..07616c8672 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -141,19 +141,19 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr) || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_100")) { int count = MWWorld::Class::get(ptr).getValue(ptr) * ptr.getRefData().getCount(); - MWWorld::ManualRef ref(esmStore, "Gold_001", count); for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) { if (Misc::StringUtils::ciEqual((*iter).get()->mRef.mRefID, "gold_001")) { - (*iter).getRefData().setCount( (*iter).getRefData().getCount() + count); + iter->getRefData().setCount(iter->getRefData().getCount() + count); flagAsModified(); return iter; } } - return addImpl(ref.getPtr()); + MWWorld::ManualRef ref(esmStore, "Gold_001", count); + return addNewStack(ref.getPtr()); } // determine whether to stack or not @@ -169,10 +169,10 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr) } } // if we got here, this means no stacking - return addImpl(ptr); + return addNewStack(ptr); } -MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImpl (const Ptr& ptr) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const Ptr& ptr) { ContainerStoreIterator it = begin(); diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index d19f1ad50f..ca6609ecf2 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -86,8 +86,8 @@ namespace MWWorld /// @return the number of items actually removed protected: - ContainerStoreIterator addImpl (const Ptr& ptr); - ///< Add the item to this container (no stacking) + ContainerStoreIterator addNewStack (const Ptr& ptr); + ///< Add the item to this container (do not try to stack it onto existing items) public: diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 505153a661..162c056d29 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -107,7 +107,7 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite // add the item again with a count of count-1, then set the count of the original (that will be equipped) to 1 int count = iterator->getRefData().getCount(); iterator->getRefData().setCount(count-1); - addImpl(*iterator); + addNewStack(*iterator); iterator->getRefData().setCount(1); } @@ -218,7 +218,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc) // add the item again with a count of count-1, then set the count of the original (that will be equipped) to 1 int count = iter->getRefData().getCount(); iter->getRefData().setCount(count-1); - addImpl(*iter); + addNewStack(*iter); iter->getRefData().setCount(1); } } From 750f1fd760f486b8937275d2974f2aa397d65d2c Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Wed, 23 Oct 2013 14:36:55 +0200 Subject: [PATCH 139/148] Edit ContainerStore::stacks for clarifications and correctness Rename arguments and fix some potential errors (add checks). --- apps/openmw/mwworld/containerstore.cpp | 37 ++++++++++++++++---------- apps/openmw/mwworld/containerstore.hpp | 3 ++- apps/openmw/mwworld/inventorystore.cpp | 16 ++++++----- apps/openmw/mwworld/inventorystore.hpp | 4 +-- 4 files changed, 36 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 07616c8672..c041beffa6 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -77,22 +77,31 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end() return ContainerStoreIterator (this); } -bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2) +bool MWWorld::ContainerStore::stacks(const Ptr& stack, const Ptr& item) { - /// \todo add current enchantment charge here when it is implemented - if ( Misc::StringUtils::ciEqual(ptr1.getCellRef().mRefID, ptr2.getCellRef().mRefID) - && MWWorld::Class::get(ptr1).getScript(ptr1) == "" // item with a script never stacks - && MWWorld::Class::get(ptr1).getEnchantment(ptr1) == "" // item with enchantment never stacks (we could revisit this later, but for now it makes selecting items in the spell window much easier) - && ptr1.getCellRef().mOwner == ptr2.getCellRef().mOwner - && ptr1.getCellRef().mSoul == ptr2.getCellRef().mSoul - // item that is already partly used up never stacks - && (!MWWorld::Class::get(ptr1).hasItemHealth(ptr1) || ptr1.getCellRef().mCharge == -1 - || MWWorld::Class::get(ptr1).getItemMaxHealth(ptr1) == ptr1.getCellRef().mCharge) - && (!MWWorld::Class::get(ptr2).hasItemHealth(ptr2) || ptr2.getCellRef().mCharge == -1 - || MWWorld::Class::get(ptr2).getItemMaxHealth(ptr2) == ptr2.getCellRef().mCharge)) - return true; + const MWWorld::Class& cls1 = MWWorld::Class::get(stack); + const MWWorld::Class& cls2 = MWWorld::Class::get(item); - return false; + /// \todo add current enchantment charge here when it is implemented + return stack != item // an item never stacks onto itself + && Misc::StringUtils::ciEqual(stack.getCellRef().mRefID, item.getCellRef().mRefID) + && stack.getCellRef().mOwner == item.getCellRef().mOwner + && stack.getCellRef().mSoul == item.getCellRef().mSoul + + // item with a script never stacks + && cls1.getScript(stack) == "" + && cls2.getScript(item) == "" + + // item with enchantment never stacks (we could revisit this later, + // but for now it makes selecting items in the spell window much easier) + && cls1.getEnchantment(stack) == "" + && cls2.getEnchantment(item) == "" + + // item that is already partly used up never stacks + && (!cls1.hasItemHealth(stack) || stack.getCellRef().mCharge == -1 + || cls1.getItemMaxHealth(stack) == stack.getCellRef().mCharge) + && (!cls2.hasItemHealth(item) || item.getCellRef().mCharge == -1 + || cls2.getItemMaxHealth(item) == item.getCellRef().mCharge); } MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, const Ptr& actorPtr) diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index ca6609ecf2..2cdefd2f4b 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -91,8 +91,9 @@ namespace MWWorld public: - virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); + virtual bool stacks (const Ptr& stack, const Ptr& item); ///< @return true if the two specified objects can stack with each other + /// @note stack is the item that is already in this container void fill (const ESM::InventoryList& items, const std::string& owner, const MWWorld::ESMStore& store); ///< Insert items into *this. diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 162c056d29..6057cfcf44 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -276,20 +276,22 @@ void MWWorld::InventoryStore::flagAsModified() mMagicEffectsUpToDate = false; } -bool MWWorld::InventoryStore::stacks(const Ptr& ptr1, const Ptr& ptr2) +bool MWWorld::InventoryStore::stacks(const Ptr& stack, const Ptr& item) { - bool canStack = MWWorld::ContainerStore::stacks(ptr1, ptr2); + bool canStack = MWWorld::ContainerStore::stacks(stack, item); if (!canStack) return false; - // don't stack if the item being checked against is currently equipped. + // don't stack if 'stack' (the item being checked against) is currently equipped. for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter) { - if (*iter != end() && ptr1 == **iter) - return false; - if (*iter != end() && ptr2 == **iter) - return false; + if (*iter != end() && stack == **iter) + { + bool stackWhenEquipped = MWWorld::Class::get(**iter).getEquipmentSlots(**iter).second; + if (!stackWhenEquipped) + return false; + } } return true; diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 9468f928f1..26ea90c3d6 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -104,9 +104,9 @@ namespace MWWorld ///< \attention This function is internal to the world model and should not be called from /// outside. - virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); + virtual bool stacks (const Ptr& stack, const Ptr& item); ///< @return true if the two specified objects can stack with each other - /// @note ptr1 is the item that is already in this container + /// @note stack is the item that is already in this container (it may be equipped) virtual int remove(const Ptr& item, int count, const Ptr& actor); ///< Remove \a count item(s) designated by \a item from this inventory. From 59c963b6cc9f21a3486dafe2b1470e4a7ff7e383 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Thu, 24 Oct 2013 02:14:05 +0200 Subject: [PATCH 140/148] Auto-equip items when a clothe or an armor is removed from inventory This fix auto-equip on corpses. --- apps/openmw/mwworld/inventorystore.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 6057cfcf44..3571f64baa 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -321,7 +321,19 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor } } - return ContainerStore::remove(item, count, actor); + int retCount = ContainerStore::remove(item, count, actor); + + // If an armor/clothing item is removed, try to find a replacement, + // but not for the player nor werewolves. + if ((actor.getRefData().getHandle() != "player") + && !(MWWorld::Class::get(actor).getNpcStats(actor).isWerewolf())) + { + std::string type = item.getTypeName(); + if ((type == typeid(ESM::Armor).name()) || (type == typeid(ESM::Clothing).name())) + autoEquip(actor); + } + + return retCount; } void MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor) From 12dbbde1e37898781bf94198b945eaf65e9c0cd9 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Fri, 1 Nov 2013 01:01:55 +0100 Subject: [PATCH 141/148] InvStore::unequipSlot: return an iterator to the unequipped item --- apps/openmw/mwworld/inventorystore.cpp | 15 +++++++++++---- apps/openmw/mwworld/inventorystore.hpp | 7 ++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 3571f64baa..4d7fa2d64d 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -336,18 +336,21 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor return retCount; } -void MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor) +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor) { ContainerStoreIterator it = getSlot(slot); + if (it != end()) { // restack item previously in this slot + ContainerStoreIterator retval = it; for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) { - if (stacks(*iter, *mSlots[slot])) + if (stacks(*iter, *it)) { - iter->getRefData().setCount(iter->getRefData().getCount() + mSlots[slot]->getRefData().getCount()); - mSlots[slot]->getRefData().setCount(0); + iter->getRefData().setCount(iter->getRefData().getCount() + it->getRefData().getCount()); + it->getRefData().setCount(0); + retval = iter; break; } } @@ -377,5 +380,9 @@ void MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor) } /// \todo update actor model + + return retval; } + + return it; } diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 26ea90c3d6..1b11d9dcea 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -113,7 +113,12 @@ namespace MWWorld /// /// @return the number of items actually removed - void unequipSlot(int slot, const Ptr& actor); + ContainerStoreIterator unequipSlot(int slot, const Ptr& actor); + ///< Unequip \a slot. + /// + /// @return an iterator to the item that was previously in the slot + /// (it can be re-stacked so its count may be different than when it + /// was equipped). }; } From 37e91a278e1390c47488f28e14ff16d8e47609ef Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Fri, 1 Nov 2013 00:21:15 +0100 Subject: [PATCH 142/148] Add InventoryStore::unequipItem() --- apps/openmw/mwworld/inventorystore.cpp | 12 ++++++++++++ apps/openmw/mwworld/inventorystore.hpp | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 4d7fa2d64d..4291c120f3 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -386,3 +386,15 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, c return it; } + +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItem(const MWWorld::Ptr& item, const MWWorld::Ptr& actor) +{ + for (int slot=0; slot Date: Fri, 1 Nov 2013 00:20:33 +0100 Subject: [PATCH 143/148] InventoryWindow: call InventoryStore::unequipItem() when an equipped item is dragged The unequipped item is also re-stacked if needed. --- apps/openmw/mwgui/inventorywindow.cpp | 53 +++++++++++++++------------ apps/openmw/mwgui/inventorywindow.hpp | 1 - 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 5c8cd2cdc4..0ae633aa05 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -145,10 +145,38 @@ namespace MWGui const ItemStack& item = mTradeModel->getItem(index); - unequipItem(item.mBase); - MWWorld::Ptr object = item.mBase; int count = item.mCount; + + if (item.mType == ItemStack::Type_Equipped) + { + MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); + MWWorld::Ptr newStack = *invStore.unequipItem(item.mBase, mPtr); + + // The unequipped item was re-stacked. We have to update the index + // since the item pointed does not exist anymore. + if (item.mBase != newStack) + { + // newIndex will store the index of the ItemStack the item was stacked on + int newIndex = -1; + for (size_t i=0; i < mTradeModel->getItemCount(); ++i) + { + if (mTradeModel->getItem(i).mBase == newStack) + { + newIndex = i; + break; + } + } + + if (newIndex == -1) + throw std::runtime_error("Can't find restacked item"); + + index = newIndex; + object = mTradeModel->getItem(index).mBase; + } + + } + bool shift = MyGUI::InputManager::getInstance().isShiftPressed(); if (MyGUI::InputManager::getInstance().isControlPressed()) count = 1; @@ -375,27 +403,6 @@ namespace MWGui return MWWorld::Ptr(); } - void InventoryWindow::unequipItem(const MWWorld::Ptr& item) - { - MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - - for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) - { - MWWorld::ContainerStoreIterator it = invStore.getSlot(slot); - if (it != invStore.end() && *it == item) - { - invStore.equip(slot, invStore.end(), mPtr); - std::string script = MWWorld::Class::get(*it).getScript(*it); - - // Unset OnPCEquip Variable on item's script, if it has a script with that variable declared - if(script != "") - (*it).getRefData().getLocals().setVarByInt(script, "onpcequip", 0); - - return; - } - } - } - void InventoryWindow::updateEncumbranceBar() { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 35140437d1..78d00fd1e7 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -103,7 +103,6 @@ namespace MWGui void onAvatarClicked(MyGUI::Widget* _sender); void onPinToggled(); - void unequipItem(const MWWorld::Ptr& item); void updateEncumbranceBar(); void notifyContentChanged(); }; From f428921b93e3d4ecc7f82291aa07f5cb5e18665d Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Thu, 24 Oct 2013 10:48:10 +0200 Subject: [PATCH 144/148] Always update the source container view on drag&drop This fix the indicator not being displayed for items auto-equipped after an other item is removed. --- apps/openmw/mwgui/container.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index bc869e5fef..5d864752fc 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -100,6 +100,9 @@ namespace MWGui finish(); targetView->update(); + + // We need to update the view since an other item could be auto-equipped. + mSourceView->update(); } void DragAndDrop::finish() From 467bd91651e9af7a8d3048df5ae1847dede680a8 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Fri, 25 Oct 2013 22:16:52 +0200 Subject: [PATCH 145/148] Update actor model on inventory change --- apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwrender/renderingmanager.cpp | 13 +++++++++++++ apps/openmw/mwrender/renderingmanager.hpp | 3 +++ apps/openmw/mwworld/inventorystore.cpp | 17 ++++++++++++++++- apps/openmw/mwworld/inventorystore.hpp | 3 +++ apps/openmw/mwworld/worldimp.cpp | 5 +++++ apps/openmw/mwworld/worldimp.hpp | 2 ++ 7 files changed, 44 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 4eb9d30880..ddf8417448 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -413,6 +413,8 @@ namespace MWBase virtual bool toggleGodMode() = 0; virtual void castSpell (const MWWorld::Ptr& actor) = 0; + + virtual void updateAnimParts(const MWWorld::Ptr& ptr) = 0; }; } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 93425191dd..817a2d236d 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -327,6 +327,19 @@ void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr) } } +void RenderingManager::updateAnimParts(const MWWorld::Ptr& ptr) +{ + NpcAnimation *anim = NULL; + + if(ptr.getRefData().getHandle() == "player") + anim = mPlayerAnimation; + else if(MWWorld::Class::get(ptr).isActor()) + anim = dynamic_cast(mActors.getAnimation(ptr)); + + if(anim) + anim->updateParts(); +} + void RenderingManager::update (float duration, bool paused) { MWBase::World *world = MWBase::Environment::get().getWorld(); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 2d08139128..8913e0a789 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -141,6 +141,9 @@ public: /// and equipment. void rebuildPtr(const MWWorld::Ptr &ptr); + /// Update actor model parts. + void updateAnimParts(const MWWorld::Ptr &ptr); + void update (float duration, bool paused); void setAmbientColour(const Ogre::ColourValue& colour); diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 4291c120f3..dbb5a80b54 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -41,6 +41,7 @@ void MWWorld::InventoryStore::initSlots (TSlots& slots) MWWorld::InventoryStore::InventoryStore() : mMagicEffectsUpToDate (false) , mSelectedEnchantItem(end()) + , mActorModelUpdateEnabled (true) { initSlots (mSlots); } @@ -114,6 +115,8 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite mSlots[slot] = iterator; flagAsModified(); + + updateActorModel(actor); } void MWWorld::InventoryStore::unequipAll(const MWWorld::Ptr& actor) @@ -148,6 +151,9 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc) TSlots slots; initSlots (slots); + // Disable model update during auto-equip + mActorModelUpdateEnabled = false; + for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter) { Ptr test = *iter; @@ -236,9 +242,12 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc) changed = true; } + mActorModelUpdateEnabled = true; + if (changed) { mSlots.swap (slots); + updateActorModel(npc); flagAsModified(); } } @@ -379,7 +388,7 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, c } } - /// \todo update actor model + updateActorModel(actor); return retval; } @@ -398,3 +407,9 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItem(const MWWor throw std::runtime_error ("attempt to unequip an item that is not currently equipped"); } + +void MWWorld::InventoryStore::updateActorModel(const MWWorld::Ptr& actor) +{ + if (mActorModelUpdateEnabled) + MWBase::Environment::get().getWorld()->updateAnimParts(actor); +} diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 199c422435..02d65d54a0 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -45,6 +45,7 @@ namespace MWWorld mutable MWMechanics::MagicEffects mMagicEffects; mutable bool mMagicEffectsUpToDate; + bool mActorModelUpdateEnabled; typedef std::vector TSlots; @@ -57,6 +58,8 @@ namespace MWWorld void initSlots (TSlots& slots); + void updateActorModel (const Ptr& actor); + public: InventoryStore(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e1a2fb096d..be71bebeee 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2135,4 +2135,9 @@ namespace MWWorld // TODO: RT_Range, RT_Touch } } + + void World::updateAnimParts(const Ptr& actor) + { + mRendering->updateAnimParts(actor); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index cd982aceee..602d8d5e74 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -473,6 +473,8 @@ namespace MWWorld virtual bool toggleGodMode(); virtual void castSpell (const MWWorld::Ptr& actor); + + virtual void updateAnimParts(const MWWorld::Ptr& ptr); }; } From d2dcf0b2036f03d3f42ad47e5d329297724fa22f Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sat, 2 Nov 2013 13:06:15 +0100 Subject: [PATCH 146/148] Add a warning comment to RefData::setCount() --- apps/openmw/mwworld/refdata.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 642f5412c8..d5701efc51 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -73,6 +73,11 @@ namespace MWWorld void setLocals (const ESM::Script& script); void setCount (int count); + /// Set object count (an object pile is a simple object with a count >1). + /// + /// \warning Do not call setCount() to add or remove objects from a + /// container or an actor's inventory. Call ContainerStore::add() or + /// ContainerStore::remove() instead. MWScript::Locals& getLocals(); From bbfd7f4c9d0d53c2be676a1591e81e626e974180 Mon Sep 17 00:00:00 2001 From: Emanuel Guevel Date: Sat, 9 Nov 2013 02:47:11 +0100 Subject: [PATCH 147/148] Disable equipped item re-stacking when the item is removed from inventory The item was not removed if it was re-stacked. --- apps/openmw/mwworld/inventorystore.cpp | 24 ++++++++++++++---------- apps/openmw/mwworld/inventorystore.hpp | 6 +++--- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index dbb5a80b54..dc2d0cab13 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -325,7 +325,8 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor if (*mSlots[slot] == item) { - unequipSlot(slot, actor); + // restacking is disabled cause it may break removal + unequipSlot(slot, actor, false); break; } } @@ -345,22 +346,25 @@ int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor return retCount; } -MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor) +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor, bool restack) { ContainerStoreIterator it = getSlot(slot); if (it != end()) { - // restack item previously in this slot ContainerStoreIterator retval = it; - for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) - { - if (stacks(*iter, *it)) + + if (restack) { + // restack item previously in this slot + for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) { - iter->getRefData().setCount(iter->getRefData().getCount() + it->getRefData().getCount()); - it->getRefData().setCount(0); - retval = iter; - break; + if (stacks(*iter, *it)) + { + iter->getRefData().setCount(iter->getRefData().getCount() + it->getRefData().getCount()); + it->getRefData().setCount(0); + retval = iter; + break; + } } } diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index 02d65d54a0..c4ad517776 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -116,12 +116,12 @@ namespace MWWorld /// /// @return the number of items actually removed - ContainerStoreIterator unequipSlot(int slot, const Ptr& actor); + ContainerStoreIterator unequipSlot(int slot, const Ptr& actor, bool restack = true); ///< Unequip \a slot. /// /// @return an iterator to the item that was previously in the slot - /// (it can be re-stacked so its count may be different than when it - /// was equipped). + /// (if \a restack is true, the item can be re-stacked so its count + /// may differ from when it was equipped). ContainerStoreIterator unequipItem(const Ptr& item, const Ptr& actor); ///< Unequip an item identified by its Ptr. An exception is thrown From ef8360f346dd7cb48358709a221616551357256e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 10 Nov 2013 21:45:27 +0100 Subject: [PATCH 148/148] silenced a warning --- apps/openmw/mwmechanics/alchemy.cpp | 134 ++++++++++++++-------------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index ab35dbdb1f..82580ce0e1 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -30,13 +30,13 @@ std::set MWMechanics::Alchemy::listEffects() const { std::map effects; - + for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter) { if (!iter->isEmpty()) { const MWWorld::LiveCellRef *ingredient = iter->get(); - + for (int i=0; i<4; ++i) if (ingredient->mBase->mData.mEffectID[i]!=-1) { @@ -48,13 +48,13 @@ std::set MWMechanics::Alchemy::listEffects() const } } } - + std::set effects2; - + for (std::map::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter) if (iter->second>1) effects2.insert (iter->first); - + return effects2; } @@ -67,7 +67,7 @@ void MWMechanics::Alchemy::applyTools (int flags, float& value) const int tool = negative ? ESM::Apparatus::Retort : ESM::Apparatus::Albemic; int setup = 0; - + if (!mTools[tool].isEmpty() && !mTools[ESM::Apparatus::Calcinator].isEmpty()) setup = 1; else if (!mTools[tool].isEmpty()) @@ -82,23 +82,23 @@ void MWMechanics::Alchemy::applyTools (int flags, float& value) const mTools[ESM::Apparatus::Calcinator].get()->mBase->mData.mQuality : 0; float quality = 1; - + switch (setup) { case 1: - + quality = negative ? 2 * toolQuality + 3 * calcinatorQuality : (magnitude && duration ? 2 * toolQuality + calcinatorQuality : 2/3.0 * (toolQuality + calcinatorQuality) + 0.5); break; - + case 2: - + quality = negative ? 1+toolQuality : (magnitude && duration ? toolQuality : toolQuality + 0.5); break; - + case 3: - + quality = magnitude && duration ? calcinatorQuality : calcinatorQuality + 0.5; break; } @@ -110,8 +110,8 @@ void MWMechanics::Alchemy::applyTools (int flags, float& value) const else { if (quality==0) - throw std::runtime_error ("invalid derived alchemy apparatus quality"); - + throw std::runtime_error ("invalid derived alchemy apparatus quality"); + value /= quality; } } @@ -141,21 +141,21 @@ void MWMechanics::Alchemy::updateEffects() for (std::set::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter) { const ESM::MagicEffect *magicEffect = - MWBase::Environment::get().getWorld()->getStore().get().find (iter->mId); - + MWBase::Environment::get().getWorld()->getStore().get().find (iter->mId); + if (magicEffect->mData.mBaseCost<=0) { std::ostringstream os; os << "invalid base cost for magic effect " << iter->mId; throw std::runtime_error (os.str()); } - + float fPotionT1MagMul = MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionT1MagMult")->getFloat(); if (fPotionT1MagMul<=0) throw std::runtime_error ("invalid gmst: fPotionT1MagMul"); - + float fPotionT1DurMult = MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionT1DurMult")->getFloat(); @@ -172,25 +172,25 @@ void MWMechanics::Alchemy::updateEffects() if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) applyTools (magicEffect->mData.mFlags, duration); - - duration = static_cast (duration+0.5); + + duration = static_cast (duration+0.5); magnitude = static_cast (magnitude+0.5); if (magnitude>0 && duration>0) { ESM::ENAMstruct effect; effect.mEffectID = iter->mId; - + effect.mSkill = effect.mAttribute = iter->mArg; // somewhat hack-ish, but should work - + effect.mRange = 0; - effect.mArea = 0; - + effect.mArea = 0; + effect.mDuration = duration; effect.mMagnMin = effect.mMagnMax = magnitude; mEffects.push_back (effect); - } + } } } @@ -204,14 +204,14 @@ const ESM::Potion *MWMechanics::Alchemy::getRecord() const { if (iter->mEffects.mList.size() != mEffects.size()) continue; - - bool mismatch = false; + + bool mismatch = false; for (int i=0; i (iter->mEffects.mList.size()); ++i) { const ESM::ENAMstruct& first = iter->mEffects.mList[i]; const ESM::ENAMstruct& second = mEffects[i]; - + if (first.mEffectID!=second.mEffectID || first.mArea!=second.mArea || first.mRange!=second.mRange || @@ -225,18 +225,18 @@ const ESM::Potion *MWMechanics::Alchemy::getRecord() const break; } } - + if (!mismatch) return &(*iter); } - + return 0; } void MWMechanics::Alchemy::removeIngredients() { bool needsUpdate = false; - + for (TIngredientsContainer::iterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter) if (!iter->isEmpty()) { @@ -248,7 +248,7 @@ void MWMechanics::Alchemy::removeIngredients() *iter = MWWorld::Ptr(); } } - + if (needsUpdate) updateEffects(); } @@ -256,37 +256,37 @@ void MWMechanics::Alchemy::removeIngredients() void MWMechanics::Alchemy::addPotion (const std::string& name) { const ESM::Potion *record = getRecord(); - + if (!record) { ESM::Potion newRecord; - + newRecord.mData.mWeight = 0; - + for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter) if (!iter->isEmpty()) newRecord.mData.mWeight += iter->get()->mBase->mData.mWeight; - + newRecord.mData.mWeight /= countIngredients(); - + newRecord.mData.mValue = mValue; newRecord.mData.mAutoCalc = 0; - + newRecord.mName = name; - int index = static_cast (std::rand()/static_cast (RAND_MAX+1)*6); + int index = static_cast (std::rand()/(static_cast (RAND_MAX)+1)*6); assert (index>=0 && index<6); - + static const char *name[] = { "standard", "bargain", "cheap", "fresh", "exclusive", "quality" }; - + newRecord.mModel = "m\\misc_potion_" + std::string (name[index]) + "_01.nif"; newRecord.mIcon = "m\\tx_potion_" + std::string (name[index]) + "_01.dds"; - + newRecord.mEffects.mList = mEffects; - + record = MWBase::Environment::get().getWorld()->createRecord (newRecord); } - + MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), record->mId); MWWorld::Class::get (mAlchemist).getContainerStore (mAlchemist).add (ref.getPtr(), mAlchemist); } @@ -300,7 +300,7 @@ float MWMechanics::Alchemy::getChance() const { const CreatureStats& creatureStats = MWWorld::Class::get (mAlchemist).getCreatureStats (mAlchemist); const NpcStats& npcStats = MWWorld::Class::get (mAlchemist).getNpcStats (mAlchemist); - + return (npcStats.getSkill (ESM::Skill::Alchemy).getModified() + 0.1 * creatureStats.getAttribute (1).getModified() @@ -321,34 +321,34 @@ int MWMechanics::Alchemy::countIngredients() const void MWMechanics::Alchemy::setAlchemist (const MWWorld::Ptr& npc) { mAlchemist = npc; - + mIngredients.resize (4); std::fill (mIngredients.begin(), mIngredients.end(), MWWorld::Ptr()); - + mTools.resize (4); - + std::fill (mTools.begin(), mTools.end(), MWWorld::Ptr()); - + mEffects.clear(); - + MWWorld::ContainerStore& store = MWWorld::Class::get (npc).getContainerStore (npc); - + for (MWWorld::ContainerStoreIterator iter (store.begin (MWWorld::ContainerStore::Type_Apparatus)); iter!=store.end(); ++iter) - { + { MWWorld::LiveCellRef* ref = iter->get(); - + int type = ref->mBase->mData.mType; - + if (type<0 || type>=static_cast (mTools.size())) throw std::runtime_error ("invalid apparatus type"); - + if (!mTools[type].isEmpty()) if (ref->mBase->mData.mQuality<=mTools[type].get()->mBase->mData.mQuality) continue; - - mTools[type] = *iter; + + mTools[type] = *iter; } } @@ -390,19 +390,19 @@ int MWMechanics::Alchemy::addIngredient (const MWWorld::Ptr& ingredient) { slot = i; break; - } - + } + if (slot==-1) return -1; - + for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter) if (!iter->isEmpty() && ingredient.get()==iter->get()) return -1; - + mIngredients[slot] = ingredient; - + updateEffects(); - + return slot; } @@ -429,7 +429,7 @@ std::string MWMechanics::Alchemy::getPotionName() const { if (const ESM::Potion *potion = getRecord()) return potion->mName; - + return ""; } @@ -437,13 +437,13 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& na { if (mTools[ESM::Apparatus::MortarPestle].isEmpty()) return Result_NoMortarAndPestle; - + if (countIngredients()<2) return Result_LessThanTwoIngredients; if (name.empty() && getPotionName().empty()) return Result_NoName; - + if (beginEffects()==endEffects()) return Result_NoEffects; @@ -456,7 +456,7 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& na addPotion (name); removeIngredients(); - + increaseSkill(); return Result_Success;