From bfb71f23c9f2e1c3b9f6525ef1ed8b1185425c6d Mon Sep 17 00:00:00 2001 From: graffy76 Date: Fri, 16 Aug 2013 17:26:23 -0500 Subject: [PATCH 001/132] 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 f956317a7..02421a788 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 02a6766b0..cf1fa1b0a 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/132] 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 add3dea40..1fafd5922 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 02421a788..b06c97008 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 b21618d5d..4c3fe9ffa 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 c3ff953ae..9346276da 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 08ead9a7a..55913d7fe 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 041a9576d..342e4d9e9 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/132] 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 1fafd5922..077f3c292 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 b06c97008..a07b854df 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 342e4d9e9..816d288a6 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/132] 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 077f3c292..fe2fe8255 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 cf1fa1b0a..99c1aaebf 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 6be152b55..726a3f158 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/132] 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 fe2fe8255..ddeb43ab7 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/132] 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 ddeb43ab7..f674e9dc7 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 a07b854df..d49906949 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 529891b4c..bbaca4805 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 000000000..ce874318d --- /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 000000000..d0d288873 --- /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 726a3f158..4648d2833 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 8fde73236..08baa2338 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/132] Moved init code to master / plugin proxy classes --- apps/launcher/datafilespage.cpp | 19 ++++++------------ 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, 30 insertions(+), 30 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index f674e9dc7..42e10c76b 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); - - mMastersProxyModel = new QSortFilterProxyModel(); - mMastersProxyModel->setFilterRegExp(QString("game")); //QString("^.*\\.esm")); - mMastersProxyModel->setFilterRole (Qt::UserRole); - mMastersProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); - mMastersProxyModel->setSourceModel(mDataFilesModel); - - mPluginsProxyModel = new PluginsProxyModel(); - mPluginsProxyModel->setFilterRegExp(QString("addon")); //^.*\\.esp")); - mPluginsProxyModel->setFilterRole (Qt::UserRole); - mPluginsProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); - mPluginsProxyModel->setSourceModel(mDataFilesModel); + mDataFilesModel = new DataFilesModel (this); + + mMastersProxyModel = new MasterProxyModel (this, 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 d49906949..dd94b0571 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 ce874318d..dc702e945 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 d0d288873..d9e12dada 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 4648d2833..61ffb8894 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 08baa2338..238d2aac7 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/132] 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 42e10c76b..7a4890051 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 a0b029330..99aa24ff3 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 dd94b0571..f770e80a1 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 4c3fe9ffa..2f4d4e381 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 bbaca4805..19af6c3b2 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 000000000..16e071891 --- /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 000000000..f17e3a909 --- /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 dc702e945..7701d4f17 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 d9e12dada..49ca369de 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/132] 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 99c1aaebf..3f63e73cc 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 61ffb8894..f6864d85c 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 238d2aac7..e148ea3b1 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/132] 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 7a4890051..d077c3e0e 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 99aa24ff3..1c528ab26 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 f770e80a1..2f3a6dc21 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 2f4d4e381..7944b7fb3 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 16e071891..6f8a86a49 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 f17e3a909..914e8bacb 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 f6864d85c..9e3cdd730 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/132] 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 d077c3e0e..5cbcae545 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 1c528ab26..db391519f 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 2f3a6dc21..9f1c72068 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 7944b7fb3..232f250be 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 6f8a86a49..e7ab9b0cf 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 914e8bacb..138ffc629 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 3f63e73cc..8e27f1f75 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 9e3cdd730..18aebc6b6 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 e148ea3b1..cfade092e 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 816d288a6..87213c5cc 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/132] 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 e7ab9b0cf..6254601dc 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 87213c5cc..cf6f30b99 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/132] 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 6254601dc..b2a5c5ba5 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 138ffc629..f1d8f6927 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 8e27f1f75..ae842381e 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 cf6f30b99..a6c1768b5 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/132] 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 5cbcae545..44f92d7fe 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 9f1c72068..252c760fa 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 b2a5c5ba5..27be996fc 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 a6c1768b5..07ad9d3ba 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/132] 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 19af6c3b2..85f1a3508 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/132] 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 db391519f..356cb88d6 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 232f250be..1b4d09745 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 85f1a3508..4f14fa9d9 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 93d83091e..000000000 --- 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 52b3fbd00..000000000 --- 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 7701d4f17..04a7f0033 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 49ca369de..6fbdd3154 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 27be996fc..16139d9e6 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/132] 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 | 79 ++++++++++--------- 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, 380 insertions(+), 249 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 44f92d7fe..2346a0b01 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 356cb88d6..f3792b1f1 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 1bbf7f897..4d5975e58 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 a4b36b95e..052fc58e4 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 cbb453ac8..2fb6e0f6b 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 380e434c2..c88efcfb9 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 252c760fa..12849d6ee 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 1b4d09745..d016ad32d 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 4f14fa9d9..8b07a4e00 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 ae842381e..2980313f0 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 0a07a536f..bc55bb6cf 100644 --- a/components/esxselector/model/datafilesmodel.hpp +++ b/components/esxselector/model/datafilesmodel.hpp @@ -6,61 +6,62 @@ #include #include +namespace EsxModel +{ + class EsmFile; -class EsmFile; + class DataFilesModel : public QAbstractTableModel + { + Q_OBJECT -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; -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 moveRow(int oldrow, int row, const QModelIndex &parent = QModelIndex()); - bool moveRow(int oldrow, int row, const QModelIndex &parent = QModelIndex()); + virtual Qt::ItemFlags flags(const QModelIndex &index) const; - 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 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); - 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 + { return QAbstractTableModel::index(row, column, parent); } - inline QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const - { return QAbstractTableModel::index(row, column, parent); } + void setEncoding(const QString &encoding); - void setEncoding(const QString &encoding); + void addFiles(const QString &path); - void addFiles(const QString &path); + void uncheckAll(); - void uncheckAll(); + QStringList checkedItems(); + QStringList uncheckedItems(); + QStringList checkedItemsPaths(); - QStringList checkedItems(); - QStringList uncheckedItems(); - QStringList checkedItemsPaths(); + Qt::CheckState checkState(const QModelIndex &index); + void setCheckState(const QModelIndex &index, Qt::CheckState state); - 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(EsmFile *item) const; - EsmFile* findItem(const QString &name); - EsmFile* item(int row) const; + signals: + void checkedItemsChanged(const QStringList &items); -signals: - void checkedItemsChanged(const QStringList &items); - -private: - bool canBeChecked(EsmFile *file) const; - void addFile(EsmFile *file); - - QList mFiles; - QHash mCheckStates; + private: + bool canBeChecked(EsmFile *file) const; + void addFile(EsmFile *file); - QString mEncoding; + 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 000000000..96b90e44e --- /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 000000000..6a3e36b53 --- /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 04a7f0033..011e5ebd5 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 6fbdd3154..fed01bdb1 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 0ff7e45cb..8c1e83695 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 f4cb4322f..64596302c 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 59271547a..8386e4e9f 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 18aebc6b6..4fde11f47 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 cfade092e..04c18c2e2 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 4d62e1399..815a1130b 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 ba10731ae..f3b251955 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 16139d9e6..2de68e5bf 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 f1d8f6927..35ef3a07c 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; -class QSortFilterProxyModel; - -namespace FileOrderList +namespace EsxModel { + class DataFilesModel; + class PluginsProxyModel; class MasterProxyModel; +} +class QSortFilterProxyModel; + +namespace EsxView +{ 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 b0f339589..8944251ae 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 14bd7b1b4..e48392ba8 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 9346276da..b765f87ca 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 55913d7fe..218948e7b 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 07ad9d3ba..523ee69cc 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/132] 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 12849d6ee..4f4aef4f2 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 d016ad32d..0e2d8f32b 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 2de68e5bf..266fd76dc 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 b765f87ca..0d709aa50 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 218948e7b..28740783b 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 523ee69cc..6235f31af 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -14,113 +14,162 @@ Qt::DefaultContextMenu + + 6 + + + 6 + - - - - - false - - - - - - - - - - - - 0 - 0 - - - - Qt::DefaultContextMenu - - - QAbstractItemView::NoEditTriggers - - - true - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - Qt::ElideLeft - - - false - - - false - - - true - - - 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 + + + - - - - - 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/132] 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 2346a0b01..15f8d9ba2 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 4f4aef4f2..69ed1c13a 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 2980313f0..49d0d6132 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 bc55bb6cf..24b36aa88 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 266fd76dc..0b4780241 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 6235f31af..ecc70dfcb 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/132] 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 15f8d9ba2..6f8d1dfac 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/132] 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 6f8d1dfac..d51952b11 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 69ed1c13a..a0360cc5e 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 0e2d8f32b..c749099d4 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 8944251ae..48be2f022 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 e48392ba8..4e0cbe339 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 ecc70dfcb..60e8b8bf5 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/132] ... --- 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 d51952b11..070f455e4 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 a0360cc5e..561f7f5d7 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 0b4780241..6cba643d2 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 35ef3a07c..658e0176c 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/132] Implementing drag and drop --- apps/opencs/view/doc/filedialog.cpp | 2 +- components/CMakeLists.txt | 3 +- components/esxselector/model/contentmodel.cpp | 428 ++++++++++++++++++ components/esxselector/model/contentmodel.hpp | 64 +++ .../esxselector/model/datafilesmodel.cpp | 216 ++++++--- .../esxselector/model/datafilesmodel.hpp | 25 +- components/esxselector/model/esmfile.cpp | 18 +- components/esxselector/model/esmfile.hpp | 5 + .../esxselector/model/masterproxymodel.cpp | 34 +- .../esxselector/model/masterproxymodel.hpp | 6 +- components/esxselector/model/modelitem.cpp | 16 +- components/esxselector/model/modelitem.hpp | 10 +- .../esxselector/model/pluginsproxymodel.cpp | 19 +- .../esxselector/model/pluginsproxymodel.hpp | 6 +- components/esxselector/model/sourcemodel.cpp | 6 + components/esxselector/model/sourcemodel.h | 18 + .../esxselector/view/contentselector.cpp | 58 ++- .../esxselector/view/contentselector.hpp | 3 + files/ui/datafilespage.ui | 63 ++- 19 files changed, 887 insertions(+), 113 deletions(-) create mode 100644 components/esxselector/model/contentmodel.cpp create mode 100644 components/esxselector/model/contentmodel.hpp create mode 100644 components/esxselector/model/sourcemodel.cpp create mode 100644 components/esxselector/model/sourcemodel.h diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index 561f7f5d7..fb031fe5f 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -27,7 +27,7 @@ CSVDoc::FileDialog::FileDialog(QWidget *parent) : resize(400, 400); - connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList))); + // connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList))); connect(projectNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateCreateButton(QString))); connect(projectCreateButton, SIGNAL(clicked()), this, SIGNAL(createNewFile())); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 8b07a4e00..0f7c5017b 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -1,5 +1,5 @@ project (Components) - +set (CMAKE_BUILD_TYPE DEBUG) # source files add_component_dir (settings @@ -74,6 +74,7 @@ if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) add_component_qt_dir (esxselector model/masterproxymodel model/modelitem model/datafilesmodel model/pluginsproxymodel model/esmfile model/naturalsort + model/contentmodel view/profilescombobox view/comboboxlineedit view/lineedit view/contentselector ) diff --git a/components/esxselector/model/contentmodel.cpp b/components/esxselector/model/contentmodel.cpp new file mode 100644 index 000000000..673665775 --- /dev/null +++ b/components/esxselector/model/contentmodel.cpp @@ -0,0 +1,428 @@ +#include "contentmodel.hpp" +#include "esmfile.hpp" +#include +#include +#include +#include + +EsxModel::ContentModel::ContentModel(QObject *parent) : + QAbstractTableModel(parent), mEncoding("win1252") +{} + +void EsxModel::ContentModel::setEncoding(const QString &encoding) +{ + mEncoding = encoding; +} + +int EsxModel::ContentModel::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return 1; +} +/* +QModelIndex EsxModel::ContentModel::parent(const QModelIndex &child) const +{ + if(!child.isValid()) + return 0; + + return child.parent(); +} + +QModelIndex EsxModel::ContentModel::index(int row, int column, const QModelIndex &parent) const +{ + +} +*/ +int EsxModel::ContentModel::rowCount(const QModelIndex &parent) const +{ + if(parent.isValid()) + return 0; + + return mFiles.size(); +} + +QVariant EsxModel::ContentModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= mFiles.size()) + return QVariant(); + + EsmFile *file = item(index.row()); + + if (!file) + return QVariant(); + + const int column = index.column(); + + switch (role) + { + case Qt::EditRole: + case Qt::DisplayRole: + { + switch (column) + { + case 0: + return file->fileName(); + case 1: + return file->author(); + case 2: + return QString("%1 kB").arg(int((file->size() + 1023) / 1024)); + case 3: + return file->modified().toString(Qt::ISODate); + case 4: + return file->accessed().toString(Qt::TextDate); + case 5: + return file->version(); + case 6: + return file->path(); + case 7: + return file->masters().join(", "); + case 8: + return file->description(); + } + + return QVariant(); + } + + case Qt::TextAlignmentRole: + { + switch (column) + { + case 0: + case 1: + return Qt::AlignLeft + Qt::AlignVCenter; + case 2: + case 3: + case 4: + case 5: + return Qt::AlignRight + Qt::AlignVCenter; + default: + return Qt::AlignLeft + Qt::AlignVCenter; + } + return QVariant(); + } + + case Qt::ToolTipRole: + { + if (column != 0) + return QVariant(); + + if (file->version() == 0.0f) + return QVariant(); // Data not set + + return QString("Author: %1
\ + Version: %2
\ +
Description:
%3
\ +
Dependencies: %4
") + .arg(file->author()) + .arg(QString::number(file->version())) + .arg(file->description()) + .arg(file->masters().join(", ")); + } + + case Qt::UserRole: + { + if (file->masters().size() == 0) + return "game"; + else + return "addon"; + } + + default: + return QVariant(); + } +} + +Qt::ItemFlags EsxModel::ContentModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + EsmFile *file = item(index.row()); + + if (!file) + return Qt::NoItemFlags; + + Qt::ItemFlags dragDropFlags = Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + Qt::ItemFlags checkFlags = Qt::ItemIsUserCheckable; + Qt::ItemFlags defaultFlags = Qt::ItemIsDropEnabled | Qt::ItemIsSelectable; + + if (canBeChecked(file)) + return Qt::ItemIsEnabled | dragDropFlags | checkFlags | defaultFlags; + else + return defaultFlags; +} + +bool EsxModel::ContentModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.isValid() && role == Qt::EditRole) + { + QString fname = value.value(); + mFiles.replace(index.row(), findItem(fname)); + emit dataChanged(index, index); + return true; + } + + return false; +} + +bool EsxModel::ContentModel::insertRows(int position, int rows, const QModelIndex &parent) +{ + beginInsertRows(parent, position, position+rows-1); + + for (int row = 0; row < rows; ++row) + mFiles.insert(position, new EsmFile); + + endInsertRows(); + return true; +} + +bool EsxModel::ContentModel::removeRows(int position, int rows, const QModelIndex &parent) +{ + beginRemoveRows(parent, position, position+rows-1); + + for (int row = 0; row < rows; ++row) + mFiles.removeAt(position); + + endRemoveRows(); + emit dataChanged(index(0,0,parent), index(rowCount()-1, 0, parent)); + return true; +} + +Qt::DropActions EsxModel::ContentModel::supportedDropActions() const +{ + return Qt::CopyAction | Qt::MoveAction; +} + +QStringList EsxModel::ContentModel::mimeTypes() const +{ + QStringList types; + types << "application/omwcontent"; + return types; +} + +QMimeData *EsxModel::ContentModel::mimeData(const QModelIndexList &indexes) const +{ + QMimeData *mimeData = new QMimeData(); + QByteArray encodedData; + + QDataStream stream(&encodedData, QIODevice::WriteOnly); + + foreach (const QModelIndex &index, indexes) + { + if (index.isValid()) + { + QString text = data(index, Qt::DisplayRole).toString(); + stream << text; + } + } + + mimeData->setData("application/omwcontent", encodedData); + return mimeData; +} + +bool EsxModel::ContentModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) +{ + if (action == Qt::IgnoreAction) + return true; + + if (!data->hasFormat("application/omwcontent")) + return false; + + if (column > 0) + return false; + + int beginRow; + + if (row != -1) + beginRow = row; + else if (parent.isValid()) + beginRow = parent.row(); + else + beginRow = rowCount(); + + QByteArray encodedData = data->data("application/omwcontent"); + QDataStream stream(&encodedData, QIODevice::ReadOnly); + QStringList newItems; + int rows = 0; + + while (!stream.atEnd()) + { + QString text; + stream >> text; + newItems << text; + ++rows; + } + + insertRows(beginRow, rows, QModelIndex()); + + foreach (const QString &text, newItems) + { + QModelIndex idx = index(beginRow, 0, QModelIndex()); + setData(idx, text); + beginRow++; + } + + return true; +} + +void EsxModel::ContentModel::addFile(EsmFile *file) +{ + emit beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); + mFiles.append(file); + emit endInsertRows(); +} + +void EsxModel::ContentModel::addFiles(const QString &path) +{ + QDir dir(path); + QStringList filters; + filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon"; + dir.setNameFilters(filters); + + // Create a decoder for non-latin characters in esx metadata + QTextCodec *codec; + + if (mEncoding == QLatin1String("win1252")) { + codec = QTextCodec::codecForName("windows-1252"); + } else if (mEncoding == QLatin1String("win1251")) { + codec = QTextCodec::codecForName("windows-1251"); + } else if (mEncoding == QLatin1String("win1250")) { + codec = QTextCodec::codecForName("windows-1250"); + } else { + return; // This should never happen; + } + + QTextDecoder *decoder = codec->makeDecoder(); + + foreach (const QString &path, dir.entryList()) { + QFileInfo info(dir.absoluteFilePath(path)); + EsmFile *file = new EsmFile(path); + + try { + ESM::ESMReader fileReader; + ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(mEncoding.toStdString())); + fileReader.setEncoder(&encoder); + fileReader.open(dir.absoluteFilePath(path).toStdString()); + + std::vector mlist = fileReader.getMasters(); + + QStringList masters; + + for (unsigned int i = 0; i < mlist.size(); ++i) { + QString master = QString::fromStdString(mlist[i].name); + masters.append(master); + } + + file->setAuthor(decoder->toUnicode(fileReader.getAuthor().c_str())); + file->setSize(info.size()); + file->setDates(info.lastModified(), info.lastRead()); + file->setVersion(fileReader.getFVer()); + file->setPath(info.absoluteFilePath()); + file->setMasters(masters); + file->setDescription(decoder->toUnicode(fileReader.getDesc().c_str())); + + + // Put the file in the table + if (findItem(path) == 0) + addFile(file); + + } catch(std::runtime_error &e) { + // An error occurred while reading the .esp + qWarning() << "Error reading addon file: " << e.what(); + continue; + } + + } + + delete decoder; +} + +EsxModel::EsmFile* EsxModel::ContentModel::findItem(const QString &name) +{ + for (int i = 0; i < mFiles.size(); ++i) + { + if (name == item(i)->fileName()) + return item(i); + } + + // Not found + return 0; +} + +EsxModel::EsmFile* EsxModel::ContentModel::item(int row) const +{ + if (row >= 0 && row < mFiles.count()) + return mFiles.at(row); + + return 0; +} + +QModelIndex EsxModel::ContentModel::indexFromItem(EsmFile *item) const +{ + if (item) + //return createIndex(mFiles.indexOf(item), 0); + return index(mFiles.indexOf(item),0); + + return QModelIndex(); +} + +Qt::CheckState EsxModel::ContentModel::checkState(const QModelIndex &index) +{ + return mCheckStates[item(index.row())->fileName()]; +} + +void EsxModel::ContentModel::setCheckState(const QModelIndex &index, Qt::CheckState state) +{ + if (!index.isValid()) + return; + + QString name = item(index.row())->fileName(); + mCheckStates[name] = state; + + // Force a redraw of the view since unchecking one item can affect another + QModelIndex firstIndex = indexFromItem(mFiles.first()); + QModelIndex lastIndex = indexFromItem(mFiles.last()); + + emit dataChanged(firstIndex, lastIndex); + //emit checkedItemsChanged(checkedItems()); + +} + +bool EsxModel::ContentModel::canBeChecked(const EsmFile *file) const +{ + //element can be checked if all its dependencies are + foreach (const QString &master, file->masters()) + { + if (!mCheckStates.contains(master) || mCheckStates[master] != Qt::Checked) + return false; + } + return true; +} + +EsxModel::ContentFileList EsxModel::ContentModel::checkedItems() const +{ + ContentFileList list; + + for (int i = 0; i < mFiles.size(); ++i) + { + EsmFile *file = item(i); + + // Only add the items that are in the checked list and available + if (mCheckStates[file->fileName()] == Qt::Checked && canBeChecked(file)) + list << file; + } + + return list; +} + +void EsxModel::ContentModel::uncheckAll() +{ + emit layoutAboutToBeChanged(); + mCheckStates.clear(); + emit layoutChanged(); +} diff --git a/components/esxselector/model/contentmodel.hpp b/components/esxselector/model/contentmodel.hpp new file mode 100644 index 000000000..a585ab63b --- /dev/null +++ b/components/esxselector/model/contentmodel.hpp @@ -0,0 +1,64 @@ +#ifndef CONTENTMODEL_HPP +#define CONTENTMODEL_HPP + +#include + +namespace EsxModel +{ + class EsmFile; + + typedef QList ContentFileList; + + class ContentModel : public QAbstractTableModel + { + Q_OBJECT + public: + explicit ContentModel(QObject *parent = 0); + + void setEncoding(const QString &encoding); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + QVariant data(const QModelIndex &index, int role) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()); + bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()); + + Qt::DropActions supportedDropActions() const; + QStringList mimeTypes() const; + QMimeData *mimeData(const QModelIndexList &indexes) const; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + + void addFiles(const QString &path); + + QModelIndex indexFromItem(EsmFile *item) const; + EsxModel::EsmFile *findItem(const QString &name); + + Qt::CheckState checkState(const QModelIndex &index); + void setCheckState(const QModelIndex &index, Qt::CheckState state); + ContentFileList checkedItems() const; + void uncheckAll(); +/* + QModelIndex index(int row, int column, const QModelIndex &parent) const; + QModelIndex parent(const QModelIndex &child) const; +*/ + private: + + void addFile(EsmFile *file); + EsmFile* item(int row) const; + bool canBeChecked(const EsmFile *file) const; + + ContentFileList mFiles; + QHash mCheckStates; + QString mEncoding; + + signals: + + public slots: + + }; +} +#endif // CONTENTMODEL_HPP diff --git a/components/esxselector/model/datafilesmodel.cpp b/components/esxselector/model/datafilesmodel.cpp index 49d0d6132..ee940bb27 100644 --- a/components/esxselector/model/datafilesmodel.cpp +++ b/components/esxselector/model/datafilesmodel.cpp @@ -48,13 +48,12 @@ void EsxModel::DataFilesModel::setCheckState(const QModelIndex &index, Qt::Check Qt::CheckState EsxModel::DataFilesModel::checkState(const QModelIndex &index) { - EsmFile *file = item(index.row()); - return mCheckStates[file->fileName()]; + return mCheckStates[item(index.row())->fileName()]; } int EsxModel::DataFilesModel::columnCount(const QModelIndex &parent) const { - return parent.isValid() ? 0 : 9; + return parent.isValid() ? 0 : 1; } int EsxModel::DataFilesModel::rowCount(const QModelIndex &parent) const @@ -82,7 +81,7 @@ QVariant EsxModel::DataFilesModel::data(const QModelIndex &index, int role) cons if (!index.isValid()) return QVariant(); - EsmFile *file = item(index.row()); + const EsmFile *file = item(index.row()); if (!file) return QVariant(); @@ -90,6 +89,7 @@ QVariant EsxModel::DataFilesModel::data(const QModelIndex &index, int role) cons const int column = index.column(); switch (role) { + case Qt::EditRole: case Qt::DisplayRole: { switch (column) { @@ -172,25 +172,26 @@ Qt::ItemFlags EsxModel::DataFilesModel::flags(const QModelIndex &index) const if (!index.isValid()) return Qt::NoItemFlags; - EsmFile *file = item(index.row()); + const EsmFile *file = item(index.row()); + + Qt::ItemFlags dragDropFlags = Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + Qt::ItemFlags checkFlags = Qt::ItemIsUserCheckable | Qt::ItemIsSelectable; if (!file) return Qt::NoItemFlags; - if (canBeChecked(file)) { - if (index.column() == 0) { - return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; - } else { - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; - } - } else { - if (index.column() == 0) { - return Qt::ItemIsUserCheckable | Qt::ItemIsSelectable; - } else { - return Qt::ItemIsSelectable; - } + if (canBeChecked(file)) + { + if (index.column() == 0) + return dragDropFlags | checkFlags | Qt::ItemIsEnabled; + else + return Qt::ItemIsDropEnabled | Qt::ItemIsEnabled | Qt::ItemIsSelectable; } + if (index.column() == 0) + return dragDropFlags | checkFlags; + + return Qt::ItemIsDropEnabled | Qt::ItemIsSelectable; } QVariant EsxModel::DataFilesModel::headerData(int section, Qt::Orientation orientation, int role) const @@ -210,22 +211,27 @@ QVariant EsxModel::DataFilesModel::headerData(int section, Qt::Orientation orien case 7: return tr("Masters"); case 8: return tr("Description"); } - } /* else { - // Show row numbers - return ++section; } -*/ return QVariant(); } bool EsxModel::DataFilesModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) - return false; + return false; + + if (role == Qt::EditRole) + { + qDebug() << "replacing: " << mFiles.at(index.row())->fileName(); +// mFiles.replace(index.row(), value.value()); + qDebug() << "with: " << mFiles.at(index.row())->fileName(); + emit dataChanged(index, index); + return true; + } return false; } - +//!!!!!!!!!!!!!!!!!!!!!!! bool lessThanEsmFile(const EsxModel::EsmFile *e1, const EsxModel::EsmFile *e2) { //Masters first then alphabetically @@ -236,16 +242,15 @@ bool lessThanEsmFile(const EsxModel::EsmFile *e1, const EsxModel::EsmFile *e2) return e1->fileName().toLower() < e2->fileName().toLower(); } - +//!!!!!!!!!!!!!!!!!!!!!!! bool lessThanDate(const EsxModel::EsmFile *e1, const EsxModel::EsmFile *e2) { - if (e1->modified().toString(Qt::ISODate) < e2->modified().toString(Qt::ISODate)) { + if (e1->modified().toString(Qt::ISODate) < e2->modified().toString(Qt::ISODate)) return true; - } else { + else return false; - } } - +//!!!!!!!!!!!!!!!!!!!!!!! void EsxModel::DataFilesModel::sort(int column, Qt::SortOrder order) { emit layoutAboutToBeChanged(); @@ -259,7 +264,7 @@ void EsxModel::DataFilesModel::sort(int column, Qt::SortOrder order) emit layoutChanged(); } -void EsxModel::DataFilesModel::addFile(EsmFile *file) +void EsxModel::DataFilesModel::addFile(const EsmFile *file) { emit beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); mFiles.append(file); @@ -331,22 +336,23 @@ void EsxModel::DataFilesModel::addFiles(const QString &path) delete decoder; } -QModelIndex EsxModel::DataFilesModel::indexFromItem(EsmFile *item) const +QModelIndex EsxModel::DataFilesModel::indexFromItem(const EsmFile *item) const { if (item) - return createIndex(mFiles.indexOf(item), 0); + //return createIndex(mFiles.indexOf(item), 0); + return index(mFiles.indexOf(item),0); return QModelIndex(); } -EsxModel::EsmFile* EsxModel::DataFilesModel::findItem(const QString &name) +const EsxModel::EsmFile* EsxModel::DataFilesModel::findItem(const QString &name) { - QList::ConstIterator it; - QList::ConstIterator itEnd = mFiles.constEnd(); + EsmFileList::ConstIterator it; + EsmFileList::ConstIterator itEnd = mFiles.constEnd(); int i = 0; for (it = mFiles.constBegin(); it != itEnd; ++it) { - EsmFile *file = item(i); + const EsmFile *file = item(i); ++i; if (name == file->fileName()) @@ -357,12 +363,12 @@ EsxModel::EsmFile* EsxModel::DataFilesModel::findItem(const QString &name) return 0; } -EsxModel::EsmFile* EsxModel::DataFilesModel::item(int row) const +const EsxModel::EsmFile* EsxModel::DataFilesModel::item(int row) const { if (row >= 0 && row < mFiles.count()) return mFiles.at(row); - else - return 0; + + return 0; } EsxModel::EsmFileList EsxModel::DataFilesModel::checkedItems() @@ -372,14 +378,11 @@ EsxModel::EsmFileList EsxModel::DataFilesModel::checkedItems() EsmFileList::ConstIterator it; EsmFileList::ConstIterator itEnd = mFiles.constEnd(); - int i = 0; for (it = mFiles.constBegin(); it != itEnd; ++it) { - EsmFile *file = *it; - // Only add the items that are in the checked list and available - if (mCheckStates[file->fileName()] == Qt::Checked && canBeChecked(file)) - list << file; + if (mCheckStates[(*it)->fileName()] == Qt::Checked && canBeChecked(*it)) + list << (*it); } return list; @@ -389,12 +392,12 @@ QStringList EsxModel::DataFilesModel::checkedItemsPaths() { QStringList list; - QList::ConstIterator it; - QList::ConstIterator itEnd = mFiles.constEnd(); + EsmFileList::ConstIterator it; + EsmFileList::ConstIterator itEnd = mFiles.constEnd(); int i = 0; for (it = mFiles.constBegin(); it != itEnd; ++it) { - EsmFile *file = item(i); + const EsmFile *file = item(i); ++i; if (mCheckStates[file->fileName()] == Qt::Checked && canBeChecked(file)) @@ -403,7 +406,6 @@ QStringList EsxModel::DataFilesModel::checkedItemsPaths() return list; } - void EsxModel::DataFilesModel::uncheckAll() { emit layoutAboutToBeChanged(); @@ -411,18 +413,17 @@ void EsxModel::DataFilesModel::uncheckAll() emit layoutChanged(); } +/* EsxModel::EsmFileList EsxModel::DataFilesModel::uncheckedItems() { EsmFileList list; EsmFileList checked = checkedItems(); EsmFileList::ConstIterator it; - EsmFileList::ConstIterator itEnd = mFiles.constEnd(); - int i = 0; - for (it = mFiles.constBegin(); it != itEnd; ++it) { - EsmFile *file = item(i); - ++i; + for (it = mFiles.constBegin(); it != mFiles.constEnd(); ++it) + { + const EsmFile *file = *it; // Add the items that are not in the checked list if (!checked.contains(file)) @@ -431,8 +432,8 @@ EsxModel::EsmFileList EsxModel::DataFilesModel::uncheckedItems() return list; } - -bool EsxModel::DataFilesModel::canBeChecked(EsmFile *file) const +*/ +bool EsxModel::DataFilesModel::canBeChecked(const EsmFile *file) const { //element can be checked if all its dependencies are foreach (const QString &master, file->masters()) @@ -442,3 +443,110 @@ bool EsxModel::DataFilesModel::canBeChecked(EsmFile *file) const } return true; } + +Qt::DropActions EsxModel::DataFilesModel::supportedDropActions() const +{ + return Qt::CopyAction | Qt::MoveAction; +} + +QStringList EsxModel::DataFilesModel::mimeTypes() const +{ + QStringList types; + types << "application/omwcontent"; + return types; +} + +QMimeData *EsxModel::DataFilesModel::mimeData(const QModelIndexList &indexes) const +{ +// if (indexes.at(0).isValid()) +// return new EsmFile(*item(indexes.at(0).row())); + + return 0; +} + +bool EsxModel::DataFilesModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) +{ + if (action == Qt::IgnoreAction) + return true; + + if (action != Qt::MoveAction) + return false; + + if (!data->hasFormat("application/omwcontent")) + return false; + + int dropRow = row; + + if (dropRow == -1) + { + if (parent.isValid()) + dropRow = parent.row(); + else + dropRow = rowCount(QModelIndex()); + } + + if (parent.isValid()) + qDebug() << "parent: " << parent.data().toString(); + qDebug() << "dragged file: " << (qobject_cast(data))->fileName(); +// qDebug() << "inserting file: " << droppedfile->fileName() << " ahead of " << file->fileName(); + insertRows (dropRow, 1, QModelIndex()); + + + const EsmFile *draggedFile = qobject_cast(data); + + int dragRow = -1; + + for (int i = 0; i < mFiles.size(); ++i) + if (draggedFile->fileName() == mFiles.at(i)->fileName()) + { + dragRow = i; + break; + } + + for (int i = 0; i < mFiles.count(); ++i) + { + qDebug() << "index: " << i << "file: " << item(i)->fileName(); + qDebug() << mFiles.at(i)->fileName(); + } + + qDebug() << "drop row: " << dropRow << "; drag row: " << dragRow; +// const EsmFile *file = qobject_cast(data); + // int index = mFiles.indexOf(file); + //qDebug() << "file name: " << file->fileName() << "; index: " << index; + mFiles.swap(dropRow, dragRow); + //setData(index(startRow, 0), varFile); + emit dataChanged(index(0,0), index(rowCount(),0)); + return true; +} + +bool EsxModel::DataFilesModel::insertRows(int row, int count, const QModelIndex &parent) +{ + qDebug() << "inserting row: " << row << " count: " << count; + beginInsertRows(QModelIndex(),row, row+count-1); + + EsmFile *file = new EsmFile(); + + for (int i = 0; i < count; ++i) + mFiles.insert(row + i, file); + + endInsertRows(); + return true; +} + +bool EsxModel::DataFilesModel::removeRows(int row, int count, const QModelIndex &parent) +{ + qDebug() << "removing row: " << row << " count: " << count; + beginRemoveRows(QModelIndex(), row, row+count-1); + + for (int i = 0; i < count; ++i) + { + mFiles.removeAt(i); + } + + endRemoveRows(); + qDebug() <<"remove success"; + + emit dataChanged(parent, index(rowCount()-1, 0, parent)); + return true; +} + diff --git a/components/esxselector/model/datafilesmodel.hpp b/components/esxselector/model/datafilesmodel.hpp index 24b36aa88..4f23cd530 100644 --- a/components/esxselector/model/datafilesmodel.hpp +++ b/components/esxselector/model/datafilesmodel.hpp @@ -10,7 +10,7 @@ namespace EsxModel { class EsmFile; - typedef QList EsmFileList; + typedef QList EsmFileList; class DataFilesModel : public QAbstractTableModel { @@ -21,6 +21,8 @@ namespace EsxModel virtual ~DataFilesModel(); virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + bool removeRows(int row, int count, const QModelIndex &parent); + bool insertRows(int row, int count, const QModelIndex &parent); bool moveRow(int oldrow, int row, const QModelIndex &parent = QModelIndex()); @@ -33,7 +35,10 @@ namespace EsxModel void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); inline QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const - { return QAbstractTableModel::index(row, column, parent); } + { + QModelIndex idx = QAbstractTableModel::index(row, 0, parent); + return idx; + } void setEncoding(const QString &encoding); @@ -41,6 +46,11 @@ namespace EsxModel void uncheckAll(); + Qt::DropActions supportedDropActions() const; + QStringList mimeTypes() const; + QMimeData *mimeData(const QModelIndexList &indexes) const; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + EsmFileList checkedItems(); EsmFileList uncheckedItems(); QStringList checkedItemsPaths(); @@ -48,16 +58,17 @@ namespace EsxModel Qt::CheckState checkState(const QModelIndex &index); void setCheckState(const QModelIndex &index, Qt::CheckState state); - QModelIndex indexFromItem(EsmFile *item) const; - EsmFile* findItem(const QString &name); - EsmFile* item(int row) const; + QModelIndex indexFromItem(const EsmFile *item) const; + const EsmFile* findItem(const QString &name); + const EsmFile* item(int row) const; signals: void checkedItemsChanged(const EsmFileList &items); private: - bool canBeChecked(EsmFile *file) const; - void addFile(EsmFile *file); + + bool canBeChecked(const EsmFile *file) const; + void addFile(const EsmFile *file); EsmFileList mFiles; QHash mCheckStates; diff --git a/components/esxselector/model/esmfile.cpp b/components/esxselector/model/esmfile.cpp index 96b90e44e..95cf70312 100644 --- a/components/esxselector/model/esmfile.cpp +++ b/components/esxselector/model/esmfile.cpp @@ -1,13 +1,17 @@ #include "esmfile.hpp" EsxModel::EsmFile::EsmFile(QString fileName, ModelItem *parent) - : ModelItem(parent) -{ - mFileName = fileName; - mSize = 0; - mVersion = 0.0f; -} - + : ModelItem(parent), mFileName(fileName), mSize(0), mVersion(0.0f) +{} +/* +EsxModel::EsmFile::EsmFile(const EsmFile &file) + : ModelItem(file.parent()), mFileName(file.mFileName), mSize(file.mSize), + mVersion(file.mVersion), mAuthor(file.mAuthor), mModified(file.mModified), + mAccessed(file.mAccessed), mPath(file.mPath), mMasters(file.mMasters), + mDescription(file.mDescription) +{} + +*/ void EsxModel::EsmFile::setFileName(const QString &fileName) { mFileName = fileName; diff --git a/components/esxselector/model/esmfile.hpp b/components/esxselector/model/esmfile.hpp index 6a3e36b53..0cda018b3 100644 --- a/components/esxselector/model/esmfile.hpp +++ b/components/esxselector/model/esmfile.hpp @@ -14,7 +14,9 @@ namespace EsxModel Q_PROPERTY(QString filename READ fileName) public: + EsmFile(QString fileName = QString(), ModelItem *parent = 0); + // EsmFile(const EsmFile &); ~EsmFile() {} @@ -38,6 +40,7 @@ namespace EsxModel inline QStringList masters() const { return mMasters; } inline QString description() const { return mDescription; } + //inline ModelItem *parent() const { return ModelItem::parent(); this->} private: QString mFileName; @@ -53,4 +56,6 @@ namespace EsxModel }; } +Q_DECLARE_METATYPE (EsxModel::EsmFile *) + #endif diff --git a/components/esxselector/model/masterproxymodel.cpp b/components/esxselector/model/masterproxymodel.cpp index 011e5ebd5..46d68ca51 100644 --- a/components/esxselector/model/masterproxymodel.cpp +++ b/components/esxselector/model/masterproxymodel.cpp @@ -1,4 +1,6 @@ #include "masterproxymodel.hpp" +#include +#include EsxModel::MasterProxyModel::MasterProxyModel(QObject *parent, QAbstractTableModel* model) : QSortFilterProxyModel(parent) @@ -7,10 +9,36 @@ EsxModel::MasterProxyModel::MasterProxyModel(QObject *parent, QAbstractTableMode setFilterRole (Qt::UserRole); if (model) - setSourceModel (model); + setSourceModel (model); + //connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(slotSourceModelChanged(QModelIndex, QModelIndex))); } - +/* QVariant EsxModel::MasterProxyModel::data(const QModelIndex &index, int role) const { - return QSortFilterProxyModel::data (index, role); + if (index.isValid()) + return QSortFilterProxyModel::data (index, role); + + return 0; +} +*/ +void EsxModel::MasterProxyModel::slotSourceModelChanged(QModelIndex topLeft, QModelIndex botRight) +{ + qDebug() << "source data changed.. updating master proxy"; + emit dataChanged(index(0,0), index(rowCount()-1,0)); + + int curRow = -1; +/* + for (int i = 0; i < rowCount() - 1; ++i) + { + if (index(i,0).data(Qt::CheckState) == Qt::Checked) + { + curRow = i; + break; + } + } + + reset(); +*/ + if (curRow != -1); + // index(curRow, 0).setDataQt::CheckState) } diff --git a/components/esxselector/model/masterproxymodel.hpp b/components/esxselector/model/masterproxymodel.hpp index fed01bdb1..8a5c73032 100644 --- a/components/esxselector/model/masterproxymodel.hpp +++ b/components/esxselector/model/masterproxymodel.hpp @@ -2,6 +2,8 @@ #define MASTERPROXYMODEL_HPP #include +#include +#include class QAbstractTableModel; @@ -12,12 +14,12 @@ namespace EsxModel Q_OBJECT public: explicit MasterProxyModel(QObject *parent = 0, QAbstractTableModel *model = 0); - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + // virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; signals: public slots: - + void slotSourceModelChanged(QModelIndex topLeft, QModelIndex botRight); }; } #endif // MASTERPROXYMODEL_HPP diff --git a/components/esxselector/model/modelitem.cpp b/components/esxselector/model/modelitem.cpp index 8c1e83695..03b19f691 100644 --- a/components/esxselector/model/modelitem.cpp +++ b/components/esxselector/model/modelitem.cpp @@ -2,9 +2,14 @@ EsxModel::ModelItem::ModelItem(ModelItem *parent) : mParentItem(parent) - , QObject(parent) { } +/* +EsxModel::ModelItem::ModelItem(const ModelItem *parent) + // : mParentItem(parent) +{ +} +*/ EsxModel::ModelItem::~ModelItem() { @@ -12,11 +17,18 @@ EsxModel::ModelItem::~ModelItem() } -EsxModel::ModelItem *EsxModel::ModelItem::parent() +EsxModel::ModelItem *EsxModel::ModelItem::parent() const { return mParentItem; } +bool EsxModel::ModelItem::hasFormat(const QString &mimetype) const +{ + if (mimetype == "application/omwcontent") + return true; + + return QMimeData::hasFormat(mimetype); +} int EsxModel::ModelItem::row() const { if (mParentItem) diff --git a/components/esxselector/model/modelitem.hpp b/components/esxselector/model/modelitem.hpp index 64596302c..5ee5e417e 100644 --- a/components/esxselector/model/modelitem.hpp +++ b/components/esxselector/model/modelitem.hpp @@ -1,20 +1,22 @@ #ifndef MODELITEM_HPP #define MODELITEM_HPP -#include +#include #include namespace EsxModel { - class ModelItem : public QObject + class ModelItem : public QMimeData { Q_OBJECT public: ModelItem(ModelItem *parent = 0); + //ModelItem(const ModelItem *parent = 0); + ~ModelItem(); - ModelItem *parent(); + ModelItem *parent() const; int row() const; int childCount() const; @@ -24,6 +26,8 @@ namespace EsxModel void appendChild(ModelItem *child); void removeChild(int row); + bool hasFormat(const QString &mimetype) const; + //virtual bool acceptChild(ModelItem *child); protected: diff --git a/components/esxselector/model/pluginsproxymodel.cpp b/components/esxselector/model/pluginsproxymodel.cpp index 4fde11f47..412367b64 100644 --- a/components/esxselector/model/pluginsproxymodel.cpp +++ b/components/esxselector/model/pluginsproxymodel.cpp @@ -1,7 +1,8 @@ #include "pluginsproxymodel.hpp" -#include "datafilesmodel.hpp" +#include "contentmodel.hpp" +#include -EsxModel::PluginsProxyModel::PluginsProxyModel(QObject *parent, DataFilesModel *model) : +EsxModel::PluginsProxyModel::PluginsProxyModel(QObject *parent, ContentModel *model) : QSortFilterProxyModel(parent) { setFilterRegExp (QString("addon")); @@ -22,12 +23,18 @@ QVariant EsxModel::PluginsProxyModel::data(const QModelIndex &index, int role) c { case Qt::CheckStateRole: { - if (index.column() != 0) + if (index.column() != 0) return QVariant(); - return static_cast(sourceModel())->checkState(mapToSource(index)); + return static_cast(sourceModel())->checkState(mapToSource(index)); } - }; + } + return QSortFilterProxyModel::data(index, role); +} + +bool EsxModel::PluginsProxyModel::removeRows(int position, int rows, const QModelIndex &parent) +{ + bool success = QSortFilterProxyModel::removeRows(position, rows, parent); - return QSortFilterProxyModel::data (index, role); + return success; } diff --git a/components/esxselector/model/pluginsproxymodel.hpp b/components/esxselector/model/pluginsproxymodel.hpp index 04c18c2e2..4415df716 100644 --- a/components/esxselector/model/pluginsproxymodel.hpp +++ b/components/esxselector/model/pluginsproxymodel.hpp @@ -8,7 +8,7 @@ class QAbstractTableModel; namespace EsxModel { - class DataFilesModel; + class ContentModel; class PluginsProxyModel : public QSortFilterProxyModel { @@ -16,10 +16,12 @@ namespace EsxModel public: - explicit PluginsProxyModel(QObject *parent = 0, DataFilesModel *model = 0); + explicit PluginsProxyModel(QObject *parent = 0, ContentModel *model = 0); ~PluginsProxyModel(); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + + bool removeRows(int row, int count, const QModelIndex &parent); }; } diff --git a/components/esxselector/model/sourcemodel.cpp b/components/esxselector/model/sourcemodel.cpp new file mode 100644 index 000000000..7b54adba9 --- /dev/null +++ b/components/esxselector/model/sourcemodel.cpp @@ -0,0 +1,6 @@ +#include "sourcemodel.h" + +SourceModel::SourceModel(QObject *parent) : + QAbstractTableClass(parent) +{ +} diff --git a/components/esxselector/model/sourcemodel.h b/components/esxselector/model/sourcemodel.h new file mode 100644 index 000000000..cf5145675 --- /dev/null +++ b/components/esxselector/model/sourcemodel.h @@ -0,0 +1,18 @@ +#ifndef SOURCEMODEL_H +#define SOURCEMODEL_H + +#include + +class SourceModel : public QAbstractTableClass +{ + Q_OBJECT +public: + explicit SourceModel(QObject *parent = 0); + +signals: + +public slots: + +}; + +#endif // SOURCEMODEL_H diff --git a/components/esxselector/view/contentselector.cpp b/components/esxselector/view/contentselector.cpp index 6cba643d2..bc7cc2b8b 100644 --- a/components/esxselector/view/contentselector.cpp +++ b/components/esxselector/view/contentselector.cpp @@ -3,6 +3,8 @@ #include "../model/datafilesmodel.hpp" #include "../model/masterproxymodel.hpp" #include "../model/pluginsproxymodel.hpp" +#include "../model/contentmodel.hpp" +#include "../model/esmfile.hpp" #include @@ -14,7 +16,33 @@ EsxView::ContentSelector::ContentSelector(QWidget *parent) : QDialog(parent) { setupUi(this); - buildModelsAndViews(); + // buildModelsAndViews(); + buildDragDropModelView(); +} +void EsxView::ContentSelector::buildDragDropModelView() +{ + mContentModel = new EsxModel::ContentModel(); + + //mContentModel->addFiles("/home/joel/Projects/OpenMW/Data_Files"); + mMasterProxyModel = new EsxModel::MasterProxyModel(this, mContentModel); + mPluginsProxyModel = new EsxModel::PluginsProxyModel(this, mContentModel); + + tableView->setModel (mPluginsProxyModel); + + masterView->setPlaceholderText(QString("Select a game file...")); + masterView->setModel(mMasterProxyModel); + pluginView->setModel(mPluginsProxyModel); + + profilesComboBox->setPlaceholderText(QString("Select a profile...")); + + updateViews(); + connect(pluginView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotPluginTableItemClicked(const QModelIndex &))); + connect(masterView, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentMasterIndexChanged(int))); + connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); + + + connect(mContentModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); + connect(tableView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotPluginTableItemClicked(const QModelIndex &))); } void EsxView::ContentSelector::buildModelsAndViews() @@ -22,15 +50,15 @@ void EsxView::ContentSelector::buildModelsAndViews() // Models mDataFilesModel = new EsxModel::DataFilesModel (this); - mMasterProxyModel = new EsxModel::MasterProxyModel (this, mDataFilesModel); - mPluginsProxyModel = new EsxModel::PluginsProxyModel (this, mDataFilesModel); + // mMasterProxyModel = new EsxModel::MasterProxyModel (this, mDataFilesModel); + // mPluginsProxyModel = new EsxModel::PluginsProxyModel (this, mDataFilesModel); masterView->setPlaceholderText(QString("Select a game file...")); masterView->setModel(mMasterProxyModel); pluginView->setModel(mPluginsProxyModel); profilesComboBox->setPlaceholderText(QString("Select a profile...")); - + updateViews(); connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); connect(pluginView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotPluginTableItemClicked(const QModelIndex &))); connect(masterView, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentMasterIndexChanged(int))); @@ -39,15 +67,15 @@ void EsxView::ContentSelector::buildModelsAndViews() void EsxView::ContentSelector::addFiles(const QString &path) { - mDataFilesModel->addFiles(path); - mDataFilesModel->sort(3); // Sort by date accessed + mContentModel->addFiles(path); + mContentModel->sort(3); // Sort by date accessed masterView->setCurrentIndex(-1); - mDataFilesModel->uncheckAll(); + mContentModel->uncheckAll(); } void EsxView::ContentSelector::setEncoding(const QString &encoding) { - mDataFilesModel->setEncoding(encoding); + mContentModel->setEncoding(encoding); } void EsxView::ContentSelector::setCheckState(QModelIndex index, QSortFilterProxyModel *model) @@ -62,15 +90,20 @@ void EsxView::ContentSelector::setCheckState(QModelIndex index, QSortFilterProxy if (sourceIndex.isValid()) { - (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) - ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) - : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); + (mContentModel->checkState(sourceIndex) == Qt::Checked) + ? mContentModel->setCheckState(sourceIndex, Qt::Unchecked) + : mContentModel->setCheckState(sourceIndex, Qt::Checked); } } QStringList EsxView::ContentSelector::checkedItemsPaths() { - return mDataFilesModel->checkedItemsPaths(); + QStringList itemPaths; + + foreach( const EsxModel::EsmFile *file, mContentModel->checkedItems()) + itemPaths << file->path(); + + return itemPaths; } void EsxView::ContentSelector::updateViews() @@ -106,5 +139,6 @@ void EsxView::ContentSelector::slotCurrentMasterIndexChanged(int index) void EsxView::ContentSelector::slotPluginTableItemClicked(const QModelIndex &index) { + qDebug() << "setting checkstate in plugin..."; setCheckState(index, mPluginsProxyModel); } diff --git a/components/esxselector/view/contentselector.hpp b/components/esxselector/view/contentselector.hpp index 658e0176c..06cc8f3f0 100644 --- a/components/esxselector/view/contentselector.hpp +++ b/components/esxselector/view/contentselector.hpp @@ -7,6 +7,7 @@ namespace EsxModel { + class ContentModel; class DataFilesModel; class PluginsProxyModel; class MasterProxyModel; @@ -23,6 +24,7 @@ namespace EsxView protected: EsxModel::DataFilesModel *mDataFilesModel; + EsxModel::ContentModel *mContentModel; EsxModel::MasterProxyModel *mMasterProxyModel; EsxModel::PluginsProxyModel *mPluginsProxyModel; @@ -37,6 +39,7 @@ namespace EsxView void setCheckState(QModelIndex index, QSortFilterProxyModel *model); QStringList checkedItemsPaths(); void on_checkAction_triggered(); + void buildDragDropModelView(); signals: void profileChanged(int index); diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 60e8b8bf5..76689627b 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -7,28 +7,19 @@ 0 0 518 - 304 + 310
Qt::DefaultContextMenu - + Content - - - 3 - - - 6 - - - 6 - + @@ -41,7 +32,7 @@ - + @@ -56,6 +47,18 @@ QAbstractItemView::NoEditTriggers + + true + + + false + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + true @@ -82,6 +85,40 @@ + + + + QAbstractItemView::NoEditTriggers + + + true + + + false + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + false + + + false + + + From c961abce963d4d9681861510dad796c0fb65dd7a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 14 Sep 2013 14:00:07 +0200 Subject: [PATCH 024/132] 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 4cc64f2df..5d59492c6 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/132] 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 aa6f6ba76..367c43eb7 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 71761cdae..8f7472c5f 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 4731c58fa..703098852 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 000000000..99b765770 --- /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 3020936f3..1f96c60b4 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 42b5a6b24..bdd65b44a 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 a29d7c8b7..3604b451c 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 868650572..7cd80347d 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 342e2d754..5fddf08d3 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 155f79902..ff9948bf6 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 b42135651..c8c437cbd 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 30a3f01ca..662bdadee 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 a309763a1..00b45cd93 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 056639219..880ddafcd 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 6f4567e57..000000000 --- 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 803861203..1d49028ed 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 652345c6d..693bdaa59 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 054f87169..59edbc41b 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/132] 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 8f7472c5f..83a374eb2 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 703098852..c7d9a038e 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 9c00d4ea7..d5f2071c4 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/132] 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 367c43eb7..8d09eb645 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 1d49028ed..1e8f4def5 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 693bdaa59..79c909724 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 d5f2071c4..000000000 --- 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 59edbc41b..000000000 --- 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/132] 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 83a374eb2..d5c310b8d 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 c7d9a038e..7b8114ecc 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/132] 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 d5c310b8d..7f47e8c70 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 7b8114ecc..2fadbda55 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 1e8f4def5..cd4653280 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 79c909724..0079fab34 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/132] 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 7f47e8c70..8af5a2c0d 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 2fadbda55..316eda78f 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/132] 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 8d09eb645..b6de9295d 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 d7138f671..9b2217559 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 3532721ea..5a0395510 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 000000000..dda4ad12a --- /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 000000000..b89ba5f6d --- /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 000000000..97facf612 --- /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 000000000..1549c9640 --- /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 000000000..379870859 --- /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 000000000..b8b6f3878 --- /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/132] various ESMWriter fixes --- components/esm/esmwriter.cpp | 283 +++++++++++++++++------------------ components/esm/esmwriter.hpp | 181 +++++++++++----------- components/esm/loadtes3.hpp | 2 +- 3 files changed, 236 insertions(+), 230 deletions(-) diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 3ea6bd350..a6aa82665 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); -} + unsigned int ESMWriter::getVersion() const + { + return mHeader.mData.version; + } -void ESMWriter::setRecordCount (int count) -{ - mHeader.mData.records = count; -} + void ESMWriter::setVersion(unsigned int ver) + { + mHeader.mData.version = ver; + } -void ESMWriter::setFormat (int format) -{ - mHeader.mFormat = format; -} + void ESMWriter::setAuthor(const std::string& auth) + { + mHeader.mData.author.assign (auth); + } -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::setDescription(const std::string& desc) + { + mHeader.mData.desc.assign (desc); + } -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::setRecordCount (int count) + { + mHeader.mData.records = count; + } -void ESMWriter::save(std::ostream& file) -{ - m_recordCount = 0; - m_stream = &file; + void ESMWriter::setFormat (int format) + { + mHeader.mFormat = format; + } - startRecord("TES3", 0); + void ESMWriter::addMaster(const std::string& name, uint64_t size) + { + Header::MasterData d; + d.name = name; + d.size = size; + mHeader.mMaster.push_back(d); + } - mHeader.save (*this); + void ESMWriter::save(const std::string& file) + { + std::ofstream fs(file.c_str(), std::ios_base::out | std::ios_base::trunc); + save(fs); + } - endRecord("TES3"); -} + void ESMWriter::save(std::ostream& file) + { + mRecordCount = 0; + mRecords.clear(); + mStream = &file; -void ESMWriter::close() -{ - m_stream->flush(); + startRecord("TES3", 0); - if (!m_records.empty()) - throw "Unclosed record remaining"; -} + mHeader.save (*this); -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); -} + endRecord("TES3"); + } -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::close() + { + if (!mRecords.empty()) + throw std::runtime_error ("Unclosed record remaining"); + } -void ESMWriter::endRecord(const std::string& name) -{ - RecordData rec = m_records.back(); - assert(rec.name == name); - m_records.pop_back(); + 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); + } - m_stream->seekp(rec.position); + 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); + } - count = false; - write((char*)&rec.size, sizeof(int)); - count = true; + void ESMWriter::endRecord(const std::string& name) + { + RecordData rec = mRecords.back(); + assert(rec.name == name); + mRecords.pop_back(); - m_stream->seekp(0, std::ios::end); + mStream->seekp(rec.position); -} + mCounting = false; + write (reinterpret_cast (&rec.size), sizeof(int)); + mCounting = true; -void ESMWriter::writeHNString(const std::string& name, const std::string& data) -{ - startSubRecord(name); - writeHString(data); - endRecord(name); -} + mStream->seekp(0, std::ios::end); -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) + void ESMWriter::writeHNString(const std::string& name, const std::string& data) { - for (size_t i = data.size(); i < size; ++i) - write("\0",1); + startSubRecord(name); + writeHString(data); + endRecord(name); } - endRecord(name); -} - -void ESMWriter::writeHString(const std::string& data) -{ - if (data.size() == 0) - write("\0", 1); - else + void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) { - // Convert to UTF8 and return - std::string ascii = m_encoder->getLegacyEnc(data); + assert(data.size() <= size); + startSubRecord(name); + writeHString(data); + + if (data.size() < size) + { + for (size_t i = data.size(); i < size; ++i) + write("\0",1); + } - write(ascii.c_str(), ascii.size()); + endRecord(name); } -} -void ESMWriter::writeHCString(const std::string& data) -{ - writeHString(data); - if (data.size() > 0 && data[data.size()-1] != '\0') - write("\0", 1); -} + 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::writeName(const std::string& name) -{ - assert((name.size() == 4 && name[3] != '\0')); - write(name.c_str(), name.size()); -} + void ESMWriter::writeHCString(const std::string& data) + { + writeHString(data); + if (data.size() > 0 && data[data.size()-1] != '\0') + write("\0", 1); + } -void ESMWriter::write(const char* data, size_t size) -{ - if (count && !m_records.empty()) + void ESMWriter::writeName(const std::string& name) { - for (std::list::iterator it = m_records.begin(); it != m_records.end(); ++it) - it->size += size; + assert((name.size() == 4 && name[3] != '\0')); + write(name.c_str(), name.size()); } - m_stream->write(data, 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; + } -void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) -{ - m_encoder = encoder; -} + 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 be3ae33ab..b0925463a 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 b73a4c31e..5614d295f 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/132] 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 dda4ad12a..5607883ad 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 97facf612..078762992 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 1549c9640..0b64896f7 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 379870859..a49a0699b 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 b8b6f3878..3f42b4653 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/132] 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 078762992..e7c9799ec 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/132] 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 a6aa82665..95ad44811 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/132] 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 5607883ad..67073ca43 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 e7c9799ec..797b32eae 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 0b64896f7..914a2d585 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/132] 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 1440dbd13..cf4951de7 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 38499b2dd..b06cb529a 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 95cf24d33..e91059b26 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 31889914c..47cb0b99e 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 88f87d6e2..bc126846b 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 9f5b87aed..04adcc5cd 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 7d4851a5f..96cc19d43 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 0fd564361..a44d1b198 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 fd022af7e..dcae845d0 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 a62990590..6b072ee11 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 dbb69c066..187069c2e 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 3ede85342..8f0435292 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 4b8d2b763..01233a055 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 ed9d335be..d47643c6c 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 e64c8705d..4dbdf1314 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 eaef42be8..5a38605e3 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 e95a8a860..a5d986f65 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 3ad9b1b95..a8fd36aef 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 3a70ac786..d9db11889 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 68042e246..688e9dd75 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 cb500f674..9d19f02c7 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 434ddf68e..1ecb5e418 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 d8d0c1291..57d3278d8 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 51288b291..c417fceab 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 bdc461462..ef07430c7 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 4f85e6ee8..f241dca8d 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 10b00970f..c623155df 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 816d03cb2..13fae865b 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 853c8bd50..0cbb4acd1 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 b2bbab73d..c854b5290 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 86d05b8a5..30b70b35b 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 279e2ea3f..80e0fbd1c 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 6904df15a..2b840ccf4 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 fb50d5e9f..e014ca37e 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 61f3f763d..0fe5027dc 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 a4c7b7d58..f666ac67a 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 77ffc6489..2b927c56e 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 c4e278368..4b4c3a1ec 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 999f93ad9..3cdc3a0bd 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 e2712d462..c8be51802 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 891b99647..11f65a87f 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 ccb519acd..e1c2d4408 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 72e16c0ce..06ff97ef2 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 fe1cc1b04..3a7df4506 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 a6e0c2ecb..9c37c7da0 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 90f8fcf35..1985da2cd 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 2361ed9eb..351768e96 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 7e31a4116..1bc9ae41c 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 5e286535f..03e67924c 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 60c475040..8e54bcc5c 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 9c1fd1f5c..3d3bcd67b 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 b54a91276..ab3f5e9e6 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 7339cac56..f5fb7fd5b 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 89a2b8c65..3f279c7c8 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 3f0b76d6e..9a341f0de 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 03eac52bd..318769ec0 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 953066cb2..aea5a4f31 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 e523e9fa7..dc1bc164b 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 6e6d987d4..3d0816948 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 060645b5f..9eaeff704 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 009bc5ef3..009548c59 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 79d92397f..f023fd217 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 882addcb9..65d1f8055 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 c3f50fc4d..d14433a78 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 729f8404e..0fb4c9750 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 55b896bcd..d0a8256ab 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 955424e2b..e9e1d0d79 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 6ecec8ebb..a53a98070 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 41c7f507a..fd42b9ee8 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 f2a3d9a10..a6075d65a 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 ced6daa2e..59bfa0169 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 83812bad9..771e7ead0 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 2c1b018d9..8afb85602 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 be7e83900..450224faa 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 676a835c3..f6a2c4950 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 384f87454..2436173cb 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 42d524226..9b992c960 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 a6226c154..2756676ef 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 07af2b5e9..0f6b0f84a 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 f8e38ac09..6c9bb1fed 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 8149fe4ce..5c0bd956f 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 3a620962d..b34bd29f1 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 ae50de517..f51b7be47 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 713fe96b5..2326f00f4 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 c9346dafc..38206422b 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 1adb7d05b..df42c0c49 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 253712396..e21d8924a 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 b62179ccb..42810d3af 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 dd886cf7f..24d3c3d0a 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 52999270a..934bdda7a 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/132] 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 9b2217559..525f18a20 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 67073ca43..e0180bee4 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 914a2d585..9787679c6 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 b0925463a..fc64c4a13 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/132] 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 9787679c6..96b1fe17f 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/132] 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 e0180bee4..7e0b10d66 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/132] Implemneting drag and drop --- apps/launcher/datafilespage.cpp | 32 +- apps/launcher/datafilespage.hpp | 1 + components/CMakeLists.txt | 2 +- components/esxselector/model/contentmodel.cpp | 327 +++++++----- components/esxselector/model/contentmodel.hpp | 9 +- .../esxselector/model/datafilesmodel.cpp | 482 ++++++++++-------- 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 | 146 +++--- .../esxselector/view/contentselector.hpp | 13 +- 12 files changed, 650 insertions(+), 511 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 070f455e4..7069737ef 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 f3792b1f1..ed92da749 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 0f7c5017b..b79fa027e 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 673665775..bfbb1bef7 100644 --- a/components/esxselector/model/contentmodel.cpp +++ b/components/esxselector/model/contentmodel.cpp @@ -21,26 +21,60 @@ int EsxModel::ContentModel::columnCount(const QModelIndex &parent) const return 1; } -/* -QModelIndex EsxModel::ContentModel::parent(const QModelIndex &child) const + +int EsxModel::ContentModel::rowCount(const QModelIndex &parent) const { - if(!child.isValid()) + if(parent.isValid()) return 0; - return child.parent(); + return mFiles.size(); } -QModelIndex EsxModel::ContentModel::index(int row, int column, const QModelIndex &parent) const +EsxModel::EsmFile* EsxModel::ContentModel::item(int row) const { + if (row >= 0 && row < mFiles.count()) + return mFiles.at(row); + return 0; } -*/ -int EsxModel::ContentModel::rowCount(const QModelIndex &parent) const + +EsxModel::EsmFile* EsxModel::ContentModel::findItem(const QString &name) { - if(parent.isValid()) - return 0; + for (int i = 0; i < mFiles.size(); ++i) + { + if (name == item(i)->fileName()) + return item(i); + } - return mFiles.size(); + 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 @@ -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; } + return QVariant(); } -Qt::ItemFlags EsxModel::ContentModel::flags(const QModelIndex &index) const +bool EsxModel::ContentModel::setData(const QModelIndex &index, const QVariant &value, int role) { - if (!index.isValid()) - return Qt::NoItemFlags; + if(!index.isValid()) + return false; EsmFile *file = item(index.row()); + QString fileName = file->fileName(); - if (!file) - return Qt::NoItemFlags; + switch(role) + { + case Qt::EditRole: + { + QStringList list = value.toStringList(); - Qt::ItemFlags dragDropFlags = Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; - Qt::ItemFlags checkFlags = Qt::ItemIsUserCheckable; - Qt::ItemFlags defaultFlags = Qt::ItemIsDropEnabled | Qt::ItemIsSelectable; + //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)); - if (canBeChecked(file)) - return Qt::ItemIsEnabled | dragDropFlags | checkFlags | defaultFlags; - else - return defaultFlags; -} + //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)); -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; + //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) -{ - 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 +bool EsxModel::ContentModel::isChecked(const QString& name) const { - if (item) - //return createIndex(mFiles.indexOf(item), 0); - return index(mFiles.indexOf(item),0); - - return QModelIndex(); + return (mCheckStates[name] == Qt::Checked); } -Qt::CheckState EsxModel::ContentModel::checkState(const QModelIndex &index) +void EsxModel::ContentModel::setCheckState(const QString &name, bool isChecked) { - 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(); - 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()); + Qt::CheckState state = Qt::Unchecked; - emit dataChanged(firstIndex, lastIndex); - //emit checkedItemsChanged(checkedItems()); + if (isChecked) + state = Qt::Checked; -} - -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; + mCheckStates[name] = state; } EsxModel::ContentFileList EsxModel::ContentModel::checkedItems() const diff --git a/components/esxselector/model/contentmodel.hpp b/components/esxselector/model/contentmodel.hpp index a585ab63b..61c823ab3 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 ee940bb27..c98f70b16 100644 --- a/components/esxselector/model/datafilesmodel.cpp +++ b/components/esxselector/model/datafilesmodel.cpp @@ -24,56 +24,55 @@ EsxModel::DataFilesModel::~DataFilesModel() { } -void EsxModel::DataFilesModel::setEncoding(const QString &encoding) +int EsxModel::DataFilesModel::rowCount(const QModelIndex &parent) const { - mEncoding = encoding; + return parent.isValid() ? 0 : mFiles.count(); } -void EsxModel::DataFilesModel::setCheckState(const QModelIndex &index, Qt::CheckState state) +int EsxModel::DataFilesModel::columnCount(const QModelIndex &parent) const { - if (!index.isValid()) - return; - - QString name = item(index.row())->fileName(); - mCheckStates[name] = state; + return parent.isValid() ? 0 : 1; +} - // Force a redraw of the view since unchecking one item can affect another - QModelIndex firstIndex = indexFromItem(mFiles.first()); - QModelIndex lastIndex = indexFromItem(mFiles.last()); +const EsxModel::EsmFile* EsxModel::DataFilesModel::findItem(const QString &name) +{ + for (int i = 0; i < mFiles.size(); ++i) + { + const EsmFile *file = item(i); - emit dataChanged(firstIndex, lastIndex); - emit checkedItemsChanged(checkedItems()); + if (name == file->fileName()) + return file; + } + return 0; } -Qt::CheckState EsxModel::DataFilesModel::checkState(const QModelIndex &index) +const EsxModel::EsmFile* EsxModel::DataFilesModel::item(int row) const { - return mCheckStates[item(index.row())->fileName()]; -} + if (row >= 0 && row < mFiles.count()) + return mFiles.at(row); -int EsxModel::DataFilesModel::columnCount(const QModelIndex &parent) const -{ - return parent.isValid() ? 0 : 1; + return 0; } -int EsxModel::DataFilesModel::rowCount(const QModelIndex &parent) const +Qt::ItemFlags EsxModel::DataFilesModel::flags(const QModelIndex &index) const { - return parent.isValid() ? 0 : mFiles.count(); -} + if (!index.isValid()) + return Qt::NoItemFlags; + const EsmFile *file = item(index.row()); -bool EsxModel::DataFilesModel::moveRow(int oldrow, int row, const QModelIndex &parent) -{ - if (oldrow < 0 || row < 0 || oldrow == row) - return false; + if (!file) + return Qt::NoItemFlags; - emit layoutAboutToBeChanged(); - //emit beginMoveRows(parent, oldrow, oldrow, parent, row); - mFiles.swap(oldrow, row); - //emit endInsertRows(); - emit layoutChanged(); + Qt::ItemFlags dragDropFlags = Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + Qt::ItemFlags checkFlags = Qt::ItemIsUserCheckable | Qt::ItemIsEditable; + Qt::ItemFlags defaultFlags = Qt::ItemIsDropEnabled | Qt::ItemIsSelectable; - return true; + if (canBeChecked(file)) + return defaultFlags | dragDropFlags | checkFlags | Qt::ItemIsEnabled; + else + return defaultFlags; } QVariant EsxModel::DataFilesModel::data(const QModelIndex &index, int role) const @@ -81,6 +80,9 @@ QVariant EsxModel::DataFilesModel::data(const QModelIndex &index, int role) cons if (!index.isValid()) return QVariant(); + if (index.row() >= mFiles.size()) + return QVariant(); + const EsmFile *file = item(index.row()); if (!file) @@ -88,46 +90,46 @@ QVariant EsxModel::DataFilesModel::data(const QModelIndex &index, int role) cons const int column = index.column(); - switch (role) { + switch (role) + { case Qt::EditRole: - case Qt::DisplayRole: { + case Qt::DisplayRole: + { - switch (column) { + 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: + 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(); } + break; } - case Qt::TextAlignmentRole: { - switch (column) { + 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; + return Qt::AlignRight + Qt::AlignVCenter; default: return Qt::AlignLeft + Qt::AlignVCenter; } + break; } case Qt::ToolTipRole: @@ -150,7 +152,7 @@ QVariant EsxModel::DataFilesModel::data(const QModelIndex &index, int role) cons return tooltip; - + break; } case Qt::UserRole: @@ -159,39 +161,242 @@ QVariant EsxModel::DataFilesModel::data(const QModelIndex &index, int role) cons return "game"; else return "addon"; + + break; } + case Qt::UserRole + 1: + //return check state here + break; + default: return QVariant(); + break; } } -Qt::ItemFlags EsxModel::DataFilesModel::flags(const QModelIndex &index) const +bool EsxModel::DataFilesModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid()) - return Qt::NoItemFlags; + return false; - const EsmFile *file = item(index.row()); + switch (role) + { + case Qt::EditRole: + { + const EsmFile *file = item(index.row()); - Qt::ItemFlags dragDropFlags = Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; - Qt::ItemFlags checkFlags = Qt::ItemIsUserCheckable | Qt::ItemIsSelectable; + // 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)); + } - if (!file) - return Qt::NoItemFlags; + //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); - if (canBeChecked(file)) + 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.column() == 0) - return dragDropFlags | checkFlags | Qt::ItemIsEnabled; + 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 - return Qt::ItemIsDropEnabled | Qt::ItemIsEnabled | Qt::ItemIsSelectable; + dropRow = rowCount(QModelIndex()); } - if (index.column() == 0) - return dragDropFlags | checkFlags; + 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; - return Qt::ItemIsDropEnabled | Qt::ItemIsSelectable; + 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 @@ -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 95cf70312..0e7f18373 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 0cda018b3..9a1ea8af1 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 46d68ca51..df74d0356 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 412367b64..c543672b0 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 bc7cc2b8b..1f5e36d65 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); - 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 &))); + masterView->setCurrentIndex(-1); + masterView->setCurrentIndex(0); } -void EsxView::ContentSelector::buildModelsAndViews() +void EsxView::ContentSelector::buildPluginsView() { - // Models - mDataFilesModel = new EsxModel::DataFilesModel (this); - - // mMasterProxyModel = new EsxModel::MasterProxyModel (this, mDataFilesModel); - // mPluginsProxyModel = new EsxModel::PluginsProxyModel (this, mDataFilesModel); + mPluginsProxyModel = new QSortFilterProxyModel(this); + mPluginsProxyModel->setFilterRegExp (QString("addon")); + mPluginsProxyModel->setFilterRole (Qt::UserRole); + mPluginsProxyModel->setDynamicSortFilter (true); + mPluginsProxyModel->setSourceModel (mContentModel); - masterView->setPlaceholderText(QString("Select a game file...")); - masterView->setModel(mMasterProxyModel); + tableView->setModel (mPluginsProxyModel); 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(tableView, 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); @@ -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; + + QAbstractItemModel *const model = masterView->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; - // Not a signal-slot call - if (!object) - return; + model->setData(model->index(index, 0), true, Qt::UserRole + 1); - setCheckState(mMasterProxyModel->index(index, 0), mMasterProxyModel); + 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 06cc8f3f0..e074fe688 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/132] Finished implementing drag / drop refactored datafilesmodel (now contentmodel) refactored esmfil --- components/esxselector/model/contentmodel.cpp | 296 ++++++++---------- components/esxselector/model/contentmodel.hpp | 19 +- components/esxselector/model/esmfile.cpp | 67 +++- components/esxselector/model/esmfile.hpp | 52 +-- .../esxselector/view/contentselector.cpp | 6 +- files/ui/datafilespage.ui | 39 +-- 6 files changed, 222 insertions(+), 257 deletions(-) diff --git a/components/esxselector/model/contentmodel.cpp b/components/esxselector/model/contentmodel.cpp index bfbb1bef7..d23ea7801 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.count()) + if (row >= 0 && row < mFiles.size()) return mFiles.at(row); return 0; } -EsxModel::EsmFile* EsxModel::ContentModel::findItem(const QString &name) +EsxModel::EsmFile *EsxModel::ContentModel::item(int row) { - for (int i = 0; i < mFiles.size(); ++i) + if (row >= 0 && row < mFiles.count()) + return mFiles.at(row); + + return 0; +} +const EsxModel::EsmFile *EsxModel::ContentModel::findItem(const QString &name) const +{ + 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()); + { + setCheckState(fileName, value.toBool()); - emit dataChanged(index, index); + emit dataChanged(index, index); - for(int i = 0; i < mFiles.size(); i++) + foreach (EsmFile *file, mFiles) + { + 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")) + if (column > 0) return false; - if (column > 0) + if (!data->hasFormat(mMimeType)) return false; - int beginRow; + 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()) { + + QString value; QStringList values; + QStringList masters; - for (int i = 0; i < EsmFile::sPropertyCount; ++i) - stream >> values; + 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; - - 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 = mCodec->makeDecoder(); - 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(); - - QStringList masters; + foreach (const ESM::Header::MasterData &item, fileReader.getMasters()) + file->addMaster(QString::fromStdString(item.name)); - 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 61c823ab3..6a2dd88ca 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; - - signals: + QTextCodec *mCodec; - 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 0e7f18373..bd76dc4a0 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 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 Property_FileName: + 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 9a1ea8af1..4f6d7a624 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 bool isMaster() const { return (mMasters.size() == 0); } + inline void addMaster (const QString &name) {mMasters.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) + .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 1f5e36d65..4f8ac7253 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 76689627b..5b0a6d229 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/132] 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 a60e9f0e2..6ccf9c3f3 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 7069737ef..1526b705a 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(); - - 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); - } + // mContentSelector. + mContentModel->uncheckAll(); - 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(); + + foreach(const ContentSelectorModel::EsmFile *item, items) { - if (item->masters().size() == 0) { + 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 ed92da749..9c7b0538e 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 e71fc429f..4d9ce14d6 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 052fc58e4..51928c09a 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 2fb6e0f6b..de3a9fb72 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 9a6832ec0..ba1dfb57e 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 ef7123c20..e5e7514ce 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 fb031fe5f..1d6bed7a7 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 c749099d4..d0c3461b9 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 7703f2d23..963316070 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 34ef43ab5..7053bb973 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 d23ea7801..b85da25c6 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; + + if (setState) + { + setCheckState(fileName, success); + emit dataChanged(index, index); + + } + else + return success; - emit dataChanged(index, index); + + 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 6a2dd88ca..a8ff103da 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 bd76dc4a0..9dfe49eba 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 4f6d7a624..743b1c5a6 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 000000000..e1d737c2d --- /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 5ee5e417e..57214b09c 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 815a1130b..df647a4a0 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 f3b251955..1aef2f57b 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 000000000..615e9a846 --- /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 e074fe688..8032b0449 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 48be2f022..b6fdfa805 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 4e0cbe339..ab1c37203 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 0d709aa50..cb0ba7b77 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 28740783b..d81c1e6a5 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 edc724cd2..3bf194c4e 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 d8d0c1291..9793391ed 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 c98f70b16..000000000 --- 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 4f23cd530..000000000 --- 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 df74d0356..000000000 --- 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 8a5c73032..000000000 --- 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 03b19f691..000000000 --- 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 c543672b0..000000000 --- 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 4415df716..000000000 --- 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 7b54adba9..000000000 --- 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 cf5145675..000000000 --- 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 4f8ac7253..000000000 --- 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 5b0a6d229..82d00922b 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/132] 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 1526b705a..44392794b 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 910819700..2784bca8c 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 f578dc4ae..d970cffee 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 1d6bed7a7..9dce090a1 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 d0c3461b9..7782dd94e 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 c8f33e92d..9cd2fad42 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 c51c29632..ff09d71a3 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 615e9a846..e6ed0ec56 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 8032b0449..5af53dc46 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 82d00922b..949407759 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/132] 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 ba1dfb57e..104afa03b 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 9dce090a1..4d7c5bc15 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 7782dd94e..0c914b932 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 aa97682ff..9ad7ea169 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/132] 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 104afa03b..800f3984e 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 248ebf2c5..abf9496e4 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 4d7c5bc15..4426d2e49 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 0c914b932..37119ff0d 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/132] 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 525f18a20..37294bcd1 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 5a0395510..b1c6a0273 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 797b32eae..fd6234594 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/132] 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 d970cffee..461cfb345 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 9dce090a1..68aab27d5 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 b85da25c6..c1394ef47 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 a8ff103da..a2a57f850 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/132] 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 c1394ef47..db6431810 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/132] 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 7e0b10d66..c9035cfd4 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 96b1fe17f..d5c4a69af 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 dcae845d0..6ba0df0b3 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 6b072ee11..88f27de27 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 187069c2e..f6bfc6a11 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 8f0435292..141765aa8 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 01233a055..29ea78acc 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 d47643c6c..adc8e071f 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 4dbdf1314..ec8ff4f20 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 5a38605e3..991f4e185 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 a5d986f65..4015e6c91 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 a8fd36aef..9623caa31 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 d9db11889..c8b7e9478 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 688e9dd75..f96fbd709 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 9d19f02c7..55e1e7f65 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 1ecb5e418..9f9435c8f 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 9b3aa09e8..c22c1b22b 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 c417fceab..61d586b9d 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 ef07430c7..33489eec4 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 f241dca8d..3e489bb58 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 c623155df..d64564d77 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 13fae865b..50896622a 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 0cbb4acd1..7bdf9f05b 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 c854b5290..2808b67b5 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 30b70b35b..650de0801 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 80e0fbd1c..99c4f5225 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 2b840ccf4..280739aca 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 e014ca37e..f64ecb5a0 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 0fe5027dc..3997d7753 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 f666ac67a..c56b06337 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 2b927c56e..ee2b7f7ac 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 4b4c3a1ec..a1e885f23 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 3cdc3a0bd..f6ba8c6ab 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 c8be51802..61fa90263 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 11f65a87f..9c257e068 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 e1c2d4408..a78ed1a1b 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 06ff97ef2..51b2e2dc9 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 3a7df4506..21d66339a 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 9c37c7da0..6b66ac832 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 1985da2cd..4f248cc65 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 351768e96..2589ea7b8 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 1bc9ae41c..0e0243362 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 03e67924c..85f2d5e7d 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 8e54bcc5c..ede200d79 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 3d3bcd67b..5649f9980 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 ab3f5e9e6..6385b9a71 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 f5fb7fd5b..9dcc6177a 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 3f279c7c8..c02bb46b6 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 9a341f0de..74eb37197 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 318769ec0..9ffce78a7 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 aea5a4f31..c44e2b006 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 dc1bc164b..bd28c8488 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 3d0816948..5e84428b2 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 9eaeff704..332c27786 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 220b3bdf3..613cbd2d8 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 a183821b7..2ca09e8ae 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 452eea53e..576bd18c0 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 7c26cb549..9fff2d885 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 009548c59..d9e691669 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 f023fd217..c87c2545f 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 65d1f8055..3b5330e9f 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 d14433a78..9ee49552d 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 0fb4c9750..caa3d7e0e 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 d0a8256ab..b89b2ddeb 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 e9e1d0d79..e50e43a74 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 a53a98070..7d5736d9b 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 fd42b9ee8..fa4271e26 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 a6075d65a..1992c951b 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 59bfa0169..a7132828d 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 771e7ead0..5b404b0e4 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 8afb85602..30460c17a 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 450224faa..d5200d4c1 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 f6a2c4950..b6724e938 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 2436173cb..1b9db5bcf 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 9b992c960..1a8ca6335 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 2756676ef..5509661c1 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 0f6b0f84a..49c9eb54e 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 6c9bb1fed..04a0984fd 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 5c0bd956f..2c98d796d 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 b34bd29f1..cbf5366c4 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 f51b7be47..69b04bb23 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 2326f00f4..d09ad883e 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 38206422b..a71f22dc2 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 df42c0c49..d912d1058 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 74d578ba7..a86c9b6f4 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 e21d8924a..1d0b149df 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 42810d3af..fde716b91 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/132] 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 c9035cfd4..98af661fd 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 fd6234594..5da92f014 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 d5c4a69af..2d32f38cb 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 cda2711cc..3a6f70d31 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 22f83150d..a479735db 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 9457937f1..8f59b0fe7 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 e221fbc7c..9595ab23b 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/132] 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 5da92f014..ee2943ef4 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 7eb96a5c3..1bd818db4 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 e900bb10f..ebbafe711 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/132] 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 37294bcd1..f496d32ec 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 ee2943ef4..23a88bc17 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 1bd818db4..c6423b76a 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 ebbafe711..eb6325a25 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/132] 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 800f3984e..ead4d2a98 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 abf9496e4..16f6b9516 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 f496d32ec..f9aa7dfc0 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 b1c6a0273..979b47734 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 b079109ea..1978c0e53 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 dfded8d5c..622a135a5 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 98af661fd..adcfca576 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 b89ba5f6d..cd1bbef98 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 23a88bc17..d48e22012 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().setFormat (0); + mState.getWriter().clearMaster(); - 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()); + mState.getWriter().setFormat (0); - 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 2d32f38cb..367431fc1 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 a49a0699b..4a1abb888 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 3f42b4653..8cf7883e5 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 95ad44811..f39aa2b89 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 fc64c4a13..104f97f90 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 a86c9b6f4..87a8d1d57 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/132] 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 160402aa4..1bacdc077 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/132] 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 1a2bf9df1..f50212e56 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 c6423b76a..a5e98fc60 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/132] (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 f9aa7dfc0..68e164c79 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 979b47734..7843cfbfe 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 8af5a2c0d..d29cc2631 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/132] 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 47925ea57..640c9fe78 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 82d38d22c..437d01c8d 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/132] 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 5616a4a48..06649b01b 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 69b20583a..bf1387067 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/132] 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 adcfca576..2b0056e72 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 d48e22012..d68c72317 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 367431fc1..ff94116fd 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 bd86f9ba0..dd7ebfe93 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 96cc19d43..a80427bbe 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 a44d1b198..bc3dd7bdc 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/132] 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 932ea27b5..241f198cb 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 baaf75289..b7b1a9db0 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/132] 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 68e164c79..7f609f9f7 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 a5e98fc60..9227a5965 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 eb6325a25..5d7fbd291 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/132] 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 ead4d2a98..a43059795 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 7f609f9f7..5c29d9f61 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 7843cfbfe..d171dacae 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 1978c0e53..1d6c88dcc 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 622a135a5..28a21216a 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/132] 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 8732b3eab..b8b7e4c9d 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() ) ); - - 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() ) ); + cfg.erase("content"); + cfg.insert( std::make_pair("content", 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 b367e2a1e..807b1b5ff 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 a2eccbaf9..d29301ed3 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(); - - // Append .esm if not already there - std::string::size_type sep = str.find_last_of ("."); - if (sep == std::string::npos) - { - str += ".esm"; - } -} + if (file.find_last_of(".") == std::string::npos) + { + throw std::runtime_error("Missing extension in content file!"); + } -// 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 665b0094c..553d29068 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 27afd734a..33f740b31 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++) - { - engine.addMaster(master[i]); - } - for (std::vector::size_type i = 0; i < plugin.size(); i++) + StringsVector::const_iterator it(content.begin()); + StringsVector::const_iterator end(content.end()); + for (; it != end; ++it) { - 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 000000000..c57935c90 --- /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 000000000..1b8880d37 --- /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 000000000..d799c3f15 --- /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 000000000..8562a4fe0 --- /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 000000000..cb9faa430 --- /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 f3d4c81b7..b7b23e5c1 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); - } - - for (std::vector::size_type i = 0; i < plugins.size(); i++, idx++) - { - boost::filesystem::path pluginPath (fileCollections.getCollection (".esp").getPath (plugins[i])); + gameContentLoader.addLoader(".esm", &esmLoader); + gameContentLoader.addLoader(".esp", &esmLoader); + gameContentLoader.addLoader(".omwgame", &omwLoader); + gameContentLoader.addLoader(".omwaddon", &omwLoader); - std::cout << "Loading ESP " << pluginPath.string() << "\n"; - listener->setLabel(pluginPath.filename().string()); + loadContentFiles(fileCollections, contentFiles, gameContentLoader); - // 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 53b01f1ab..d39189282 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/132] 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 44392794b..43f09d168 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 205879bc3..7b2356cd0 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/132] 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 | 99 +++----- apps/opencs/view/doc/filedialog.hpp | 38 ++-- .../contentselector/view/contentselector.cpp | 213 +++++++++++++++--- .../contentselector/view/contentselector.hpp | 51 ++++- 6 files changed, 283 insertions(+), 142 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 44392794b..828b2f2ba 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 ba1dfb57e..fc9168e2e 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 68aab27d5..5a97a7a26 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() { + return ContentSelectorView::ContentSelector::instance().filename(); +} - if (widgetNumber == 1) - mEnable_1 = enable; - - if (widgetNumber == 2) - mEnable_2 = enable; - - projectCreateButton->setEnabled(mEnable_1 && mEnable_2); +QStringList CSVDoc::FileDialog::selectedFilepaths() +{ + return ContentSelectorView::ContentSelector::instance().selectedFiles(); } -QString CSVDoc::FileDialog::fileName() +void CSVDoc::FileDialog::showDialog() { - return mFileWidget->getName(); + 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())); -void CSVDoc::FileDialog::slotAdjusterChanged(bool value) -{ - emit signalUpdateCreateButton(mAdjusterWidget->isValid(), 2); + showDialog(); } -void CSVDoc::FileDialog::slotGameFileSelected(int value) +void CSVDoc::FileDialog::slotRejected() { - emit signalUpdateCreateButton(value > -1, 1); + close(); } diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index 7782dd94e..88d408b5c 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(); + QStringList selectedFilepaths(); - QString fileName(); + 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 e6ed0ec56..d9caea3be 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) +{ + 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) { - setupUi(this); + 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::buildLoadAddonView() +{ + 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; } -void ContentSelectorView::ContentSelector::updateViews() +QStringList ContentSelectorView::ContentSelector::selectedFiles() const { - // 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(); + 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 5af53dc46..48c3ae103 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/132] Implementing ContentSelector class in DataFilesPage Moved AdjusterWidget / FileWidget to ContentSelectorView --- apps/launcher/datafilespage.cpp | 336 ++++-------------- 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, 314 insertions(+), 357 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 828b2f2ba..b298f8a14 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();*/ -} - -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 (&cSelector, SIGNAL (signalProfileRenamed (QString, QString)), + this, SLOT (slotProfileRenamed (QString, QString))); - connect(addonView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotAddonTableItemClicked(const QModelIndex &))); -} + connect (&cSelector, SIGNAL (signalProfileChanged (QString, QString)), + this, SLOT (slotProfileChanged (QString, QString))); -void DataFilesPage::buildProfilesView() -{ - profilesComboBox->setPlaceholderText(QString("Select a profile...")); - connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentProfileIndexChanged(int))); -} + connect (&cSelector, SIGNAL (signalProfileDeleted (QString)), + this, SLOT (slotProfileDeleted (QString))); -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); -} - -void DataFilesPage::setProfilesComboBoxIndex(int index) -{ - 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); - } + mLauncherSettings.remove(QString("Profiles/") + item + QString("/master")); + mLauncherSettings.remove(QString("Profiles/") + item + QString("/plugin")); } -void DataFilesPage::profileChanged(const QString &previous, const QString ¤t) +void DataFilesPage::slotProfileChanged(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(); + + QStringList paths = mGameSettings.getDataDirs(); - QAbstractItemModel *const model = gameFileView->model(); - QSortFilterProxyModel *proxy = dynamic_cast(model); + foreach (const QString &path, paths) + cSelector.addFiles(path); - if (proxy) - proxy->setDynamicSortFilter(false); + QString dataLocal = mGameSettings.getDataLocal(); - if (oldIndex > -1) - model->setData(model->index(oldIndex, 0), false, Qt::UserRole + 1); + if (!dataLocal.isEmpty()) + cSelector.addFiles(dataLocal); - oldIndex = index; + QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/")); + QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); - model->setData(model->index(index, 0), true, Qt::UserRole + 1); - if (proxy) - proxy->setDynamicSortFilter(true); -} + foreach (const QString &item, profiles) + cSelector.addProfile (item); -void DataFilesPage::slotAddonTableItemClicked(const QModelIndex &index) -{ - QAbstractItemModel *const model = addonView->model(); - //QSortFilterProxyModel *proxy = dynamic_cast(model); + cSelector.addProfile (profile, true); - if (model->data(index, Qt::CheckStateRole).toInt() == Qt::Unchecked) - model->setData(index, Qt::Checked, Qt::CheckStateRole); - else - model->setData(index, Qt::Unchecked, Qt::CheckStateRole); + loadSettings(); } diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index 9c7b0538e..6ed5d9ce9 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 032f70916..311e3a25c 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 00547a2ba..9ae12c7a7 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 fc9168e2e..401f3839f 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 5a97a7a26..b1b72dc1f 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 88d408b5c..acc35189d 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 98681c499..265b98305 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 7053bb973..ebce4578b 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 d9caea3be..cb0774f68 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 48c3ae103..caf9cc670 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 cb0ba7b77..29001189d 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 d81c1e6a5..560c42c10 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 949407759..73d7a4902 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/132] 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 b298f8a14..eee530672 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 cb0774f68..54199626e 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/132] 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 92cabffff..49dedd829 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 eee530672..a705ae37d 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"); + + unsigned char flags; - flags = ContentSelectorView::Flag_Content | ContentSelectorView::Flag_Profile; + flags = ContentSelectorView::Flag_Content | ContentSelectorView::Flag_Profile; - ContentSelectorView::ContentSelector::configure(this, flags); + 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")); - - if (profile.isEmpty()) - return; + 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/") + profile + QString("/master"), Qt::MatchExactly); - QStringList addons = mLauncherSettings.values(QString("Profiles/") + profile + QString("/plugin"), Qt::MatchExactly); + mSelector->clearCheckStates(); - foreach (const QString &file, addons) - files.append(file); + if (files.size() > 0) + mSelector->setGameFile(files.at(0)); + else + mSelector->setGameFile(); - 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::slotProfileRenamed(const QString &previous, const QString ¤t) +void DataFilesPage::slotAddNewProfile(const QString &profile) { - if (previous.isEmpty()) - return; - - // Save the new profile name - mLauncherSettings.setValue(QString("Profiles/currentprofile"), current); + saveSettings(); + mSelector->clearCheckStates(); + mSelector->addProfile(profile, true); + mSelector->setGameFile(); saveSettings(); - // Remove the old one - mLauncherSettings.remove(QString("Profiles/") + previous + QString("/master")); - mLauncherSettings.remove(QString("Profiles/") + previous + QString("/plugin")); + emit signalProfileChanged(mSelector->getProfileIndex(profile)); +} - // Remove the profile from the combobox - ContentSelectorView::ContentSelector::instance().removeProfile (previous); +void DataFilesPage::slotProfileDeleted (const QString &item) +{ + removeProfile (item); +} - loadSettings(); +void DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString ¤t) +{ + changeProfiles(previous, current); + emit signalProfileChanged(mSelector->getProfileIndex(current)); } -void DataFilesPage::slotProfileAdded() +void DataFilesPage::slotProfileRenamed(const QString &previous, const QString ¤t) { - TextInputDialog newDialog (tr("New Profile"), tr("Profile name:"), this); + if (previous.isEmpty()) + return; - // connect(mNewDialog->lineEdit(), SIGNAL(textChanged(QString)), - // this, SLOT(updateOkButton(QString))); + // Save the new profile name + saveSettings(); - if (newDialog.exec() == QDialog::Accepted) - { - QString profile = newDialog.lineEdit()->text(); + // Remove the old one + removeProfile (previous); - ContentSelectorView::ContentSelector - ::instance().addProfile(profile, true); - } + loadSettings(); } -void DataFilesPage::setProfilesComboBoxIndex(int index) +void DataFilesPage::slotProfileChanged(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 6ed5d9ce9..47e28493d 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 4d9ce14d6..2c6c711ea 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 311e3a25c..fe9ca141e 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 46900c595..fc1ed1c69 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 4306396bd..42edfadb1 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 b1b72dc1f..efa31100a 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 ebce4578b..103c6f412 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 db6431810..a9796a1fb 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 a2a57f850..feea3643b 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 54199626e..6965c948e 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,78 +268,58 @@ 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)); - - enableProfilesComboBox(); + setProfile (ui.profilesComboBox->findText(item)); } -QString ContentSelectorView::ContentSelector::getProfileText() const +void ContentSelectorView::ContentSelector::setProfile(int index) { - return ui.profilesComboBox->currentText(); + //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); + } } -void ContentSelectorView::ContentSelector::enableProfilesComboBox() +QString ContentSelectorView::ContentSelector::getProfileText() const { - if (!ui.profilesComboBox->isEnabled()) - ui.profilesComboBox->setEnabled(true); - - if (!ui.deleteProfileAction->isEnabled()) - ui.deleteProfileAction->setEnabled(true); - - ui.projectButtonBox->button(QDialogButtonBox::Open)->setEnabled (true); + return ui.profilesComboBox->currentText(); } -QStringList ContentSelectorView::ContentSelector::checkedItemsPaths() +QAbstractItemModel *ContentSelectorView::ContentSelector::profilesModel() const { - 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 caf9cc670..64b9b3732 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 29001189d..0e9905df4 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 560c42c10..fc87a94b4 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 51928c09a..6bb92f113 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 de3a9fb72..a0b7e350a 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; - int exec(); +public: -private: - QDialogButtonBox *mButtonBox; + explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0); + QString getText() const; + + int exec(); +private slots: + void slotUpdateOkButton(QString text); }; diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 73d7a4902..0cafd606a 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/132] 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 401f3839f..ae10ec642 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 efa31100a..2017ab103 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 a9796a1fb..8bb052d3d 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 feea3643b..0d6c52cc6 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/132] 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 a43059795..4c75ed1cc 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 16f6b9516..77ba0993e 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 e5e7514ce..bec09bd4a 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/132] 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 4c75ed1cc..d5888a312 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 77ba0993e..763cde100 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 5c29d9f61..afb68b440 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 d171dacae..e6d1b0c87 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 1d6c88dcc..024c46bea 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 28a21216a..b80a18642 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/132] 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 d5888a312..97c958f3d 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 763cde100..16f6b9516 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 bec09bd4a..e5e7514ce 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/132] 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 97c958f3d..9abc9ee78 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 afb68b440..32c728f88 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/132] 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 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 AoB#j- literal 0 HcmV?d00001 From cafea438bdc1108c0a6cdf1f183b453ee2ef2bf9 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 20 Oct 2013 09:44:10 +0200 Subject: [PATCH 076/132] 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-s(uCL|53?mdFQ-x=FF#KKW+aN8{gW9bWh5YW7MCQJWF~`cbVAtZ53?~Z Mzl3yKbunxO07-RSssI20 delta 7 OcmcZ Date: Sun, 20 Oct 2013 09:50:16 +0200 Subject: [PATCH 077/132] 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 625239aa0..a436d1fc3 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/132] This appears to work. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a436d1fc3..2a902eb83 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/132] 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 2a902eb83..df2986715 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 32c728f88..fd2b32861 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 b138533bf307332852d907b181114f53b2bf4d91 Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Sun, 20 Oct 2013 17:21:09 +0200 Subject: [PATCH 080/132] 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 df2986715..cfb682cdb 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 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 F{s+fR@9+Qs From b23df42817a0ed45a460e2680b98d02b2e19ab1d Mon Sep 17 00:00:00 2001 From: Marek Kochanowicz Date: Mon, 21 Oct 2013 16:06:16 +0200 Subject: [PATCH 081/132] 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 9abc9ee78..94faa713b 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 082/132] 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 2b0056e72..b756a9dc1 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 083/132] 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 e6d1b0c87..fb929cca6 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 084/132] 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 fd2b32861..b56460a03 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 fb929cca6..437b0c513 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 085/132] 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 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 F{s+fR@9+Qs 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 086/132] 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 94faa713b..a05b515d3 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 34bdc056f..cc886f9cc 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 087/132] Reimplemented constentselector view class --- apps/launcher/CMakeLists.txt | 11 + apps/launcher/datafilespage.cpp | 201 +++++++++---- 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 | 109 ++++--- apps/opencs/view/doc/filedialog.hpp | 44 +-- .../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, 692 insertions(+), 838 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 49dedd829..18c555a24 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 a705ae37d..2140caaf9 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) { - setObjectName ("DataFilesPage"); - - unsigned char flags; + ui.setupUi (this); - flags = ContentSelectorView::Flag_Content | ContentSelectorView::Flag_Profile; - - ContentSelectorView::ContentSelector::configure(this, flags); - mSelector = &ContentSelectorView::ContentSelector::instance(); + setObjectName ("DataFilesPage"); + mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); + 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; + setProfile (previous, current, savePrevious); + } +} - if (!previous.isEmpty()) - index = mSelector->getProfileIndex(previous); +void DataFilesPage::setProfile (const QString &previous, const QString ¤t, bool savePrevious) +{ + //abort if no change (poss. duplicate signal) + if (previous == current) + return; - // Store the previous profile if it exists - if ( (index != -1) && savePrevious) - saveSettings(previous); + if (!previous.isEmpty() && savePrevious) + saveSettings (previous); - loadSettings(); -} + ui.profilesComboBox->setCurrentIndex (ui.profilesComboBox->findText (current)); -void DataFilesPage::slotAddNewProfile(const QString &profile) -{ - saveSettings(); - mSelector->clearCheckStates(); - mSelector->addProfile(profile, true); - mSelector->setGameFile(); - saveSettings(); + loadSettings(); - emit signalProfileChanged(mSelector->getProfileIndex(profile)); + 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)); +} + +void DataFilesPage::addProfile (const QString &profile, bool setAsCurrent) +{ + if (profile.isEmpty()) + return; + + if (ui.profilesComboBox->findText (profile) != -1) + return; + + ui.profilesComboBox->addItem (profile); + + if (setAsCurrent) + setProfile (ui.profilesComboBox->findText (profile), false); } -int DataFilesPage::profilesIndex() const +void DataFilesPage::on_deleteProfileAction_triggered() { - return mSelector->getProfileIndex(mSelector->getProfileText()); + 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 47e28493d..cc054a4e4 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 + { return ui.profilesComboBox->model(); } - QAbstractItemModel* profilesModel() const; - int profilesIndex() const; + int profilesIndex() const + { return ui.profilesComboBox->currentIndex(); } - void writeConfig(QString profile = QString()); + //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 fe9ca141e..e514755fe 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 df647a4a0..348707580 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 ab1c37203..2dd7da32b 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 0e9905df4..c14330724 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)); - - // 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); -} + int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); -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 000000000..1e27f66a9 --- /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 6bb92f113..9957e7dda 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 a0b7e350a..148bbd152 100644 --- a/components/contentselector/view/textinputdialog.hpp +++ b/apps/launcher/utils/textinputdialog.hpp @@ -3,23 +3,30 @@ #include -class QDialogButtonBox; +#include "lineedit.hpp" -namespace ContentSelectorView { - class LineEdit; -} +class QDialogButtonBox; +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 9ae12c7a7..bc78352d4 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 ae10ec642..e78357bc5 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 2017ab103..49e90ec81 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); -} -void CSVDoc::FileDialog::addFiles(const QString &path) -{ - ContentSelectorView::ContentSelector::addFiles(path); + setObjectName ("FileDialog"); + mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); } -QString CSVDoc::FileDialog::filename() +void CSVDoc::FileDialog::addFiles(const QString &path) { - 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); -void CSVDoc::FileDialog::newFile() -{ - setWindowTitle(tr("New")); + connect (mFileWidget, SIGNAL (nameChanged(const QString &, bool)), + this, SLOT (slotUpdateCreateButton(const QString &, bool))); + + connect (mSelector, SIGNAL (signalCurrentGamefileIndexChanged (int)), + this, SLOT (slotUpdateCreateButton (int))); - ContentSelectorView::ContentSelector::configure(this, mNewFileFlags); + connect (ui.projectButtonBox, SIGNAL (accepted()), this, SIGNAL (createNewFile())); - connect (&ContentSelectorView::ContentSelector::instance(), - SIGNAL (accepted()), this, SIGNAL (createNewFile())); + connect (ui.projectButtonBox, SIGNAL (rejected()), this, SLOT (slotRejected())); +} - connect (&ContentSelectorView::ContentSelector::instance(), - SIGNAL (rejected()), this, SLOT (slotRejected())); +void CSVDoc::FileDialog::buildOpenFileView() +{ + setWindowTitle(tr("Open")); + ui.projectGroupBox->setTitle (QString("")); - 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 acc35189d..78883791e 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 showDialog (DialogType dialogType); - void openFile(); - void newFile(); 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 265b98305..98681c499 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 103c6f412..acb70b04d 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 8bb052d3d..accc149cb 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 000000000..1d773b62d --- /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 000000000..e3888af2c --- /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 1aef2f57b..000000000 --- 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 6965c948e..7a7e1fd9d 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) -{ - assert(!mInstance); - mInstance = new ContentSelector (subject, flags); -} - -ContentSelectorView::ContentSelector& ContentSelectorView::ContentSelector::instance() +ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) : + QObject(parent) { - - 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); - } -} - -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"); + mContentModel->addFiles(path); - 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 64b9b3732..0882abfb7 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 b6fdfa805..000000000 --- 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 fc87a94b4..000000000 --- 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 000000000..b9b5ba5a0 --- /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 0cafd606a..eb5ebc61d 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 000000000..114345e53 --- /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 088/132] 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 2140caaf9..ca404c5d8 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 e78357bc5..284762812 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 49e90ec81..5438effff 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 78883791e..85582e95f 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 7a7e1fd9d..cb405f092 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 0882abfb7..163b19855 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 089/132] 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 284762812..1099226d2 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 5438effff..1ac476cb2 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 85582e95f..3b93f42a8 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 cb405f092..3d615906d 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 090/132] 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 1099226d2..6396563f2 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 1ac476cb2..e82cc30cb 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 accc149cb..0674642ee 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 3d615906d..33b31b00c 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 163b19855..1e24a5523 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 091/132] Implemented Launcher namespace --- apps/launcher/datafilespage.cpp | 44 +++--- apps/launcher/datafilespage.hpp | 94 ++++++------ apps/launcher/graphicspage.cpp | 24 +-- apps/launcher/graphicspage.hpp | 85 ++++++----- apps/launcher/main.cpp | 3 +- apps/launcher/maindialog.cpp | 41 ++--- apps/launcher/maindialog.hpp | 78 +++++----- apps/launcher/playpage.cpp | 8 +- apps/launcher/playpage.hpp | 30 ++-- apps/launcher/settings/gamesettings.cpp | 12 +- apps/launcher/settings/gamesettings.hpp | 74 ++++----- 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 | 144 +++++++++--------- apps/launcher/textslotmsgbox.cpp | 2 +- apps/launcher/textslotmsgbox.hpp | 14 +- apps/launcher/unshieldthread.cpp | 26 ++-- apps/launcher/unshieldthread.hpp | 64 ++++---- apps/launcher/utils/checkablemessagebox.cpp | 159 +++++++++----------- apps/launcher/utils/checkablemessagebox.hpp | 138 +++++++++-------- apps/launcher/utils/textinputdialog.cpp | 10 +- apps/launcher/utils/textinputdialog.hpp | 41 ++--- 24 files changed, 588 insertions(+), 555 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index ca404c5d8..e246b4515 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 cc054a4e4..e394e6f41 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 2c6c711ea..1ed1abaeb 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 d233ea12e..7f5dcae1e 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 - -public: - GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0); - - void saveSettings(); - bool loadSettings(); - -public slots: - void rendererChanged(const QString &renderer); - void screenChanged(int screen); - -private slots: - void slotFullScreenChanged(int state); - void slotStandardToggled(bool checked); - -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 - - Files::ConfigurationManager &mCfgMgr; - GraphicsSettings &mGraphicsSettings; - - QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer); - QStringList getAvailableResolutions(int screen); - QRect getMaximumResolution(); - - bool setupOgre(); - bool setupSDL(); -}; - + class GraphicsSettings; + + class GraphicsPage : public QWidget, private Ui::GraphicsPage + { + Q_OBJECT + + public: + GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0); + + void saveSettings(); + bool loadSettings(); + + public slots: + void rendererChanged(const QString &renderer); + void screenChanged(int screen); + + private slots: + void slotFullScreenChanged(int state); + void slotStandardToggled(bool checked); + + 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 + + Files::ConfigurationManager &mCfgMgr; + GraphicsSettings &mGraphicsSettings; + + 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 f67f5edcf..0b5e62a66 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 e514755fe..dca9720ac 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 824dff6e8..5b8e4908e 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 + class PlayPage; + class GraphicsPage; + class DataFilesPage; + class UnshieldThread; + +#ifndef WIN32 + bool expansions(Launcher::UnshieldThread& cd); +#endif -public: - MainDialog(); - bool setup(); - bool showFirstRunDialog(); + class MainDialog : public QMainWindow, private Ui::MainWindow + { + Q_OBJECT -public slots: - void changePage(QListWidgetItem *current, QListWidgetItem *previous); - void play(); + public: + explicit MainDialog(QWidget *parent = 0); + bool setup(); + bool showFirstRunDialog(); -private: - void createIcons(); - void createPages(); + public slots: + void changePage(QListWidgetItem *current, QListWidgetItem *previous); + void play(); - bool setupLauncherSettings(); - bool setupGameSettings(); - bool setupGraphicsSettings(); + private: + void createIcons(); + void createPages(); - void loadSettings(); - void saveSettings(); - bool writeSettings(); + bool setupLauncherSettings(); + bool setupGameSettings(); + bool setupGraphicsSettings(); - 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 loadSettings(); + void saveSettings(); + bool writeSettings(); - void closeEvent(QCloseEvent *event); + 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); - PlayPage *mPlayPage; - GraphicsPage *mGraphicsPage; - DataFilesPage *mDataFilesPage; + void closeEvent(QCloseEvent *event); - Files::ConfigurationManager mCfgMgr; + PlayPage *mPlayPage; + GraphicsPage *mGraphicsPage; + DataFilesPage *mDataFilesPage; - GameSettings mGameSettings; - GraphicsSettings mGraphicsSettings; - LauncherSettings mLauncherSettings; + Files::ConfigurationManager mCfgMgr; -}; + GameSettings mGameSettings; + GraphicsSettings mGraphicsSettings; + LauncherSettings mLauncherSettings; + }; +} #endif diff --git a/apps/launcher/playpage.cpp b/apps/launcher/playpage.cpp index fc1ed1c69..6cfb9686f 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 42edfadb1..1dc5bb0fe 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 205879bc3..5231753f2 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 55b2107e2..11f06b027 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 setMultiValue(const QString &key, const QString &value) - { - QStringList values = mSettings.values(key); - if (!values.contains(value)) - mSettings.insertMulti(key, value); - } + inline void setValue(const QString &key, const QString &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); + } - 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 void remove(const QString &key) + { + mSettings.remove(key); + } - QStringList values(const QString &key, const QStringList &defaultValues = QStringList()); - bool readFile(QTextStream &stream); - bool writeFile(QTextStream &stream); + 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; } -private: - Files::ConfigurationManager &mCfgMgr; + QStringList values(const QString &key, const QStringList &defaultValues = QStringList()); + bool readFile(QTextStream &stream); + bool writeFile(QTextStream &stream); - void validatePaths(); - QMap mSettings; + private: + Files::ConfigurationManager &mCfgMgr; - 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 0c5580091..9dad3dee6 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 3e8617849..6f7c13547 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 5d298e814..7c97144ea 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 60c6f86bc..8acc389a9 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 ed8ada56c..3a1cf8e30 100644 --- a/apps/launcher/settings/settingsbase.hpp +++ b/apps/launcher/settings/settingsbase.hpp @@ -7,103 +7,105 @@ #include #include -template -class SettingsBase +namespace Launcher { + template + class SettingsBase + { -public: - SettingsBase() { mMultiValue = false; } - ~SettingsBase() {} + public: + SettingsBase() { mMultiValue = false; } + ~SettingsBase() {} - inline QString value(const QString &key, const QString &defaultValue = QString()) - { - return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key); - } + 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) - { - QStringList values = mSettings.values(key); - if (!values.contains(value)) - mSettings.insert(key, value); - } + inline void setValue(const QString &key, const QString &value) + { + QStringList values = mSettings.values(key); + if (!values.contains(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 setMultiValueEnabled(bool enable) - { - mMultiValue = enable; - } + inline void setMultiValueEnabled(bool enable) + { + mMultiValue = enable; + } - inline void remove(const QString &key) - { - mSettings.remove(key); - } + inline void remove(const QString &key) + { + mSettings.remove(key); + } - Map getSettings() {return mSettings;} + Map getSettings() {return mSettings;} - bool readFile(QTextStream &stream) - { - mCache.clear(); + bool readFile(QTextStream &stream) + { + mCache.clear(); - QString sectionPrefix; + QString sectionPrefix; - QRegExp sectionRe("^\\[([^]]+)\\]"); - QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); + QRegExp sectionRe("^\\[([^]]+)\\]"); + QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); - while (!stream.atEnd()) { - QString line = stream.readLine(); + while (!stream.atEnd()) { + QString line = stream.readLine(); - if (line.isEmpty() || line.startsWith("#")) - continue; + if (line.isEmpty() || line.startsWith("#")) + continue; - if (sectionRe.exactMatch(line)) { - sectionPrefix = sectionRe.cap(1); - sectionPrefix.append("/"); - continue; - } + if (sectionRe.exactMatch(line)) { + sectionPrefix = sectionRe.cap(1); + sectionPrefix.append("/"); + continue; + } - if (keyRe.indexIn(line) != -1) { + if (keyRe.indexIn(line) != -1) { - QString key = keyRe.cap(1).trimmed(); - QString value = keyRe.cap(2).trimmed(); + QString key = keyRe.cap(1).trimmed(); + QString value = keyRe.cap(2).trimmed(); - if (!sectionPrefix.isEmpty()) - key.prepend(sectionPrefix); + if (!sectionPrefix.isEmpty()) + key.prepend(sectionPrefix); - mSettings.remove(key); + mSettings.remove(key); - QStringList values = mCache.values(key); + QStringList values = mCache.values(key); - if (!values.contains(value)) { - if (mMultiValue) { - mCache.insertMulti(key, value); - } else { - mCache.insert(key, value); + 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 0607d1cc6..62d9cf576 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 a29e2c354..a0fefaa25 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 69b241365..d0dbeb1bd 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 655cb5b53..de6a32b44 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; - protected: - virtual void run(); + boost::filesystem::path mOutputPath; - 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 41207a8de..2f775af57 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); - - 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); - - 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); - - checkBox = new QCheckBox(q); - checkBox->setText(CheckableMessageBox::tr("Do not ask again")); - - buttonBox = new QDialogButtonBox(q); - buttonBox->setOrientation(Qt::Horizontal); - buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); - - 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 = 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); - } - - QLabel *pixmapLabel; - QLabel *messageLabel; - QCheckBox *checkBox; - QDialogButtonBox *buttonBox; - QAbstractButton *clickedButton; -}; +{ + 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); + + 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); + + 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(Launcher::CheckableMessageBox::tr("Do not ask again")); + + buttonBox = new QDialogButtonBox(q); + buttonBox->setOrientation(Qt::Horizontal); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + + 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 = 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); +} -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 93fd43fe1..09a501b9c 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) - -public: - explicit CheckableMessageBox(QWidget *parent); - virtual ~CheckableMessageBox(); - - 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); - - QString text() const; - void setText(const QString &); - - bool isChecked() const; - void setChecked(bool s); - - QString checkBoxText() const; - void setCheckBoxText(const QString &); - - bool isCheckBoxVisible() const; - void setCheckBoxVisible(bool); - - QDialogButtonBox::StandardButtons standardButtons() const; - void setStandardButtons(QDialogButtonBox::StandardButtons s); - QPushButton *button(QDialogButtonBox::StandardButton b) const; - QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role); - - QDialogButtonBox::StandardButton defaultButton() const; - void setDefaultButton(QDialogButtonBox::StandardButton s); - - // See static QMessageBox::standardPixmap() - QPixmap iconPixmap() const; - void setIconPixmap (const QPixmap &p); - - // 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; -}; - + class CheckableMessageBoxPrivate + { + public: + + QLabel *pixmapLabel; + QLabel *messageLabel; + QCheckBox *checkBox; + QDialogButtonBox *buttonBox; + QAbstractButton *clickedButton; + + public: + CheckableMessageBoxPrivate(QDialog *q); + }; + + 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) + + public: + explicit CheckableMessageBox(QWidget *parent); + virtual ~CheckableMessageBox(); + + 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); + + QString text() const; + void setText(const QString &); + + bool isChecked() const; + void setChecked(bool s); + + QString checkBoxText() const; + void setCheckBoxText(const QString &); + + bool isCheckBoxVisible() const; + void setCheckBoxVisible(bool); + + QDialogButtonBox::StandardButtons standardButtons() const; + void setStandardButtons(QDialogButtonBox::StandardButtons s); + QPushButton *button(QDialogButtonBox::StandardButton b) const; + QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role); + + QDialogButtonBox::StandardButton defaultButton() const; + void setDefaultButton(QDialogButtonBox::StandardButton s); + + // See static QMessageBox::standardPixmap() + QPixmap iconPixmap() const; + void setIconPixmap (const QPixmap &p); + + // 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 9957e7dda..76cbe32d0 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 148bbd152..bb01778be 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 { - public: - explicit DialogLineEdit (QWidget *parent = 0); - }; + Q_OBJECT - DialogLineEdit *mLineEdit; - QDialogButtonBox *mButtonBox; + class DialogLineEdit : public LineEdit + { + public: + explicit DialogLineEdit (QWidget *parent = 0); + }; -public: + DialogLineEdit *mLineEdit; + QDialogButtonBox *mButtonBox; - explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0); - ~TextInputDialog () {} + public: + + explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0); + ~TextInputDialog () {} - QString getText() const; + QString getText() const; - int exec(); + int exec(); -private slots: - void slotUpdateOkButton(QString text); - -}; + private slots: + void slotUpdateOkButton(QString text); + + }; +} #endif // TEXTINPUTDIALOG_HPP From 8c9a5de26f2558c64338f4780806788b637510fa Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 25 Oct 2013 23:47:53 +0200 Subject: [PATCH 092/132] Fix an irritating error message --- apps/launcher/graphicspage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 9308c1d57..97a24d54b 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -133,7 +133,7 @@ bool GraphicsPage::setupOgre() msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("
Could not select a valid render system

\ - Please make sure the plugins.cfg file exists and contains a valid rendering plugin.
")); + Please make sure Ogre plugins were installed correctly.
")); msgBox.exec(); return false; } From ba365ff49ed9be07af0244389077c3fc71b36d7a Mon Sep 17 00:00:00 2001 From: graffy76 Date: Fri, 25 Oct 2013 19:23:03 -0500 Subject: [PATCH 093/132] 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 d36ab65e9..e246b4515 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 03758b16e..51cc490c7 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 16f6b9516..ef013bc8f 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 cc886f9cc..590a19439 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 0d723bef1..a7b37be5b 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 0bf7c6951..fb6c0a0e8 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 be268f372..30f2f5d56 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 094/132] 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 51cc490c7..a8006d2b4 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 ef013bc8f..930aa9d64 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 fb6c0a0e8..eb94aa5f4 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 30f2f5d56..777ee31e4 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 114345e53..b3af166da 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 095/132] 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 eb94aa5f4..903e78cf1 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 096/132] 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 a8006d2b4..1c861ed67 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 23eaf90846125869bb5fd93fe84d7209995f45b2 Mon Sep 17 00:00:00 2001 From: "Alex \"rainChu\" Haddad" Date: Sun, 27 Oct 2013 04:05:01 -0400 Subject: [PATCH 097/132] Breath meter flashes when drowning --- apps/openmw/mwgui/hud.cpp | 22 +++++++++++++++++++++- apps/openmw/mwgui/hud.hpp | 5 ++++- files/mygui/openmw_hud.layout | 1 + files/mygui/openmw_progress.skin.xml | 9 +++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index edcd49738..e7b9f9c01 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -28,6 +28,7 @@ namespace MWGui , mStamina(NULL) , mDrowning(NULL) , mDrowningFrame(NULL) + , mDrowningFlash(NULL) , mWeapImage(NULL) , mSpellImage(NULL) , mWeapStatus(NULL) @@ -53,6 +54,7 @@ namespace MWGui , mSpellVisible(true) , mWorldMouseOver(false) , mEnemyHealthTimer(0) + , mIsDrowning(false) { setCoord(0,0, width, height); @@ -75,6 +77,7 @@ namespace MWGui //Drowning bar getWidget(mDrowningFrame, "DrowningFrame"); getWidget(mDrowning, "Drowning"); + getWidget(mDrowningFlash, "Flash"); mDrowning->setProgressRange(200); const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); @@ -207,7 +210,15 @@ namespace MWGui void HUD::setDrowningTimeLeft(float time) { - mDrowning->setProgressPosition(time/20.0*200.0); + size_t progress = time/20.0*200.0; + mDrowning->setProgressPosition(progress); + + bool isDrowning = (progress == 0); + if (isDrowning && !mIsDrowning) // Just started drowning + mDrowningFlashTheta = 0.0f; // Start out on bright red every time. + + mDrowningFlash->setVisible(isDrowning); + mIsDrowning = isDrowning; } void HUD::setDrowningBarVisible(bool visible) @@ -367,6 +378,9 @@ namespace MWGui mEnemyHealth->setVisible(false); mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() + MyGUI::IntPoint(0,20)); } + + if (mIsDrowning) + mDrowningFlashTheta += dt * Ogre::Math::TWO_PI; } void HUD::onResChange(int width, int height) @@ -609,6 +623,12 @@ namespace MWGui mEnemyHealth->setProgressRange(100); mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100); } + + if (mIsDrowning) + { + float intensity = (cos(mDrowningFlashTheta) + 1.0f) / 2.0f; + mDrowningFlash->setColour(MyGUI::Colour(intensity, intensity, intensity)); + } } void HUD::setEnemy(const MWWorld::Ptr &enemy) diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index c40742a60..04206fbc8 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -67,7 +67,7 @@ namespace MWGui MyGUI::ImageBox* mCrosshair; MyGUI::TextBox* mCellNameBox; MyGUI::TextBox* mWeaponSpellBox; - MyGUI::Widget* mDrowningFrame; + MyGUI::Widget *mDrowningFrame, *mDrowningFlash; MyGUI::Widget* mDummy; @@ -101,6 +101,9 @@ namespace MWGui MWWorld::Ptr mEnemy; float mEnemyHealthTimer; + bool mIsDrowning; + float mDrowningFlashTheta; + void onWorldClicked(MyGUI::Widget* _sender); void onWorldMouseOver(MyGUI::Widget* _sender, int x, int y); void onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new); diff --git a/files/mygui/openmw_hud.layout b/files/mygui/openmw_hud.layout index e39777dd0..72d337e45 100644 --- a/files/mygui/openmw_hud.layout +++ b/files/mygui/openmw_hud.layout @@ -45,6 +45,7 @@ + diff --git a/files/mygui/openmw_progress.skin.xml b/files/mygui/openmw_progress.skin.xml index 35114ffeb..f5418e3f8 100644 --- a/files/mygui/openmw_progress.skin.xml +++ b/files/mygui/openmw_progress.skin.xml @@ -17,6 +17,11 @@ + + + + + @@ -65,6 +70,10 @@ + + + + From 489475a32f14e24e4554b393261280abc947c194 Mon Sep 17 00:00:00 2001 From: Lukasz Gromanowski Date: Sun, 27 Oct 2013 19:57:05 +0100 Subject: [PATCH 098/132] 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 0d723bef1..a7b37be5b 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 099/132] 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 | 29 ++++++++++++------- apps/launcher/settings/gamesettings.hpp | 10 +++++-- .../contentselector/model/contentmodel.cpp | 10 ++++++- 5 files changed, 44 insertions(+), 49 deletions(-) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index 43f09d168..5e19ba9c9 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 032f70916..0f59b3aaf 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 7b2356cd0..211e31927 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 content = mSettings.values(QString("content")); + for (int i = content.count(); i--;) { + stream << "content=" << content.at(i) << "\n"; } - QStringList plugins = mSettings.values(QString("plugin")); - for (int i = plugins.count(); i--;) { - stream << "content=" << plugins.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 true; + return result; } diff --git a/apps/launcher/settings/gamesettings.hpp b/apps/launcher/settings/gamesettings.hpp index 55b2107e2..c009c30ed 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 b85da25c6..fb011198c 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 100/132] 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 b1eec63c3..332d46075 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 461cfb345..627f89c1f 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 903e78cf1..57b61ff8b 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 777ee31e4..d9fd56943 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 33b31b00c..b9e518931 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 1e24a5523..da1c39973 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 101/132] 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 625239aa0..d0de9f771 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 7865f8dba..c4d9d2746 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 102/132] 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 625239aa0..5a87a1a40 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 4f35fd8184bbe45da391f39e9ec49064fd26deb5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 30 Oct 2013 13:05:28 +0100 Subject: [PATCH 103/132] Removed a workaround from Renderer, some cleanup --- apps/openmw/engine.cpp | 3 +- apps/openmw/mwbase/environment.cpp | 1 + apps/openmw/mwbase/environment.hpp | 5 ++++ apps/openmw/mwgui/mainmenu.cpp | 2 +- apps/openmw/mwinput/inputmanagerimp.cpp | 10 ------- apps/openmw/mwinput/inputmanagerimp.hpp | 3 +- libs/openengine/ogre/renderer.cpp | 39 +------------------------ libs/openengine/ogre/renderer.hpp | 25 ---------------- 8 files changed, 11 insertions(+), 77 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index e1fd3a0af..7e344c4db 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -503,7 +503,8 @@ void OMW::Engine::go() MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript); // Start the main rendering loop - mOgre->start(); + while (!mEnvironment.getRequestExit()) + Ogre::Root::getSingleton().renderOneFrame(); // Save user settings settings.saveUser(settingspath); diff --git a/apps/openmw/mwbase/environment.cpp b/apps/openmw/mwbase/environment.cpp index 5a13a50ec..6b309025c 100644 --- a/apps/openmw/mwbase/environment.cpp +++ b/apps/openmw/mwbase/environment.cpp @@ -13,6 +13,7 @@ #include "windowmanager.hpp" MWBase::Environment *MWBase::Environment::sThis = 0; +bool MWBase::Environment::sExit = false; MWBase::Environment::Environment() : mWorld (0), mSoundManager (0), mScriptManager (0), mWindowManager (0), diff --git a/apps/openmw/mwbase/environment.hpp b/apps/openmw/mwbase/environment.hpp index a80e7ef87..466302907 100644 --- a/apps/openmw/mwbase/environment.hpp +++ b/apps/openmw/mwbase/environment.hpp @@ -32,6 +32,8 @@ namespace MWBase InputManager *mInputManager; float mFrameDuration; + static bool sExit; + Environment (const Environment&); ///< not implemented @@ -44,6 +46,9 @@ namespace MWBase ~Environment(); + static void setRequestExit () { sExit = true; } + static bool getRequestExit () { return sExit; } + void setWorld (World *world); void setSoundManager (SoundManager *soundManager); diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 1db6e9ecd..2c42dc210 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -77,7 +77,7 @@ namespace MWGui else if (sender == mButtons["options"]) MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); else if (sender == mButtons["exitgame"]) - Ogre::Root::getSingleton ().queueEndRendering (); + MWBase::Environment::get().setRequestExit(); else if (sender == mButtons["newgame"]) { MWBase::Environment::get().getWorld()->startNewGame(); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 4746260ed..c4a360cd4 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -182,9 +182,6 @@ namespace MWInput case A_GameMenu: toggleMainMenu (); break; - case A_Quit: - exitNow(); - break; case A_Screenshot: screenshot(); break; @@ -814,13 +811,6 @@ namespace MWInput mAlwaysRunActive = !mAlwaysRunActive; } - // Exit program now button (which is disabled in GUI mode) - void InputManager::exitNow() - { - if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) - Ogre::Root::getSingleton().queueEndRendering (); - } - void InputManager::resetIdleTime() { if (mTimeIdle < 0) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index 5f9a752d7..b38c80e0e 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -177,7 +177,6 @@ namespace MWInput void activate(); void toggleWalking(); void toggleAutoMove(); - void exitNow(); void rest(); void quickKey (int index); @@ -194,7 +193,7 @@ namespace MWInput A_GameMenu, - A_Quit, // Exit the program + A_Unused, A_Screenshot, // Take a screenshot diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index 912781240..d078e3c61 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -28,21 +28,6 @@ using namespace Ogre; using namespace OEngine::Render; -#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE - -CustomRoot::CustomRoot(const Ogre::String& pluginFileName, - const Ogre::String& configFileName, - const Ogre::String& logFileName) -: Ogre::Root(pluginFileName, configFileName, logFileName) -{} - -bool CustomRoot::isQueuedEnd() const -{ - return mQueuedEnd; -} - -#endif - void OgreRenderer::cleanup() { delete mFader; @@ -60,23 +45,6 @@ void OgreRenderer::cleanup() unloadPlugins(); } -void OgreRenderer::start() -{ -#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE - // we need this custom main loop because otherwise Ogre's Carbon message pump will - // steal input events even from our Cocoa window - // There's no way to disable Ogre's message pump other that comment pump code in Ogre's source - do { - if (!mRoot->renderOneFrame()) { - break; - } - - } while (!mRoot->isQueuedEnd()); -#else - mRoot->startRendering(); -#endif -} - void OgreRenderer::loadPlugins() { #ifdef ENABLE_PLUGIN_GL @@ -158,20 +126,15 @@ void OgreRenderer::configure(const std::string &logPath, // Set up logging first new LogManager; Log *log = LogManager::getSingleton().createLog(logPath + std::string("Ogre.log")); - logging = _logging; - if(logging) + if(_logging) // Full log detail log->setLogDetail(LL_BOREME); else // Disable logging log->setDebugOutputEnabled(false); -#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE - mRoot = new CustomRoot("", "", ""); -#else mRoot = new Root("", "", ""); -#endif #if defined(ENABLE_PLUGIN_GL) || defined(ENABLE_PLUGIN_Direct3D9) || defined(ENABLE_PLUGIN_CgProgramManager) || defined(ENABLE_PLUGIN_OctreeSceneManager) || defined(ENABLE_PLUGIN_ParticleFX) loadPlugins(); diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index 89edc567d..c6a838805 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -27,18 +27,13 @@ #include "OgreTexture.h" #include -#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE -#include -#endif struct SDL_Window; struct SDL_Surface; namespace Ogre { -#if OGRE_PLATFORM != OGRE_PLATFORM_APPLE class Root; -#endif class RenderWindow; class SceneManager; class Camera; @@ -61,17 +56,6 @@ namespace OEngine std::string icon; }; -#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE - class CustomRoot : public Ogre::Root { - public: - bool isQueuedEnd() const; - - CustomRoot(const Ogre::String& pluginFileName = "plugins.cfg", - const Ogre::String& configFileName = "ogre.cfg", - const Ogre::String& logFileName = "Ogre.log"); - }; -#endif - class Fader; class WindowSizeListener @@ -82,11 +66,7 @@ namespace OEngine class OgreRenderer { -#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE - CustomRoot *mRoot; -#else Ogre::Root *mRoot; -#endif Ogre::RenderWindow *mWindow; SDL_Window *mSDLWindow; Ogre::SceneManager *mScene; @@ -110,7 +90,6 @@ namespace OEngine Fader* mFader; std::vector mEmitterFactories; std::vector mAffectorFactories; - bool logging; WindowSizeListener* mWindowListener; @@ -139,7 +118,6 @@ namespace OEngine , mD3D9Plugin(NULL) #endif , mFader(NULL) - , logging(false) { } @@ -167,9 +145,6 @@ namespace OEngine /// Kill the renderer. void cleanup(); - /// Start the main rendering loop - void start(); - void loadPlugins(); void unloadPlugins(); From 636d399c7f89b854db2621499f54b17042d94b77 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 30 Oct 2013 14:04:33 +0100 Subject: [PATCH 104/132] 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 625239aa0..6fd64aa3f 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 97a24d54b..b7f59c781 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 d233ea12e..29a95c36a 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 f67f5edcf..cb1a51d8c 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 7e344c4db..020283f2d 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 04423dc6f..ef1556038 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 000000000..92a6ed012 --- /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 000000000..b6fe4631a --- /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 d078e3c61..2a438e1fe 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 c6a838805..e4af0bf77 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 105/132] 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 dc3918368..000000000 --- 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 106/132] 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 1c861ed67..63afe7a9d 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 332d46075..09e58690f 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"); + + bool isFilePathChanged = (path.parent_path().string() != mLocalData.string()); + + if (isLegacyPath) + path.replace_extension (addon ? ".omwaddon" : ".omwgame"); - if (path.parent_path().string()==mLocalData.string()) + //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 627f89c1f..91e308236 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 57b61ff8b..c0cda26dc 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 107/132] 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 f918cfebf..ed6a6b29e 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 9a6832ec0..31cbc3dba 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 ef7123c20..9f55f6004 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 000000000..c8b37e9bb --- /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 000000000..355a6331e --- /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 e3618c549..83b30be13 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 df74bba3b..e71ed909e 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 108/132] 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 09e58690f..5ebfacd03 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 c0cda26dc..ce7d7dfb0 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 0674642ee..ea389c2bf 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 9dfe49eba..35f78386c 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 743b1c5a6..fc0cca8a2 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 b9e518931..d12c8cb24 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 109/132] 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 7703f2d23..5cee7efd9 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 110/132] 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 e246b4515..71d072599 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)); + + if (!gameFile.isEmpty()) + mSelector->setGameFile (gameFile); +/* else + { + //throw gamefile error here. + }*/ + } + + QStringList missingFiles; + QStringList foundFiles; - mSelector->setGameFile(gameFile); - mSelector->setCheckStates(addons); + 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 e394e6f41..37603a210 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 32eb2071f..83c099529 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 56917a79f..60236200a 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 5ebfacd03..09e58690f 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 ce7d7dfb0..80b3066dd 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 065b9c497..7661f9608 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 0d6c52cc6..ae49dd27d 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 35f78386c..a0a09105a 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 fc0cca8a2..ca24b52d1 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 d12c8cb24..4758dd5a0 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 111/132] 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 80b3066dd..ab56415a1 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 7661f9608..0fb1b4216 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 112/132] 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 71d072599..5ae850566 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 1e27f66a9..7b83c41b2 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 0fb1b4216..5f3575eb4 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; + + 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()); + } - return Qt::NoItemFlags; + // 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()); - - emit dataChanged(index, index); + success = (flags (index) & Qt::ItemIsEnabled); - 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 ae49dd27d..8c8c2124b 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 4758dd5a0..b962fa9ce 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 113/132] 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 273494ff3..4012a1fbd 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 114/132] 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 5ae850566..5452b7398 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 63afe7a9d..e879cb25e 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 115/132] 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 5452b7398..734277b97 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(); - - QString gameFile (""); + QStringList filepaths; - 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 83c099529..41113c35a 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 7c97144ea..705453555 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 b962fa9ce..e9599de49 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 da1c39973..a25eb20ae 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 116/132] updated changelog --- readme.txt | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/readme.txt b/readme.txt index c4d9d2746..a790ca9e3 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 117/132] 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 c57935c90..46bd7d3f9 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 118/132] updated changelog --- readme.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/readme.txt b/readme.txt index a790ca9e3..afcfadea3 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 119/132] 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 a1553d0b0..000000000 --- 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 e1fd3a0af..35ff6593b 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 1ec1e08cb..21153a474 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 zcma&M1#lffj5cU?%*@Qp%y!Jo95XXBL(CL2GsJAiF~s=H%rP@F(=)wy*?;%H+q!viC{-o}ynrZc@C5@UQGz=ode;l$u!&(1V%i!+7UboY5g5@c(-^DHmrtPYo+GOJyroPhZahy&D&zKNz2+BCFNja*&o<7ga1y)@JHArx(y$FNMz&mCBFb5~gDY?!e=_={$Wl zU74HK7Va{|T&9L@*0U%JOI5P`L^hM|z~=L6cW(>5;GEC)ko()kr_VuiKSjJ@*ezDF zMzY{k7qWIibP$sP6=l8FZk(&-=KY>rTGC%{$r7|54$MwY8JXYm5KT!HZW0hYLtcJB z>9~{czU4GiYQ>Rm3VP4ne3eLE#cE&I9m+@=b5iINxG}17;;C3zT3tWufQ6}?%iKVA zfbirQC7fl1qC)Mc(^B^7*<&cI$Ri9vm-b54 zfFq_h2VPp~E2DQrGH5EV?H1kRP(8m99Buy|>sbga%7x6jD~gMpVrFrhnDUE}*Pl)3 zr&bb4l4(`r>7Dg(jlQNv*o-q!O4TR7YjG%U`=~aGUmX3z#4I($lZZ|Zn`?-6#aS^A z<)2X?H!97<9{gJpTk5HG1(SOY7Kr8fkvq-(EhWkPP3AN!#dCHs(ZKdz=~FA4B*2!E zdE;1L@j1C`@mD%i79<1nlB!}poklMP;e@{#`QuWqLgpm$g;V`Ahj_1dOu=8y$8WII zYU&?mDJ_G@rC1UsgTl%@os+UGJ(&b$W>Q97VF7qX6Pva|k#Y9$+!@claFr|DOR{c# zh&DVaFu^>k6@P7vLfK1vaeEjABs%LhlW>yBfHjQ_Y@4UG z?5Q*~O7&i8gB`De@gVfu44fhkaol1*=Y^>8VpBJyBRNN z++V@3{svq0%e092gNM!VO)zV0du!1J)U20HE9ol8Nxd_#@65G-bUhL*m z{6L*iQIV0n0UjZOIw^x#_sM+~m8OY;ka}_*(Z*5j*GEz1dXI8TLOAqC@ zOq*o$fmhaHyhC$W7ACG@?8~CNnf*@j$>{LlcCPudLtZUJBQvgT=2I1YrFMLIZws=w z6h%OV58c^1?mYyBd;K5J3E`EEO{CuYw^T-)VA^d6BeUCZLD%>{!HkWa!YTsUUkmtw zfqo%f9iQ3J*#CWQEfdVfx*bGkS#cm zzsk5fHxrGV*#@;3*Cp9u5Dl9H&w9Tej>(*l3_6*Mt27$>&4t}46yLCa(_P7r#9p6{ zl00A4e8Z|o=`@7E!zojIA_gtj)_cG<=s$nb+OS@(n+@}h99OM0b~P}K4F9_H66tO0 zRWah7zUG#t9H~CV!cd^YC`m~ZFTkZ6{4^b^Avr{I za#}o(C#bpawO`ju=N~pN(NUWq_g4GqM!w^PHfu1ueF78MxAXFu=5Z5r)w&YA{IU5E z6fpA8t>`$fh3vx=o#g5B4ZJ$w}UOxL_4L$X9eO{OAo3 z0mmr{v%R%5HT?PLO#a;1yYVwlxS2+4=s1fCc4=+zf4FCA4(>kdY&UK>%x`zO?`9r) zaq+uY+Thy~cyAmLZVYHYDSF+{AZl29u1)sK@;5%Bm{kNlOeAKx0Mn2_XIy$dgu!Co zG`L!>N??B;@vcwdOGAlRAY0GP`y~<@iHPqb zbjC(F6)@XR6g;q~`%(KL(hJr;YIBbTb%kbqO9>w6T6mSdHj4qyTH-utKVYyy`Qn2` zxXOG@_CMp6cxs_}>t2T36eB?(jsmyX`E%1VnsJ>m4MDnasxBo)Ta~){yrRl+P9Cv%bBFL|~KE zH`@9!XZaSXd+Ixc3!325#rsgSc$Nd0>=$+4~&+TAo1MktL zqb+2zmG9BS<0@)8ug&5osNLcN3v;iiC^wLI-ll)d^8EX zdNQ2Z04@_X_I~(phjhJi53zMl#md3kMN6O+R=Fi=;1PROoStf6pO)i}7oNwRm zf~mF}pLq`+WNxnX?r~j&jAus|61SC$gKv<#od>{)K$&IzeU=e$#Ws{N*qwPJX04=1 z_G*-^@o;CJ&(L@C;#GIWJR$goGQnRU(-CmbB7D9C?EA8lj}i#&^5W0jhugSvdeblp z>dAPyb2rX=zC~o-ikam*ZL{iAJ>Y0K{M-z6Ig_*P;`6=Pb=!_QiU(%%LT>@6`o7OX zUft1>dQiE9B{@L{FDBO;PoA6;cDn%H@_ZG!4aCRO=_h@kmt@I1!~y9C1+^ZRt*i?U=2^+6 zvohUS#>ef?S$!8EvH+3BY~_H{*pW>RSnRB{f;+#0 z)7S@G4zlYu#&anf=j!o!yekU{enjjxxm`dDemDv-ymyfV^Aer-e%T?7o)^B+R(N#r z-Ux=L?yPcqoHy6W}y*AL&r>=3YnGb*T&fW0H#xlyw&GSs3C6_TZr4Fu{d0e^xCe~-{o11N6`4@qH(a}aIIji&s4!9^ipADybJJ;pwXy-1c zb0>b(&+bv*Du7Hp&&S5iM{wX6?%bVairW#sMIj+9r#Z zWtPJ#Uuf1)o8S_(bGG!t7pR9HWo$V&zP+!i-8Z6dL=R78eUH=gCV8q5VxEtN$C%6^ zh`>DAkocRVTyX!14@uB?<9@r?zzwSDD;40yZ{%`iHpTU0rc8FKkMy(Q)x+`RcbV*` zmu$#I$mwb02ac@Ct*!-82n?~s>tPxE^m{UBJ>;J)2+koVe2%V;q!r$PtMT5bjQ7c+ zZ%+2R?6Q5~y2tvkZxovyiAG)l^DfyZ&7d2Lof`fQRpOxW_q;MOo{gTIZMs|LPZZ}? zG7e>qMw|0N&V!((3r>e_`D1;TA7Zn4COJ1OH)5_ zz>CwyGLyZBs-8WdXJC8o?4nC^t7z&{OZ5*tX|ouyQV9Niv=Y{s1Ns57?Jx+S31)PO z%Lf41O9oTSp1Y7wIHKQLTexf?clC-)h$lHOMye||_tO_;pE?OEGHELgMo8=X7U>+f#OU(eW&6yw@Q6--{f-#b4>*BY`Pms+aq*6fMnEtlyhKZZp1zF$l#HF8?F|NgJE z2btO5x=%i6S|wG=f-UD}NBQ1JjSSn2`Y4V3AVhDrdY``+BufU%w!8IZGT`$knX=ry zACsq-m3H_QOY1dQGj-n2Pi%#Y#5IzO1#fSix{WL3L35y8Ifo^G<5AK*y;{9OFHau9 z&hy0Xva)&Y3;VggBpDl<`qW7Eq!WsnU-T39i5@BeLG=L>m(6}@CqAYsxxsFOgAKc5 z_N_9lQ4|jR-@0|0E&koc+sE6RPms~5fJEe}5cO?kX@1G5v0PoNq$TACGqXvW!z;Y-1yf61{d>$Hzz_%xdh0 zN>o7dq~O!i)wQ;!eQ|YW@6FrA;i~K7a|3kS0lqN_zP;h)w^<(BqKy=IIJK{AZ7rRa zf7*I3H;HXMIR~De0>XT}wDQvY|B?TBTCPZ1C+*Agu1QO~i-s;$@`0dr@o$5~G<)4y z?JVOB9xK;zeI_^+Tko%5{8bxQHa0foqc?_9L{@W>XpOsW`_>k!yhNq8LKY%yX>^J_g2}wxi$9grvIr@J%8?&Zh!LAYpq>d zSrm$m8k+G`wts&9%6gUa_tIIZfWzuMp?BP9u}YMfzS!9LZSw&O-f zaG`FV?-kn6#o?Ka{|u$}q==_=Mt*}p<07rx7b-0Z6)jwoLbR&y5qUR-+#SG2loMt_ zA2|N`)gqOrN|l23p*@wt<@~=QUiXL9B8yFiDJ68uMGGryV*DP*mv)N*@NYI#g`5E+ z2?UE(v$-7X{H^VU)*^N~_1<1q84V0vqM&}U0?yx;Q?;`u?rva9x%&{2tvt~n|DE4w zX;m}X+!gTeTs;C7>*>}1USAHU$9nm7e~CV{B$qX>4cg;u0k4lv2zUzAuitW_baOFw zZ=JbfFCyYs5nuog3b|cbJnnh(;;SR-ax|?uDF}825aDO@cpcZBaluzkpI#IRc$nUs zf)jEUn;arO7js8h@N>INU7yomJ#5%cP58NOW-bLDqKtV$meU)$1qB*;34?-`3_OS<}Xk z0AM*MJcl?(I43w~J?A~AGRHaxUEEU~Q7kJl8=4d<$STBRM?k&WIV!x+JQ^@fF?U<7 z*|VCz%OfFK%rR#_Cta*uoF^_M?h|nhE%HSWqQ)rd^Me$iDh{#=m>Ly)&Tc1$LJ4?B zf$>|EAGJ^#6@BpYU?V#_X;w!gdWwRD2SDET0Cb^Ds)3BVwV^^BmS|CzKaP%*!@zpM z4%hI#CU+)K;EMe|IFQ3|2+~J;Tpz>-?C^W&3j>Jq77s@Z<|Adl5h6tT4ssVd4h@P- z>x2DvFs={f!)bU28dSI+0td1g219+Y#gV}Wf4f5q3;78|*rh98&TT%vZ?-n^sB5QF zIj4<{KGg>8Zx`N2bp2lAi&!2)5yAtH9qS$;L6r+%gxHUX6-PW$YcA=-}>vb(iY}mLXxAqn+v1QaA}5 zN2vHEn|B2{mV|3Fmb-XMB^Sf@s$qK$Ky1oj7?&6=dh8hcG~47fG=!g#?mZKyx6Z8r%ip95yK9a$gu-HbR@NKxrKJ3tkNp$LMb?O7^9cFn^HyGG&lXtp+XT^PaeH*@I-Xg8rgt?kW4 zB|wL{l}XS9^u}7*1N^dSv+&Dw%JEWKP-~WXm^9D8*G43Y$m78qIOK4 z#9>oHZ8B(tnUBbj6@o2AacaB(C3o{dd`kPVF?@|_h?$gHSQU%N>^VbLkcKFCZrJqu zLCm?`lqRgFSI+Ktl2_Ukuf(Kcr7`xG+P5j`z^QJ_0rTTX7S35S((huwmQDL&rM62ItdaF+-N|ND!|q$fWX8k`cG2N(~uN z<&xC8eN(=bC9CC5{s9#amLC>Nj^gB4HI%tz_&Jv(#i{t%!2S#9w2@P=I`CDRtQ`V-n_a+ehfR774O<%YX1@nnKt6o_W@#K%7RP80ja{IL;cn zF;tK(+LE3`p74u(LbIvG{;#ZJ8M%)h85&Z`-%{)r`vxq&bk8@+YIMF`r_t^u=IU}i z+{iu1RR@j|?gE5L{d3HO1IhcsJ&{E#ZKGvCfMk{zqz}XO{0-ef5GRv#HxqQW zEIc~_nkgqKz*0DL+!rG441)FmQfUM_JK?_+Y8790>BzyHg$pa@>8>dXI! zrh-X4Ll8^lW2?6U8~pr#Hiat|RYK5(KKsoCl>@qGp+a=-oOie4$Y6ri?u>VP2B$7b z;adVWIlD&tt4QIC{E4To-Pr$u|0VvP!Q|zHM9i8<(lp7)F)A7oEy*c%cVDCx+xQ>8 z6N|OiI5wzWxjVsKuQ)c?UadPTKo&zV*KHn7{2PNW^evpgH;*HWZPEOrqPs*dK3-5j z^x-FO5SV>iOqdx+1t3!o%nI&9y6N&JCC+~&=uS;%mq4c6sQ zT})+7jGyB70!V_`A&$zn1Og)dT=^i*u9goJ$h>qexk`#dd%}oH@K2dOxqEqa6h}6i z26c-OiIBU$)kDZN9M<|%`8@@213bREnCw}5)uQLNrrRh?!ePHWWQMGAsySp=VhF@n zs*ea_2P6n#=VXerVxC0l8bkXpKzdf{JZ5 zb}Lrx=A8yZaQ08k3sjheayz5HA4>anu|rReCq{2!ftjud$NsA!5C$`SCw+15Jo{?9 z8`_;(!OdL!Hyn0m-L#F!!CLV|1gN1?;8L6+1h+Twe35cqjGE@Rv^dTLMWO|k(6ldb zs?5APH1SW2Z_2D6Ndk-pMq1(r_!&p*dP}nY$R=R3$hJnHaokZn$x_%Y;x0~{DvYT5-8g`b zDeCm1pwbuTmT}kys;C;+3TV_uR^JLJ*k6I}GroiUXI(qq{x& zcQ^oD`t9-y=aF}7Bkxw$F5H`y^IsQUYl*&n>RC?E+m*lqz>Q`1iqPR}pBbNRhudY%wHA|ZQQ;eX7C6e6RM*i=8WO`yo1Uk{XqT?f5^SmT# zw8){W-arvFDBpb22_Xa?DAbYda=q$cJ&!8M3}G7{C2mo?R2(m!C(ak<9eNG71@DjU z3E_$Vhcy6xlvvAx%)ZgF$|1$h~eE>!~WY5K~cW86}gU8SsrlL zLS2+PDgd?uPEM@kmm-yNBiK6ax7%gfn7`v-apu9S;)(}I(>nOyHM?Md;HNeu2d(Wj z$j8}9(F2st#~?T&nGD2k*{Dg$Xc%&EPE$@_dQE2CewmACeotaW&Z9x*?WO&lPc87O znIrjIUuVW)i8`6kPkJbKH7LVmrS4MVUa53hkC^!omM|@nD<9&QmibB`XQnKJ=i8UA zSk<|>&&Y5C*XggA%%9{`@pR*i40*;3wiV{XKWicvwDvXbOGy}>4bf<42Cm1`H z(vmp~FNxW%r)q2;g}*AJeqTPfpuqlWpRTbBn%^M~+RQ&NF$W3j{n-(M?gXkV_s*WK zx)9C{iw%K#WZ+rW4xTK|PswnRd-qWoNufwJgwcEp_hA7@*l1*_FEqX;rIRvYvN(N& zz@@lhJw_n)ZW?wGVqlHAnNG);rAHLiT7F6DG;ExFsea?C0*}p4t$(ul{1VJ* z+F{D2CgxvQzs6LU;x8ldo3YG?RK_TUDE@)}6#oOS;Qx0NsD;Y_PoZx~$NLiOc>OsT z-hbXr64$@ojm7|i-W5{|q`NJM6GQw^A0~qaW$y=s-Do@VY!p;jJBY$O$6n>Wpxr!x zhP!vVid!6DCpl4KDE@=SIRAeU(`0(YPv*r?=0$S-sA~>r9Y;VMK?F`-jd_#FVug^6 z71Y2DY;dD_2JER|&O7PDbHL3@f@r%#p1JsQJGXf0i3mS2fY6o$@x)$t1%cBta${E()6+mJLWF+fO^z<+%>X|Z=FT1pe z#%md6H+OIq7hX)^Vz$KoF`J2_y#L3Mi@X-n;}`kR$|DCxVnsBw&8Le#?ch2#BX zNCCnd3p3eD%sr;DlrXPD=uV_QqlN8nayX$rJHUkrAE0X?Do|iY#o=)bBW6KoYj4zI zsr%)$h>XVa6)s{#)lEUEX8>LzH9i;i@bCQ^O;m>Lz#i?eww2sK7}jycFF4CQdNd(_ z%B-W2=3FGi_^>w-dC3zu`vK_> z+dJadBX_QVF;n?;f!F`VLB0hLwoX_iq1t8Ll+MgSBbNQ z|5Y$TE!8>i>el`pTR+@wAB^^}f0mqa3HG!C560D(9C(h-xpi8c@^5i?qKm=;v4?BX z#@`8p#NcmEPJPrYdOUC>6Q5>uk$IN8!G190Un(NfQcr4Mak-V8BLwfIbb~P`gR3WlQ9c}o`(Q!!rUzz5svLi< zvFO`A1iXPa;BM$;4;>xbddkC3=6LK zFuX~Qyob&d7~99lRnHETP%db%yEQ(&Z< z4|*>CjbZ-4+2Jph>K-E-rH(ZkjWC^;Y$n`^9mT^is~e6}5!>#clRfA3{@u7m$u5(h z*L~u)Q0m%JGrb=g`Ul!=FG%k!%fK~HP`Gj%4P@`PftoPQeSJ(qCYbj=8b$jY&?GGR z92yB~s_KsZFj^lcj_3Kgde7uv@NH_yJbwn$-QB_&{zf;?A7B4LnBQWk*UcQtkqS>| zNGaKIXK0U}0rI13AkGz#1Y=f%;qk5MlzHOZub_T8qkQgJzN;=WvG-@Lj{Pq&c_tT} z0$gFP(OuJZ`TbD|`i3X=X4I!p==FZ!`B2aqyXLyo=FfXv*GSV-M=P5>8gkiyzbP>! z+TY(1gX=@|)0aPZh(};v>4%ob1jt79^Iz17(?9d5+rrb0HD6Qi`CtGvW#o;+=eTs0 zU;L!XzKoGjC4tB-isDx<#|>r~`#KsILyf2o-^iiEr4o)HyU@*BJ%^~Jw6P`(cY|_; z^&RoM`TlGX4Oq?=f0iZ753Lll_CJ~7xWhCAK|v<4ut45fT5nLs+#Vf!1;vn9Dq)1B z&}ZO1C6Dm$DHrf$uMKhx@{V?Cul(J6VtXY^s42*(s>uf2?Vr2B3(Y z43*3{!jXd$=ceSj`CZ_haWp?D!(yg)g2Tq-D7&T6U2CQGZ+;NcFynBYMr_!R%H2Y1 z4Cz{04cQff1wW@4O8@A`Eo~tM+1>!&azl-HQ+x=2r zm#i{KA6qb0K;7WT9j!kI_%kxco8^9cwZO4ZK3QxC3UyI2N0rzdNpvZ^MeEiIb~^C# zVrR#A_(OfjuSd14(WdN4AMxrh-d z$cZ>d7&UQPt8RJW-(PWfCv@1qyV2v+O4(sj=cp{$*(Y`ZFD5-14+S)<#)bQ@VV|;> z@ZBg@^mZso@uP@+36F7ipRaKfKCuJZw@ea8-rO9n!GhtqkIsNJ{mZllf;$ zMuvanv;1Ku9!+1L)8)8d)+4Ej4_PJDxM==qhA=&c$-&gNw|@}_4D|!SHKbYOdNO%K z)JEY{`J+F2O?^}U>d63+>6|0SywN)ZfL-m&PxvFi5s^fARs3;>AXj_1F{%)CaGcFI zOsyckqOxG0F?D2zZkgUW-W$Aa?H0#m$xXLKELJJb)V2khtGivXAj`adD0~R+-@o?2 zw$~U^c1u|FP+KdEn0O|B>^&)d5rOZy5jYK+mADB&_22QBdoPUGe)%tzjYa;yp-_}F z_egZvM|jc@@w+CFV1*`o)a~F#vdympoo@x?4Z)*mD>r)kZzN*h`YbXoeCpgk?K3WP z99cX*rNy&MU142v%^k{vH-F3{fPv_TuCj$JEZ#}}-u|?}Ud+OKDNn5)|ZGup`H~O(U?uWlhR6u7Ct|HyNvmwrWBfY(;9=onzu+ z(q@c!XnkZGMAOXitH?9ObLd%!$KR2J`D6;q;^IW5bC~N_^1hh?cT-;(QO+?LCd^Ah zFyvELMQK=fV;H2k_bx+gjwxI48Pmd7^DQ~6kA}HRN+>Chk9g6=4gdkf-$W8_O?86S z{e-s|#LhF+Rxrwxr$uM)bI0qj*Tk^|mtwj67A-TIV`IYusefV;^&>CI$?c8F?|WCx z{fyGzGn6%9=L3>)mmMp<_Xr8^P=DZW&o|E+9!{SKgg?v%hoa4fe@wE=TSBBsJcu)i zJBVM1=Sgfx2uXB_qlG?)qlVjO{Sb&kp^5dumHWr}2)NOHL;Z&PjryD5H_30T-$;)T zwrr>5r|NTD#Z=@x>FE(_w1%Z-)Vlw+wZ^hu)`DF+V+$;LomSb+vFxu>arOLp{QGry z*}BzOrex)Ari7KlB%wuqf6PCoYDorp=0t?IMNH{R06CFgB)Olg8KZ2^f7re;?ka|{ zX@AJ<@-gCSV$;&QF>sHou`j23XazdvkggVs8$0p!*FKsnCgBS$C^mNFMe#^AdIVm;bh`}#_Y$6HcnJMILD$Mlp!0o+WfN5Y5r63E77`mA*@W&;#YtHC|4K#vhMl~mb8jVQg@VQAIbuYfpP zQJRP#nXlHvao?ryGo}*K)raT9TZ`@jH zSoV!RcI&VPFUJ)(Y3aTD?2hdr*m`E?=E=X;M`$*uC#ku!{Z_pN{NjDXobndpGP>y} zbUd@c;j^9e=fQibpxxrSo5bVfDqHCPqq_M|J9z?UMx@vQy+?V1x;iUed^YFXg(SS& z@mk7EiEMqk;MR>ASK&6cu3uld!^eSM7#S`vx3yJJ~6hza#>r@fyw*H&>`RM zs=~zTfvREPbkEI76>=2Ka$nSGudVna=A-i}6brBA?YoBreo2Zv{n4FG+`Q7>_0_yI zr3)X)`V#)KNoxK-PC98Y8klWkQ)A6T<)umTy(fAlX@U=&#!7g~R0If|+L$5s=~>yl zc9q5DCaUSg_ zsUlBIyH zI=d>v(A|}uithruT?2&MCBm+~OKFbU$^D*E_(mb}BzfJr_!wZQZ@PXp)Zd$rcXSg5 zHnvAAH~v4w7UVq6mD{S~S^=TYR)Wt~o!40?jx@qB!sWiCx5C4`ldfiG!legXll1zB z7HUEAgdh0dS3*J!_lR{DD7eSGQ)WP4=Fj<>6mNw?U?Syj_kk}MUS#8N zV?Nk@;m9$%d7hl@WA^iFnZG!rGTDd}Qz2CCb4{=OOOpcU{ZH50TVkj1G>{Q7)$(uu zGK^p4LytXsAvhuPZ-%GxBNG)}&ceosqqoeBnxQTKD}T5GUN78ATR6~^^T;oV4taFU zJy@!Tz3DgzWAXKANO;Ov z{85h2^FC%TUuK3sx-(8rJU9$PNKA~Tih8%3(ZVGF5B$S4g^JCw*zJ7Gt0S4DWX^((?X22&paB=KiNz^<4*Z%Bk<#+ zg*!DQ6Khx%U+6$t+mY(cMnL`JAr4tg>Elp~UgM{sl*AtN5Tdm>LFQ{S%gl5$)kx1> zv&ZH)FMX12%2-yR=kl!vTD1G_x;$?i*C!D>@h;zD?v*(+*%@axBaND)Vba(w3EjgO znK%?v0#F9lnj#wBU5Wa*g$eJn^9%FrK#n{O0}1h4Ryw4)cCI$Z#WJJ1wE}dH9u_n& z&HWV4gN8a3gGj-`*%)(dNqD-JEyM5Bxyu}&J=Bt3N2F5$#s)$vx`wB+jrBxH6lhu| zs$<{5{Tn8n#@^7$Pf;^?jy#UFlpH-ibp@s)c@ZQFbxdR;(cz$>zPSRvbRztd*j2o7 zUj=)ov1iDgkCuKwGW#7f|)x` z>SlEvg}-SJKS^z}m7aLuB(o#6Jxh1W4kxhI!4!qpHvj2x>*{QQX# zZ8`oW`v#ua6=P{UmRcWT9~F~ILD}B;7gZDi!HQ9JknO z)i3W`GrH`n!CaG|EOVmOXEVB{t3eghjp#`L?})hs`Uk=8EPXE-FnE~PA^G`uCk$+K z=K+{y_*W*I#Rk*ehA=Ke2#n+^jp{`l;ya`eTjb3Jdl1O=^TVLxD5}A(F5&e=zvc#G ztoW>U<=?UE7luW@YU%5)Ho;^CXktrF-&HcTy<%rUyFhFguj+XH*{nWy|D(Bd0iae{p0vF4RSssvw3A zUn+h><`h_>a!cUIcG7H13&r?;czwTC0jHB47dx&t@Oze+;>$UOVUk>6m3+%gctFI! z5H%@FrY|?+(N{=o14(8DdSm9ln{Jb;jCHYATC7Z#K0#yO%f@C3fq3yJhTcMEnf8D{ zrs}tCdvDI^wN;}EbC!MAM%ksBj)7RC*qKqt`#BrVHflV?82*P^K`Jaw)`u!?LWx(M zg28GA$@tWhSd?h#FK`>5(B>@IkaU|⁡xddcV`NI&{-&A;hf~zQpRC??m3?0ZBLV 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 Z|2M=R(QxzpFvfG(^Em(jU#TDd{1aQG&olr4 From c3d9bc31bbcdee7418eb888ccb175e9dd9a6b599 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 5 Nov 2013 03:02:28 +0100 Subject: [PATCH 120/132] Fix exiting OpenMW via the window close button (regression) --- apps/openmw/mwinput/inputmanagerimp.cpp | 5 +++++ apps/openmw/mwinput/inputmanagerimp.hpp | 1 + extern/sdl4ogre/events.h | 2 ++ extern/sdl4ogre/sdlinputwrapper.cpp | 3 ++- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index c4a360cd4..f9ea3dfc3 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -641,6 +641,11 @@ namespace MWInput mOgre.windowResized(x,y); } + void InputManager::windowClosed() + { + MWBase::Environment::setRequestExit(); + } + void InputManager::toggleMainMenu() { if (MyGUI::InputManager::getInstance ().isModalAny()) diff --git a/apps/openmw/mwinput/inputmanagerimp.hpp b/apps/openmw/mwinput/inputmanagerimp.hpp index b38c80e0e..e7b7d6c7f 100644 --- a/apps/openmw/mwinput/inputmanagerimp.hpp +++ b/apps/openmw/mwinput/inputmanagerimp.hpp @@ -97,6 +97,7 @@ namespace MWInput virtual void windowVisibilityChange( bool visible ); virtual void windowFocusChange( bool have_focus ); virtual void windowResized (int x, int y); + virtual void windowClosed (); virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue); diff --git a/extern/sdl4ogre/events.h b/extern/sdl4ogre/events.h index e6e8434cb..48adb4545 100644 --- a/extern/sdl4ogre/events.h +++ b/extern/sdl4ogre/events.h @@ -70,6 +70,8 @@ public: /** @remarks The window got / lost input focus */ virtual void windowFocusChange( bool have_focus ) {} + virtual void windowClosed () {} + virtual void windowResized (int x, int y) {} }; diff --git a/extern/sdl4ogre/sdlinputwrapper.cpp b/extern/sdl4ogre/sdlinputwrapper.cpp index df74bba3b..e7cbd549c 100644 --- a/extern/sdl4ogre/sdlinputwrapper.cpp +++ b/extern/sdl4ogre/sdlinputwrapper.cpp @@ -103,7 +103,8 @@ namespace SFO handleWindowEvent(evt); break; case SDL_QUIT: - Ogre::Root::getSingleton().queueEndRendering(); + if (mWindowListener) + mWindowListener->windowClosed(); break; default: std::cerr << "Unhandled SDL event of type " << evt.type << std::endl; From bf5529819d9c907b6fe28b8fdd72337c2fd7fbd2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 5 Nov 2013 19:50:24 +0100 Subject: [PATCH 121/132] Added combo box widget and use it in options menu. Fixes to VBox / HBox. Added savegame dialog. --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwgui/mainmenu.cpp | 15 +++++ apps/openmw/mwgui/savegamedialog.cpp | 49 ++++++++++++++ apps/openmw/mwgui/savegamedialog.hpp | 37 +++++++++++ apps/openmw/mwgui/settingswindow.cpp | 42 ++---------- apps/openmw/mwgui/settingswindow.hpp | 8 +-- apps/openmw/mwgui/widgets.cpp | 40 +++++++++-- files/mygui/CMakeLists.txt | 1 + files/mygui/openmw_list.skin.xml | 13 ++++ files/mygui/openmw_resources.xml | 18 +++++ files/mygui/openmw_savegame_dialog.layout | 81 +++++++++++++++++++++++ files/mygui/openmw_settings_window.layout | 19 ++++-- 12 files changed, 274 insertions(+), 51 deletions(-) create mode 100644 apps/openmw/mwgui/savegamedialog.cpp create mode 100644 apps/openmw/mwgui/savegamedialog.hpp create mode 100644 files/mygui/openmw_savegame_dialog.layout diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index b367e2a1e..f07bbab86 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -34,7 +34,7 @@ add_openmw_dir (mwgui enchantingdialog trainingwindow travelwindow imagebutton exposedwindow cursor spellicons merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview - tradeitemmodel companionitemmodel pickpocketitemmodel fontloader controllers + tradeitemmodel companionitemmodel pickpocketitemmodel fontloader controllers savegamedialog ) add_openmw_dir (mwdialogue diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 2c42dc210..fa7ed2ace 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -9,6 +9,8 @@ #include "../mwbase/journal.hpp" #include "../mwbase/dialoguemanager.hpp" +#include "savegamedialog.hpp" + namespace MWGui { @@ -85,6 +87,19 @@ namespace MWGui MWBase::Environment::get().getDialogueManager()->clear(); MWBase::Environment::get().getJournal()->clear(); } + + else if (sender == mButtons["loadgame"]) + { + MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); + dialog->setLoadOrSave(true); + dialog->setVisible(true); + } + else if (sender == mButtons["savegame"]) + { + MWGui::SaveGameDialog* dialog = new MWGui::SaveGameDialog(); + dialog->setLoadOrSave(false); + dialog->setVisible(true); + } } } diff --git a/apps/openmw/mwgui/savegamedialog.cpp b/apps/openmw/mwgui/savegamedialog.cpp new file mode 100644 index 000000000..a1acd3588 --- /dev/null +++ b/apps/openmw/mwgui/savegamedialog.cpp @@ -0,0 +1,49 @@ +#include "savegamedialog.hpp" +#include "widgets.hpp" + + +namespace MWGui +{ + + SaveGameDialog::SaveGameDialog() + : WindowModal("openmw_savegame_dialog.layout") + { + getWidget(mScreenshot, "Screenshot"); + getWidget(mCharacterSelection, "SelectCharacter"); + getWidget(mInfoText, "InfoText"); + getWidget(mOkButton, "OkButton"); + getWidget(mCancelButton, "CancelButton"); + getWidget(mSaveList, "SaveList"); + getWidget(mSaveNameEdit, "SaveNameEdit"); + getWidget(mSpacer, "Spacer"); + mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked); + mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked); + + } + + void SaveGameDialog::open() + { + center(); + } + + void SaveGameDialog::setLoadOrSave(bool load) + { + mSaveNameEdit->setVisible(!load); + mCharacterSelection->setUserString("Hidden", load ? "false" : "true"); + mCharacterSelection->setVisible(load); + mSpacer->setUserString("Hidden", load ? "false" : "true"); + + center(); + } + + void SaveGameDialog::onCancelButtonClicked(MyGUI::Widget *sender) + { + setVisible(false); + } + + void SaveGameDialog::onOkButtonClicked(MyGUI::Widget *sender) + { + setVisible(false); + } + +} diff --git a/apps/openmw/mwgui/savegamedialog.hpp b/apps/openmw/mwgui/savegamedialog.hpp new file mode 100644 index 000000000..1a3178ef3 --- /dev/null +++ b/apps/openmw/mwgui/savegamedialog.hpp @@ -0,0 +1,37 @@ +#ifndef OPENMW_MWGUI_SAVEGAMEDIALOG_H +#define OPENMW_MWGUI_SAVEGAMEDIALOG_H + +#include "windowbase.hpp" + +namespace MWGui +{ + + class SaveGameDialog : public MWGui::WindowModal + { + public: + SaveGameDialog(); + + virtual void open(); + + void setLoadOrSave(bool load); + + void onCancelButtonClicked (MyGUI::Widget* sender); + void onOkButtonClicked (MyGUI::Widget* sender); + + + private: + MyGUI::ImageBox* mScreenshot; + + MyGUI::ComboBox* mCharacterSelection; + MyGUI::EditBox* mInfoText; + MyGUI::Button* mOkButton; + MyGUI::Button* mCancelButton; + MyGUI::ListBox* mSaveList; + MyGUI::EditBox* mSaveNameEdit; + MyGUI::Widget* mSpacer; + + }; + +} + +#endif diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 923b9d01d..1969c5651 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -31,9 +31,7 @@ namespace std::string textureFilteringToStr(const std::string& val) { - if (val == "none") - return "None"; - else if (val == "anisotropic") + if (val == "anisotropic") return "Anisotropic"; else if (val == "bilinear") return "Bilinear"; @@ -145,7 +143,7 @@ namespace MWGui mReflectObjectsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mReflectTerrainButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mReflectActorsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - mTextureFilteringButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onTextureFilteringToggled); + mTextureFilteringButton->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onTextureFilteringChanged); mVSyncButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mFPSButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onFpsToggled); mMenuTransparencySlider->eventScrollChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onSliderChangePosition); @@ -157,7 +155,7 @@ namespace MWGui mShadowsEnabledButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mShadowsLargeDistance->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); - mShadowsTextureSize->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadowTextureSize); + mShadowsTextureSize->eventComboChangePosition += MyGUI::newDelegate(this, &SettingsWindow::onShadowTextureSizeChanged); mActorShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mStaticsShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mMiscShadows->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); @@ -297,22 +295,9 @@ namespace MWGui mResolutionList->setIndexSelected(MyGUI::ITEM_NONE); } - void SettingsWindow::onShadowTextureSize(MyGUI::Widget* _sender) + void SettingsWindow::onShadowTextureSizeChanged(MyGUI::ComboBox *_sender, size_t pos) { - std::string size = mShadowsTextureSize->getCaption(); - - if (size == "512") - size = "1024"; - else if (size == "1024") - size = "2048"; - else if (size == "2048") - size = "4096"; - else - size = "512"; - - mShadowsTextureSize->setCaption(size); - - Settings::Manager::setString("texture size", "Shadows", size); + Settings::Manager::setString("texture size", "Shadows", _sender->getItemNameAt(pos)); apply(); } @@ -482,22 +467,9 @@ namespace MWGui apply(); } - void SettingsWindow::onTextureFilteringToggled(MyGUI::Widget* _sender) + void SettingsWindow::onTextureFilteringChanged(MyGUI::ComboBox* _sender, size_t pos) { - std::string current = Settings::Manager::getString("texture filtering", "General"); - std::string next; - if (current == "none") - next = "bilinear"; - else if (current == "bilinear") - next = "trilinear"; - else if (current == "trilinear") - next = "anisotropic"; - else - next = "none"; - - mTextureFilteringButton->setCaption(textureFilteringToStr(next)); - - Settings::Manager::setString("texture filtering", "General", next); + Settings::Manager::setString("texture filtering", "General", Misc::StringUtils::lowerCase(_sender->getItemNameAt(pos))); apply(); } diff --git a/apps/openmw/mwgui/settingswindow.hpp b/apps/openmw/mwgui/settingswindow.hpp index a585bda7e..c81a86ab0 100644 --- a/apps/openmw/mwgui/settingswindow.hpp +++ b/apps/openmw/mwgui/settingswindow.hpp @@ -42,7 +42,7 @@ namespace MWGui MyGUI::ScrollBar* mViewDistanceSlider; MyGUI::ScrollBar* mFOVSlider; MyGUI::ScrollBar* mAnisotropySlider; - MyGUI::Button* mTextureFilteringButton; + MyGUI::ComboBox* mTextureFilteringButton; MyGUI::TextBox* mAnisotropyLabel; MyGUI::Widget* mAnisotropyBox; MyGUI::Button* mWaterShaderButton; @@ -55,7 +55,7 @@ namespace MWGui MyGUI::Button* mShadowsEnabledButton; MyGUI::Button* mShadowsLargeDistance; - MyGUI::Button* mShadowsTextureSize; + MyGUI::ComboBox* mShadowsTextureSize; MyGUI::Button* mActorShadows; MyGUI::Button* mStaticsShadows; MyGUI::Button* mMiscShadows; @@ -76,7 +76,7 @@ namespace MWGui void onOkButtonClicked(MyGUI::Widget* _sender); void onFpsToggled(MyGUI::Widget* _sender); - void onTextureFilteringToggled(MyGUI::Widget* _sender); + void onTextureFilteringChanged(MyGUI::ComboBox* _sender, size_t pos); void onSliderChangePosition(MyGUI::ScrollBar* scroller, size_t pos); void onButtonToggled(MyGUI::Widget* _sender); void onResolutionSelected(MyGUI::ListBox* _sender, size_t index); @@ -85,7 +85,7 @@ namespace MWGui void onShadersToggled(MyGUI::Widget* _sender); void onShaderModeToggled(MyGUI::Widget* _sender); - void onShadowTextureSize(MyGUI::Widget* _sender); + void onShadowTextureSizeChanged(MyGUI::ComboBox* _sender, size_t pos); void onRebindAction(MyGUI::Widget* _sender); void onInputTabMouseWheel(MyGUI::Widget* _sender, int _rel); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 3fc3187e8..c37ae15fa 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -689,22 +689,26 @@ namespace MWGui int total_width = 0; int total_height = 0; std::vector< std::pair > sizes; + sizes.resize(count); for (unsigned int i = 0; i < count; ++i) { MyGUI::Widget* w = getChildAt(i); bool hstretch = w->getUserString ("HStretch") == "true"; + bool hidden = w->getUserString("Hidden") == "true"; + if (hidden) + continue; h_stretched_count += hstretch; AutoSizedWidget* aw = dynamic_cast(w); if (aw) { - sizes.push_back(std::make_pair(aw->getRequestedSize (), hstretch)); + sizes[i] = std::make_pair(aw->getRequestedSize (), hstretch); total_width += aw->getRequestedSize ().width; total_height = std::max(total_height, aw->getRequestedSize ().height); } else { - sizes.push_back (std::make_pair(w->getSize(), hstretch)); + sizes[i] = std::make_pair(w->getSize(), hstretch); total_width += w->getSize().width; if (!(w->getUserString("VStretch") == "true")) total_height = std::max(total_height, w->getSize().height); @@ -729,8 +733,13 @@ namespace MWGui MyGUI::Widget* w = getChildAt(i); + bool hidden = w->getUserString("Hidden") == "true"; + if (hidden) + continue; + bool vstretch = w->getUserString ("VStretch") == "true"; - int height = vstretch ? total_height : sizes[i].first.height; + int max_height = getSize().height - mPadding*2; + int height = vstretch ? max_height : sizes[i].first.height; MyGUI::IntCoord widgetCoord; widgetCoord.left = curX; @@ -774,6 +783,10 @@ namespace MWGui MyGUI::IntSize size(0,0); for (unsigned int i = 0; i < getChildCount (); ++i) { + bool hidden = getChildAt(i)->getUserString("Hidden") == "true"; + if (hidden) + continue; + AutoSizedWidget* w = dynamic_cast(getChildAt(i)); if (w) { @@ -810,21 +823,27 @@ namespace MWGui int total_height = 0; int total_width = 0; std::vector< std::pair > sizes; + sizes.resize(count); for (unsigned int i = 0; i < count; ++i) { MyGUI::Widget* w = getChildAt(i); + + bool hidden = w->getUserString("Hidden") == "true"; + if (hidden) + continue; + bool vstretch = w->getUserString ("VStretch") == "true"; v_stretched_count += vstretch; AutoSizedWidget* aw = dynamic_cast(w); if (aw) { - sizes.push_back(std::make_pair(aw->getRequestedSize (), vstretch)); + sizes[i] = std::make_pair(aw->getRequestedSize (), vstretch); total_height += aw->getRequestedSize ().height; total_width = std::max(total_width, aw->getRequestedSize ().width); } else { - sizes.push_back (std::make_pair(w->getSize(), vstretch)); + sizes[i] = std::make_pair(w->getSize(), vstretch); total_height += w->getSize().height; if (!(w->getUserString("HStretch") == "true")) @@ -850,8 +869,13 @@ namespace MWGui MyGUI::Widget* w = getChildAt(i); + bool hidden = w->getUserString("Hidden") == "true"; + if (hidden) + continue; + bool hstretch = w->getUserString ("HStretch") == "true"; - int width = hstretch ? total_width : sizes[i].first.width; + int maxWidth = getSize().width - mPadding*2; + int width = hstretch ? maxWidth : sizes[i].first.width; MyGUI::IntCoord widgetCoord; widgetCoord.top = curY; @@ -890,6 +914,10 @@ namespace MWGui MyGUI::IntSize size(0,0); for (unsigned int i = 0; i < getChildCount (); ++i) { + bool hidden = getChildAt(i)->getUserString("Hidden") == "true"; + if (hidden) + continue; + AutoSizedWidget* w = dynamic_cast(getChildAt(i)); if (w) { diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 1ec1e08cb..3329c01f9 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -81,6 +81,7 @@ set(MYGUI_FILES openmw_merchantrepair.layout openmw_repair.layout openmw_companion_window.layout + openmw_savegame_dialog.layout smallbars.png DejaVuLGCSansMono.ttf markers.png diff --git a/files/mygui/openmw_list.skin.xml b/files/mygui/openmw_list.skin.xml index 02c11c354..7972527ac 100644 --- a/files/mygui/openmw_list.skin.xml +++ b/files/mygui/openmw_list.skin.xml @@ -126,6 +126,19 @@ + + + + + + + + + + + + + diff --git a/files/mygui/openmw_resources.xml b/files/mygui/openmw_resources.xml index 2c3908a1b..3901ab825 100644 --- a/files/mygui/openmw_resources.xml +++ b/files/mygui/openmw_resources.xml @@ -301,4 +301,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_savegame_dialog.layout b/files/mygui/openmw_savegame_dialog.layout new file mode 100644 index 000000000..18de6a239 --- /dev/null +++ b/files/mygui/openmw_savegame_dialog.layout @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files/mygui/openmw_settings_window.layout b/files/mygui/openmw_settings_window.layout index ebfaf678a..e6002b51d 100644 --- a/files/mygui/openmw_settings_window.layout +++ b/files/mygui/openmw_settings_window.layout @@ -216,7 +216,11 @@ - + + + + + @@ -308,9 +312,9 @@ - + - + @@ -336,9 +340,14 @@ - + + + + + + - + From 9e2b1942fc119d05b6f6a9e7e1638f44d663a03d Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 5 Nov 2013 22:50:53 +0100 Subject: [PATCH 122/132] Fix journal for not installed tribunal (Options button should become Topics). Don't log an error when optional journal buttons (Tribunal) are not found. --- apps/openmw/mwgui/imagebutton.cpp | 5 +++-- apps/openmw/mwgui/imagebutton.hpp | 2 +- apps/openmw/mwgui/journalwindow.cpp | 27 ++++++++++++++++++++------- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/imagebutton.cpp b/apps/openmw/mwgui/imagebutton.cpp index 98f05373b..f2565f5c0 100644 --- a/apps/openmw/mwgui/imagebutton.cpp +++ b/apps/openmw/mwgui/imagebutton.cpp @@ -42,12 +42,13 @@ namespace MWGui ImageBox::onMouseButtonPressed(_left, _top, _id); } - MyGUI::IntSize ImageButton::getRequestedSize() + MyGUI::IntSize ImageButton::getRequestedSize(bool logError) { Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().getByName(mImageNormal); if (texture.isNull()) { - std::cerr << "ImageButton: can't find " << mImageNormal << std::endl; + if (logError) + std::cerr << "ImageButton: can't find " << mImageNormal << std::endl; return MyGUI::IntSize(0,0); } return MyGUI::IntSize (texture->getWidth(), texture->getHeight()); diff --git a/apps/openmw/mwgui/imagebutton.hpp b/apps/openmw/mwgui/imagebutton.hpp index f531e2246..f4191a3a5 100644 --- a/apps/openmw/mwgui/imagebutton.hpp +++ b/apps/openmw/mwgui/imagebutton.hpp @@ -14,7 +14,7 @@ namespace MWGui MYGUI_RTTI_DERIVED(ImageButton) public: - MyGUI::IntSize getRequestedSize(); + MyGUI::IntSize getRequestedSize(bool logError = true); protected: virtual void setPropertyOverride(const std::string& _key, const std::string& _value); diff --git a/apps/openmw/mwgui/journalwindow.cpp b/apps/openmw/mwgui/journalwindow.cpp index ab8dc1584..f3c9e9c73 100644 --- a/apps/openmw/mwgui/journalwindow.cpp +++ b/apps/openmw/mwgui/journalwindow.cpp @@ -137,15 +137,28 @@ namespace getPage (QuestsPage)->adviseLinkClicked (callback); } - adjustButton(OptionsBTN); + adjustButton(OptionsBTN, true); adjustButton(PrevPageBTN); adjustButton(NextPageBTN); adjustButton(CloseBTN); adjustButton(CancelBTN); - adjustButton(ShowAllBTN); - adjustButton(ShowActiveBTN); + adjustButton(ShowAllBTN, true); + adjustButton(ShowActiveBTN, true); adjustButton(JournalBTN); + MWGui::ImageButton* optionsButton = getWidget(OptionsBTN); + if (optionsButton->getWidth() == 0) + { + // If tribunal is not installed (-> no options button), we still want the Topics button available, + // so place it where the options button would have been + MWGui::ImageButton* topicsButton = getWidget(TopicsBTN); + topicsButton->detachFromWidget(); + topicsButton->attachToWidget(optionsButton->getParent()); + topicsButton->setPosition(optionsButton->getPosition()); + topicsButton->eventMouseButtonClick.clear(); + topicsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &JournalWindowImpl::notifyOptions); + } + MWGui::ImageButton* nextButton = getWidget(NextPageBTN); if (nextButton->getSize().width == 64) { @@ -155,7 +168,7 @@ namespace } adjustButton(TopicsBTN); - adjustButton(QuestsBTN); + adjustButton(QuestsBTN, true); int width = getWidget(TopicsBTN)->getSize().width + getWidget(QuestsBTN)->getSize().width; int topicsWidth = getWidget(TopicsBTN)->getSize().width; int pageWidth = getWidget(RightBookPage)->getSize().width; @@ -167,12 +180,12 @@ namespace mAllQuests = false; } - void adjustButton (char const * name) + void adjustButton (char const * name, bool optional = false) { MWGui::ImageButton* button = getWidget(name); - MyGUI::IntSize diff = button->getSize() - button->getRequestedSize(); - button->setSize(button->getRequestedSize()); + MyGUI::IntSize diff = button->getSize() - button->getRequestedSize(!optional); + button->setSize(button->getRequestedSize(!optional)); if (button->getAlign().isRight()) button->setPosition(button->getPosition() + MyGUI::IntPoint(diff.width,0)); From e94da61ff03f77897a7a9100864d6c5d165735a0 Mon Sep 17 00:00:00 2001 From: Jordan Ayers Date: Tue, 5 Nov 2013 19:38:45 -0600 Subject: [PATCH 123/132] 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 816f42e3d..9b742742d 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 124/132] 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 9b742742d..eb087ea07 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 bd8826677..bb2a69a7a 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 125/132] 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 eb087ea07..b829f219d 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 bb2a69a7a..b80aaae41 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 4b4d2dfd1..76ae65eb9 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 126/132] 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 4d45264ae..efa2f4764 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 d317331e5..7498044ab 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 127/132] 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 4d45264ae..9e6f4eb9c 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 cbe96a217020c0424a76a6256b40bc25f34b9a7e Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 9 Nov 2013 07:51:46 +0100 Subject: [PATCH 128/132] Refactor ActiveSpells to track range type. Added basic self range magic. --- apps/openmw/mwbase/world.hpp | 2 + apps/openmw/mwgui/spellicons.cpp | 27 ++++++--- apps/openmw/mwgui/tooltips.cpp | 2 +- apps/openmw/mwmechanics/activespells.cpp | 74 ++++++++++++++++++------ apps/openmw/mwmechanics/activespells.hpp | 26 ++++++++- apps/openmw/mwmechanics/alchemy.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 5 ++ apps/openmw/mwrender/animation.cpp | 3 + apps/openmw/mwworld/worldimp.cpp | 73 +++++++++++++++++++++++ apps/openmw/mwworld/worldimp.hpp | 2 + 10 files changed, 187 insertions(+), 29 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index c39e87826..8ae563e12 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -406,6 +406,8 @@ namespace MWBase virtual bool getGodModeState() = 0; virtual bool toggleGodMode() = 0; + + virtual void castSpell (const MWWorld::Ptr& actor) = 0; }; } diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index 0c303485a..5d002792a 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -97,6 +97,8 @@ namespace MWGui } // add lasting effect spells/potions etc + + // TODO: Move this to ActiveSpells const MWMechanics::ActiveSpells::TContainer& activeSpells = stats.getActiveSpells().getActiveSpells(); for (MWMechanics::ActiveSpells::TContainer::const_iterator it = activeSpells.begin(); it != activeSpells.end(); ++it) @@ -105,31 +107,36 @@ namespace MWGui float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); + int i=0; for (std::vector::const_iterator effectIt = list.mList.begin(); - effectIt != list.mList.end(); ++effectIt) + effectIt != list.mList.end(); ++effectIt, ++i) { + if (effectIt->mRange != it->second.mRange) + continue; + + float randomFactor = it->second.mRandom[i]; + const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld ()->getStore ().get().find(effectIt->mEffectID); MagicEffectInfo effectInfo; - effectInfo.mSource = getSpellDisplayName (it->first); + effectInfo.mSource = it->second.mName; effectInfo.mKey = MWMechanics::EffectKey (effectIt->mEffectID); if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill) effectInfo.mKey.mArg = effectIt->mSkill; else if (magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute) effectInfo.mKey.mArg = effectIt->mAttribute; - effectInfo.mMagnitude = effectIt->mMagnMin + (effectIt->mMagnMax-effectIt->mMagnMin) * it->second.second; + effectInfo.mMagnitude = effectIt->mMagnMin + (effectIt->mMagnMax-effectIt->mMagnMin) * randomFactor; effectInfo.mRemainingTime = effectIt->mDuration + - (it->second.first - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale; + (it->second.mTimeStamp - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale; // ingredients need special casing for their magnitude / duration - /// \todo duplicated from ActiveSpells, helper function? if (MWBase::Environment::get().getWorld()->getStore().get().search (it->first)) { - effectInfo.mRemainingTime = effectIt->mDuration * it->second.second + - (it->second.first - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale; + effectInfo.mRemainingTime = effectIt->mDuration * randomFactor + + (it->second.mTimeStamp - MWBase::Environment::get().getWorld()->getTimeStamp())*3600/timeScale; - effectInfo.mMagnitude = static_cast (0.05*it->second.second / (0.1 * magicEffect->mData.mBaseCost)); + effectInfo.mMagnitude = static_cast (0.05*randomFactor / (0.1 * magicEffect->mData.mBaseCost)); } effects[effectIt->mEffectID].push_back (effectInfo); @@ -288,6 +295,10 @@ namespace MWGui ESM::EffectList SpellIcons::getSpellEffectList (const std::string& id) { + if (const ESM::Enchantment* enchantment = + MWBase::Environment::get().getWorld()->getStore().get().search (id)) + return enchantment->mEffects; + if (const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get().search (id)) return spell->mEffects; diff --git a/apps/openmw/mwgui/tooltips.cpp b/apps/openmw/mwgui/tooltips.cpp index 3a609aa91..85c71575b 100644 --- a/apps/openmw/mwgui/tooltips.cpp +++ b/apps/openmw/mwgui/tooltips.cpp @@ -467,7 +467,7 @@ namespace MWGui } Widgets::MWDynamicStatPtr chargeWidget = enchantArea->createWidget ("MW_ChargeBar", chargeCoord, MyGUI::Align::Default, "ToolTipEnchantCharge"); - chargeWidget->setValue(charge, charge); + chargeWidget->setValue(charge, maxCharge); totalSize.height += 24; } } diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 9aca6b7b7..c433ac5e7 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -15,6 +15,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/soundmanager.hpp" #include "../mwworld/class.hpp" @@ -64,15 +65,19 @@ namespace MWMechanics { std::pair > effects = getEffectList (iter->first); - const MWWorld::TimeStamp& start = iter->second.first; - float magnitude = iter->second.second; + const MWWorld::TimeStamp& start = iter->second.mTimeStamp; - for (std::vector::const_iterator iter (effects.first.mList.begin()); - iter!=effects.first.mList.end(); ++iter) + int i = 0; + for (std::vector::const_iterator effectIter (effects.first.mList.begin()); + effectIter!=effects.first.mList.end(); ++effectIter, ++i) { - if (iter->mDuration) + float magnitude = iter->second.mRandom[i]; + if (effectIter->mRange != iter->second.mRange) + continue; + + if (effectIter->mDuration) { - int duration = iter->mDuration; + int duration = effectIter->mDuration; if (effects.second.first) duration *= magnitude; @@ -89,9 +94,9 @@ namespace MWMechanics { const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find ( - iter->mEffectID); + effectIter->mEffectID); - if (iter->mDuration==0) + if (effectIter->mDuration==0) { param.mMagnitude = static_cast (magnitude / (0.1 * magicEffect->mData.mBaseCost)); @@ -104,9 +109,9 @@ namespace MWMechanics } else param.mMagnitude = static_cast ( - (iter->mMagnMax-iter->mMagnMin)*magnitude + iter->mMagnMin); + (effectIter->mMagnMax-effectIter->mMagnMin)*magnitude + effectIter->mMagnMin); - mEffects.add (*iter, param); + mEffects.add (*effectIter, param); } } } @@ -115,6 +120,10 @@ namespace MWMechanics std::pair > ActiveSpells::getEffectList (const std::string& id) const { + if (const ESM::Enchantment* enchantment = + MWBase::Environment::get().getWorld()->getStore().get().search (id)) + return std::make_pair (enchantment->mEffects, std::make_pair(false, false)); + if (const ESM::Spell *spell = MWBase::Environment::get().getWorld()->getStore().get().search (id)) return std::make_pair (spell->mEffects, std::make_pair(false, false)); @@ -156,7 +165,7 @@ namespace MWMechanics : mSpellsChanged (false), mLastUpdate (MWBase::Environment::get().getWorld()->getTimeStamp()) {} - bool ActiveSpells::addSpell (const std::string& id, const MWWorld::Ptr& actor) + bool ActiveSpells::addSpell (const std::string& id, const MWWorld::Ptr& actor, ESM::RangeType range, const std::string& name) { std::pair > effects = getEffectList (id); bool stacks = effects.second.second; @@ -196,11 +205,41 @@ namespace MWMechanics random *= 0.25 * x; } + ActiveSpellParams params; + for (unsigned int i=0; i (std::rand()) / RAND_MAX); + params.mRange = range; + params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp(); + params.mName = name; + if (iter==mSpells.end() || stacks) - mSpells.insert (std::make_pair (id, - std::make_pair (MWBase::Environment::get().getWorld()->getTimeStamp(), random))); + mSpells.insert (std::make_pair (id, params)); else - iter->second = std::make_pair (MWBase::Environment::get().getWorld()->getTimeStamp(), random); + iter->second = params; + + // Play sounds + for (std::vector::const_iterator iter (effects.first.mList.begin()); + iter!=effects.first.mList.end(); ++iter) + { + if (iter->mRange != range) + continue; + + const ESM::MagicEffect *magicEffect = + MWBase::Environment::get().getWorld()->getStore().get().find ( + iter->mEffectID); + + static const std::string schools[] = { + "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" + }; + + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(!magicEffect->mHitSound.empty()) + sndMgr->playSound3D(actor, magicEffect->mHitSound, 1.0f, 1.0f); + else + sndMgr->playSound3D(actor, schools[magicEffect->mData.mSchool]+" hit", 1.0f, 1.0f); + + break; + } mSpellsChanged = true; @@ -249,13 +288,14 @@ namespace MWMechanics duration = iter->mDuration; } - if (effects.second.first) - duration *= iterator->second.second; + // Scale duration by magnitude if needed + if (effects.second.first && iterator->second.mRandom.size()) + duration *= iterator->second.mRandom.front(); double scaledDuration = duration * MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); - double usedUp = MWBase::Environment::get().getWorld()->getTimeStamp()-iterator->second.first; + double usedUp = MWBase::Environment::get().getWorld()->getTimeStamp()-iterator->second.mTimeStamp; if (usedUp>=scaledDuration) return 0; diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 8c859b2cb..e3f882b9a 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -9,6 +9,8 @@ #include "magiceffects.hpp" +#include + namespace ESM { struct Spell; @@ -22,6 +24,22 @@ namespace MWWorld namespace MWMechanics { + struct ActiveSpellParams + { + // Only apply effects of this range type + ESM::RangeType mRange; + + // When the spell was added + MWWorld::TimeStamp mTimeStamp; + + // Random factor for each effect + std::vector mRandom; + + // Display name, we need this for enchantments, which don't have a name - so you need to supply the + // name of the item with the enchantment to addSpell + std::string mName; + }; + /// \brief Lasting spell effects /// /// \note The name of this class is slightly misleading, since it also handels lasting potion @@ -30,7 +48,7 @@ namespace MWMechanics { public: - typedef std::multimap > TContainer; + typedef std::multimap TContainer; typedef TContainer::const_iterator TIterator; private: @@ -51,9 +69,13 @@ namespace MWMechanics ActiveSpells(); - bool addSpell (const std::string& id, const MWWorld::Ptr& actor); + bool addSpell (const std::string& id, const MWWorld::Ptr& actor, ESM::RangeType range = ESM::RT_Self, const std::string& name = ""); ///< Overwrites an existing spell with the same ID. If the spell does not have any /// non-instant effects, it is ignored. + /// @param id + /// @param actor + /// @param range Only effects with range type \a range will be applied + /// @param name Display name for enchantments, since they don't have a name in their record /// /// \return Has the spell been added? diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index 1d992be41..adfd6d5fc 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -273,7 +273,7 @@ void MWMechanics::Alchemy::addPotion (const std::string& name) newRecord.mName = name; - int index = static_cast (std::rand()/static_cast (RAND_MAX)*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" }; diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index c4260d907..8c88b3ad0 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -531,6 +531,11 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun else sndMgr->playSound3D(mPtr, schools[effect->mData.mSchool]+" cast", 1.0f, 1.0f); } + if (inv.getSelectedEnchantItem() != inv.end()) + { + // Enchanted items cast immediately (no animation) + MWBase::Environment::get().getWorld()->castSpell(mPtr); + } } else if(mWeaponType == WeapType_PickProbe) { diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 545060fe3..53d697fdd 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -653,6 +653,9 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Thrust); else if(evt.compare(off, len, "hit") == 0) MWWorld::Class::get(mPtr).hit(mPtr); + + else if (groupname == "spellcast" && evt.substr(evt.size()-7, 7) == "release") + MWBase::Environment::get().getWorld()->castSpell(mPtr); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9635feaf3..2c8236f3b 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -19,6 +19,8 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/movement.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/spellsuccess.hpp" + #include "../mwrender/sky.hpp" #include "../mwrender/animation.hpp" @@ -1983,4 +1985,75 @@ namespace MWWorld return mGodMode; } + void World::castSpell(const Ptr &actor) + { + MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); + stats.setAttackingOrSpell(false); + + std::string selectedSpell = stats.getSpells().getSelectedSpell(); + if (!selectedSpell.empty()) + { + const ESM::Spell* spell = getStore().get().search(selectedSpell); + + // Check mana + MWMechanics::DynamicStat magicka = stats.getMagicka(); + if (magicka.getCurrent() < spell->mData.mCost) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientSP}"); + return; + } + + // Reduce mana + magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost); + stats.setMagicka(magicka); + + // Check success + int successChance = MWMechanics::getSpellSuccessChance(selectedSpell, actor); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (roll >= successChance) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}"); + // TODO sound: "Spell Failure " + return; + } + + + actor.getClass().getCreatureStats(actor).getActiveSpells().addSpell(selectedSpell, actor, ESM::RT_Self); + // TODO: RT_Range, RT_Touch + return; + } + + InventoryStore& inv = actor.getClass().getInventoryStore(actor); + if (inv.getSelectedEnchantItem() != inv.end()) + { + MWWorld::Ptr item = *inv.getSelectedEnchantItem(); + std::string id = item.getClass().getEnchantment(item); + const ESM::Enchantment* enchantment = getStore().get().search (id); + + // Check if there's enough charge left + const float enchantCost = enchantment->mData.mCost; + MWMechanics::NpcStats &stats = MWWorld::Class::get(actor).getNpcStats(actor); + int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified(); + const float castCost = enchantCost - (enchantCost / 100) * (eSkill - 10); + + if (item.getCellRef().mEnchantmentCharge == -1) + item.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge; + + if (item.getCellRef().mEnchantmentCharge < castCost) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); + return; + } + + // Reduce charge + item.getCellRef().mEnchantmentCharge -= castCost; + + std::string itemName = item.getClass().getName(item); + actor.getClass().getCreatureStats(actor).getActiveSpells().addSpell(id, actor, ESM::RT_Self, itemName); + + + // TODO: RT_Range, RT_Touch + } + } + } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index e275756e8..6d05df6ff 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -453,6 +453,8 @@ namespace MWWorld virtual bool getGodModeState(); virtual bool toggleGodMode(); + + virtual void castSpell (const MWWorld::Ptr& actor); }; } From 1051611ffaba6fcf7dbb09830ecd2f7786fa50af Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 9 Nov 2013 07:59:17 +0100 Subject: [PATCH 129/132] Added spell failure sound --- apps/openmw/mwworld/worldimp.cpp | 33 +++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 2c8236f3b..5b7eb7bb1 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1996,24 +1996,47 @@ namespace MWWorld const ESM::Spell* spell = getStore().get().search(selectedSpell); // Check mana + bool fail = false; MWMechanics::DynamicStat magicka = stats.getMagicka(); if (magicka.getCurrent() < spell->mData.mCost) { MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientSP}"); - return; + fail = true; } // Reduce mana - magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost); - stats.setMagicka(magicka); + if (!fail) + { + magicka.setCurrent(magicka.getCurrent() - spell->mData.mCost); + stats.setMagicka(magicka); + } // Check success int successChance = MWMechanics::getSpellSuccessChance(selectedSpell, actor); int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] - if (roll >= successChance) + if (!fail && roll >= successChance) { MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}"); - // TODO sound: "Spell Failure " + fail = true; + } + + if (fail) + { + // Failure sound + for (std::vector::const_iterator iter (spell->mEffects.mList.begin()); + iter!=spell->mEffects.mList.end(); ++iter) + { + const ESM::MagicEffect *magicEffect = getStore().get().find ( + iter->mEffectID); + + static const std::string schools[] = { + "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" + }; + + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + sndMgr->playSound3D(actor, "Spell Failure " + schools[magicEffect->mData.mSchool], 1.0f, 1.0f); + break; + } return; } From 976344f0a352c6904c04a6f13996aa140035b520 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 9 Nov 2013 08:07:40 +0100 Subject: [PATCH 130/132] Handle CastOnce enchantments --- apps/openmw/mwworld/worldimp.cpp | 43 ++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 5b7eb7bb1..a91f1e00c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2053,27 +2053,44 @@ namespace MWWorld std::string id = item.getClass().getEnchantment(item); const ESM::Enchantment* enchantment = getStore().get().search (id); - // Check if there's enough charge left - const float enchantCost = enchantment->mData.mCost; - MWMechanics::NpcStats &stats = MWWorld::Class::get(actor).getNpcStats(actor); - int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified(); - const float castCost = enchantCost - (enchantCost / 100) * (eSkill - 10); - if (item.getCellRef().mEnchantmentCharge == -1) - item.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge; + if (enchantment->mData.mType == ESM::Enchantment::WhenUsed) + { + // Check if there's enough charge left + const float enchantCost = enchantment->mData.mCost; + MWMechanics::NpcStats &stats = MWWorld::Class::get(actor).getNpcStats(actor); + int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified(); + const float castCost = enchantCost - (enchantCost / 100) * (eSkill - 10); + + if (item.getCellRef().mEnchantmentCharge == -1) + item.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge; + + if (item.getCellRef().mEnchantmentCharge < castCost) + { + MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); + return; + } - if (item.getCellRef().mEnchantmentCharge < castCost) + // Reduce charge + item.getCellRef().mEnchantmentCharge -= castCost; + } + if (enchantment->mData.mType == ESM::Enchantment::CastOnce) { - MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}"); - return; + item.getRefData().setCount(item.getRefData().getCount()-1); } - // Reduce charge - item.getCellRef().mEnchantmentCharge -= castCost; - std::string itemName = item.getClass().getName(item); actor.getClass().getCreatureStats(actor).getActiveSpells().addSpell(id, actor, ESM::RT_Self, itemName); + if (!item.getRefData().getCount()) + { + // Item was used up + MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); + inv.setSelectedEnchantItem(inv.end()); + } + else + MWBase::Environment::get().getWindowManager()->setSelectedEnchantItem(item); // Set again to show the modified charge + // TODO: RT_Range, RT_Touch } From 3ea7d58ca8b48ed252345fbad73dddbeea1815b4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 9 Nov 2013 10:34:46 +0100 Subject: [PATCH 131/132] Advance skill on successfull spell cast --- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwworld/worldimp.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index f07bbab86..e2dbf40de 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -68,7 +68,7 @@ add_openmw_dir (mwclass add_openmw_dir (mwmechanics mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow - aiescort aiactivate repair enchanting pathfinding security + aiescort aiactivate repair enchanting pathfinding security spellsuccess ) add_openmw_dir (mwbase diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a91f1e00c..6c18ec4fd 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2040,6 +2040,7 @@ namespace MWWorld return; } + actor.getClass().skillUsageSucceeded(actor, MWMechanics::spellSchoolToSkill(MWMechanics::getSpellSchool(selectedSpell, actor)), 0); actor.getClass().getCreatureStats(actor).getActiveSpells().addSpell(selectedSpell, actor, ESM::RT_Self); // TODO: RT_Range, RT_Touch From 3e58655a902ffe96311d8060ca61d3f1377d5b41 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sat, 9 Nov 2013 10:49:00 +0100 Subject: [PATCH 132/132] Implemented Fortify/Drain skill magic effects. Scroll of icarian flight works! --- apps/openmw/mwmechanics/actors.cpp | 15 +++++++++++++++ apps/openmw/mwmechanics/actors.hpp | 3 +++ 2 files changed, 18 insertions(+) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 42851dea3..39276e964 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -47,6 +47,7 @@ namespace MWMechanics if(!paused) { updateDrowning(ptr, duration); + calculateNpcStatModifiers(ptr); updateEquippedLight(ptr, duration); } } @@ -170,6 +171,20 @@ namespace MWMechanics } } + void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr) + { + NpcStats &npcStats = MWWorld::Class::get(ptr).getNpcStats(ptr); + const MagicEffects &effects = npcStats.getMagicEffects(); + + // skills + for(int i = 0;i < ESM::Skill::Length;++i) + { + Stat& skill = npcStats.getSkill(i); + skill.setModifier(effects.get(EffectKey(ESM::MagicEffect::FortifySkill, i)).mMagnitude - + effects.get(EffectKey(ESM::MagicEffect::DrainSkill, i)).mMagnitude); + } + } + void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration) { MWBase::World *world = MWBase::Environment::get().getWorld(); diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index a77e52ba3..eeef22635 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -34,11 +34,14 @@ namespace MWMechanics void updateNpc(const MWWorld::Ptr &ptr, float duration, bool paused); + + void adjustMagicEffects (const MWWorld::Ptr& creature); void calculateDynamicStats (const MWWorld::Ptr& ptr); void calculateCreatureStatModifiers (const MWWorld::Ptr& ptr); + void calculateNpcStatModifiers (const MWWorld::Ptr& ptr); void calculateRestoration (const MWWorld::Ptr& ptr, float duration);