Merge branch 'openmw-27'

Conflicts:
	apps/launcher/graphicspage.hpp
	components/CMakeLists.txt
actorid
Marc Zinnschlag 11 years ago
commit 418ccf705d

@ -318,6 +318,9 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg
"${OpenMW_BINARY_DIR}/opencs.cfg") "${OpenMW_BINARY_DIR}/opencs.cfg")
configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters
"${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY)
if (NOT WIN32 AND NOT APPLE) if (NOT WIN32 AND NOT APPLE)
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop
@ -457,10 +460,20 @@ if(WIN32)
"${OpenMW_SOURCE_DIR}/Daedric Font License.txt" "${OpenMW_SOURCE_DIR}/Daedric Font License.txt"
"${OpenMW_BINARY_DIR}/settings-default.cfg" "${OpenMW_BINARY_DIR}/settings-default.cfg"
"${OpenMW_BINARY_DIR}/transparency-overrides.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" "${OpenMW_BINARY_DIR}/Release/openmw.exe"
DESTINATION ".") 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 ".") INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".")
SET(CPACK_GENERATOR "NSIS") SET(CPACK_GENERATOR "NSIS")
@ -470,7 +483,13 @@ if(WIN32)
SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR})
SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR}) SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR})
SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) 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_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'")
SET(CPACK_NSIS_DELETE_ICONS_EXTRA " SET(CPACK_NSIS_DELETE_ICONS_EXTRA "
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP

@ -305,14 +305,14 @@ int load(Arguments& info)
info.data.author = esm.getAuthor(); info.data.author = esm.getAuthor();
info.data.description = esm.getDesc(); info.data.description = esm.getDesc();
info.data.masters = esm.getMasters(); info.data.masters = esm.getGameFiles();
if (!quiet) if (!quiet)
{ {
std::cout << "Author: " << esm.getAuthor() << std::endl std::cout << "Author: " << esm.getAuthor() << std::endl
<< "Description: " << esm.getDesc() << std::endl << "Description: " << esm.getDesc() << std::endl
<< "File format version: " << esm.getFVer() << std::endl; << "File format version: " << esm.getFVer() << std::endl;
std::vector<ESM::Header::MasterData> m = esm.getMasters(); std::vector<ESM::Header::MasterData> m = esm.getGameFiles();
if (!m.empty()) if (!m.empty())
{ {
std::cout << "Masters:" << std::endl; std::cout << "Masters:" << std::endl;

@ -11,7 +11,9 @@ set(LAUNCHER
settings/launchersettings.cpp settings/launchersettings.cpp
utils/checkablemessagebox.cpp utils/checkablemessagebox.cpp
utils/profilescombobox.cpp
utils/textinputdialog.cpp utils/textinputdialog.cpp
utils/lineedit.cpp
${CMAKE_SOURCE_DIR}/files/launcher/launcher.rc ${CMAKE_SOURCE_DIR}/files/launcher/launcher.rc
) )
@ -32,8 +34,9 @@ set(LAUNCHER_HEADER
settings/settingsbase.hpp settings/settingsbase.hpp
utils/checkablemessagebox.hpp utils/checkablemessagebox.hpp
utils/profilescombobox.hpp
utils/textinputdialog.hpp utils/textinputdialog.hpp
utils/lineedit.hpp
) )
if(NOT WIN32) if(NOT WIN32)
LIST(APPEND LAUNCHER_HEADER unshieldthread.hpp) LIST(APPEND LAUNCHER_HEADER unshieldthread.hpp)
@ -48,8 +51,11 @@ set(LAUNCHER_HEADER_MOC
playpage.hpp playpage.hpp
textslotmsgbox.hpp textslotmsgbox.hpp
utils/checkablemessagebox.hpp
utils/textinputdialog.hpp utils/textinputdialog.hpp
utils/checkablemessagebox.hpp
utils/profilescombobox.hpp
utils/lineedit.hpp
) )
if(NOT WIN32) if(NOT WIN32)
@ -62,6 +68,7 @@ set(LAUNCHER_UI
${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui ${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui
${CMAKE_SOURCE_DIR}/files/ui/mainwindow.ui ${CMAKE_SOURCE_DIR}/files/ui/mainwindow.ui
${CMAKE_SOURCE_DIR}/files/ui/playpage.ui ${CMAKE_SOURCE_DIR}/files/ui/playpage.ui
${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui
) )
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER}) source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER})

@ -4,548 +4,307 @@
#include <QMessageBox> #include <QMessageBox>
#include <QCheckBox> #include <QCheckBox>
#include <QMenu> #include <QMenu>
#include <QSortFilterProxyModel>
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#include <components/fileorderlist/model/datafilesmodel.hpp> #include <components/contentselector/model/esmfile.hpp>
#include <components/fileorderlist/model/pluginsproxymodel.hpp>
#include <components/fileorderlist/model/esm/esmfile.hpp>
#include <components/fileorderlist/utils/lineedit.hpp> #include <components/contentselector/model/naturalsort.hpp>
#include <components/fileorderlist/utils/naturalsort.hpp>
#include <components/fileorderlist/utils/profilescombobox.hpp> #include "utils/textinputdialog.hpp"
#include "utils/profilescombobox.hpp"
#include "settings/gamesettings.hpp" #include "settings/gamesettings.hpp"
#include "settings/launchersettings.hpp" #include "settings/launchersettings.hpp"
#include "utils/textinputdialog.hpp" #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) : mCfgMgr(cfg)
, mGameSettings(gameSettings) , mGameSettings(gameSettings)
, mLauncherSettings(launcherSettings) , mLauncherSettings(launcherSettings)
, QWidget(parent) , QWidget(parent)
{ {
setupUi(this); ui.setupUi (this);
setObjectName ("DataFilesPage");
// Models mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget);
mDataFilesModel = new DataFilesModel(this);
mMastersProxyModel = new QSortFilterProxyModel();
mMastersProxyModel->setFilterRegExp(QString("^.*\\.esm"));
mMastersProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
mMastersProxyModel->setSourceModel(mDataFilesModel);
mPluginsProxyModel = new PluginsProxyModel();
mPluginsProxyModel->setFilterRegExp(QString("^.*\\.esp"));
mPluginsProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
mPluginsProxyModel->setSourceModel(mDataFilesModel);
mFilterProxyModel = new QSortFilterProxyModel();
mFilterProxyModel->setDynamicSortFilter(true);
mFilterProxyModel->setSourceModel(mPluginsProxyModel);
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<int> 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(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews()));
connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)));
connect(splitter, SIGNAL(splitterMoved(int,int)), this, SLOT(updateSplitter())); buildView();
createActions();
setupDataFiles(); setupDataFiles();
} }
void DataFilesPage::createActions() void Launcher::DataFilesPage::loadSettings()
{
// 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()
{ {
// Set the encoding to the one found in openmw.cfg or the default
mDataFilesModel->setEncoding(mGameSettings.value(QString("encoding"), QString("win1252")));
QStringList paths = mGameSettings.getDataDirs(); QStringList paths = mGameSettings.getDataDirs();
paths.insert (0, mDataLocal);
PathIterator pathIterator (paths);
foreach (const QString &path, paths) { QString profileName = ui.profilesComboBox->currentText();
mDataFilesModel->addFiles(path);
}
QString dataLocal = mGameSettings.getDataLocal();
if (!dataLocal.isEmpty())
mDataFilesModel->addFiles(dataLocal);
// Sort by date accessed for now QStringList files = mLauncherSettings.values(QString("Profiles/") + profileName, Qt::MatchExactly);
mDataFilesModel->sort(3);
QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/")); QStringList filepaths;
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();
}
void DataFilesPage::loadSettings() foreach (const QString &file, files)
{ {
QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); QString filepath = pathIterator.findFirstPath (file);
if (profile.isEmpty())
return;
mDataFilesModel->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) { if (!filepath.isEmpty())
QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(master)); filepaths << filepath;
if (index.isValid())
mDataFilesModel->setCheckState(index, Qt::Checked);
} }
foreach (const QString &plugin, plugins) { mSelector->setProfileContent (filepaths);
QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(plugin));
if (index.isValid())
mDataFilesModel->setCheckState(index, Qt::Checked);
}
} }
void DataFilesPage::saveSettings() void Launcher::DataFilesPage::saveSettings(const QString &profile)
{ {
if (mDataFilesModel->rowCount() < 1) QString profileName = profile;
return;
QString profile = mLauncherSettings.value(QString("Profiles/currentprofile"));
if (profile.isEmpty()) { if (profileName.isEmpty())
profile = profilesComboBox->currentText(); profileName = ui.profilesComboBox->currentText();
mLauncherSettings.setValue(QString("Profiles/currentprofile"), profile);
}
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master")); //retrieve the files selected for the profile
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin")); ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();
mGameSettings.remove(QString("master")); removeProfile (profileName);
mGameSettings.remove(QString("plugin"));
QStringList items = mDataFilesModel->checkedItems(); mGameSettings.remove(QString("content"));
foreach(const QString &item, items) { //set the value of the current profile (not necessarily the profile being saved!)
mLauncherSettings.setValue(QString("Profiles/currentprofile"), ui.profilesComboBox->currentText());
if (item.endsWith(QString(".esm"), Qt::CaseInsensitive)) { foreach(const ContentSelectorModel::EsmFile *item, items) {
mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/master"), item);
mGameSettings.setMultiValue(QString("master"), item);
} else if (item.endsWith(QString(".esp"), Qt::CaseInsensitive)) { if (item->gameFiles().size() == 0) {
mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/plugin"), item); mLauncherSettings.setMultiValue(QString("Profiles/") + profileName, item->fileName());
mGameSettings.setMultiValue(QString("plugin"), item); mGameSettings.setMultiValue(QString("content"), item->fileName());
} else {
mLauncherSettings.setMultiValue(QString("Profiles/") + profileName, item->fileName());
mGameSettings.setMultiValue(QString("content"), item->fileName());
} }
} }
} }
void DataFilesPage::updateOkButton(const QString &text) void Launcher::DataFilesPage::buildView()
{ {
// We do this here because we need the profiles combobox text ui.verticalLayout->insertWidget (0, mSelector->uiWidget());
if (text.isEmpty()) {
mNewProfileDialog->setOkButtonEnabled(false);
return;
}
(profilesComboBox->findText(text) == -1) //tool buttons
? mNewProfileDialog->setOkButtonEnabled(true) ui.newProfileButton->setToolTip ("Create a new profile");
: mNewProfileDialog->setOkButtonEnabled(false); ui.deleteProfileButton->setToolTip ("Delete an existing profile");
}
void DataFilesPage::updateSplitter() //combo box
{ ui.profilesComboBox->addItem ("Default");
// Sigh, update the saved splitter size in settings only when moved ui.profilesComboBox->setPlaceholderText (QString("Select a profile..."));
// Since getting mSplitter->sizes() if page is hidden returns invalid values
QList<int> sizes = splitter->sizes();
mLauncherSettings.setValue(QString("General/MastersTable/width"), QString::number(sizes.at(0))); // Add the actions to the toolbuttons
mLauncherSettings.setValue(QString("General/PluginsTable/width"), QString::number(sizes.at(1))); ui.newProfileButton->setDefaultAction (ui.newProfileAction);
} ui.deleteProfileButton->setDefaultAction (ui.deleteProfileAction);
void DataFilesPage::updateViews() //establish connections
{ connect (ui.profilesComboBox, SIGNAL (currentIndexChanged(int)),
// Ensure the columns are hidden because sort() re-enables them this, SLOT (slotProfileChanged(int)));
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) connect (ui.profilesComboBox, SIGNAL (profileRenamed(QString, QString)),
{ this, SLOT (slotProfileRenamed(QString, QString)));
profilesComboBox->setCurrentIndex(index);
connect (ui.profilesComboBox, SIGNAL (signalProfileChanged(QString, QString)),
this, SLOT (slotProfileChangedByUser(QString, QString)));
} }
void DataFilesPage::slotCurrentIndexChanged(int index) void Launcher::DataFilesPage::removeProfile(const QString &profile)
{ {
emit profileChanged(index); mLauncherSettings.remove(QString("Profiles/") + profile + QString("/game"));
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/addon"));
} }
QAbstractItemModel* DataFilesPage::profilesComboBoxModel() QAbstractItemModel *Launcher::DataFilesPage::profilesModel() const
{ {
return profilesComboBox->model(); return ui.profilesComboBox->model();
} }
int DataFilesPage::profilesComboBoxIndex() int Launcher::DataFilesPage::profilesIndex() const
{ {
return profilesComboBox->currentIndex(); return ui.profilesComboBox->currentIndex();
} }
void DataFilesPage::on_newProfileAction_triggered() void Launcher::DataFilesPage::setProfile(int index, bool savePrevious)
{ {
if (mNewProfileDialog->exec() == QDialog::Accepted) { if (index >= -1 && index < ui.profilesComboBox->count())
QString profile = mNewProfileDialog->lineEdit()->text(); {
profilesComboBox->addItem(profile); QString previous = ui.profilesComboBox->itemText(ui.profilesComboBox->currentIndex());
profilesComboBox->setCurrentIndex(profilesComboBox->findText(profile)); QString current = ui.profilesComboBox->itemText(index);
setProfile (previous, current, savePrevious);
} }
} }
void DataFilesPage::on_deleteProfileAction_triggered() void Launcher::DataFilesPage::setProfile (const QString &previous, const QString &current, bool savePrevious)
{ {
QString profile = profilesComboBox->currentText(); //abort if no change (poss. duplicate signal)
if (previous == current)
if (profile.isEmpty()) return;
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 <b>%0</b>?").arg(profile));
QAbstractButton *deleteButton = if (!previous.isEmpty() && savePrevious)
msgBox.addButton(tr("Delete"), QMessageBox::ActionRole); saveSettings (previous);
msgBox.exec(); ui.profilesComboBox->setCurrentProfile (ui.profilesComboBox->findText (current));
if (msgBox.clickedButton() == deleteButton) { loadSettings();
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master"));
mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin"));
// Remove the profile from the combobox checkForDefaultProfile();
profilesComboBox->removeItem(profilesComboBox->findText(profile));
}
} }
void DataFilesPage::on_checkAction_triggered() void Launcher::DataFilesPage::slotProfileDeleted (const QString &item)
{ {
if (pluginsTable->hasFocus()) removeProfile (item);
setPluginsCheckstates(Qt::Checked);
if (mastersTable->hasFocus())
setMastersCheckstates(Qt::Checked);
} }
void DataFilesPage::on_uncheckAction_triggered() void Launcher::DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString &current)
{ {
if (pluginsTable->hasFocus()) setProfile(previous, current, true);
setPluginsCheckstates(Qt::Unchecked); emit signalProfileChanged (ui.profilesComboBox->findText(current));
if (mastersTable->hasFocus())
setMastersCheckstates(Qt::Unchecked);
} }
void DataFilesPage::setMastersCheckstates(Qt::CheckState state) void Launcher::DataFilesPage::slotProfileRenamed(const QString &previous, const QString &current)
{ {
if (!mastersTable->selectionModel()->hasSelection()) { if (previous.isEmpty())
return; return;
}
QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes();
foreach (const QModelIndex &index, indexes)
{
if (!index.isValid())
return;
QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index); // Save the new profile name
saveSettings();
if (!sourceIndex.isValid()) // Remove the old one
return; removeProfile (previous);
mDataFilesModel->setCheckState(sourceIndex, state); loadSettings();
}
} }
void DataFilesPage::setPluginsCheckstates(Qt::CheckState state) void Launcher::DataFilesPage::slotProfileChanged(int index)
{ {
if (!pluginsTable->selectionModel()->hasSelection()) { setProfile (index, true);
return;
}
QModelIndexList indexes = pluginsTable->selectionModel()->selectedIndexes();
foreach (const QModelIndex &index, indexes)
{
if (!index.isValid())
return;
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(
mFilterProxyModel->mapToSource(index));
if (!sourceIndex.isValid())
return;
mDataFilesModel->setCheckState(sourceIndex, state);
}
} }
void DataFilesPage::setCheckState(QModelIndex index) void Launcher::DataFilesPage::setupDataFiles()
{ {
if (!index.isValid()) QStringList paths = mGameSettings.getDataDirs();
return;
QObject *object = QObject::sender(); foreach (const QString &path, paths)
mSelector->addFiles(path);
// Not a signal-slot call mDataLocal = mGameSettings.getDataLocal();
if (!object)
return;
if (!mDataLocal.isEmpty())
mSelector->addFiles(mDataLocal);
if (object->objectName() == QLatin1String("PluginsTable")) { QStringList profiles;
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource( QString currentProfile = mLauncherSettings.getSettings().value("Profiles/currentprofile");
mFilterProxyModel->mapToSource(index));
if (sourceIndex.isValid()) { foreach (QString key, mLauncherSettings.getSettings().keys())
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked) {
? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) if (key.contains("Profiles/"))
: mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); {
QString profile = key.mid (9);
if (profile != "currentprofile")
{
if (!profiles.contains(profile))
profiles << profile;
}
} }
} }
if (object->objectName() == QLatin1String("MastersTable")) { foreach (const QString &item, profiles)
QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index); addProfile (item, false);
if (sourceIndex.isValid()) {
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked)
? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked)
: mDataFilesModel->setCheckState(sourceIndex, Qt::Checked);
}
}
return; setProfile (ui.profilesComboBox->findText(currentProfile), false);
}
void DataFilesPage::filterChanged(const QString filter) loadSettings();
{
QRegExp regExp(filter, Qt::CaseInsensitive, QRegExp::FixedString);
mFilterProxyModel->setFilterRegExp(regExp);
} }
void DataFilesPage::profileChanged(const QString &previous, const QString &current) void Launcher::DataFilesPage::on_newProfileAction_triggered()
{ {
// Prevent the deletion of the default profile TextInputDialog newDialog (tr("New Profile"), tr("Profile name:"), this);
if (current == QLatin1String("Default")) {
deleteProfileAction->setEnabled(false);
profilesComboBox->setEditEnabled(false);
} else {
deleteProfileAction->setEnabled(true);
profilesComboBox->setEditEnabled(true);
}
if (previous.isEmpty()) if (newDialog.exec() != QDialog::Accepted)
return; return;
if (profilesComboBox->findText(previous) == -1) QString profile = newDialog.getText();
return; // Profile was deleted
// Store the previous profile
mLauncherSettings.setValue(QString("Profiles/currentprofile"), previous);
saveSettings();
mLauncherSettings.setValue(QString("Profiles/currentprofile"), current);
loadSettings();
}
void DataFilesPage::profileRenamed(const QString &previous, const QString &current) if (profile.isEmpty())
{ return;
if (previous.isEmpty())
return;
// Save the new profile name
mLauncherSettings.setValue(QString("Profiles/currentprofile"), current);
saveSettings(); saveSettings();
// Remove the old one mSelector->clearCheckStates();
mLauncherSettings.remove(QString("Profiles/") + previous + QString("/master"));
mLauncherSettings.remove(QString("Profiles/") + previous + QString("/plugin"));
// Remove the profile from the combobox addProfile(profile, true);
profilesComboBox->removeItem(profilesComboBox->findText(previous));
loadSettings(); mSelector->setGameFile();
saveSettings();
emit signalProfileChanged (ui.profilesComboBox->findText(profile));
} }
void DataFilesPage::showContextMenu(const QPoint &point) void Launcher::DataFilesPage::addProfile (const QString &profile, bool setAsCurrent)
{ {
QObject *object = QObject::sender(); if (profile.isEmpty())
return;
// Not a signal-slot call if (ui.profilesComboBox->findText (profile) != -1)
if (!object)
return; return;
if (object->objectName() == QLatin1String("PluginsTable")) { ui.profilesComboBox->addItem (profile);
if (!pluginsTable->selectionModel()->hasSelection())
return;
QPoint globalPos = pluginsTable->mapToGlobal(point); if (setAsCurrent)
QModelIndexList indexes = pluginsTable->selectionModel()->selectedIndexes(); setProfile (ui.profilesComboBox->findText (profile), false);
}
// Show the check/uncheck actions depending on the state of the selected items void Launcher::DataFilesPage::on_deleteProfileAction_triggered()
uncheckAction->setEnabled(false); {
checkAction->setEnabled(false); QString profile = ui.profilesComboBox->currentText();
foreach (const QModelIndex &index, indexes) if (profile.isEmpty())
{ return;
if (!index.isValid())
return;
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource( if (!showDeleteMessageBox (profile))
mFilterProxyModel->mapToSource(index)); return;
if (!sourceIndex.isValid()) // Remove the profile from the combobox
return; ui.profilesComboBox->removeItem (ui.profilesComboBox->findText (profile));
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked) removeProfile(profile);
? uncheckAction->setEnabled(true)
: checkAction->setEnabled(true);
}
// Show menu saveSettings();
mContextMenu->exec(globalPos);
}
if (object->objectName() == QLatin1String("MastersTable")) { loadSettings();
if (!mastersTable->selectionModel()->hasSelection())
return;
QPoint globalPos = mastersTable->mapToGlobal(point); checkForDefaultProfile();
QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes(); }
// Show the check/uncheck actions depending on the state of the selected items void Launcher::DataFilesPage::checkForDefaultProfile()
uncheckAction->setEnabled(false); {
checkAction->setEnabled(false); //don't allow deleting "Default" profile
bool success = (ui.profilesComboBox->currentText() != "Default");
foreach (const QModelIndex &index, indexes) ui.deleteProfileAction->setEnabled (success);
{ ui.profilesComboBox->setEditEnabled (success);
if (!index.isValid()) }
return;
QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index); bool Launcher::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 <b>%0</b>?").arg(text));
if (!sourceIndex.isValid()) QAbstractButton *deleteButton =
return; msgBox.addButton(tr("Delete"), QMessageBox::ActionRole);
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked) msgBox.exec();
? uncheckAction->setEnabled(true)
: checkAction->setEnabled(true);
}
mContextMenu->exec(globalPos); return (msgBox.clickedButton() == deleteButton);
}
} }

@ -1,88 +1,136 @@
#ifndef DATAFILESPAGE_H #ifndef DATAFILESPAGE_H
#define DATAFILESPAGE_H #define DATAFILESPAGE_H
#include "ui_datafilespage.h"
#include <QWidget> #include <QWidget>
#include <QModelIndex>
#include "ui_datafilespage.h"
#include <QDir>
#include <QFile>
class QSortFilterProxyModel; class QSortFilterProxyModel;
class QAbstractItemModel; class QAbstractItemModel;
class QAction;
class QMenu; class QMenu;
class DataFilesModel;
class TextInputDialog;
class GameSettings;
class LauncherSettings;
class PluginsProxyModel;
namespace Files { struct ConfigurationManager; } namespace Files { struct ConfigurationManager; }
namespace ContentSelectorView { class ContentSelector; }
class DataFilesPage : public QWidget, private Ui::DataFilesPage namespace Launcher
{ {
Q_OBJECT class TextInputDialog;
class GameSettings;
class LauncherSettings;
class ProfilesComboBox;
class DataFilesPage : public QWidget
{
Q_OBJECT
ContentSelectorView::ContentSelector *mSelector;
Ui::DataFilesPage ui;
public:
explicit DataFilesPage (Files::ConfigurationManager &cfg, GameSettings &gameSettings,
LauncherSettings &launcherSettings, QWidget *parent = 0);
QAbstractItemModel* profilesModel() const;
int profilesIndex() const;
//void writeConfig(QString profile = QString());
void saveSettings(const QString &profile = "");
void loadSettings();
signals:
void signalProfileChanged (int index);
public slots:
void slotProfileChanged (int index);
private slots:
void slotProfileChangedByUser(const QString &previous, const QString &current);
void slotProfileRenamed(const QString &previous, const QString &current);
void slotProfileDeleted(const QString &item);
public: void on_newProfileAction_triggered();
DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent = 0); void on_deleteProfileAction_triggered();
QAbstractItemModel* profilesComboBoxModel(); private:
int profilesComboBoxIndex();
void writeConfig(QString profile = QString()); QMenu *mContextMenu;
void saveSettings();
signals: Files::ConfigurationManager &mCfgMgr;
void profileChanged(int index);
public slots: GameSettings &mGameSettings;
void setCheckState(QModelIndex index); LauncherSettings &mLauncherSettings;
void setProfilesComboBoxIndex(int index);
void filterChanged(const QString filter); QString mDataLocal;
void showContextMenu(const QPoint &point);
void profileChanged(const QString &previous, const QString &current);
void profileRenamed(const QString &previous, const QString &current);
void updateOkButton(const QString &text);
void updateSplitter();
void updateViews();
// Action slots void setPluginsCheckstates(Qt::CheckState state);
void on_newProfileAction_triggered();
void on_deleteProfileAction_triggered();
void on_checkAction_triggered();
void on_uncheckAction_triggered();
private slots: void buildView();
void slotCurrentIndexChanged(int index); void setupDataFiles();
void setupConfig();
void readConfig();
void setProfile (int index, bool savePrevious);
void setProfile (const QString &previous, const QString &current, bool savePrevious);
void removeProfile (const QString &profile);
bool showDeleteMessageBox (const QString &text);
void addProfile (const QString &profile, bool setAsCurrent);
void checkForDefaultProfile();
private: class PathIterator
DataFilesModel *mDataFilesModel; {
QStringList::ConstIterator mCitEnd;
QStringList::ConstIterator mCitCurrent;
QStringList::ConstIterator mCitBegin;
QString mFile;
QString mFilePath;
PluginsProxyModel *mPluginsProxyModel; public:
QSortFilterProxyModel *mMastersProxyModel; PathIterator (const QStringList &list)
{
mCitBegin = list.constBegin();
mCitCurrent = mCitBegin;
mCitEnd = list.constEnd();
}
QSortFilterProxyModel *mFilterProxyModel; QString findFirstPath (const QString &file)
{
mCitCurrent = mCitBegin;
mFile = file;
return path();
}
QMenu *mContextMenu; QString findNextPath () { return path(); }
Files::ConfigurationManager &mCfgMgr; private:
GameSettings &mGameSettings; QString path ()
LauncherSettings &mLauncherSettings; {
bool success = false;
QDir dir;
QFileInfo file;
TextInputDialog *mNewProfileDialog; while (!success)
{
if (mCitCurrent == mCitEnd)
break;
void setMastersCheckstates(Qt::CheckState state); dir.setPath (*(mCitCurrent++));
void setPluginsCheckstates(Qt::CheckState state); file.setFile (dir.absoluteFilePath (mFile));
void createActions(); success = file.exists();
void setupDataFiles(); }
void setupConfig();
void readConfig();
void loadSettings(); if (success)
return file.absoluteFilePath();
}; return "";
}
};
};
}
#endif #endif

@ -14,7 +14,7 @@
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#include <components/fileorderlist/utils/naturalsort.hpp> #include <components/contentselector/model/naturalsort.hpp>
#include "settings/graphicssettings.hpp" #include "settings/graphicssettings.hpp"
@ -30,11 +30,12 @@ QString getAspect(int x, int y)
return QString(QString::number(xaspect) + ":" + QString::number(yaspect)); 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) : mCfgMgr(cfg)
, mGraphicsSettings(graphicsSetting) , mGraphicsSettings(graphicsSetting)
, QWidget(parent) , QWidget(parent)
{ {
setObjectName ("GraphicsPage");
setupUi(this); setupUi(this);
// Set the maximum res we can set in windowed mode // Set the maximum res we can set in windowed mode
@ -49,7 +50,7 @@ GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &g
} }
bool GraphicsPage::setupOgre() bool Launcher::GraphicsPage::setupOgre()
{ {
try try
{ {
@ -115,7 +116,7 @@ bool GraphicsPage::setupOgre()
return true; return true;
} }
bool GraphicsPage::setupSDL() bool Launcher::GraphicsPage::setupSDL()
{ {
int displays = SDL_GetNumVideoDisplays(); int displays = SDL_GetNumVideoDisplays();
@ -138,7 +139,7 @@ bool GraphicsPage::setupSDL()
return true; return true;
} }
bool GraphicsPage::loadSettings() bool Launcher::GraphicsPage::loadSettings()
{ {
if (!setupSDL()) if (!setupSDL())
return false; return false;
@ -177,7 +178,7 @@ bool GraphicsPage::loadSettings()
return true; return true;
} }
void GraphicsPage::saveSettings() void Launcher::GraphicsPage::saveSettings()
{ {
vSyncCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/vsync"), QString("true")) vSyncCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/vsync"), QString("true"))
: mGraphicsSettings.setValue(QString("Video/vsync"), QString("false")); : mGraphicsSettings.setValue(QString("Video/vsync"), QString("false"));
@ -204,7 +205,7 @@ void GraphicsPage::saveSettings()
mGraphicsSettings.setValue(QString("Video/screen"), QString::number(screenComboBox->currentIndex())); 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; QStringList result;
@ -237,7 +238,7 @@ QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSy
return result; return result;
} }
QStringList GraphicsPage::getAvailableResolutions(int screen) QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen)
{ {
QStringList result; QStringList result;
SDL_DisplayMode mode; SDL_DisplayMode mode;
@ -284,7 +285,7 @@ QStringList GraphicsPage::getAvailableResolutions(int screen)
return result; return result;
} }
QRect GraphicsPage::getMaximumResolution() QRect Launcher::GraphicsPage::getMaximumResolution()
{ {
QRect max; QRect max;
int screens = QApplication::desktop()->screenCount(); int screens = QApplication::desktop()->screenCount();
@ -299,7 +300,7 @@ QRect GraphicsPage::getMaximumResolution()
return max; return max;
} }
void GraphicsPage::rendererChanged(const QString &renderer) void Launcher::GraphicsPage::rendererChanged(const QString &renderer)
{ {
mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString()); mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString());
@ -308,7 +309,7 @@ void GraphicsPage::rendererChanged(const QString &renderer)
antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem)); antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem));
} }
void GraphicsPage::screenChanged(int screen) void Launcher::GraphicsPage::screenChanged(int screen)
{ {
if (screen >= 0) { if (screen >= 0) {
resolutionComboBox->clear(); resolutionComboBox->clear();
@ -316,7 +317,7 @@ void GraphicsPage::screenChanged(int screen)
} }
} }
void GraphicsPage::slotFullScreenChanged(int state) void Launcher::GraphicsPage::slotFullScreenChanged(int state)
{ {
if (state == Qt::Checked) { if (state == Qt::Checked) {
standardRadioButton->toggle(); standardRadioButton->toggle();
@ -330,7 +331,7 @@ void GraphicsPage::slotFullScreenChanged(int state)
} }
} }
void GraphicsPage::slotStandardToggled(bool checked) void Launcher::GraphicsPage::slotStandardToggled(bool checked)
{ {
if (checked) { if (checked) {
resolutionComboBox->setEnabled(true); resolutionComboBox->setEnabled(true);

@ -11,46 +11,47 @@
#include "ui_graphicspage.h" #include "ui_graphicspage.h"
class GraphicsSettings;
namespace Files { struct ConfigurationManager; } namespace Files { struct ConfigurationManager; }
class GraphicsPage : public QWidget, private Ui::GraphicsPage namespace Launcher
{ {
Q_OBJECT class GraphicsSettings;
public: class GraphicsPage : public QWidget, private Ui::GraphicsPage
GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0); {
Q_OBJECT
void saveSettings(); public:
bool loadSettings(); GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0);
public slots: void saveSettings();
void rendererChanged(const QString &renderer); bool loadSettings();
void screenChanged(int screen);
private slots: public slots:
void slotFullScreenChanged(int state); void rendererChanged(const QString &renderer);
void slotStandardToggled(bool checked); void screenChanged(int screen);
private: private slots:
OgreInit::OgreInit mOgreInit; void slotFullScreenChanged(int state);
void slotStandardToggled(bool checked);
Ogre::Root *mOgre; private:
Ogre::RenderSystem *mSelectedRenderSystem; OgreInit::OgreInit mOgreInit;
Ogre::RenderSystem *mOpenGLRenderSystem; Ogre::Root *mOgre;
Ogre::RenderSystem *mDirect3DRenderSystem; Ogre::RenderSystem *mSelectedRenderSystem;
Ogre::RenderSystem *mOpenGLRenderSystem;
Ogre::RenderSystem *mDirect3DRenderSystem;
Files::ConfigurationManager &mCfgMgr;
GraphicsSettings &mGraphicsSettings;
Files::ConfigurationManager &mCfgMgr; QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer);
GraphicsSettings &mGraphicsSettings; QStringList getAvailableResolutions(int screen);
QRect getMaximumResolution();
QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer);
QStringList getAvailableResolutions(int screen);
QRect getMaximumResolution();
bool setupOgre();
bool setupSDL();
};
bool setupOgre();
bool setupSDL();
};
}
#endif #endif

@ -47,7 +47,7 @@ int main(int argc, char *argv[])
// Support non-latin characters // Support non-latin characters
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
MainDialog mainWin; Launcher::MainDialog mainWin;
if (mainWin.setup()) { if (mainWin.setup()) {
mainWin.show(); mainWin.show();
@ -59,4 +59,3 @@ int main(int argc, char *argv[])
SDL_Quit(); SDL_Quit();
return returnValue; return returnValue;
} }

@ -1,5 +1,6 @@
#include "maindialog.hpp" #include "maindialog.hpp"
#include <QPushButton>
#include <QFontDatabase> #include <QFontDatabase>
#include <QInputDialog> #include <QInputDialog>
#include <QFileDialog> #include <QFileDialog>
@ -23,8 +24,8 @@
#include "graphicspage.hpp" #include "graphicspage.hpp"
#include "datafilespage.hpp" #include "datafilespage.hpp"
MainDialog::MainDialog() Launcher::MainDialog::MainDialog(QWidget *parent)
: mGameSettings(mCfgMgr) : mGameSettings(mCfgMgr), QMainWindow (parent)
{ {
// Install the stylesheet font // Install the stylesheet font
QFile file; QFile file;
@ -69,7 +70,7 @@ MainDialog::MainDialog()
createIcons(); createIcons();
} }
void MainDialog::createIcons() void Launcher::MainDialog::createIcons()
{ {
if (!QIcon::hasThemeIcon("document-new")) if (!QIcon::hasThemeIcon("document-new"))
QIcon::setThemeName("tango"); QIcon::setThemeName("tango");
@ -101,15 +102,15 @@ void MainDialog::createIcons()
} }
void MainDialog::createPages() void Launcher::MainDialog::createPages()
{ {
mPlayPage = new PlayPage(this); mPlayPage = new PlayPage(this);
mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this); mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this);
mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this); mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
// Set the combobox of the play page to imitate the combobox on the datafilespage // Set the combobox of the play page to imitate the combobox on the datafilespage
mPlayPage->setProfilesComboBoxModel(mDataFilesPage->profilesComboBoxModel()); mPlayPage->setProfilesModel(mDataFilesPage->profilesModel());
mPlayPage->setProfilesComboBoxIndex(mDataFilesPage->profilesComboBoxIndex()); mPlayPage->setProfilesIndex(mDataFilesPage->profilesIndex());
// Add the pages to the stacked widget // Add the pages to the stacked widget
pagesWidget->addWidget(mPlayPage); pagesWidget->addWidget(mPlayPage);
@ -121,12 +122,12 @@ void MainDialog::createPages()
connect(mPlayPage, SIGNAL(playButtonClicked()), this, SLOT(play())); connect(mPlayPage, SIGNAL(playButtonClicked()), this, SLOT(play()));
connect(mPlayPage, SIGNAL(profileChanged(int)), mDataFilesPage, SLOT(setProfilesComboBoxIndex(int))); connect(mPlayPage, SIGNAL(signalProfileChanged(int)), mDataFilesPage, SLOT(slotProfileChanged(int)));
connect(mDataFilesPage, SIGNAL(profileChanged(int)), mPlayPage, SLOT(setProfilesComboBoxIndex(int))); connect(mDataFilesPage, SIGNAL(signalProfileChanged(int)), mPlayPage, SLOT(setProfilesIndex(int)));
} }
bool MainDialog::showFirstRunDialog() bool Launcher::MainDialog::showFirstRunDialog()
{ {
QStringList iniPaths; QStringList iniPaths;
@ -261,19 +262,11 @@ bool MainDialog::showFirstRunDialog()
// Add a new profile // Add a new profile
if (msgBox.isChecked()) { if (msgBox.isChecked()) {
mLauncherSettings.setValue(QString("Profiles/currentprofile"), QString("Imported")); mLauncherSettings.setValue(QString("Profiles/currentprofile"), QString("Imported"));
mLauncherSettings.remove(QString("Profiles/Imported/content"));
mLauncherSettings.remove(QString("Profiles/Imported/master")); QStringList contents = mGameSettings.values(QString("content"));
mLauncherSettings.remove(QString("Profiles/Imported/plugin")); foreach (const QString &content, contents) {
mLauncherSettings.setMultiValue(QString("Profiles/Imported/content"), content);
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);
} }
} }
@ -282,7 +275,7 @@ bool MainDialog::showFirstRunDialog()
return true; return true;
} }
bool MainDialog::setup() bool Launcher::MainDialog::setup()
{ {
if (!setupLauncherSettings()) if (!setupLauncherSettings())
return false; return false;
@ -311,15 +304,33 @@ bool MainDialog::setup()
return true; return true;
} }
void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) void Launcher::MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous)
{ {
if (!current) if (!current)
current = previous; current = previous;
pagesWidget->setCurrentIndex(iconWidget->row(current)); int currentIndex = iconWidget->row(current);
int previousIndex = iconWidget->row(previous);
pagesWidget->setCurrentIndex(currentIndex);
DataFilesPage *previousPage = dynamic_cast<DataFilesPage *>(pagesWidget->widget(previousIndex));
DataFilesPage *currentPage = dynamic_cast<DataFilesPage *>(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() bool Launcher::MainDialog::setupLauncherSettings()
{ {
mLauncherSettings.setMultiValueEnabled(true); mLauncherSettings.setMultiValueEnabled(true);
@ -356,7 +367,7 @@ bool MainDialog::setupLauncherSettings()
} }
#ifndef WIN32 #ifndef WIN32
bool expansions(UnshieldThread& cd) bool Launcher::expansions(Launcher::UnshieldThread& cd)
{ {
if(cd.BloodmoonDone()) if(cd.BloodmoonDone())
{ {
@ -367,7 +378,7 @@ bool expansions(UnshieldThread& cd)
QMessageBox expansionsBox; QMessageBox expansionsBox;
expansionsBox.setText(QObject::tr("<br>Would you like to install expansions now ? (make sure you have the disc)<br> \ expansionsBox.setText(QObject::tr("<br>Would you like to install expansions now ? (make sure you have the disc)<br> \
If you want to install both Bloodmoon and Tribunal, you have to install Tribunal first.<br>")); If you want to install both Bloodmoon and Tribunal, you have to install Tribunal first.<br>"));
QAbstractButton* tribunalButton = NULL; QAbstractButton* tribunalButton = NULL;
if(!cd.TribunalDone()) if(!cd.TribunalDone())
tribunalButton = expansionsBox.addButton(QObject::tr("&Tribunal"), QMessageBox::ActionRole); tribunalButton = expansionsBox.addButton(QObject::tr("&Tribunal"), QMessageBox::ActionRole);
@ -386,7 +397,7 @@ bool expansions(UnshieldThread& cd)
{ {
TextSlotMsgBox cdbox; 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(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&)));
QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject())); QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject()));
@ -405,7 +416,7 @@ bool expansions(UnshieldThread& cd)
{ {
TextSlotMsgBox cdbox; 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(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&)));
QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject())); QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject()));
@ -427,7 +438,7 @@ bool expansions(UnshieldThread& cd)
} }
#endif // WIN32 #endif // WIN32
bool MainDialog::setupGameSettings() bool Launcher::MainDialog::setupGameSettings()
{ {
QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string());
QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string());
@ -467,7 +478,7 @@ bool MainDialog::setupGameSettings()
foreach (const QString path, mGameSettings.getDataDirs()) { foreach (const QString path, mGameSettings.getDataDirs()) {
QDir dir(path); QDir dir(path);
QStringList filters; QStringList filters;
filters << "*.esp" << "*.esm"; filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon";
if (!dir.entryList(filters).isEmpty()) if (!dir.entryList(filters).isEmpty())
dataDirs.append(path); dataDirs.append(path);
@ -485,12 +496,12 @@ bool MainDialog::setupGameSettings()
QAbstractButton *dirSelectButton = QAbstractButton *dirSelectButton =
msgBox.addButton(QObject::tr("Browse to &Install..."), QMessageBox::ActionRole); msgBox.addButton(QObject::tr("Browse to &Install..."), QMessageBox::ActionRole);
#ifndef WIN32 #ifndef WIN32
QAbstractButton *cdSelectButton = QAbstractButton *cdSelectButton =
msgBox.addButton(QObject::tr("Browse to &CD..."), QMessageBox::ActionRole); msgBox.addButton(QObject::tr("Browse to &CD..."), QMessageBox::ActionRole);
#endif #endif
msgBox.exec(); msgBox.exec();
@ -505,14 +516,14 @@ bool MainDialog::setupGameSettings()
#ifndef WIN32 #ifndef WIN32
else if(msgBox.clickedButton() == cdSelectButton) { else if(msgBox.clickedButton() == cdSelectButton) {
UnshieldThread cd; UnshieldThread cd;
{ {
TextSlotMsgBox cdbox; 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(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&)));
QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject())); QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject()));
cd.SetMorrowindPath( cd.SetMorrowindPath(
QFileDialog::getOpenFileName( QFileDialog::getOpenFileName(
NULL, NULL,
@ -526,11 +537,11 @@ bool MainDialog::setupGameSettings()
QObject::tr("Select where to extract files to"), QObject::tr("Select where to extract files to"),
QDir::currentPath(), QDir::currentPath(),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks).toUtf8().constData()); QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks).toUtf8().constData());
cd.start(); cd.start();
cdbox.exec(); cdbox.exec();
} }
while(expansions(cd)); while(expansions(cd));
selectedFile = QString::fromStdString(cd.GetMWEsmPath()); selectedFile = QString::fromStdString(cd.GetMWEsmPath());
@ -550,7 +561,7 @@ bool MainDialog::setupGameSettings()
return true; return true;
} }
bool MainDialog::setupGraphicsSettings() bool Launcher::MainDialog::setupGraphicsSettings()
{ {
mGraphicsSettings.setMultiValueEnabled(false); mGraphicsSettings.setMultiValueEnabled(false);
@ -604,7 +615,7 @@ bool MainDialog::setupGraphicsSettings()
return true; return true;
} }
void MainDialog::loadSettings() void Launcher::MainDialog::loadSettings()
{ {
int width = mLauncherSettings.value(QString("General/MainWindow/width")).toInt(); int width = mLauncherSettings.value(QString("General/MainWindow/width")).toInt();
int height = mLauncherSettings.value(QString("General/MainWindow/height")).toInt(); int height = mLauncherSettings.value(QString("General/MainWindow/height")).toInt();
@ -616,7 +627,7 @@ void MainDialog::loadSettings()
move(posX, posY); move(posX, posY);
} }
void MainDialog::saveSettings() void Launcher::MainDialog::saveSettings()
{ {
QString width = QString::number(this->width()); QString width = QString::number(this->width());
QString height = QString::number(this->height()); QString height = QString::number(this->height());
@ -634,7 +645,7 @@ void MainDialog::saveSettings()
} }
bool MainDialog::writeSettings() bool Launcher::MainDialog::writeSettings()
{ {
// Now write all config files // Now write all config files
saveSettings(); saveSettings();
@ -727,13 +738,13 @@ bool MainDialog::writeSettings()
return true; return true;
} }
void MainDialog::closeEvent(QCloseEvent *event) void Launcher::MainDialog::closeEvent(QCloseEvent *event)
{ {
writeSettings(); writeSettings();
event->accept(); event->accept();
} }
void MainDialog::play() void Launcher::MainDialog::play()
{ {
if (!writeSettings()) { if (!writeSettings()) {
qApp->quit(); qApp->quit();
@ -742,11 +753,11 @@ void MainDialog::play()
if(!mGameSettings.hasMaster()) { if(!mGameSettings.hasMaster()) {
QMessageBox msgBox; QMessageBox msgBox;
msgBox.setWindowTitle(tr("No master file selected")); msgBox.setWindowTitle(tr("No game file selected"));
msgBox.setIcon(QMessageBox::Warning); msgBox.setIcon(QMessageBox::Warning);
msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.setText(tr("<br><b>You do not have any master files selected.</b><br><br> \ msgBox.setText(tr("<br><b>You do not have no game file selected.</b><br><br> \
OpenMW will not start without a master file selected.<br>")); OpenMW will not start without a game file selected.<br>"));
msgBox.exec(); msgBox.exec();
return; return;
} }
@ -756,7 +767,7 @@ void MainDialog::play()
qApp->quit(); 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; QString path = name;
#ifdef Q_OS_WIN #ifdef Q_OS_WIN

@ -11,57 +11,59 @@
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
class QListWidget;
class QListWidgetItem; class QListWidgetItem;
class QStackedWidget;
class QStringList;
class QStringListModel;
class QString;
class PlayPage; namespace Launcher
class GraphicsPage;
class DataFilesPage;
class MainDialog : public QMainWindow, private Ui::MainWindow
{ {
Q_OBJECT class PlayPage;
class GraphicsPage;
class DataFilesPage;
class UnshieldThread;
#ifndef WIN32
bool expansions(Launcher::UnshieldThread& cd);
#endif
public: class MainDialog : public QMainWindow, private Ui::MainWindow
MainDialog(); {
bool setup(); Q_OBJECT
bool showFirstRunDialog();
public slots: public:
void changePage(QListWidgetItem *current, QListWidgetItem *previous); explicit MainDialog(QWidget *parent = 0);
void play(); bool setup();
bool showFirstRunDialog();
private: public slots:
void createIcons(); void changePage(QListWidgetItem *current, QListWidgetItem *previous);
void createPages(); void play();
bool setupLauncherSettings(); private:
bool setupGameSettings(); void createIcons();
bool setupGraphicsSettings(); void createPages();
void loadSettings(); bool setupLauncherSettings();
void saveSettings(); bool setupGameSettings();
bool writeSettings(); bool setupGraphicsSettings();
inline bool startProgram(const QString &name, bool detached = false) { return startProgram(name, QStringList(), detached); } void loadSettings();
bool startProgram(const QString &name, const QStringList &arguments, bool detached = false); 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; void closeEvent(QCloseEvent *event);
GraphicsPage *mGraphicsPage;
DataFilesPage *mDataFilesPage;
Files::ConfigurationManager mCfgMgr; PlayPage *mPlayPage;
GraphicsPage *mGraphicsPage;
DataFilesPage *mDataFilesPage;
GameSettings mGameSettings; Files::ConfigurationManager mCfgMgr;
GraphicsSettings mGraphicsSettings;
LauncherSettings mLauncherSettings;
}; GameSettings mGameSettings;
GraphicsSettings mGraphicsSettings;
LauncherSettings mLauncherSettings;
};
}
#endif #endif

@ -6,8 +6,9 @@
#include <QPlastiqueStyle> #include <QPlastiqueStyle>
#endif #endif
PlayPage::PlayPage(QWidget *parent) : QWidget(parent) Launcher::PlayPage::PlayPage(QWidget *parent) : QWidget(parent)
{ {
setObjectName ("PlayPage");
setupUi(this); setupUi(this);
// Hacks to get the stylesheet look properly // Hacks to get the stylesheet look properly
@ -17,27 +18,22 @@ PlayPage::PlayPage(QWidget *parent) : QWidget(parent)
#endif #endif
profilesComboBox->setView(new QListView()); 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())); connect(playButton, SIGNAL(clicked()), this, SLOT(slotPlayClicked()));
} }
void PlayPage::setProfilesComboBoxModel(QAbstractItemModel *model) void Launcher::PlayPage::setProfilesModel(QAbstractItemModel *model)
{ {
profilesComboBox->setModel(model); profilesComboBox->setModel(model);
} }
void PlayPage::setProfilesComboBoxIndex(int index) void Launcher::PlayPage::setProfilesIndex(int index)
{ {
profilesComboBox->setCurrentIndex(index); profilesComboBox->setCurrentIndex(index);
} }
void PlayPage::slotCurrentIndexChanged(int index) void Launcher::PlayPage::slotPlayClicked()
{
emit profileChanged(index);
}
void PlayPage::slotPlayClicked()
{ {
emit playButtonClicked(); emit playButtonClicked();
} }

@ -9,27 +9,28 @@ class QComboBox;
class QPushButton; class QPushButton;
class QAbstractItemModel; class QAbstractItemModel;
class PlayPage : public QWidget, private Ui::PlayPage namespace Launcher
{ {
Q_OBJECT class PlayPage : public QWidget, private Ui::PlayPage
{
Q_OBJECT
public: public:
PlayPage(QWidget *parent = 0); PlayPage(QWidget *parent = 0);
void setProfilesComboBoxModel(QAbstractItemModel *model); void setProfilesModel(QAbstractItemModel *model);
signals: signals:
void profileChanged(int index); void signalProfileChanged(int index);
void playButtonClicked(); void playButtonClicked();
public slots: public slots:
void setProfilesComboBoxIndex(int index); void setProfilesIndex(int index);
private slots: private slots:
void slotCurrentIndexChanged(int index); void slotPlayClicked();
void slotPlayClicked();
}; };
}
#endif #endif

@ -9,6 +9,7 @@
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#include <boost/version.hpp> #include <boost/version.hpp>
/** /**
* Workaround for problems with whitespaces in paths in older versions of Boost library * Workaround for problems with whitespaces in paths in older versions of Boost library
*/ */
@ -26,16 +27,16 @@ namespace boost
#endif /* (BOOST_VERSION <= 104600) */ #endif /* (BOOST_VERSION <= 104600) */
GameSettings::GameSettings(Files::ConfigurationManager &cfg) Launcher::GameSettings::GameSettings(Files::ConfigurationManager &cfg)
: mCfgMgr(cfg) : mCfgMgr(cfg)
{ {
} }
GameSettings::~GameSettings() Launcher::GameSettings::~GameSettings()
{ {
} }
void GameSettings::validatePaths() void Launcher::GameSettings::validatePaths()
{ {
if (mSettings.isEmpty() || !mDataDirs.isEmpty()) if (mSettings.isEmpty() || !mDataDirs.isEmpty())
return; // Don't re-validate paths if they are already parsed return; // Don't re-validate paths if they are already parsed
@ -81,14 +82,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()) if (!mSettings.values(key).isEmpty())
return mSettings.values(key); return mSettings.values(key);
return defaultValues; return defaultValues;
} }
bool GameSettings::readFile(QTextStream &stream) bool Launcher::GameSettings::readFile(QTextStream &stream)
{ {
QMap<QString, QString> cache; QMap<QString, QString> cache;
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
@ -130,7 +131,7 @@ bool GameSettings::readFile(QTextStream &stream)
return true; return true;
} }
bool GameSettings::writeFile(QTextStream &stream) bool Launcher::GameSettings::writeFile(QTextStream &stream)
{ {
// Iterate in reverse order to preserve insertion order // Iterate in reverse order to preserve insertion order
QMapIterator<QString, QString> i(mSettings); QMapIterator<QString, QString> i(mSettings);
@ -139,13 +140,13 @@ bool GameSettings::writeFile(QTextStream &stream)
while (i.hasPrevious()) { while (i.hasPrevious()) {
i.previous(); i.previous();
if (i.key() == QLatin1String("master") || i.key() == QLatin1String("plugin")) if (i.key() == QLatin1String("content"))
continue; continue;
// Quote paths with spaces // Quote paths with spaces
if (i.key() == QLatin1String("data") if (i.key() == QLatin1String("data")
|| i.key() == QLatin1String("data-local") || i.key() == QLatin1String("data-local")
|| i.key() == QLatin1String("resources")) || i.key() == QLatin1String("resources"))
{ {
if (i.value().contains(QChar(' '))) if (i.value().contains(QChar(' ')))
{ {
@ -161,15 +162,24 @@ bool GameSettings::writeFile(QTextStream &stream)
} }
QStringList masters = mSettings.values(QString("master")); QStringList content = mSettings.values(QString("content"));
for (int i = masters.count(); i--;) { for (int i = content.count(); i--;) {
stream << "master=" << masters.at(i) << "\n"; stream << "content=" << content.at(i) << "\n";
} }
QStringList plugins = mSettings.values(QString("plugin")); return true;
for (int i = plugins.count(); i--;) { }
stream << "plugin=" << plugins.at(i) << "\n";
bool Launcher::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;
} }

@ -8,55 +8,61 @@
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
namespace Files { typedef std::vector<boost::filesystem::path> PathContainer; namespace Files
struct ConfigurationManager;}
class GameSettings
{ {
public: typedef std::vector<boost::filesystem::path> PathContainer;
GameSettings(Files::ConfigurationManager &cfg); struct ConfigurationManager;
~GameSettings(); }
inline QString value(const QString &key, const QString &defaultValue = QString()) namespace Launcher
{
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) inline void setValue(const QString &key, const QString &value)
{ {
QStringList values = mSettings.values(key); mSettings.insert(key, value);
if (!values.contains(value)) }
mSettings.insertMulti(key, value);
}
inline void remove(const QString &key) inline void setMultiValue(const QString &key, const QString &value)
{ {
mSettings.remove(key); QStringList values = mSettings.values(key);
} if (!values.contains(value))
mSettings.insertMulti(key, value);
}
inline void remove(const QString &key)
{
mSettings.remove(key);
}
inline QStringList getDataDirs() { return mDataDirs; } inline QStringList getDataDirs() { return mDataDirs; }
inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); } inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); }
inline QString getDataLocal() {return mDataLocal; } inline QString getDataLocal() {return mDataLocal; }
inline bool hasMaster() { return mSettings.count(QString("master")) > 0; }
QStringList values(const QString &key, const QStringList &defaultValues = QStringList()); bool hasMaster();
bool readFile(QTextStream &stream);
bool writeFile(QTextStream &stream);
private: QStringList values(const QString &key, const QStringList &defaultValues = QStringList());
Files::ConfigurationManager &mCfgMgr; bool readFile(QTextStream &stream);
bool writeFile(QTextStream &stream);
void validatePaths(); private:
QMap<QString, QString> mSettings; Files::ConfigurationManager &mCfgMgr;
QStringList mDataDirs; void validatePaths();
QString mDataLocal; QMap<QString, QString> mSettings;
};
QStringList mDataDirs;
QString mDataLocal;
};
}
#endif // GAMESETTINGS_HPP #endif // GAMESETTINGS_HPP

@ -5,15 +5,15 @@
#include <QRegExp> #include <QRegExp>
#include <QMap> #include <QMap>
GraphicsSettings::GraphicsSettings() Launcher::GraphicsSettings::GraphicsSettings()
{ {
} }
GraphicsSettings::~GraphicsSettings() Launcher::GraphicsSettings::~GraphicsSettings()
{ {
} }
bool GraphicsSettings::writeFile(QTextStream &stream) bool Launcher::GraphicsSettings::writeFile(QTextStream &stream)
{ {
QString sectionPrefix; QString sectionPrefix;
QRegExp sectionRe("([^/]+)/(.+)$"); QRegExp sectionRe("([^/]+)/(.+)$");

@ -3,14 +3,16 @@
#include "settingsbase.hpp" #include "settingsbase.hpp"
class GraphicsSettings : public SettingsBase<QMap<QString, QString> > namespace Launcher
{ {
public: class GraphicsSettings : public SettingsBase<QMap<QString, QString> >
GraphicsSettings(); {
~GraphicsSettings(); public:
GraphicsSettings();
~GraphicsSettings();
bool writeFile(QTextStream &stream); bool writeFile(QTextStream &stream);
};
};
}
#endif // GRAPHICSSETTINGS_HPP #endif // GRAPHICSSETTINGS_HPP

@ -5,15 +5,17 @@
#include <QRegExp> #include <QRegExp>
#include <QMap> #include <QMap>
LauncherSettings::LauncherSettings() #include <QDebug>
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<QString, QString> settings = SettingsBase::getSettings(); QMap<QString, QString> settings = SettingsBase::getSettings();
@ -34,7 +36,7 @@ QStringList LauncherSettings::values(const QString &key, Qt::MatchFlags flags)
return result; return result;
} }
QStringList LauncherSettings::subKeys(const QString &key) QStringList Launcher::LauncherSettings::subKeys(const QString &key)
{ {
QMap<QString, QString> settings = SettingsBase::getSettings(); QMap<QString, QString> settings = SettingsBase::getSettings();
QStringList keys = settings.uniqueKeys(); QStringList keys = settings.uniqueKeys();
@ -44,12 +46,9 @@ QStringList LauncherSettings::subKeys(const QString &key)
QStringList result; QStringList result;
foreach (const QString &currentKey, keys) { foreach (const QString &currentKey, keys) {
if (keyRe.indexIn(currentKey) != -1) { if (keyRe.indexIn(currentKey) != -1) {
QString prefixedKey = keyRe.cap(1); QString prefixedKey = keyRe.cap(1);
if(prefixedKey.startsWith(key)) { if(prefixedKey.startsWith(key)) {
QString subKey = prefixedKey.remove(key); QString subKey = prefixedKey.remove(key);
if (!subKey.isEmpty()) if (!subKey.isEmpty())
result.append(subKey); result.append(subKey);
@ -61,7 +60,7 @@ QStringList LauncherSettings::subKeys(const QString &key)
return result; return result;
} }
bool LauncherSettings::writeFile(QTextStream &stream) bool Launcher::LauncherSettings::writeFile(QTextStream &stream)
{ {
QString sectionPrefix; QString sectionPrefix;
QRegExp sectionRe("([^/]+)/(.+)$"); QRegExp sectionRe("([^/]+)/(.+)$");

@ -3,17 +3,19 @@
#include "settingsbase.hpp" #include "settingsbase.hpp"
class LauncherSettings : public SettingsBase<QMap<QString, QString> > namespace Launcher
{ {
public: class LauncherSettings : public SettingsBase<QMap<QString, QString> >
LauncherSettings(); {
~LauncherSettings(); public:
LauncherSettings();
~LauncherSettings();
QStringList subKeys(const QString &key); QStringList subKeys(const QString &key);
QStringList values(const QString &key, Qt::MatchFlags flags = Qt::MatchExactly); QStringList values(const QString &key, Qt::MatchFlags flags = Qt::MatchExactly);
bool writeFile(QTextStream &stream); bool writeFile(QTextStream &stream);
};
};
}
#endif // LAUNCHERSETTINGS_HPP #endif // LAUNCHERSETTINGS_HPP

@ -7,103 +7,105 @@
#include <QRegExp> #include <QRegExp>
#include <QMap> #include <QMap>
template <class Map> namespace Launcher
class SettingsBase
{ {
template <class Map>
class SettingsBase
{
public: public:
SettingsBase() { mMultiValue = false; } SettingsBase() { mMultiValue = false; }
~SettingsBase() {} ~SettingsBase() {}
inline QString value(const QString &key, const QString &defaultValue = QString()) inline QString value(const QString &key, const QString &defaultValue = QString())
{ {
return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key); return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key);
} }
inline void setValue(const QString &key, const QString &value) inline void setValue(const QString &key, const QString &value)
{ {
QStringList values = mSettings.values(key); QStringList values = mSettings.values(key);
if (!values.contains(value)) if (!values.contains(value))
mSettings.insert(key, value); mSettings.insert(key, value);
} }
inline void setMultiValue(const QString &key, const QString &value) inline void setMultiValue(const QString &key, const QString &value)
{ {
QStringList values = mSettings.values(key); QStringList values = mSettings.values(key);
if (!values.contains(value)) if (!values.contains(value))
mSettings.insertMulti(key, value); mSettings.insertMulti(key, value);
} }
inline void setMultiValueEnabled(bool enable) inline void setMultiValueEnabled(bool enable)
{ {
mMultiValue = enable; mMultiValue = enable;
} }
inline void remove(const QString &key) inline void remove(const QString &key)
{ {
mSettings.remove(key); mSettings.remove(key);
} }
Map getSettings() {return mSettings;} Map getSettings() {return mSettings;}
bool readFile(QTextStream &stream) bool readFile(QTextStream &stream)
{ {
mCache.clear(); mCache.clear();
QString sectionPrefix; QString sectionPrefix;
QRegExp sectionRe("^\\[([^]]+)\\]"); QRegExp sectionRe("^\\[([^]]+)\\]");
QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$");
while (!stream.atEnd()) { while (!stream.atEnd()) {
QString line = stream.readLine(); QString line = stream.readLine();
if (line.isEmpty() || line.startsWith("#")) if (line.isEmpty() || line.startsWith("#"))
continue; continue;
if (sectionRe.exactMatch(line)) { if (sectionRe.exactMatch(line)) {
sectionPrefix = sectionRe.cap(1); sectionPrefix = sectionRe.cap(1);
sectionPrefix.append("/"); sectionPrefix.append("/");
continue; continue;
} }
if (keyRe.indexIn(line) != -1) { if (keyRe.indexIn(line) != -1) {
QString key = keyRe.cap(1).trimmed(); QString key = keyRe.cap(1).trimmed();
QString value = keyRe.cap(2).trimmed(); QString value = keyRe.cap(2).trimmed();
if (!sectionPrefix.isEmpty()) if (!sectionPrefix.isEmpty())
key.prepend(sectionPrefix); key.prepend(sectionPrefix);
mSettings.remove(key); mSettings.remove(key);
QStringList values = mCache.values(key); QStringList values = mCache.values(key);
if (!values.contains(value)) { if (!values.contains(value)) {
if (mMultiValue) { if (mMultiValue) {
mCache.insertMulti(key, value); mCache.insertMulti(key, value);
} else { } else {
mCache.insert(key, value); mCache.insert(key, value);
}
} }
} }
} }
}
if (mSettings.isEmpty()) { if (mSettings.isEmpty()) {
mSettings = mCache; // This is the first time we read a file 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; return true;
} }
// Merge the changed keys with those which didn't private:
mSettings.unite(mCache); Map mSettings;
return true; Map mCache;
}
private:
Map mSettings;
Map mCache;
bool mMultiValue;
};
bool mMultiValue;
};
}
#endif // SETTINGSBASE_HPP #endif // SETTINGSBASE_HPP

@ -1,6 +1,6 @@
#include "textslotmsgbox.hpp" #include "textslotmsgbox.hpp"
void TextSlotMsgBox::setTextSlot(const QString& string) void Launcher::TextSlotMsgBox::setTextSlot(const QString& string)
{ {
setText(string); setText(string);
} }

@ -3,11 +3,13 @@
#include <QMessageBox> #include <QMessageBox>
class TextSlotMsgBox : public QMessageBox namespace Launcher
{ {
Q_OBJECT class TextSlotMsgBox : public QMessageBox
public slots: {
void setTextSlot(const QString& string); Q_OBJECT
}; public slots:
void setTextSlot(const QString& string);
};
}
#endif #endif

@ -292,30 +292,30 @@ namespace
} }
bool UnshieldThread::SetMorrowindPath(const std::string& path) bool Launcher::UnshieldThread::SetMorrowindPath(const std::string& path)
{ {
mMorrowindPath = path; mMorrowindPath = path;
return true; return true;
} }
bool UnshieldThread::SetTribunalPath(const std::string& path) bool Launcher::UnshieldThread::SetTribunalPath(const std::string& path)
{ {
mTribunalPath = path; mTribunalPath = path;
return true; return true;
} }
bool UnshieldThread::SetBloodmoonPath(const std::string& path) bool Launcher::UnshieldThread::SetBloodmoonPath(const std::string& path)
{ {
mBloodmoonPath = path; mBloodmoonPath = path;
return true; return true;
} }
void UnshieldThread::SetOutputPath(const std::string& path) void Launcher::UnshieldThread::SetOutputPath(const std::string& path)
{ {
mOutputPath = 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; bool success;
bfs::path dirname; bfs::path dirname;
@ -349,7 +349,7 @@ bool UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, cons
return success; 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;
unshield = unshield_open(cab.c_str()); 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; bfs::path outputDataFilesDir = mOutputPath;
outputDataFilesDir /= "Data Files"; outputDataFilesDir /= "Data Files";
@ -475,7 +475,7 @@ bool UnshieldThread::extract()
return true; return true;
} }
void UnshieldThread::Done() void Launcher::UnshieldThread::Done()
{ {
// Get rid of unnecessary files // Get rid of unnecessary files
bfs::remove_all(mOutputPath / "extract-temp"); 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")); 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(); return findFile(mOutputPath / "Data Files", "morrowind.esm").string();
} }
bool UnshieldThread::TribunalDone() bool Launcher::UnshieldThread::TribunalDone()
{ {
return mTribunalDone; return mTribunalDone;
} }
bool UnshieldThread::BloodmoonDone() bool Launcher::UnshieldThread::BloodmoonDone()
{ {
return mBloodmoonDone; return mBloodmoonDone;
} }
void UnshieldThread::run() void Launcher::UnshieldThread::run()
{ {
extract(); extract();
emit close(); emit close();
} }
UnshieldThread::UnshieldThread() Launcher::UnshieldThread::UnshieldThread()
{ {
unshield_set_log_level(0); unshield_set_log_level(0);
mMorrowindDone = false; mMorrowindDone = false;

@ -7,50 +7,52 @@
#include <libunshield.h> #include <libunshield.h>
class UnshieldThread : public QThread namespace Launcher
{ {
Q_OBJECT class UnshieldThread : public QThread
{
Q_OBJECT
public: public:
bool SetMorrowindPath(const std::string& path); bool SetMorrowindPath(const std::string& path);
bool SetTribunalPath(const std::string& path); bool SetTribunalPath(const std::string& path);
bool SetBloodmoonPath(const std::string& path); bool SetBloodmoonPath(const std::string& path);
void SetOutputPath(const std::string& path); void SetOutputPath(const std::string& path);
bool extract();
bool TribunalDone(); bool extract();
bool BloodmoonDone();
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); private:
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;
bool mMorrowindDone; void extract_cab(const boost::filesystem::path& cab, const boost::filesystem::path& output_dir, bool extract_ini = false);
bool mTribunalDone; bool extract_file(Unshield* unshield, boost::filesystem::path output_dir, const char* prefix, int index);
bool mBloodmoonDone;
boost::filesystem::path mOutputPath; boost::filesystem::path mMorrowindPath;
boost::filesystem::path mTribunalPath;
boost::filesystem::path mBloodmoonPath;
bool mMorrowindDone;
bool mTribunalDone;
bool mBloodmoonDone;
protected: boost::filesystem::path mOutputPath;
virtual void run();
signals:
void signalGUI(QString);
void close();
};
protected:
virtual void run();
signals:
void signalGUI(QString);
void close();
};
}
#endif #endif

@ -54,72 +54,61 @@
Emulates the QMessageBox API with Emulates the QMessageBox API with
static conveniences. The message label can open external URLs. static conveniences. The message label can open external URLs.
*/ */
Launcher::CheckableMessageBoxPrivate::CheckableMessageBoxPrivate(QDialog *q)
class CheckableMessageBoxPrivate
{
public:
CheckableMessageBoxPrivate(QDialog *q)
: clickedButton(0) : clickedButton(0)
{ {
QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
pixmapLabel = new QLabel(q); pixmapLabel = new QLabel(q);
sizePolicy.setHorizontalStretch(0); sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0); sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth()); sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth());
pixmapLabel->setSizePolicy(sizePolicy); pixmapLabel->setSizePolicy(sizePolicy);
pixmapLabel->setVisible(false); pixmapLabel->setVisible(false);
QSpacerItem *pixmapSpacer = QSpacerItem *pixmapSpacer =
new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding);
messageLabel = new QLabel(q); messageLabel = new QLabel(q);
messageLabel->setMinimumSize(QSize(300, 0)); messageLabel->setMinimumSize(QSize(300, 0));
messageLabel->setWordWrap(true); messageLabel->setWordWrap(true);
messageLabel->setOpenExternalLinks(true); messageLabel->setOpenExternalLinks(true);
messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse); messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse);
QSpacerItem *checkBoxRightSpacer = QSpacerItem *checkBoxRightSpacer =
new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
QSpacerItem *buttonSpacer = QSpacerItem *buttonSpacer =
new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum); new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum);
checkBox = new QCheckBox(q); checkBox = new QCheckBox(q);
checkBox->setText(CheckableMessageBox::tr("Do not ask again")); checkBox->setText(Launcher::CheckableMessageBox::tr("Do not ask again"));
buttonBox = new QDialogButtonBox(q); buttonBox = new QDialogButtonBox(q);
buttonBox->setOrientation(Qt::Horizontal); buttonBox->setOrientation(Qt::Horizontal);
buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
QVBoxLayout *verticalLayout = new QVBoxLayout(); QVBoxLayout *verticalLayout = new QVBoxLayout();
verticalLayout->addWidget(pixmapLabel); verticalLayout->addWidget(pixmapLabel);
verticalLayout->addItem(pixmapSpacer); verticalLayout->addItem(pixmapSpacer);
QHBoxLayout *horizontalLayout_2 = new QHBoxLayout(); QHBoxLayout *horizontalLayout_2 = new QHBoxLayout();
horizontalLayout_2->addLayout(verticalLayout); horizontalLayout_2->addLayout(verticalLayout);
horizontalLayout_2->addWidget(messageLabel); horizontalLayout_2->addWidget(messageLabel);
QHBoxLayout *horizontalLayout = new QHBoxLayout(); QHBoxLayout *horizontalLayout = new QHBoxLayout();
horizontalLayout->addWidget(checkBox); horizontalLayout->addWidget(checkBox);
horizontalLayout->addItem(checkBoxRightSpacer); horizontalLayout->addItem(checkBoxRightSpacer);
QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q); QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q);
verticalLayout_2->addLayout(horizontalLayout_2); verticalLayout_2->addLayout(horizontalLayout_2);
verticalLayout_2->addLayout(horizontalLayout); verticalLayout_2->addLayout(horizontalLayout);
verticalLayout_2->addItem(buttonSpacer); verticalLayout_2->addItem(buttonSpacer);
verticalLayout_2->addWidget(buttonBox); verticalLayout_2->addWidget(buttonBox);
} }
QLabel *pixmapLabel;
QLabel *messageLabel;
QCheckBox *checkBox;
QDialogButtonBox *buttonBox;
QAbstractButton *clickedButton;
};
CheckableMessageBox::CheckableMessageBox(QWidget *parent) : Launcher::CheckableMessageBox::CheckableMessageBox(QWidget *parent) :
QDialog(parent), QDialog(parent),
d(new CheckableMessageBoxPrivate(this)) d(new Launcher::CheckableMessageBoxPrivate(this))
{ {
setModal(true); setModal(true);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
@ -129,102 +118,102 @@ CheckableMessageBox::CheckableMessageBox(QWidget *parent) :
SLOT(slotClicked(QAbstractButton*))); SLOT(slotClicked(QAbstractButton*)));
} }
CheckableMessageBox::~CheckableMessageBox() Launcher::CheckableMessageBox::~CheckableMessageBox()
{ {
delete d; delete d;
} }
void CheckableMessageBox::slotClicked(QAbstractButton *b) void Launcher::CheckableMessageBox::slotClicked(QAbstractButton *b)
{ {
d->clickedButton = b; d->clickedButton = b;
} }
QAbstractButton *CheckableMessageBox::clickedButton() const QAbstractButton *Launcher::CheckableMessageBox::clickedButton() const
{ {
return d->clickedButton; return d->clickedButton;
} }
QDialogButtonBox::StandardButton CheckableMessageBox::clickedStandardButton() const QDialogButtonBox::StandardButton Launcher::CheckableMessageBox::clickedStandardButton() const
{ {
if (d->clickedButton) if (d->clickedButton)
return d->buttonBox->standardButton(d->clickedButton); return d->buttonBox->standardButton(d->clickedButton);
return QDialogButtonBox::NoButton; return QDialogButtonBox::NoButton;
} }
QString CheckableMessageBox::text() const QString Launcher::CheckableMessageBox::text() const
{ {
return d->messageLabel->text(); return d->messageLabel->text();
} }
void CheckableMessageBox::setText(const QString &t) void Launcher::CheckableMessageBox::setText(const QString &t)
{ {
d->messageLabel->setText(t); d->messageLabel->setText(t);
} }
QPixmap CheckableMessageBox::iconPixmap() const QPixmap Launcher::CheckableMessageBox::iconPixmap() const
{ {
if (const QPixmap *p = d->pixmapLabel->pixmap()) if (const QPixmap *p = d->pixmapLabel->pixmap())
return QPixmap(*p); return QPixmap(*p);
return QPixmap(); return QPixmap();
} }
void CheckableMessageBox::setIconPixmap(const QPixmap &p) void Launcher::CheckableMessageBox::setIconPixmap(const QPixmap &p)
{ {
d->pixmapLabel->setPixmap(p); d->pixmapLabel->setPixmap(p);
d->pixmapLabel->setVisible(!p.isNull()); d->pixmapLabel->setVisible(!p.isNull());
} }
bool CheckableMessageBox::isChecked() const bool Launcher::CheckableMessageBox::isChecked() const
{ {
return d->checkBox->isChecked(); return d->checkBox->isChecked();
} }
void CheckableMessageBox::setChecked(bool s) void Launcher::CheckableMessageBox::setChecked(bool s)
{ {
d->checkBox->setChecked(s); d->checkBox->setChecked(s);
} }
QString CheckableMessageBox::checkBoxText() const QString Launcher::CheckableMessageBox::checkBoxText() const
{ {
return d->checkBox->text(); return d->checkBox->text();
} }
void CheckableMessageBox::setCheckBoxText(const QString &t) void Launcher::CheckableMessageBox::setCheckBoxText(const QString &t)
{ {
d->checkBox->setText(t); d->checkBox->setText(t);
} }
bool CheckableMessageBox::isCheckBoxVisible() const bool Launcher::CheckableMessageBox::isCheckBoxVisible() const
{ {
return d->checkBox->isVisible(); return d->checkBox->isVisible();
} }
void CheckableMessageBox::setCheckBoxVisible(bool v) void Launcher::CheckableMessageBox::setCheckBoxVisible(bool v)
{ {
d->checkBox->setVisible(v); d->checkBox->setVisible(v);
} }
QDialogButtonBox::StandardButtons CheckableMessageBox::standardButtons() const QDialogButtonBox::StandardButtons Launcher::CheckableMessageBox::standardButtons() const
{ {
return d->buttonBox->standardButtons(); return d->buttonBox->standardButtons();
} }
void CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s) void Launcher::CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s)
{ {
d->buttonBox->setStandardButtons(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); 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); return d->buttonBox->addButton(text, role);
} }
QDialogButtonBox::StandardButton CheckableMessageBox::defaultButton() const QDialogButtonBox::StandardButton Launcher::CheckableMessageBox::defaultButton() const
{ {
foreach (QAbstractButton *b, d->buttonBox->buttons()) foreach (QAbstractButton *b, d->buttonBox->buttons())
if (QPushButton *pb = qobject_cast<QPushButton *>(b)) if (QPushButton *pb = qobject_cast<QPushButton *>(b))
@ -233,7 +222,7 @@ QDialogButtonBox::StandardButton CheckableMessageBox::defaultButton() const
return QDialogButtonBox::NoButton; return QDialogButtonBox::NoButton;
} }
void CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s) void Launcher::CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s)
{ {
if (QPushButton *b = d->buttonBox->button(s)) { if (QPushButton *b = d->buttonBox->button(s)) {
b->setDefault(true); b->setDefault(true);
@ -242,7 +231,7 @@ void CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s)
} }
QDialogButtonBox::StandardButton QDialogButtonBox::StandardButton
CheckableMessageBox::question(QWidget *parent, Launcher::CheckableMessageBox::question(QWidget *parent,
const QString &title, const QString &title,
const QString &question, const QString &question,
const QString &checkBoxText, const QString &checkBoxText,
@ -263,7 +252,7 @@ CheckableMessageBox::question(QWidget *parent,
return mb.clickedStandardButton(); return mb.clickedStandardButton();
} }
QMessageBox::StandardButton CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db) QMessageBox::StandardButton Launcher::CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db)
{ {
return static_cast<QMessageBox::StandardButton>(int(db)); return static_cast<QMessageBox::StandardButton>(int(db));
} }

@ -34,67 +34,83 @@
#include <QMessageBox> #include <QMessageBox>
#include <QDialog> #include <QDialog>
class CheckableMessageBoxPrivate; class QCheckBox;
class CheckableMessageBox : public QDialog namespace Launcher
{ {
Q_OBJECT class CheckableMessageBoxPrivate
Q_PROPERTY(QString text READ text WRITE setText) {
Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap) public:
Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked)
Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText) QLabel *pixmapLabel;
Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons) QLabel *messageLabel;
Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton) QCheckBox *checkBox;
QDialogButtonBox *buttonBox;
public: QAbstractButton *clickedButton;
explicit CheckableMessageBox(QWidget *parent);
virtual ~CheckableMessageBox(); public:
CheckableMessageBoxPrivate(QDialog *q);
static QDialogButtonBox::StandardButton };
question(QWidget *parent,
const QString &title, class CheckableMessageBox : public QDialog
const QString &question, {
const QString &checkBoxText, Q_OBJECT
bool *checkBoxSetting, Q_PROPERTY(QString text READ text WRITE setText)
QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No, Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap)
QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No); Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked)
Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText)
QString text() const; Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons)
void setText(const QString &); Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton)
bool isChecked() const; public:
void setChecked(bool s); explicit CheckableMessageBox(QWidget *parent);
virtual ~CheckableMessageBox();
QString checkBoxText() const;
void setCheckBoxText(const QString &); static QDialogButtonBox::StandardButton
question(QWidget *parent,
bool isCheckBoxVisible() const; const QString &title,
void setCheckBoxVisible(bool); const QString &question,
const QString &checkBoxText,
QDialogButtonBox::StandardButtons standardButtons() const; bool *checkBoxSetting,
void setStandardButtons(QDialogButtonBox::StandardButtons s); QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No,
QPushButton *button(QDialogButtonBox::StandardButton b) const; QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No);
QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role);
QString text() const;
QDialogButtonBox::StandardButton defaultButton() const; void setText(const QString &);
void setDefaultButton(QDialogButtonBox::StandardButton s);
bool isChecked() const;
// See static QMessageBox::standardPixmap() void setChecked(bool s);
QPixmap iconPixmap() const;
void setIconPixmap (const QPixmap &p); QString checkBoxText() const;
void setCheckBoxText(const QString &);
// Query the result
QAbstractButton *clickedButton() const; bool isCheckBoxVisible() const;
QDialogButtonBox::StandardButton clickedStandardButton() const; void setCheckBoxVisible(bool);
// Conversion convenience QDialogButtonBox::StandardButtons standardButtons() const;
static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton); void setStandardButtons(QDialogButtonBox::StandardButtons s);
QPushButton *button(QDialogButtonBox::StandardButton b) const;
private slots: QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role);
void slotClicked(QAbstractButton *b);
QDialogButtonBox::StandardButton defaultButton() const;
private: void setDefaultButton(QDialogButtonBox::StandardButton s);
CheckableMessageBoxPrivate *d;
}; // 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 #endif // CHECKABLEMESSAGEBOX_HPP

@ -1,10 +1,12 @@
#include <QToolButton>
#include <QStyle>
#include "lineedit.hpp" #include "lineedit.hpp"
LineEdit::LineEdit(QWidget *parent) LineEdit::LineEdit(QWidget *parent)
: QLineEdit(parent) : QLineEdit(parent)
{
setupClearButton();
}
void LineEdit::setupClearButton()
{ {
mClearButton = new QToolButton(this); mClearButton = new QToolButton(this);
QPixmap pixmap(":images/clear.png"); QPixmap pixmap(":images/clear.png");
@ -15,13 +17,6 @@ LineEdit::LineEdit(QWidget *parent)
mClearButton->hide(); mClearButton->hide();
connect(mClearButton, SIGNAL(clicked()), this, SLOT(clear())); connect(mClearButton, SIGNAL(clicked()), this, SLOT(clear()));
connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateClearButton(const QString&))); 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 LineEdit::resizeEvent(QResizeEvent *) void LineEdit::resizeEvent(QResizeEvent *)

@ -11,6 +11,9 @@
#define LINEEDIT_H #define LINEEDIT_H
#include <QLineEdit> #include <QLineEdit>
#include <QStyle>
#include <QStylePainter>
#include <QToolButton>
class QToolButton; class QToolButton;
@ -18,6 +21,8 @@ class LineEdit : public QLineEdit
{ {
Q_OBJECT Q_OBJECT
QString mPlaceholderText;
public: public:
LineEdit(QWidget *parent = 0); LineEdit(QWidget *parent = 0);
@ -27,8 +32,10 @@ protected:
private slots: private slots:
void updateClearButton(const QString &text); void updateClearButton(const QString &text);
private: protected:
QToolButton *mClearButton; QToolButton *mClearButton;
void setupClearButton();
}; };
#endif // LIENEDIT_H #endif // LIENEDIT_H

@ -5,18 +5,12 @@
#include <QKeyEvent> #include <QKeyEvent>
#include "profilescombobox.hpp" #include "profilescombobox.hpp"
#include "comboboxlineedit.hpp"
ProfilesComboBox::ProfilesComboBox(QWidget *parent) : ProfilesComboBox::ProfilesComboBox(QWidget *parent) :
QComboBox(parent) ContentSelectorView::ComboBox(parent)
{ {
mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore connect(this, SIGNAL(activated(int)), this,
setEditEnabled(true); SLOT(slotIndexChangedByUser(int)));
setValidator(mValidator);
setCompleter(0);
connect(this, SIGNAL(currentIndexChanged(int)), this,
SLOT(slotIndexChanged(int)));
setInsertPolicy(QComboBox::NoInsert); setInsertPolicy(QComboBox::NoInsert);
} }
@ -37,6 +31,7 @@ void ProfilesComboBox::setEditEnabled(bool editable)
setValidator(mValidator); setValidator(mValidator);
ComboBoxLineEdit *edit = new ComboBoxLineEdit(this); ComboBoxLineEdit *edit = new ComboBoxLineEdit(this);
setLineEdit(edit); setLineEdit(edit);
setCompleter(0); setCompleter(0);
@ -45,6 +40,9 @@ void ProfilesComboBox::setEditEnabled(bool editable)
connect(lineEdit(), SIGNAL(textChanged(QString)), this, connect(lineEdit(), SIGNAL(textChanged(QString)), this,
SLOT(slotTextChanged(QString))); SLOT(slotTextChanged(QString)));
connect (lineEdit(), SIGNAL(textChanged(QString)), this,
SIGNAL (signalProfileTextChanged (QString)));
} }
void ProfilesComboBox::slotTextChanged(const QString &text) void ProfilesComboBox::slotTextChanged(const QString &text)
@ -82,11 +80,20 @@ void ProfilesComboBox::slotEditingFinished()
emit(profileRenamed(previous, current)); emit(profileRenamed(previous, current));
} }
void ProfilesComboBox::slotIndexChanged(int index) void ProfilesComboBox::slotIndexChangedByUser(int index)
{ {
if (index == -1) if (index == -1)
return; return;
emit(profileChanged(mOldProfile, currentText())); emit (signalProfileChanged(mOldProfile, currentText()));
mOldProfile = itemText(index); mOldProfile = currentText();
}
ProfilesComboBox::ComboBoxLineEdit::ComboBoxLineEdit (QWidget *parent)
: LineEdit (parent)
{
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));
} }

@ -0,0 +1,47 @@
#ifndef PROFILESCOMBOBOX_HPP
#define PROFILESCOMBOBOX_HPP
#include "components/contentselector/view/combobox.hpp"
#include "lineedit.hpp"
#include <QDebug>
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);
void setCurrentProfile(int index)
{
ComboBox::setCurrentIndex(index);
mOldProfile = currentText();
}
signals:
void signalProfileTextChanged(const QString &item);
void signalProfileChanged(const QString &previous, const QString &current);
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

@ -7,19 +7,18 @@
#include <QValidator> #include <QValidator>
#include <QLabel> #include <QLabel>
#include <components/fileorderlist/utils/lineedit.hpp> Launcher::TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) :
TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) :
QDialog(parent) QDialog(parent)
{ {
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
mButtonBox = new QDialogButtonBox(this); mButtonBox = new QDialogButtonBox(this);
mButtonBox->addButton(QDialogButtonBox::Ok); mButtonBox->addButton(QDialogButtonBox::Ok);
mButtonBox->addButton(QDialogButtonBox::Cancel); mButtonBox->addButton(QDialogButtonBox::Cancel);
mButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false);
// Line edit // Line edit
QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore
mLineEdit = new LineEdit(this); mLineEdit = new DialogLineEdit(this);
mLineEdit->setValidator(validator); mLineEdit->setValidator(validator);
mLineEdit->setCompleter(0); mLineEdit->setCompleter(0);
@ -38,34 +37,51 @@ TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWid
Q_UNUSED(title); Q_UNUSED(title);
#endif #endif
setOkButtonEnabled(false);
setModal(true); setModal(true);
connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept()));
connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject()));
connect(mLineEdit, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateOkButton(QString)));
} }
int TextInputDialog::exec() int Launcher::TextInputDialog::exec()
{ {
mLineEdit->clear(); mLineEdit->clear();
mLineEdit->setFocus(); mLineEdit->setFocus();
return QDialog::exec(); return QDialog::exec();
} }
void TextInputDialog::setOkButtonEnabled(bool enabled) QString Launcher::TextInputDialog::getText() const
{ {
QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok); return mLineEdit->text();
okButton->setEnabled(enabled); }
QPalette *palette = new QPalette(); void Launcher::TextInputDialog::slotUpdateOkButton(QString text)
palette->setColor(QPalette::Text,Qt::red); {
bool enabled = !(text.isEmpty());
mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(enabled);
if (enabled) { if (enabled)
mLineEdit->setPalette(QApplication::palette()); mLineEdit->setPalette(QApplication::palette());
} else { else
{
// Existing profile name, make the text red // Existing profile name, make the text red
QPalette *palette = new QPalette();
palette->setColor(QPalette::Text,Qt::red);
mLineEdit->setPalette(*palette); mLineEdit->setPalette(*palette);
} }
}
Launcher::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));
} }

@ -2,27 +2,39 @@
#define TEXTINPUTDIALOG_HPP #define TEXTINPUTDIALOG_HPP
#include <QDialog> #include <QDialog>
//#include "lineedit.hpp"
#include "lineedit.hpp"
class QDialogButtonBox; class QDialogButtonBox;
class LineEdit;
class TextInputDialog : public QDialog namespace Launcher
{ {
Q_OBJECT class TextInputDialog : public QDialog
public: {
explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0); Q_OBJECT
inline LineEdit *lineEdit() { return mLineEdit; }
void setOkButtonEnabled(bool enabled); 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 () {}
LineEdit *mLineEdit; QString getText() const;
int exec(); int exec();
private: private slots:
QDialogButtonBox *mButtonBox; void slotUpdateOkButton(QString text);
};
}; }
#endif // TEXTINPUTDIALOG_HPP #endif // TEXTINPUTDIALOG_HPP

@ -813,8 +813,7 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con
} }
void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) const { void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) const {
std::vector<std::string> esmFiles; std::vector<std::string> contentFiles;
std::vector<std::string> espFiles;
std::string baseGameFile("Game Files:GameFile"); std::string baseGameFile("Game Files:GameFile");
std::string 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)); std::string filetype(entry->substr(entry->length()-3));
Misc::StringUtils::toLower(filetype); Misc::StringUtils::toLower(filetype);
if(filetype.compare("esm") == 0) { if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) {
esmFiles.push_back(*entry); contentFiles.push_back(*entry);
}
else if(filetype.compare("esp") == 0) {
espFiles.push_back(*entry);
} }
} }
gameFile = ""; gameFile = "";
} }
cfg.erase("master"); cfg.erase("content");
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("master", std::vector<std::string>() ) ); cfg.insert( std::make_pair("content", std::vector<std::string>() ) );
for(std::vector<std::string>::const_iterator it=esmFiles.begin(); it!=esmFiles.end(); ++it) {
cfg["master"].push_back(*it);
}
cfg.erase("plugin");
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("plugin", std::vector<std::string>() ) );
for(std::vector<std::string>::const_iterator it=espFiles.begin(); it!=espFiles.end(); ++it) { for(std::vector<std::string>::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) {
cfg["plugin"].push_back(*it); cfg["content"].push_back(*it);
} }
} }

@ -5,11 +5,11 @@ opencs_units (. editor)
set (CMAKE_BUILD_TYPE DEBUG) set (CMAKE_BUILD_TYPE DEBUG)
opencs_units (model/doc opencs_units (model/doc
document document operation saving
) )
opencs_units_noqt (model/doc opencs_units_noqt (model/doc
documentmanager documentmanager stage savingstate savingstages
) )
opencs_hdrs_noqt (model/doc opencs_hdrs_noqt (model/doc
@ -33,18 +33,18 @@ opencs_hdrs_noqt (model/world
opencs_units (model/tools opencs_units (model/tools
tools operation reportmodel tools reportmodel
) )
opencs_units_noqt (model/tools opencs_units_noqt (model/tools
stage verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck birthsigncheck spellcheck
) )
opencs_units (view/doc opencs_units (view/doc
viewmanager view operations operation subview startup filedialog newgame filewidget viewmanager view operations operation subview startup filedialog newgame
adjusterwidget filewidget adjusterwidget
) )
@ -66,7 +66,7 @@ opencs_units (view/world
opencs_units_noqt (view/world opencs_units_noqt (view/world
dialoguesubview subviews dialoguesubview subviews
enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate
scripthighlighter idvalidator scripthighlighter idvalidator dialoguecreator
) )
@ -124,11 +124,13 @@ opencs_units (view/filter
set (OPENCS_US set (OPENCS_US
) )
set (OPENCS_RES ../../files/opencs/resources.qrc set (OPENCS_RES ${CMAKE_SOURCE_DIR}/files/opencs/resources.qrc
../../files/launcher/launcher.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}) source_group (opencs FILES ${OPENCS_SRC} ${OPENCS_HDR})

@ -8,15 +8,17 @@
#include "model/doc/document.hpp" #include "model/doc/document.hpp"
#include "model/world/data.hpp" #include "model/world/data.hpp"
#include <iostream>
CS::Editor::Editor()
CS::Editor::Editor() : mViewManager (mDocumentManager) : mDocumentManager (mCfgMgr), mViewManager (mDocumentManager)
{ {
mIpcServerName = "org.openmw.OpenCS"; mIpcServerName = "org.openmw.OpenCS";
setupDataFiles(); setupDataFiles();
mNewGame.setLocalData (mLocal); mNewGame.setLocalData (mLocal);
mFileDialog.setLocalData (mLocal);
connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ())); connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ()));
connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ())); connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ()));
@ -28,23 +30,27 @@ CS::Editor::Editor() : mViewManager (mDocumentManager)
connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ())); connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ()));
connect (&mStartup, SIGNAL (editConfig()), this, SLOT (showSettings ())); connect (&mStartup, SIGNAL (editConfig()), this, SLOT (showSettings ()));
connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles())); connect (&mFileDialog, SIGNAL(signalOpenFiles (const boost::filesystem::path&)),
connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile())); 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&)), 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() void CS::Editor::setupDataFiles()
{ {
boost::program_options::variables_map variables; boost::program_options::variables_map variables;
boost::program_options::options_description desc; boost::program_options::options_description desc("Syntax: opencs <options>\nAllowed options");
desc.add_options() desc.add_options()
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken()) ("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken())
("data-local", boost::program_options::value<std::string>()->default_value("")) ("data-local", boost::program_options::value<std::string>()->default_value(""))
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false)) ("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
("encoding", boost::program_options::value<std::string>()->default_value("win1252")); ("encoding", boost::program_options::value<std::string>()->default_value("win1252"))
("resources", boost::program_options::value<std::string>()->default_value("resources"));
boost::program_options::notify(variables); boost::program_options::notify(variables);
@ -79,13 +85,16 @@ void CS::Editor::setupDataFiles()
} }
// Set the charset for reading the esm/esp files // Set the charset for reading the esm/esp files
QString encoding = QString::fromStdString(variables["encoding"].as<std::string>()); // QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
mFileDialog.setEncoding(encoding); //mFileDialog.setEncoding(encoding);
dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end());
mDocumentManager.setResourceDir (variables["resources"].as<std::string>());
for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter)
{ {
QString path = QString::fromStdString(iter->string()); QString path = QString::fromStdString(iter->string());
mFileDialog.addFiles(path); mFileDialog.addFiles(path);
} }
@ -109,48 +118,39 @@ void CS::Editor::createGame()
void CS::Editor::createAddon() void CS::Editor::createAddon()
{ {
mStartup.hide(); mStartup.hide();
mFileDialog.showDialog (CSVDoc::ContentAction_New);
mFileDialog.newFile();
} }
void CS::Editor::loadDocument() void CS::Editor::loadDocument()
{ {
mStartup.hide(); mStartup.hide();
mFileDialog.showDialog (CSVDoc::ContentAction_Edit);
mFileDialog.openFile();
} }
void CS::Editor::openFiles() void CS::Editor::openFiles (const boost::filesystem::path &savePath)
{ {
std::vector<boost::filesystem::path> files; std::vector<boost::filesystem::path> files;
QStringList paths = mFileDialog.checkedItemsPaths();
foreach (const QString &path, paths) { foreach (const QString &path, mFileDialog.selectedFilePaths())
files.push_back(path.toStdString()); files.push_back(path.toStdString());
}
/// \todo Get the save path from the file dialogue CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, false);
CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), false);
mViewManager.addView (document); mViewManager.addView (document);
mFileDialog.hide(); mFileDialog.hide();
} }
void CS::Editor::createNewFile() void CS::Editor::createNewFile (const boost::filesystem::path &savePath)
{ {
std::vector<boost::filesystem::path> files; std::vector<boost::filesystem::path> files;
QStringList paths = mFileDialog.checkedItemsPaths();
foreach (const QString &path, paths) { foreach (const QString &path, mFileDialog.selectedFilePaths()) {
files.push_back(path.toStdString()); 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); CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, true);
mViewManager.addView (document); mViewManager.addView (document);
mFileDialog.hide(); mFileDialog.hide();

@ -26,15 +26,15 @@ namespace CS
{ {
Q_OBJECT Q_OBJECT
Files::ConfigurationManager mCfgMgr;
CSMSettings::UserSettings mUserSettings; CSMSettings::UserSettings mUserSettings;
CSMDoc::DocumentManager mDocumentManager; CSMDoc::DocumentManager mDocumentManager;
CSVDoc::ViewManager mViewManager; CSVDoc::ViewManager mViewManager;
CSVDoc::StartupDialogue mStartup; CSVDoc::StartupDialogue mStartup;
CSVDoc::NewGameDialogue mNewGame; CSVDoc::NewGameDialogue mNewGame;
CSVSettings::UserSettingsDialog mSettings; CSVSettings::UserSettingsDialog mSettings;
FileDialog mFileDialog; CSVDoc::FileDialog mFileDialog;
Files::ConfigurationManager mCfgMgr;
boost::filesystem::path mLocal; boost::filesystem::path mLocal;
void setupDataFiles(); void setupDataFiles();
@ -59,8 +59,8 @@ namespace CS
void createAddon(); void createAddon();
void loadDocument(); void loadDocument();
void openFiles(); void openFiles (const boost::filesystem::path &path);
void createNewFile(); void createNewFile (const boost::filesystem::path& path);
void createNewGame (const boost::filesystem::path& file); void createNewGame (const boost::filesystem::path& file);
void showStartup(); void showStartup();

@ -42,7 +42,7 @@ int main(int argc, char *argv[])
if(!editor.makeIPCServer()) if(!editor.makeIPCServer())
{ {
editor.connectToIPCServer(); editor.connectToIPCServer();
return 0; // return 0;
} }
return editor.run(); return editor.run();

@ -1,8 +1,15 @@
#include "document.hpp" #include "document.hpp"
#include <cassert> #include <cassert>
#include <boost/filesystem.hpp>
#ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp>
#endif
void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_iterator& begin, void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_iterator& begin,
const std::vector<boost::filesystem::path>::const_iterator& end, bool lastAsModified) const std::vector<boost::filesystem::path>::const_iterator& end, bool lastAsModified)
{ {
assert (begin!=end); assert (begin!=end);
@ -12,10 +19,10 @@ void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_i
--end2; --end2;
for (std::vector<boost::filesystem::path>::const_iterator iter (begin); iter!=end2; ++iter) for (std::vector<boost::filesystem::path>::const_iterator iter (begin); iter!=end2; ++iter)
getData().loadFile (*iter, true); getData().loadFile (*iter, true, false);
if (lastAsModified) if (lastAsModified)
getData().loadFile (*end2, false); getData().loadFile (*end2, false, false);
} }
void CSMDoc::Document::addGmsts() void CSMDoc::Document::addGmsts()
@ -2058,9 +2065,9 @@ void CSMDoc::Document::addOptionalGlobals()
{ {
static const char *sGlobals[] = static const char *sGlobals[] =
{ {
"dayspassed", "DaysPassed",
"pcwerewolf", "PCWerewolf",
"pcyear", "PCYear",
0 0
}; };
@ -2137,11 +2144,86 @@ void CSMDoc::Document::createBase()
getData().getSkills().add (record); getData().getSkills().add (record);
} }
static const char *sVoice[] =
{
"Intruder",
"Attack",
"Hello",
"Thief",
"Alarm",
"Idle",
"Flee",
"Hit",
0
};
for (int i=0; sVoice[i]; ++i)
{
ESM::Dialogue record;
record.mId = sVoice[i];
record.mType = ESM::Dialogue::Voice;
record.blank();
getData().getTopics().add (record);
}
static const char *sGreetings[] =
{
"Greeting 0",
"Greeting 1",
"Greeting 2",
"Greeting 3",
"Greeting 4",
"Greeting 5",
"Greeting 6",
"Greeting 7",
"Greeting 8",
"Greeting 9",
0
};
for (int i=0; sGreetings[i]; ++i)
{
ESM::Dialogue record;
record.mId = sGreetings[i];
record.mType = ESM::Dialogue::Greeting;
record.blank();
getData().getTopics().add (record);
}
static const char *sPersuasion[] =
{
"Intimidate Success",
"Intimidate Fail",
"Service Refusal",
"Admire Success",
"Taunt Success",
"Bribe Success",
"Info Refusal",
"Admire Fail",
"Taunt Fail",
"Bribe Fail",
0
};
for (int i=0; sPersuasion[i]; ++i)
{
ESM::Dialogue record;
record.mId = sPersuasion[i];
record.mType = ESM::Dialogue::Persuasion;
record.blank();
getData().getTopics().add (record);
}
} }
CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files, 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_)
const boost::filesystem::path& savePath, bool new_) : mSavePath (savePath), mContentFiles (files), mTools (mData), mResDir(resDir),
: mSavePath (savePath), mTools (mData) mProjectPath ((configuration.getUserPath() / "projects") /
(savePath.filename().string() + ".project")),
mSaving (*this, mProjectPath)
{ {
if (files.empty()) if (files.empty())
throw std::runtime_error ("Empty content file sequence"); throw std::runtime_error ("Empty content file sequence");
@ -2158,6 +2240,34 @@ CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files,
load (files.begin(), end, !new_); load (files.begin(), end, !new_);
} }
if (new_)
{
mData.setDescription ("");
mData.setAuthor ("");
}
/// \todo un-outcomment the else, once loading an existing content file works properly again.
else
{
if (boost::filesystem::exists (mProjectPath))
{
getData().loadFile (mProjectPath, false, true);
}
else
{
boost::filesystem::path locCustomFiltersPath (configuration.getUserPath());
locCustomFiltersPath /= "defaultfilters";
if (boost::filesystem::exists(locCustomFiltersPath))
{
boost::filesystem::copy_file (locCustomFiltersPath, mProjectPath);
} else {
boost::filesystem::path filters(mResDir);
filters /= "defaultfilters";
boost::filesystem::copy_file(filters, mProjectPath);
}
getData().loadFile (mProjectPath, false, true);
}
}
addOptionalGmsts(); addOptionalGmsts();
addOptionalGlobals(); addOptionalGlobals();
@ -2166,9 +2276,10 @@ CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files,
connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
connect (&mTools, SIGNAL (done (int)), this, SLOT (operationDone (int))); connect (&mTools, SIGNAL (done (int)), this, SLOT (operationDone (int)));
// dummy implementation -> remove when proper save is implemented. connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
mSaveCount = 0; connect (&mSaving, SIGNAL (done (int)), this, SLOT (operationDone (int)));
connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving())); connect (&mSaving, SIGNAL (reportMessage (const QString&, int)),
this, SLOT (reportMessage (const QString&, int)));
} }
CSMDoc::Document::~Document() CSMDoc::Document::~Document()
@ -2187,7 +2298,7 @@ int CSMDoc::Document::getState() const
if (!mUndoStack.isClean()) if (!mUndoStack.isClean())
state |= State_Modified; state |= State_Modified;
if (mSaveCount) if (mSaving.isRunning())
state |= State_Locked | State_Saving | State_Operation; state |= State_Locked | State_Saving | State_Operation;
if (int operations = mTools.getRunningOperations()) if (int operations = mTools.getRunningOperations())
@ -2201,12 +2312,20 @@ const boost::filesystem::path& CSMDoc::Document::getSavePath() const
return mSavePath; return mSavePath;
} }
const std::vector<boost::filesystem::path>& CSMDoc::Document::getContentFiles() const
{
return mContentFiles;
}
void CSMDoc::Document::save() void CSMDoc::Document::save()
{ {
mSaveCount = 1; if (mSaving.isRunning())
mSaveTimer.start (500); throw std::logic_error (
"Failed to initiate save, because a save operation is already running.");
mSaving.start();
emit stateChanged (getState(), this); emit stateChanged (getState(), this);
emit progress (1, 16, State_Saving, 1, this);
} }
CSMWorld::UniversalId CSMDoc::Document::verify() CSMWorld::UniversalId CSMDoc::Document::verify()
@ -2218,44 +2337,26 @@ CSMWorld::UniversalId CSMDoc::Document::verify()
void CSMDoc::Document::abortOperation (int type) void CSMDoc::Document::abortOperation (int type)
{ {
mTools.abortOperation (type);
if (type==State_Saving) if (type==State_Saving)
{ mSaving.abort();
mSaveCount=0; else
mSaveTimer.stop(); mTools.abortOperation (type);
emit stateChanged (getState(), this);
}
} }
void CSMDoc::Document::modificationStateChanged (bool clean) void CSMDoc::Document::modificationStateChanged (bool clean)
{ {
emit stateChanged (getState(), this); emit stateChanged (getState(), this);
} }
void CSMDoc::Document::reportMessage (const QString& message, int type)
void CSMDoc::Document::operationDone (int type)
{ {
emit stateChanged (getState(), this); /// \todo find a better way to get these messages to the user.
std::cout << message.toUtf8().constData() << std::endl;
} }
void CSMDoc::Document::saving() void CSMDoc::Document::operationDone (int type)
{ {
++mSaveCount; emit stateChanged (getState(), this);
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 const CSMWorld::Data& CSMDoc::Document::getData() const

@ -14,6 +14,7 @@
#include "../tools/tools.hpp" #include "../tools/tools.hpp"
#include "state.hpp" #include "state.hpp"
#include "saving.hpp"
class QAbstractItemModel; class QAbstractItemModel;
@ -23,6 +24,11 @@ namespace ESM
struct Global; struct Global;
} }
namespace Files
{
class ConfigurationManager;
}
namespace CSMDoc namespace CSMDoc
{ {
class Document : public QObject class Document : public QObject
@ -32,16 +38,17 @@ namespace CSMDoc
private: private:
boost::filesystem::path mSavePath; boost::filesystem::path mSavePath;
std::vector<boost::filesystem::path> mContentFiles;
CSMWorld::Data mData; CSMWorld::Data mData;
CSMTools::Tools mTools; 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 // 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. // using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late.
QUndoStack mUndoStack; QUndoStack mUndoStack;
int mSaveCount; ///< dummy implementation -> remove when proper save is implemented.
QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented.
// not implemented // not implemented
Document (const Document&); Document (const Document&);
Document& operator= (const Document&); Document& operator= (const Document&);
@ -64,8 +71,7 @@ namespace CSMDoc
public: public:
Document (const std::vector<boost::filesystem::path>& files, Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_);
const boost::filesystem::path& savePath, bool new_);
~Document(); ~Document();
@ -75,6 +81,10 @@ namespace CSMDoc
const boost::filesystem::path& getSavePath() const; const boost::filesystem::path& getSavePath() const;
const std::vector<boost::filesystem::path>& 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(); void save();
CSMWorld::UniversalId verify(); CSMWorld::UniversalId verify();
@ -98,10 +108,9 @@ namespace CSMDoc
void modificationStateChanged (bool clean); void modificationStateChanged (bool clean);
void operationDone (int type); void reportMessage (const QString& message, int type);
void saving(); void operationDone (int type);
///< dummy implementation -> remove when proper save is implemented.
public slots: public slots:
@ -110,3 +119,4 @@ namespace CSMDoc
} }
#endif #endif

@ -4,9 +4,22 @@
#include <algorithm> #include <algorithm>
#include <stdexcept> #include <stdexcept>
#include <boost/filesystem.hpp>
#ifndef Q_MOC_RUN
#include <components/files/configurationmanager.hpp>
#endif
#include "document.hpp" #include "document.hpp"
CSMDoc::DocumentManager::DocumentManager() {} CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& configuration)
: mConfiguration (configuration)
{
boost::filesystem::path projectPath = configuration.getUserPath() / "projects";
if (!boost::filesystem::is_directory (projectPath))
boost::filesystem::create_directories (projectPath);
}
CSMDoc::DocumentManager::~DocumentManager() CSMDoc::DocumentManager::~DocumentManager()
{ {
@ -17,7 +30,7 @@ CSMDoc::DocumentManager::~DocumentManager()
CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath, CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath,
bool new_) bool new_)
{ {
Document *document = new Document (files, savePath, new_); Document *document = new Document (mConfiguration, files, savePath, mResDir, new_);
mDocuments.push_back (document); mDocuments.push_back (document);
@ -35,4 +48,9 @@ bool CSMDoc::DocumentManager::removeDocument (Document *document)
delete document; delete document;
return mDocuments.empty(); return mDocuments.empty();
}
void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& parResDir)
{
mResDir = boost::filesystem::system_complete(parResDir);
} }

@ -6,6 +6,11 @@
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
namespace Files
{
class ConfigurationManager;
}
namespace CSMDoc namespace CSMDoc
{ {
class Document; class Document;
@ -13,18 +18,18 @@ namespace CSMDoc
class DocumentManager class DocumentManager
{ {
std::vector<Document *> mDocuments; std::vector<Document *> mDocuments;
const Files::ConfigurationManager& mConfiguration;
DocumentManager (const DocumentManager&); DocumentManager (const DocumentManager&);
DocumentManager& operator= (const DocumentManager&); DocumentManager& operator= (const DocumentManager&);
public: public:
DocumentManager(); DocumentManager (const Files::ConfigurationManager& configuration);
~DocumentManager(); ~DocumentManager();
Document *addDocument (const std::vector<boost::filesystem::path>& files, Document *addDocument (const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, bool new_);
const boost::filesystem::path& savePath, bool new_);
///< The ownership of the returned document is not transferred to the caller. ///< 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 /// \param new_ Do not load the last content file in \a files and instead create in an
@ -32,6 +37,10 @@ namespace CSMDoc
bool removeDocument (Document *document); bool removeDocument (Document *document);
///< \return last document removed? ///< \return last document removed?
void setResourceDir (const boost::filesystem::path& parResDir);
private:
boost::filesystem::path mResDir;
}; };
} }

@ -6,16 +6,16 @@
#include <QTimer> #include <QTimer>
#include "../doc/state.hpp" #include "state.hpp"
#include "stage.hpp" #include "stage.hpp"
void CSMTools::Operation::prepareStages() void CSMDoc::Operation::prepareStages()
{ {
mCurrentStage = mStages.begin(); mCurrentStage = mStages.begin();
mCurrentStep = 0; mCurrentStep = 0;
mCurrentStepTotal = 0; mCurrentStepTotal = 0;
mTotalSteps = 0; mTotalSteps = 0;
mError = false;
for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter) for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)
{ {
@ -24,38 +24,61 @@ void CSMTools::Operation::prepareStages()
} }
} }
CSMTools::Operation::Operation (int type) : mType (type) {} CSMDoc::Operation::Operation (int type, bool ordered, bool finalAlways)
: mType (type), mOrdered (ordered), mFinalAlways (finalAlways)
{
connect (this, SIGNAL (finished()), this, SLOT (operationDone()));
}
CSMTools::Operation::~Operation() CSMDoc::Operation::~Operation()
{ {
for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter) for (std::vector<std::pair<Stage *, int> >::iterator iter (mStages.begin()); iter!=mStages.end(); ++iter)
delete iter->first; delete iter->first;
} }
void CSMTools::Operation::run() void CSMDoc::Operation::run()
{ {
prepareStages(); prepareStages();
QTimer timer; QTimer timer;
timer.connect (&timer, SIGNAL (timeout()), this, SLOT (verify())); timer.connect (&timer, SIGNAL (timeout()), this, SLOT (executeStage()));
timer.start (0); timer.start (0);
exec(); exec();
} }
void CSMTools::Operation::appendStage (Stage *stage) void CSMDoc::Operation::appendStage (Stage *stage)
{ {
mStages.push_back (std::make_pair (stage, 0)); mStages.push_back (std::make_pair (stage, 0));
} }
void CSMTools::Operation::abort() bool CSMDoc::Operation::hasError() const
{ {
exit(); return mError;
} }
void CSMTools::Operation::verify() void CSMDoc::Operation::abort()
{
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()
{ {
std::vector<std::string> messages; std::vector<std::string> messages;
@ -68,7 +91,16 @@ void CSMTools::Operation::verify()
} }
else else
{ {
mCurrentStage->first->perform (mCurrentStep++, messages); try
{
mCurrentStage->first->perform (mCurrentStep++, messages);
}
catch (const std::exception& e)
{
emit reportMessage (e.what(), mType);
abort();
}
++mCurrentStepTotal; ++mCurrentStepTotal;
break; break;
} }
@ -81,4 +113,9 @@ void CSMTools::Operation::verify()
if (mCurrentStage==mStages.end()) if (mCurrentStage==mStages.end())
exit(); exit();
}
void CSMDoc::Operation::operationDone()
{
emit done (mType);
} }

@ -1,11 +1,11 @@
#ifndef CSM_TOOLS_OPERATION_H #ifndef CSM_DOC_OPERATION_H
#define CSM_TOOLS_OPERATION_H #define CSM_DOC_OPERATION_H
#include <vector> #include <vector>
#include <QThread> #include <QThread>
namespace CSMTools namespace CSMDoc
{ {
class Stage; class Stage;
@ -19,12 +19,17 @@ namespace CSMTools
int mCurrentStep; int mCurrentStep;
int mCurrentStepTotal; int mCurrentStepTotal;
int mTotalSteps; int mTotalSteps;
int mOrdered;
bool mFinalAlways;
bool mError;
void prepareStages(); void prepareStages();
public: public:
Operation (int type); 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(); virtual ~Operation();
@ -35,19 +40,25 @@ namespace CSMTools
/// ///
/// \attention Do no call this function while this Operation is running. /// \attention Do no call this function while this Operation is running.
bool hasError() const;
signals: signals:
void progress (int current, int max, int type); void progress (int current, int max, int type);
void reportMessage (const QString& message, int type); void reportMessage (const QString& message, int type);
void done (int type);
public slots: public slots:
void abort(); void abort();
private slots: private slots:
void verify(); void executeStage();
void operationDone();
}; };
} }

@ -0,0 +1,74 @@
#include "saving.hpp"
#include "../world/data.hpp"
#include "../world/idcollection.hpp"
#include "state.hpp"
#include "savingstages.hpp"
#include "document.hpp"
CSMDoc::Saving::Saving (Document& document, const boost::filesystem::path& projectPath)
: Operation (State_Saving, true, true), mDocument (document), mState (*this, projectPath)
{
// save project file
appendStage (new OpenSaveStage (mDocument, mState, true));
appendStage (new WriteHeaderStage (mDocument, mState, true));
appendStage (new WriteFilterStage (mDocument, mState, CSMFilter::Filter::Scope_Project));
appendStage (new CloseSaveStage (mState));
// save content file
appendStage (new OpenSaveStage (mDocument, mState, false));
appendStage (new WriteHeaderStage (mDocument, mState, false));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Global> >
(mDocument.getData().getGlobals(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::GameSetting> >
(mDocument.getData().getGmsts(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Skill> >
(mDocument.getData().getSkills(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Class> >
(mDocument.getData().getClasses(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Faction> >
(mDocument.getData().getFactions(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Race> >
(mDocument.getData().getRaces(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Sound> >
(mDocument.getData().getSounds(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Script> >
(mDocument.getData().getScripts(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Region> >
(mDocument.getData().getRegions(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::BirthSign> >
(mDocument.getData().getBirthsigns(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Spell> >
(mDocument.getData().getSpells(), mState));
/// \todo deal with info records for topcis and journals
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Dialogue> >
(mDocument.getData().getTopics(), mState));
appendStage (new WriteCollectionStage<CSMWorld::IdCollection<ESM::Dialogue> >
(mDocument.getData().getJournals(), mState));
appendStage (new WriteRefIdCollectionStage (mDocument, mState));
appendStage (new CloseSaveStage (mState));
appendStage (new FinalSavingStage (mDocument, mState));
}

@ -0,0 +1,27 @@
#ifndef CSM_DOC_SAVING_H
#define CSM_DOC_SAVING_H
#include <boost/filesystem/path.hpp>
#include "operation.hpp"
#include "savingstate.hpp"
namespace CSMDoc
{
class Document;
class Saving : public Operation
{
Q_OBJECT
Document& mDocument;
SavingState mState;
public:
Saving (Document& document, const boost::filesystem::path& projectPath);
};
}
#endif

@ -0,0 +1,161 @@
#include "savingstages.hpp"
#include <fstream>
#include <boost/filesystem.hpp>
#include <QUndoStack>
#include "document.hpp"
#include "savingstate.hpp"
CSMDoc::OpenSaveStage::OpenSaveStage (Document& document, SavingState& state, bool projectFile)
: mDocument (document), mState (state), mProjectFile (projectFile)
{}
int CSMDoc::OpenSaveStage::setup()
{
return 1;
}
void CSMDoc::OpenSaveStage::perform (int stage, std::vector<std::string>& messages)
{
mState.start (mDocument, mProjectFile);
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, bool simple)
: mDocument (document), mState (state), mSimple (simple)
{}
int CSMDoc::WriteHeaderStage::setup()
{
return 1;
}
void CSMDoc::WriteHeaderStage::perform (int stage, std::vector<std::string>& messages)
{
mState.getWriter().setVersion();
mState.getWriter().clearMaster();
mState.getWriter().setFormat (0);
if (mSimple)
{
mState.getWriter().setAuthor ("");
mState.getWriter().setDescription ("");
mState.getWriter().setRecordCount (0);
}
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<boost::filesystem::path> dependencies = mDocument.getContentFiles();
std::vector<boost::filesystem::path>::const_iterator end (--dependencies.end());
for (std::vector<boost::filesystem::path>::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());
}
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<std::string>& messages)
{
mDocument.getData().getReferenceables().save (stage, mState.getWriter());
}
CSMDoc::WriteFilterStage::WriteFilterStage (Document& document, SavingState& state,
CSMFilter::Filter::Scope scope)
: WriteCollectionStage<CSMWorld::IdCollection<CSMFilter::Filter> > (document.getData().getFilters(),
state),
mDocument (document), mScope (scope)
{}
void CSMDoc::WriteFilterStage::perform (int stage, std::vector<std::string>& messages)
{
const CSMWorld::Record<CSMFilter::Filter>& record =
mDocument.getData().getFilters().getRecord (stage);
if (record.get().mScope==mScope)
WriteCollectionStage<CSMWorld::IdCollection<CSMFilter::Filter> >::perform (stage, messages);
}
CSMDoc::CloseSaveStage::CloseSaveStage (SavingState& state)
: mState (state)
{}
int CSMDoc::CloseSaveStage::setup()
{
return 1;
}
void CSMDoc::CloseSaveStage::perform (int stage, std::vector<std::string>& messages)
{
mState.getStream().close();
if (!mState.getStream())
throw std::runtime_error ("saving failed");
}
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<std::string>& messages)
{
if (mState.hasError())
{
mState.getWriter().close();
mState.getStream().close();
if (boost::filesystem::exists (mState.getTmpPath()))
boost::filesystem::remove (mState.getTmpPath());
}
else if (!mState.isProjectFile())
{
if (boost::filesystem::exists (mState.getPath()))
boost::filesystem::remove (mState.getPath());
boost::filesystem::rename (mState.getTmpPath(), mState.getPath());
mDocument.getUndoStack().setClean();
}
}

@ -0,0 +1,172 @@
#ifndef CSM_DOC_SAVINGSTAGES_H
#define CSM_DOC_SAVINGSTAGES_H
#include "stage.hpp"
#include "savingstate.hpp"
#include "../world/record.hpp"
#include "../world/idcollection.hpp"
#include "../filter/filter.hpp"
namespace CSMDoc
{
class Document;
class SavingState;
class OpenSaveStage : public Stage
{
Document& mDocument;
SavingState& mState;
bool mProjectFile;
public:
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
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class WriteHeaderStage : public Stage
{
Document& mDocument;
SavingState& mState;
bool mSimple;
public:
WriteHeaderStage (Document& document, SavingState& state, bool simple);
///< \param simple Simplified header (used for project files).
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
template<class CollectionT>
class WriteCollectionStage : public Stage
{
const CollectionT& mCollection;
SavingState& mState;
public:
WriteCollectionStage (const CollectionT& collection, SavingState& state);
virtual int setup();
///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
template<class CollectionT>
WriteCollectionStage<CollectionT>::WriteCollectionStage (const CollectionT& collection,
SavingState& state)
: mCollection (collection), mState (state)
{}
template<class CollectionT>
int WriteCollectionStage<CollectionT>::setup()
{
return mCollection.getSize();
}
template<class CollectionT>
void WriteCollectionStage<CollectionT>::perform (int stage, std::vector<std::string>& 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<const char *> (&mCollection.getRecord (stage).mModified.sRecordId)[i];
mState.getWriter().startRecord (type);
mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage));
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 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<std::string>& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
class WriteFilterStage : public WriteCollectionStage<CSMWorld::IdCollection<CSMFilter::Filter> >
{
Document& mDocument;
CSMFilter::Filter::Scope mScope;
public:
WriteFilterStage (Document& document, SavingState& state, CSMFilter::Filter::Scope scope);
virtual void perform (int stage, std::vector<std::string>& 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<std::string>& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
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<std::string>& messages);
///< Messages resulting from this stage will be appended to \a messages.
};
}
#endif

@ -0,0 +1,65 @@
#include "savingstate.hpp"
#include "operation.hpp"
#include "document.hpp"
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")),
mProjectPath (projectPath), mProjectFile (false)
{
mWriter.setEncoder (&mEncoder);
}
bool CSMDoc::SavingState::hasError() const
{
return mOperation.hasError();
}
void CSMDoc::SavingState::start (Document& document, bool project)
{
mProjectFile = project;
if (mStream.is_open())
mStream.close();
mStream.clear();
if (project)
mPath = mProjectPath;
else
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;
}
bool CSMDoc::SavingState::isProjectFile() const
{
return mProjectFile;
}

@ -0,0 +1,50 @@
#ifndef CSM_DOC_SAVINGSTATE_H
#define CSM_DOC_SAVINGSTATE_H
#include <fstream>
#include <boost/filesystem/path.hpp>
#include <components/esm/esmwriter.hpp>
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;
boost::filesystem::path mProjectPath;
bool mProjectFile;
public:
SavingState (Operation& operation, const boost::filesystem::path& projectPath);
bool hasError() const;
void start (Document& document, bool project);
///< \param project Save project file instead of content file.
const boost::filesystem::path& getPath() const;
const boost::filesystem::path& getTmpPath() const;
std::ofstream& getStream();
ESM::ESMWriter& getWriter();
bool isProjectFile() const;
///< Currently saving project file? (instead of content file)
};
}
#endif

@ -0,0 +1,4 @@
#include "stage.hpp"
CSMDoc::Stage::~Stage() {}

@ -1,10 +1,10 @@
#ifndef CSM_TOOLS_STAGE_H #ifndef CSM_DOC_STAGE_H
#define CSM_TOOLS_STAGE_H #define CSM_DOC_STAGE_H
#include <vector> #include <vector>
#include <string> #include <string>
namespace CSMTools namespace CSMDoc
{ {
class Stage class Stage
{ {
@ -16,7 +16,7 @@ namespace CSMTools
///< \return number of steps ///< \return number of steps
virtual void perform (int stage, std::vector<std::string>& messages) = 0; virtual void perform (int stage, std::vector<std::string>& messages) = 0;
///< Messages resulting from this tage will be appended to \a messages. ///< Messages resulting from this stage will be appended to \a messages.
}; };
} }

@ -5,12 +5,12 @@
#include "../world/idcollection.hpp" #include "../world/idcollection.hpp"
#include "stage.hpp" #include "../doc/stage.hpp"
namespace CSMTools namespace CSMTools
{ {
/// \brief VerifyStage: make sure that birthsign records are internally consistent /// \brief VerifyStage: make sure that birthsign records are internally consistent
class BirthsignCheckStage : public Stage class BirthsignCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::BirthSign>& mBirthsigns; const CSMWorld::IdCollection<ESM::BirthSign>& mBirthsigns;

@ -5,12 +5,12 @@
#include "../world/idcollection.hpp" #include "../world/idcollection.hpp"
#include "stage.hpp" #include "../doc/stage.hpp"
namespace CSMTools namespace CSMTools
{ {
/// \brief VerifyStage: make sure that class records are internally consistent /// \brief VerifyStage: make sure that class records are internally consistent
class ClassCheckStage : public Stage class ClassCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::Class>& mClasses; const CSMWorld::IdCollection<ESM::Class>& mClasses;

@ -5,12 +5,12 @@
#include "../world/idcollection.hpp" #include "../world/idcollection.hpp"
#include "stage.hpp" #include "../doc/stage.hpp"
namespace CSMTools namespace CSMTools
{ {
/// \brief VerifyStage: make sure that faction records are internally consistent /// \brief VerifyStage: make sure that faction records are internally consistent
class FactionCheckStage : public Stage class FactionCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::Faction>& mFactions; const CSMWorld::IdCollection<ESM::Faction>& mFactions;

@ -6,7 +6,7 @@
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
#include "stage.hpp" #include "../doc/stage.hpp"
namespace CSMWorld namespace CSMWorld
{ {
@ -16,7 +16,7 @@ namespace CSMWorld
namespace CSMTools namespace CSMTools
{ {
/// \brief Verify stage: make sure that records with specific IDs exist. /// \brief Verify stage: make sure that records with specific IDs exist.
class MandatoryIdStage : public Stage class MandatoryIdStage : public CSMDoc::Stage
{ {
const CSMWorld::CollectionBase& mIdCollection; const CSMWorld::CollectionBase& mIdCollection;
CSMWorld::UniversalId mCollectionId; CSMWorld::UniversalId mCollectionId;

@ -5,12 +5,12 @@
#include "../world/idcollection.hpp" #include "../world/idcollection.hpp"
#include "stage.hpp" #include "../doc/stage.hpp"
namespace CSMTools namespace CSMTools
{ {
/// \brief VerifyStage: make sure that race records are internally consistent /// \brief VerifyStage: make sure that race records are internally consistent
class RaceCheckStage : public Stage class RaceCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::Race>& mRaces; const CSMWorld::IdCollection<ESM::Race>& mRaces;
bool mPlayable; bool mPlayable;

@ -5,12 +5,12 @@
#include "../world/idcollection.hpp" #include "../world/idcollection.hpp"
#include "stage.hpp" #include "../doc/stage.hpp"
namespace CSMTools namespace CSMTools
{ {
/// \brief VerifyStage: make sure that region records are internally consistent /// \brief VerifyStage: make sure that region records are internally consistent
class RegionCheckStage : public Stage class RegionCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::Region>& mRegions; const CSMWorld::IdCollection<ESM::Region>& mRegions;

@ -5,12 +5,12 @@
#include "../world/idcollection.hpp" #include "../world/idcollection.hpp"
#include "stage.hpp" #include "../doc/stage.hpp"
namespace CSMTools namespace CSMTools
{ {
/// \brief VerifyStage: make sure that skill records are internally consistent /// \brief VerifyStage: make sure that skill records are internally consistent
class SkillCheckStage : public Stage class SkillCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::Skill>& mSkills; const CSMWorld::IdCollection<ESM::Skill>& mSkills;

@ -5,12 +5,12 @@
#include "../world/idcollection.hpp" #include "../world/idcollection.hpp"
#include "stage.hpp" #include "../doc/stage.hpp"
namespace CSMTools namespace CSMTools
{ {
/// \brief VerifyStage: make sure that sound records are internally consistent /// \brief VerifyStage: make sure that sound records are internally consistent
class SoundCheckStage : public Stage class SoundCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::Sound>& mSounds; const CSMWorld::IdCollection<ESM::Sound>& mSounds;

@ -5,12 +5,12 @@
#include "../world/idcollection.hpp" #include "../world/idcollection.hpp"
#include "stage.hpp" #include "../doc/stage.hpp"
namespace CSMTools namespace CSMTools
{ {
/// \brief VerifyStage: make sure that spell records are internally consistent /// \brief VerifyStage: make sure that spell records are internally consistent
class SpellCheckStage : public Stage class SpellCheckStage : public CSMDoc::Stage
{ {
const CSMWorld::IdCollection<ESM::Spell>& mSpells; const CSMWorld::IdCollection<ESM::Spell>& mSpells;

@ -1,4 +0,0 @@
#include "stage.hpp"
CSMTools::Stage::~Stage() {}

@ -3,9 +3,8 @@
#include <QThreadPool> #include <QThreadPool>
#include "verifier.hpp"
#include "../doc/state.hpp" #include "../doc/state.hpp"
#include "../doc/operation.hpp"
#include "../world/data.hpp" #include "../world/data.hpp"
#include "../world/universalid.hpp" #include "../world/universalid.hpp"
@ -21,7 +20,7 @@
#include "birthsigncheck.hpp" #include "birthsigncheck.hpp"
#include "spellcheck.hpp" #include "spellcheck.hpp"
CSMTools::Operation *CSMTools::Tools::get (int type) CSMDoc::Operation *CSMTools::Tools::get (int type)
{ {
switch (type) switch (type)
{ {
@ -31,19 +30,19 @@ CSMTools::Operation *CSMTools::Tools::get (int type)
return 0; return 0;
} }
const CSMTools::Operation *CSMTools::Tools::get (int type) const const CSMDoc::Operation *CSMTools::Tools::get (int type) const
{ {
return const_cast<Tools *> (this)->get (type); return const_cast<Tools *> (this)->get (type);
} }
CSMTools::Verifier *CSMTools::Tools::getVerifier() CSMDoc::Operation *CSMTools::Tools::getVerifier()
{ {
if (!mVerifier) 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 (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)), connect (mVerifier, SIGNAL (reportMessage (const QString&, int)),
this, SLOT (verifierMessage (const QString&, int))); this, SLOT (verifierMessage (const QString&, int)));
@ -103,7 +102,7 @@ CSMWorld::UniversalId CSMTools::Tools::runVerifier()
void CSMTools::Tools::abortOperation (int type) void CSMTools::Tools::abortOperation (int type)
{ {
if (Operation *operation = get (type)) if (CSMDoc::Operation *operation = get (type))
operation->abort(); operation->abort();
} }
@ -118,7 +117,7 @@ int CSMTools::Tools::getRunningOperations() const
int result = 0; int result = 0;
for (int i=0; sOperations[i]!=-1; ++i) 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()) if (operation->isRunning())
result |= sOperations[i]; result |= sOperations[i];
@ -133,11 +132,6 @@ CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId&
return mReports.at (id.getIndex()); return mReports.at (id.getIndex());
} }
void CSMTools::Tools::verifierDone()
{
emit done (CSMDoc::State_Verifying);
}
void CSMTools::Tools::verifierMessage (const QString& message, int type) void CSMTools::Tools::verifierMessage (const QString& message, int type)
{ {
std::map<int, int>::iterator iter = mActiveReports.find (type); std::map<int, int>::iterator iter = mActiveReports.find (type);

@ -11,10 +11,13 @@ namespace CSMWorld
class UniversalId; class UniversalId;
} }
namespace CSMTools namespace CSMDoc
{ {
class Verifier;
class Operation; class Operation;
}
namespace CSMTools
{
class ReportModel; class ReportModel;
class Tools : public QObject class Tools : public QObject
@ -22,7 +25,7 @@ namespace CSMTools
Q_OBJECT Q_OBJECT
CSMWorld::Data& mData; CSMWorld::Data& mData;
Verifier *mVerifier; CSMDoc::Operation *mVerifier;
std::map<int, ReportModel *> mReports; std::map<int, ReportModel *> mReports;
int mNextReportNumber; int mNextReportNumber;
std::map<int, int> mActiveReports; // type, report number std::map<int, int> mActiveReports; // type, report number
@ -31,12 +34,12 @@ namespace CSMTools
Tools (const Tools&); Tools (const Tools&);
Tools& operator= (const Tools&); Tools& operator= (const Tools&);
Verifier *getVerifier(); CSMDoc::Operation *getVerifier();
Operation *get (int type); CSMDoc::Operation *get (int type);
///< Returns a 0-pointer, if operation hasn't been used yet. ///< 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. ///< Returns a 0-pointer, if operation hasn't been used yet.
public: public:
@ -58,8 +61,6 @@ namespace CSMTools
private slots: private slots:
void verifierDone();
void verifierMessage (const QString& message, int type); void verifierMessage (const QString& message, int type);
signals: signals:

@ -1,7 +0,0 @@
#include "verifier.hpp"
#include "../doc/state.hpp"
CSMTools::Verifier::Verifier() : Operation (CSMDoc::State_Verifying)
{}

@ -1,17 +0,0 @@
#ifndef CSM_TOOLS_VERIFIER_H
#define CSM_TOOLS_VERIFIER_H
#include "operation.hpp"
namespace CSMTools
{
class Verifier : public Operation
{
public:
Verifier();
};
}
#endif

@ -1,6 +1,31 @@
#include "collectionbase.hpp" #include "collectionbase.hpp"
#include <stdexcept>
#include "columnbase.hpp"
CSMWorld::CollectionBase::CollectionBase() {} CSMWorld::CollectionBase::CollectionBase() {}
CSMWorld::CollectionBase::~CollectionBase() {} CSMWorld::CollectionBase::~CollectionBase() {}
int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const
{
int columns = getColumns();
for (int i=0; i<columns; ++i)
if (getColumn (i).mColumnId==id)
return i;
return -1;
}
int CSMWorld::CollectionBase::findColumnIndex (Columns::ColumnId id) const
{
int index = searchColumnIndex (id);
if (index==-1)
throw std::logic_error ("invalid column index");
return index;
}

@ -4,6 +4,7 @@
#include <string> #include <string>
#include "universalid.hpp" #include "universalid.hpp"
#include "columns.hpp"
class QVariant; class QVariant;
@ -83,6 +84,13 @@ namespace CSMWorld
///< Return a sorted collection of all IDs ///< Return a sorted collection of all IDs
/// ///
/// \param listDeleted include deleted record in the list /// \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.
}; };
} }

@ -43,7 +43,8 @@ namespace CSMWorld
Display_CreatureType, Display_CreatureType,
Display_WeaponType, Display_WeaponType,
Display_RecordState, Display_RecordState,
Display_RefRecordType Display_RefRecordType,
Display_DialogueType
}; };
int mColumnId; int mColumnId;

@ -1217,6 +1217,37 @@ namespace CSMWorld
} }
}; };
template<typename ESXRecordT>
struct ScopeColumn : public Column<ESXRecordT>
{
ScopeColumn()
: Column<ESXRecordT> (Columns::ColumnId_Scope, ColumnBase::Display_Integer, 0)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return static_cast<int> (record.get().mScope);
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mScope = static_cast<CSMFilter::Filter::Scope> (data.toInt());
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
virtual bool isUserEditable() const
{
return false;
}
};
template<typename ESXRecordT> template<typename ESXRecordT>
struct PosColumn : public Column<ESXRecordT> struct PosColumn : public Column<ESXRecordT>
{ {
@ -1284,6 +1315,39 @@ namespace CSMWorld
return true; return true;
} }
}; };
template<typename ESXRecordT>
struct DialogueTypeColumn : public Column<ESXRecordT>
{
DialogueTypeColumn (bool hidden = false)
: Column<ESXRecordT> (Columns::ColumnId_DialogueType, ColumnBase::Display_DialogueType,
hidden ? 0 : ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue)
{}
virtual QVariant get (const Record<ESXRecordT>& record) const
{
return static_cast<int> (record.get().mType);
}
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
{
ESXRecordT record2 = record.get();
record2.mType = data.toInt();
record.setModified (record2);
}
virtual bool isEditable() const
{
return true;
}
virtual bool isUserEditable() const
{
return false;
}
};
} }
#endif #endif

@ -159,6 +159,8 @@ namespace CSMWorld
{ ColumnId_DoorPositionXRot, "Teleport Rot X" }, { ColumnId_DoorPositionXRot, "Teleport Rot X" },
{ ColumnId_DoorPositionYRot, "Teleport Rot Y" }, { ColumnId_DoorPositionYRot, "Teleport Rot Y" },
{ ColumnId_DoorPositionZRot, "Teleport Rot Z" }, { ColumnId_DoorPositionZRot, "Teleport Rot Z" },
{ ColumnId_DialogueType, "Dialogue Type" },
{ ColumnId_Scope, "Scope", },
{ ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue1, "Use value 1" },
{ ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue2, "Use value 2" },
@ -269,6 +271,11 @@ namespace
"unknown", "none", "short", "integer", "long", "float", "string", 0 "unknown", "none", "short", "integer", "long", "float", "string", 0
}; };
static const char *sDialogueTypeEnums[] =
{
"Topic", "Voice", "Greeting", "Persuasion", 0
};
const char **getEnumNames (CSMWorld::Columns::ColumnId column) const char **getEnumNames (CSMWorld::Columns::ColumnId column)
{ {
switch (column) switch (column)
@ -283,6 +290,7 @@ namespace
case CSMWorld::Columns::ColumnId_WeaponType: return sWeaponTypes; case CSMWorld::Columns::ColumnId_WeaponType: return sWeaponTypes;
case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums; case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums;
case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums; case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums;
case CSMWorld::Columns::ColumnId_DialogueType: return sDialogueTypeEnums;
default: return 0; default: return 0;
} }

@ -152,6 +152,8 @@ namespace CSMWorld
ColumnId_DoorPositionXRot = 139, ColumnId_DoorPositionXRot = 139,
ColumnId_DoorPositionYRot = 140, ColumnId_DoorPositionYRot = 140,
ColumnId_DoorPositionZRot = 141, ColumnId_DoorPositionZRot = 141,
ColumnId_DialogueType = 142,
ColumnId_Scope = 143,
// Allocated to a separate value range, so we don't get a collision should we ever need // Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values. // to extend the number of use values.

@ -44,6 +44,17 @@ void CSMWorld::Data::appendIds (std::vector<std::string>& ids, const CollectionB
ids.insert (ids.end(), ids2.begin(), ids2.end()); 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<collection.getSize(); ++i)
if (collection.getRecord (i).mState==state)
++number;
return number;
}
CSMWorld::Data::Data() : mRefs (mCells) CSMWorld::Data::Data() : mRefs (mCells)
{ {
mGlobals.addColumn (new StringIdColumn<ESM::Global>); mGlobals.addColumn (new StringIdColumn<ESM::Global>);
@ -141,6 +152,14 @@ CSMWorld::Data::Data() : mRefs (mCells)
mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_StarterSpell, 0x2)); mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_StarterSpell, 0x2));
mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_AlwaysSucceeds, 0x4)); mSpells.addColumn (new FlagColumn<ESM::Spell> (Columns::ColumnId_AlwaysSucceeds, 0x4));
mTopics.addColumn (new StringIdColumn<ESM::Dialogue>);
mTopics.addColumn (new RecordStateColumn<ESM::Dialogue>);
mTopics.addColumn (new DialogueTypeColumn<ESM::Dialogue>);
mJournals.addColumn (new StringIdColumn<ESM::Dialogue>);
mJournals.addColumn (new RecordStateColumn<ESM::Dialogue>);
mJournals.addColumn (new DialogueTypeColumn<ESM::Dialogue> (true));
mCells.addColumn (new StringIdColumn<Cell>); mCells.addColumn (new StringIdColumn<Cell>);
mCells.addColumn (new RecordStateColumn<Cell>); mCells.addColumn (new RecordStateColumn<Cell>);
mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_Cell)); mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_Cell));
@ -184,6 +203,7 @@ CSMWorld::Data::Data() : mRefs (mCells)
mFilters.addColumn (new RecordStateColumn<CSMFilter::Filter>); mFilters.addColumn (new RecordStateColumn<CSMFilter::Filter>);
mFilters.addColumn (new FilterColumn<CSMFilter::Filter>); mFilters.addColumn (new FilterColumn<CSMFilter::Filter>);
mFilters.addColumn (new DescriptionColumn<CSMFilter::Filter>); mFilters.addColumn (new DescriptionColumn<CSMFilter::Filter>);
mFilters.addColumn (new ScopeColumn<CSMFilter::Filter>);
addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global); addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global);
addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst); addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst);
@ -196,6 +216,8 @@ CSMWorld::Data::Data() : mRefs (mCells)
addModel (new IdTable (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region); addModel (new IdTable (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region);
addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign); addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign);
addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell); addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell);
addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic);
addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal);
addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell); addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell);
addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables,
UniversalId::Type_Referenceable); UniversalId::Type_Referenceable);
@ -319,6 +341,28 @@ CSMWorld::IdCollection<ESM::Spell>& CSMWorld::Data::getSpells()
return mSpells; return mSpells;
} }
const CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getTopics() const
{
return mTopics;
}
CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getTopics()
{
return mTopics;
}
const CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getJournals() const
{
return mJournals;
}
CSMWorld::IdCollection<ESM::Dialogue>& CSMWorld::Data::getJournals()
{
return mJournals;
}
const CSMWorld::IdCollection<CSMWorld::Cell>& CSMWorld::Data::getCells() const const CSMWorld::IdCollection<CSMWorld::Cell>& CSMWorld::Data::getCells() const
{ {
return mCells; return mCells;
@ -387,7 +431,7 @@ void CSMWorld::Data::merge()
mGlobals.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; ESM::ESMReader reader;
@ -397,6 +441,9 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
reader.open (path.string()); 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 // Note: We do not need to send update signals here, because at this point the model is not connected
// to any view. // to any view.
while (reader.hasMoreRecs()) while (reader.hasMoreRecs())
@ -447,6 +494,54 @@ 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_STAT: mReferenceables.load (reader, base, UniversalId::Type_Static); break;
case ESM::REC_WEAP: mReferenceables.load (reader, base, UniversalId::Type_Weapon); break; case ESM::REC_WEAP: mReferenceables.load (reader, base, UniversalId::Type_Weapon); break;
case ESM::REC_DIAL:
{
std::string id = reader.getHNOString ("NAME");
ESM::Dialogue record;
record.mId = id;
record.load (reader);
if (record.mType==ESM::Dialogue::Journal)
{
mJournals.load (record, base);
}
else if (record.mType==ESM::Dialogue::Deleted)
{
if (mJournals.tryDelete (id))
{
/// \todo handle info records
}
else if (mTopics.tryDelete (id))
{
/// \todo handle info records
}
else
{
/// \todo report deletion of non-existing record
}
}
else
{
mTopics.load (record, base);
}
break;
}
case ESM::REC_FILT:
if (project)
{
mFilters.load (reader, base);
mFilters.setData (mFilters.getSize()-1,
mFilters.findColumnIndex (CSMWorld::Columns::ColumnId_Scope),
static_cast<int> (CSMFilter::Filter::Scope_Project));
break;
}
// fall through (filter record in a content file is an error with format 0)
default: default:
/// \todo throw an exception instead, once all records are implemented /// \todo throw an exception instead, once all records are implemented
@ -469,10 +564,50 @@ bool CSMWorld::Data::hasId (const std::string& id) const
getRegions().searchId (id)!=-1 || getRegions().searchId (id)!=-1 ||
getBirthsigns().searchId (id)!=-1 || getBirthsigns().searchId (id)!=-1 ||
getSpells().searchId (id)!=-1 || getSpells().searchId (id)!=-1 ||
getTopics().searchId (id)!=-1 ||
getJournals().searchId (id)!=-1 ||
getCells().searchId (id)!=-1 || getCells().searchId (id)!=-1 ||
getReferenceables().searchId (id)!=-1; 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);
}
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<std::string> CSMWorld::Data::getIds (bool listDeleted) const std::vector<std::string> CSMWorld::Data::getIds (bool listDeleted) const
{ {
std::vector<std::string> ids; std::vector<std::string> ids;
@ -487,6 +622,8 @@ std::vector<std::string> CSMWorld::Data::getIds (bool listDeleted) const
appendIds (ids, mRegions, listDeleted); appendIds (ids, mRegions, listDeleted);
appendIds (ids, mBirthsigns, listDeleted); appendIds (ids, mBirthsigns, listDeleted);
appendIds (ids, mSpells, listDeleted); appendIds (ids, mSpells, listDeleted);
appendIds (ids, mTopics, listDeleted);
appendIds (ids, mJournals, listDeleted);
appendIds (ids, mCells, listDeleted); appendIds (ids, mCells, listDeleted);
appendIds (ids, mReferenceables, listDeleted); appendIds (ids, mReferenceables, listDeleted);

@ -20,6 +20,7 @@
#include <components/esm/loadregn.hpp> #include <components/esm/loadregn.hpp>
#include <components/esm/loadbsgn.hpp> #include <components/esm/loadbsgn.hpp>
#include <components/esm/loadspel.hpp> #include <components/esm/loadspel.hpp>
#include <components/esm/loaddial.hpp>
#include "../filter/filter.hpp" #include "../filter/filter.hpp"
@ -48,12 +49,16 @@ namespace CSMWorld
IdCollection<ESM::Region> mRegions; IdCollection<ESM::Region> mRegions;
IdCollection<ESM::BirthSign> mBirthsigns; IdCollection<ESM::BirthSign> mBirthsigns;
IdCollection<ESM::Spell> mSpells; IdCollection<ESM::Spell> mSpells;
IdCollection<ESM::Dialogue> mTopics;
IdCollection<ESM::Dialogue> mJournals;
IdCollection<Cell> mCells; IdCollection<Cell> mCells;
RefIdCollection mReferenceables; RefIdCollection mReferenceables;
RefCollection mRefs; RefCollection mRefs;
IdCollection<CSMFilter::Filter> mFilters; IdCollection<CSMFilter::Filter> mFilters;
std::vector<QAbstractItemModel *> mModels; std::vector<QAbstractItemModel *> mModels;
std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex; std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex;
std::string mAuthor;
std::string mDescription;
// not implemented // not implemented
Data (const Data&); Data (const Data&);
@ -66,6 +71,8 @@ namespace CSMWorld
bool listDeleted); bool listDeleted);
///< Append all IDs from collection to \a ids. ///< Append all IDs from collection to \a ids.
static int count (RecordBase::State state, const CollectionBase& collection);
public: public:
Data(); Data();
@ -116,6 +123,14 @@ namespace CSMWorld
IdCollection<ESM::Spell>& getSpells(); IdCollection<ESM::Spell>& getSpells();
const IdCollection<ESM::Dialogue>& getTopics() const;
IdCollection<ESM::Dialogue>& getTopics();
const IdCollection<ESM::Dialogue>& getJournals() const;
IdCollection<ESM::Dialogue>& getJournals();
const IdCollection<Cell>& getCells() const; const IdCollection<Cell>& getCells() const;
IdCollection<Cell>& getCells(); IdCollection<Cell>& getCells();
@ -141,8 +156,10 @@ namespace CSMWorld
void merge(); void merge();
///< Merge modified into base. ///< 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. ///< 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; bool hasId (const std::string& id) const;
@ -151,6 +168,17 @@ namespace CSMWorld
/// ///
/// \param listDeleted include deleted record in the list /// \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.
void setDescription (const std::string& description);
std::string getDescription() const;
void setAuthor (const std::string& author);
std::string getAuthor() const;
signals: signals:
void idListChanged(); void idListChanged();

@ -7,21 +7,24 @@
namespace CSMWorld namespace CSMWorld
{ {
/// \brief Single type collection of top level records /// \brief Single type collection of top level records
template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> > template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> >
class IdCollection : public Collection<ESXRecordT, IdAccessorT> class IdCollection : public Collection<ESXRecordT, IdAccessorT>
{ {
public: public:
void load (ESM::ESMReader& reader, bool base, void load (ESM::ESMReader& reader, bool base);
UniversalId::Type type = UniversalId::Type_None);
///< \param type Will be ignored, unless the collection supports multiple record types void load (const ESXRecordT& record, bool base);
bool tryDelete (const std::string& id);
///< Try deleting \a id. If the id does not exist or can't be deleted the call is ignored.
///
/// \return Has the ID been deleted?
}; };
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
void IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base, void IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base)
UniversalId::Type type)
{ {
std::string id = reader.getHNOString ("NAME"); std::string id = reader.getHNOString ("NAME");
@ -56,30 +59,62 @@ namespace CSMWorld
IdAccessorT().getId (record) = id; IdAccessorT().getId (record) = id;
record.load (reader); record.load (reader);
int index = this->searchId (IdAccessorT().getId (record)); load (record, base);
}
}
if (index==-1) template<typename ESXRecordT, typename IdAccessorT>
{ void IdCollection<ESXRecordT, IdAccessorT>::load (const ESXRecordT& record, bool base)
// new record {
Record<ESXRecordT> record2; int index = this->searchId (IdAccessorT().getId (record));
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2.mBase : record2.mModified) = record;
this->appendRecord (record2); if (index==-1)
} {
// new record
Record<ESXRecordT> record2;
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
(base ? record2.mBase : record2.mModified) = record;
this->appendRecord (record2);
}
else
{
// old record
Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
if (base)
record2.mBase = record;
else else
{ record2.setModified (record);
// old record
Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
if (base) this->setRecord (index, record2);
record2.mBase = record; }
else }
record2.setModified (record);
this->setRecord (index, record2); template<typename ESXRecordT, typename IdAccessorT>
} bool IdCollection<ESXRecordT, IdAccessorT>::tryDelete (const std::string& id)
{
int index = this->searchId (id);
if (index==-1)
return false;
Record<ESXRecordT> record = Collection<ESXRecordT, IdAccessorT>::getRecord (index);
if (record.isDeleted())
return false;
if (record.mState==RecordBase::State_ModifiedOnly)
{
Collection<ESXRecordT, IdAccessorT>::removeRows (index, 1);
} }
else
{
record.mState = RecordBase::State_Deleted;
this->setRecord (index, record);
}
return true;
} }
} }

@ -161,21 +161,10 @@ const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id)
int CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const int CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const
{ {
int columns = mIdCollection->getColumns(); return mIdCollection->searchColumnIndex (id);
for (int i=0; i<columns; ++i)
if (mIdCollection->getColumn (i).mColumnId==id)
return i;
return -1;
} }
int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const
{ {
int index = searchColumnIndex (id); return mIdCollection->findColumnIndex (id);
if (index==-1)
throw std::logic_error ("invalid column index");
return index;
} }

@ -539,3 +539,8 @@ std::vector<std::string> CSMWorld::RefIdCollection::getIds (bool listDeleted) co
{ {
return mData.getIds (listDeleted); return mData.getIds (listDeleted);
} }
void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const
{
mData.save (index, writer);
}

@ -9,6 +9,11 @@
#include "collectionbase.hpp" #include "collectionbase.hpp"
#include "refiddata.hpp" #include "refiddata.hpp"
namespace ESM
{
class ESMWriter;
}
namespace CSMWorld namespace CSMWorld
{ {
class RefIdAdapter; class RefIdAdapter;
@ -94,6 +99,8 @@ namespace CSMWorld
///< Return a sorted collection of all IDs ///< Return a sorted collection of all IDs
/// ///
/// \param listDeleted include deleted record in the list /// \param listDeleted include deleted record in the list
void save (int index, ESM::ESMWriter& writer) const;
}; };
} }

@ -218,3 +218,16 @@ std::vector<std::string> CSMWorld::RefIdData::getIds (bool listDeleted) const
return ids; return ids;
} }
void CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const
{
LocalIndex localIndex = globalToLocalIndex (index);
std::map<UniversalId::Type, RefIdDataContainerBase *>::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);
}

@ -23,6 +23,7 @@
#include <components/esm/loadweap.hpp> #include <components/esm/loadweap.hpp>
#include <components/esm/loadnpc.hpp> #include <components/esm/loadnpc.hpp>
#include <components/esm/loadmisc.hpp> #include <components/esm/loadmisc.hpp>
#include <components/esm/esmwriter.hpp>
#include "record.hpp" #include "record.hpp"
#include "universalid.hpp" #include "universalid.hpp"
@ -51,6 +52,8 @@ namespace CSMWorld
virtual void erase (int index, int count) = 0; virtual void erase (int index, int count) = 0;
virtual std::string getId (int index) const = 0; virtual std::string getId (int index) const = 0;
virtual void save (int index, ESM::ESMWriter& writer) const = 0;
}; };
template<typename RecordT> template<typename RecordT>
@ -71,6 +74,8 @@ namespace CSMWorld
virtual void erase (int index, int count); virtual void erase (int index, int count);
virtual std::string getId (int index) const; virtual std::string getId (int index) const;
virtual void save (int index, ESM::ESMWriter& writer) const;
}; };
template<typename RecordT> template<typename RecordT>
@ -123,6 +128,31 @@ namespace CSMWorld
return mContainer.at (index).get().mId; return mContainer.at (index).get().mId;
} }
template<typename RecordT>
void RefIdDataContainer<RecordT>::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<const char *> (&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 class RefIdData
{ {
public: public:
@ -187,6 +217,8 @@ namespace CSMWorld
///< Return a sorted collection of all IDs ///< Return a sorted collection of all IDs
/// ///
/// \param listDeleted include deleted record in the list /// \param listDeleted include deleted record in the list
void save (int index, ESM::ESMWriter& writer) const;
}; };
} }

@ -29,6 +29,8 @@ namespace
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables,
"Referenceables", 0 }, "Referenceables", 0 },
@ -54,6 +56,8 @@ namespace
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", ":./land.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", ":./land.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", ":./birthsign.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", ":./birthsign.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":./spell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":./spell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" },

@ -87,6 +87,10 @@ namespace CSMWorld
Type_RegionMap, Type_RegionMap,
Type_Filter, Type_Filter,
Type_Filters, Type_Filters,
Type_Topics,
Type_Topic,
Type_Journals,
Type_Journal,
Type_Scene Type_Scene
}; };

@ -11,7 +11,7 @@
#include <QStyle> #include <QStyle>
CSVDoc::AdjusterWidget::AdjusterWidget (QWidget *parent) CSVDoc::AdjusterWidget::AdjusterWidget (QWidget *parent)
: QWidget (parent), mValid (false) : QWidget (parent), mValid (false), mAction (ContentAction_Undefined)
{ {
QHBoxLayout *layout = new QHBoxLayout (this); QHBoxLayout *layout = new QHBoxLayout (this);
@ -30,6 +30,11 @@ CSVDoc::AdjusterWidget::AdjusterWidget (QWidget *parent)
setLayout (layout); setLayout (layout);
} }
void CSVDoc::AdjusterWidget::setAction (ContentAction action)
{
mAction = action;
}
void CSVDoc::AdjusterWidget::setLocalData (const boost::filesystem::path& localData) void CSVDoc::AdjusterWidget::setLocalData (const boost::filesystem::path& localData)
{ {
mLocalData = localData; mLocalData = localData;
@ -43,41 +48,60 @@ boost::filesystem::path CSVDoc::AdjusterWidget::getPath() const
return mResultPath; return mResultPath;
} }
bool CSVDoc::AdjusterWidget::isValid() const
{
return mValid;
}
void CSVDoc::AdjusterWidget::setFilenameCheck (bool doCheck)
{
mDoFilenameCheck = doCheck;
}
void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon) void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon)
{ {
QString message; QString message;
if (name.isEmpty()) mValid = (!name.isEmpty());
if (!mValid)
{ {
mValid = false;
message = "No name."; message = "No name.";
} }
else else
{ {
boost::filesystem::path path (name.toUtf8().data()); boost::filesystem::path path (name.toUtf8().data());
path.replace_extension (addon ? ".omwaddon" : ".omwgame"); bool isLegacyPath = (path.extension() == ".esm" ||
path.extension() == ".esp");
if (path.parent_path().string()==mLocalData.string()) bool isFilePathChanged = (path.parent_path().string() != mLocalData.string());
if (isLegacyPath)
path.replace_extension (addon ? ".omwaddon" : ".omwgame");
//if the file came from data-local and is not a legacy file to be converted,
//don't worry about doing a file check.
if (!isFilePathChanged && !isLegacyPath)
{ {
// path already points to the local data directory // path already points to the local data directory
message = QString::fromUtf8 (("Will be saved as: " + path.string()).c_str()); message = QString::fromUtf8 (("Will be saved as: " + path.string()).c_str());
mResultPath = path; mResultPath = path;
mValid = true;
} }
//in all other cases, ensure the path points to data-local and do an existing file check
else else
{ {
// path points somewhere else or is a leaf name. // 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()); message = QString::fromUtf8 (("Will be saved as: " + path.string()).c_str());
mResultPath = path; mResultPath = path;
mValid = true;
if (boost::filesystem::exists (path)) if (boost::filesystem::exists (path))
{ {
/// \todo add an user setting to make this an error. /// \todo add an user setting to make this an error.
message += "<p>But a file with the same name already exists. If you continue, it will be overwritten."; message += "<p>A file with the same name already exists. If you continue, it will be overwritten.";
} }
} }
} }
@ -88,4 +112,4 @@ void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon)
pixmap (QSize (16, 16))); pixmap (QSize (16, 16)));
emit stateChanged (mValid); emit stateChanged (mValid);
} }

@ -9,21 +9,36 @@ class QLabel;
namespace CSVDoc namespace CSVDoc
{ {
enum ContentAction
{
ContentAction_New,
ContentAction_Edit,
ContentAction_Undefined
};
class AdjusterWidget : public QWidget class AdjusterWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public:
boost::filesystem::path mLocalData; boost::filesystem::path mLocalData;
QLabel *mMessage; QLabel *mMessage;
QLabel *mIcon; QLabel *mIcon;
bool mValid; bool mValid;
boost::filesystem::path mResultPath; boost::filesystem::path mResultPath;
ContentAction mAction;
bool mDoFilenameCheck;
public: public:
AdjusterWidget (QWidget *parent = 0); AdjusterWidget (QWidget *parent = 0);
void setLocalData (const boost::filesystem::path& localData); void setLocalData (const boost::filesystem::path& localData);
void setAction (ContentAction action);
void setFilenameCheck (bool doCheck);
bool isValid() const;
boost::filesystem::path getPath() const; boost::filesystem::path getPath() const;
///< This function must not be called if there is no valid path. ///< This function must not be called if there is no valid path.
@ -38,4 +53,4 @@ namespace CSVDoc
}; };
} }
#endif #endif

@ -9,264 +9,166 @@
#include <QSpacerItem> #include <QSpacerItem>
#include <QPushButton> #include <QPushButton>
#include <QLabel> #include <QLabel>
#include <QGroupBox>
#include <components/fileorderlist/model/datafilesmodel.hpp> #include "components/contentselector/model/esmfile.hpp"
#include <components/fileorderlist/model/pluginsproxymodel.hpp> #include "components/contentselector/view/contentselector.hpp"
#include <components/fileorderlist/model/esm/esmfile.hpp>
#include <components/fileorderlist/utils/lineedit.hpp> #include "filewidget.hpp"
#include "adjusterwidget.hpp"
FileDialog::FileDialog(QWidget *parent) : CSVDoc::FileDialog::FileDialog(QWidget *parent) :
QDialog(parent) QDialog(parent), mSelector (0), mFileWidget (0), mAdjusterWidget (0)
{ {
setupUi(this); ui.setupUi (this);
resize(400, 400);
// Models setObjectName ("FileDialog");
mDataFilesModel = new DataFilesModel(this); mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget);
mAdjusterWidget = new AdjusterWidget (this);
mMastersProxyModel = new QSortFilterProxyModel(); }
mMastersProxyModel->setFilterRegExp(QString("^.*\\.esm"));
mMastersProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
mMastersProxyModel->setSourceModel(mDataFilesModel);
mPluginsProxyModel = new PluginsProxyModel();
mPluginsProxyModel->setFilterRegExp(QString("^.*\\.esp"));
mPluginsProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
mPluginsProxyModel->setSourceModel(mDataFilesModel);
mFilterProxyModel = new QSortFilterProxyModel();
mFilterProxyModel->setDynamicSortFilter(true);
mFilterProxyModel->setSourceModel(mPluginsProxyModel);
QCheckBox checkBox;
unsigned int height = checkBox.sizeHint().height() + 4;
mastersTable->setModel(mMastersProxyModel); void CSVDoc::FileDialog::addFiles(const QString &path)
mastersTable->setObjectName("MastersTable"); {
mastersTable->setContextMenuPolicy(Qt::CustomContextMenu); mSelector->addFiles(path);
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 QStringList CSVDoc::FileDialog::selectedFilePaths()
mastersTable->verticalHeader()->setDefaultSectionSize(height); {
mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); QStringList filePaths;
mastersTable->verticalHeader()->hide();
pluginsTable->setModel(mFilterProxyModel); foreach (ContentSelectorModel::EsmFile *file, mSelector->selectedFiles() )
pluginsTable->setObjectName("PluginsTable"); filePaths.append(file->filePath());
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); return filePaths;
pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); }
// Hide the profile elements void CSVDoc::FileDialog::setLocalData (const boost::filesystem::path& localData)
profileLabel->hide(); {
profilesComboBox->hide(); mAdjusterWidget->setLocalData (localData);
newProfileButton->hide(); }
deleteProfileButton->hide();
// Add some extra widgets void CSVDoc::FileDialog::showDialog (ContentAction action)
QHBoxLayout *nameLayout = new QHBoxLayout(); {
QSpacerItem *spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); mAction = action;
mNameLabel = new QLabel(tr("File Name:"), this); ui.projectGroupBoxLayout->insertWidget (0, mAdjusterWidget);
QRegExpValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9\\s]*$")); switch (mAction)
mNameLineEdit = new LineEdit(this); {
mNameLineEdit->setValidator(validator); case ContentAction_New:
buildNewFileView();
break;
nameLayout->addSpacerItem(spacer); case ContentAction_Edit:
nameLayout->addWidget(mNameLabel); buildOpenFileView();
nameLayout->addWidget(mNameLineEdit); break;
mButtonBox = new QDialogButtonBox(this); default:
break;
}
mCreateButton = new QPushButton(tr("Create"), this); mAdjusterWidget->setFilenameCheck (mAction == ContentAction_New);
mCreateButton->setEnabled(false);
verticalLayout->addLayout(nameLayout); //connections common to both dialog view flavors
verticalLayout->addWidget(mButtonBox); connect (mSelector, SIGNAL (signalCurrentGamefileIndexChanged (int)),
this, SLOT (slotUpdateAcceptButton (int)));
// Set sizes connect (ui.projectButtonBox, SIGNAL (rejected()), this, SLOT (slotRejected()));
QList<int> sizeList;
sizeList << 175;
sizeList << 200;
splitter->setSizes(sizeList); show();
raise();
activateWindow();
}
resize(600, 400); void CSVDoc::FileDialog::buildNewFileView()
{
setWindowTitle(tr("Create a new addon"));
connect(mDataFilesModel, SIGNAL(layoutChanged()), this, SLOT(updateViews())); QPushButton* createButton = ui.projectButtonBox->button (QDialogButtonBox::Ok);
connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList))); createButton->setText ("Create");
connect(mNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateCreateButton(QString))); createButton->setEnabled (false);
connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); mFileWidget = new FileWidget (this);
connect(pluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); mFileWidget->setType (true);
connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); mFileWidget->extensionLabelIsVisible(true);
connect(mCreateButton, SIGNAL(clicked()), this, SLOT(createButtonClicked())); ui.projectGroupBoxLayout->insertWidget (0, mFileWidget);
connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept())); connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)),
connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject())); mAdjusterWidget, SLOT (setName (const QString&, bool)));
}
void FileDialog::updateViews() connect (mFileWidget, SIGNAL (nameChanged(const QString &, bool)),
{ this, SLOT (slotUpdateAcceptButton(const QString &, bool)));
// 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 (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotNewFile()));
} }
void FileDialog::updateOpenButton(const QStringList &items) void CSVDoc::FileDialog::buildOpenFileView()
{ {
QPushButton *openButton = mButtonBox->button(QDialogButtonBox::Open); setWindowTitle(tr("Open"));
ui.projectGroupBox->setTitle (QString(""));
if (!openButton)
return;
openButton->setEnabled(!items.isEmpty()); ui.projectButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false);
}
void FileDialog::updateCreateButton(const QString &name) connect (mSelector, SIGNAL (signalAddonFileSelected (int)), this, SLOT (slotUpdateAcceptButton (int)));
{ connect (mSelector, SIGNAL (signalAddonFileUnselected (int)), this, SLOT (slotUpdateAcceptButton (int)));
if (!mCreateButton->isVisible())
return;
mCreateButton->setEnabled(!name.isEmpty()); connect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotOpenFile()));
} }
void FileDialog::filterChanged(const QString &filter) void CSVDoc::FileDialog::slotUpdateAcceptButton (int)
{ {
QRegExp filterRe(filter, Qt::CaseInsensitive, QRegExp::FixedString); QString name = "";
mFilterProxyModel->setFilterRegExp(filterRe);
}
void FileDialog::addFiles(const QString &path) if (mAction == ContentAction_New)
{ name = mFileWidget->getName();
mDataFilesModel->addFiles(path);
mDataFilesModel->sort(3); // Sort by date accessed
}
void FileDialog::setEncoding(const QString &encoding) slotUpdateAcceptButton (name, true);
{
mDataFilesModel->setEncoding(encoding);
} }
void FileDialog::setCheckState(QModelIndex index) void CSVDoc::FileDialog::slotUpdateAcceptButton(const QString &name, bool)
{ {
if (!index.isValid()) bool success = (mSelector->selectedFiles().size() > 0);
return;
QObject *object = QObject::sender();
// Not a signal-slot call
if (!object)
return;
bool isNew = (mAction == ContentAction_New);
if (object->objectName() == QLatin1String("PluginsTable")) { if (isNew)
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource( success = success && !(name.isEmpty());
mFilterProxyModel->mapToSource(index)); else
{
if (sourceIndex.isValid()) { ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back();
(mDataFilesModel->checkState(sourceIndex) == Qt::Checked) mAdjusterWidget->setName (file->filePath(), !file->isGameFile());
? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked)
: mDataFilesModel->setCheckState(sourceIndex, Qt::Checked);
}
} }
if (object->objectName() == QLatin1String("MastersTable")) { ui.projectButtonBox->button (QDialogButtonBox::Ok)->setEnabled (success);
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() QString CSVDoc::FileDialog::filename() const
{ {
return mDataFilesModel->checkedItemsPaths(); if (mAction == ContentAction_New)
} return "";
QString FileDialog::fileName() return mSelector->currentFile();
{
return mNameLineEdit->text();
} }
void FileDialog::openFile() void CSVDoc::FileDialog::slotRejected()
{ {
setWindowTitle(tr("Open")); emit rejected();
close();
mNameLabel->hide();
mNameLineEdit->hide();
mCreateButton->hide();
mButtonBox->removeButton(mCreateButton);
mButtonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Open);
QPushButton *openButton = mButtonBox->button(QDialogButtonBox::Open);
openButton->setEnabled(false);
show();
raise();
activateWindow();
} }
void FileDialog::newFile() void CSVDoc::FileDialog::slotNewFile()
{ {
setWindowTitle(tr("New")); emit signalCreateNewFile (mAdjusterWidget->getPath());
mNameLabel->show();
mNameLineEdit->clear();
mNameLineEdit->show();
mCreateButton->show();
mButtonBox->setStandardButtons(QDialogButtonBox::Cancel);
mButtonBox->addButton(mCreateButton, QDialogButtonBox::ActionRole);
show();
raise();
activateWindow();
} }
void FileDialog::accept() void CSVDoc::FileDialog::slotOpenFile()
{ {
emit openFiles(); ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back();
}
void FileDialog::createButtonClicked() mAdjusterWidget->setName (file->filePath(), !file->isGameFile());
{
emit createNewFile(); emit signalOpenFiles (mAdjusterWidget->getPath());
} }

@ -4,63 +4,71 @@
#include <QDialog> #include <QDialog>
#include <QModelIndex> #include <QModelIndex>
#include "ui_datafilespage.h" #include <boost/filesystem/path.hpp>
#include "adjusterwidget.hpp"
class QDialogButtonBox; #ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED
class QSortFilterProxyModel; #define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED
class QAbstractItemModel; Q_DECLARE_METATYPE (boost::filesystem::path)
class QPushButton; #endif
class QStringList;
class QString; #include "ui_filedialog.h"
class QMenu;
class DataFilesModel; class DataFilesModel;
class PluginsProxyModel; class PluginsProxyModel;
class FileDialog : public QDialog, private Ui::DataFilesPage namespace ContentSelectorView
{
class ContentSelector;
}
namespace CSVDoc
{ {
Q_OBJECT class FileWidget;
public:
explicit FileDialog(QWidget *parent = 0); class FileDialog : public QDialog
void addFiles(const QString &path); {
void setEncoding(const QString &encoding); Q_OBJECT
private:
ContentSelectorView::ContentSelector *mSelector;
Ui::FileDialog ui;
ContentAction mAction;
FileWidget *mFileWidget;
AdjusterWidget *mAdjusterWidget;
public:
void openFile(); explicit FileDialog(QWidget *parent = 0);
void newFile(); void showDialog (ContentAction action);
void accepted();
QStringList checkedItemsPaths(); void addFiles (const QString &path);
QString fileName();
signals: QString filename() const;
void openFiles(); QStringList selectedFilePaths();
void createNewFile();
public slots:
void accept();
private slots: void setLocalData (const boost::filesystem::path& localData);
void updateViews();
void updateOpenButton(const QStringList &items);
void updateCreateButton(const QString &name);
void setCheckState(QModelIndex index);
void filterChanged(const QString &filter); private:
void createButtonClicked(); void buildNewFileView();
void buildOpenFileView();
private: signals:
QLabel *mNameLabel;
LineEdit *mNameLineEdit;
QPushButton *mCreateButton; void signalOpenFiles (const boost::filesystem::path &path);
QDialogButtonBox *mButtonBox; void signalCreateNewFile (const boost::filesystem::path &path);
DataFilesModel *mDataFilesModel; void signalUpdateAcceptButton (bool, int);
PluginsProxyModel *mPluginsProxyModel; private slots:
QSortFilterProxyModel *mMastersProxyModel;
QSortFilterProxyModel *mFilterProxyModel;
};
void slotNewFile();
void slotOpenFile();
void slotUpdateAcceptButton (int);
void slotUpdateAcceptButton (const QString &, bool);
void slotRejected();
};
}
#endif // FILEDIALOG_HPP #endif // FILEDIALOG_HPP

@ -50,4 +50,9 @@ QString CSVDoc::FileWidget::getName() const
void CSVDoc::FileWidget::textChanged (const QString& text) void CSVDoc::FileWidget::textChanged (const QString& text)
{ {
emit nameChanged (getName(), mAddon); emit nameChanged (getName(), mAddon);
} }
void CSVDoc::FileWidget::extensionLabelIsVisible(bool visible)
{
mType->setVisible(visible);
}

@ -27,6 +27,8 @@ namespace CSVDoc
QString getName() const; QString getName() const;
void extensionLabelIsVisible(bool visible);
private slots: private slots:
void textChanged (const QString& text); void textChanged (const QString& text);

@ -6,7 +6,10 @@
#include <QDialog> #include <QDialog>
#include <QMetaType> #include <QMetaType>
#ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED
#define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED
Q_DECLARE_METATYPE (boost::filesystem::path) Q_DECLARE_METATYPE (boost::filesystem::path)
#endif
class QPushButton; class QPushButton;

@ -104,6 +104,17 @@ CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2)
layout->addWidget (createButtons()); layout->addWidget (createButtons());
layout->addWidget (createTools()); layout->addWidget (createTools());
/// \todo remove this label once loading and saving are fully implemented
QLabel *warning = new QLabel ("<font color=Red>WARNING:<p>OpenCS is in alpha stage.<br>The code for loading and saving is incomplete.<br>This version of OpenCS is only a preview.<br>Do NOT use it for real editing!<br>You will lose records both on loading and on saving.<p>Please note:<br>If you lose data and come to the OpenMW forum to complain,<br>we will mock you.</font color>");
QFont font;
font.setPointSize (12);
font.setBold (true);
warning->setFont (font);
layout->addWidget (warning, 1);
setLayout (layout); setLayout (layout);
QRect scr = QApplication::desktop()->screenGeometry(); QRect scr = QApplication::desktop()->screenGeometry();

@ -163,6 +163,14 @@ void CSVDoc::View::setupMechanicsMenu()
QAction *spells = new QAction (tr ("Spells"), this); QAction *spells = new QAction (tr ("Spells"), this);
connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView())); connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView()));
mechanics->addAction (spells); mechanics->addAction (spells);
QAction *topics = new QAction (tr ("Topics"), this);
connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView()));
mechanics->addAction (topics);
QAction *journals = new QAction (tr ("Journals"), this);
connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView()));
mechanics->addAction (journals);
} }
void CSVDoc::View::setupAssetsMenu() void CSVDoc::View::setupAssetsMenu()
@ -412,6 +420,16 @@ void CSVDoc::View::addSceneSubView()
addSubView (CSMWorld::UniversalId::Type_Scene); addSubView (CSMWorld::UniversalId::Type_Scene);
} }
void CSVDoc::View::addTopicsSubView()
{
addSubView (CSMWorld::UniversalId::Type_Topics);
}
void CSVDoc::View::addJournalsSubView()
{
addSubView (CSMWorld::UniversalId::Type_Journals);
}
void CSVDoc::View::abortOperation (int type) void CSVDoc::View::abortOperation (int type)
{ {
mDocument->abortOperation (type); mDocument->abortOperation (type);

@ -166,6 +166,10 @@ namespace CSVDoc
void addSceneSubView(); void addSceneSubView();
void addTopicsSubView();
void addJournalsSubView();
void toggleShowStatusBar (bool show); void toggleShowStatusBar (bool show);
}; };
} }

@ -74,7 +74,8 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
{ CSMWorld::ColumnBase::Display_ArmorType, CSMWorld::Columns::ColumnId_ArmorType, false }, { CSMWorld::ColumnBase::Display_ArmorType, CSMWorld::Columns::ColumnId_ArmorType, false },
{ CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false }, { CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false },
{ CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, false }, { CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, false },
{ CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false } { CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false },
{ CSMWorld::ColumnBase::Display_DialogueType, CSMWorld::Columns::ColumnId_DialogueType, false }
}; };
for (std::size_t i=0; i<sizeof (sMapping)/sizeof (Mapping); ++i) for (std::size_t i=0; i<sizeof (sMapping)/sizeof (Mapping); ++i)

@ -6,6 +6,11 @@
#include "../../model/filter/filter.hpp" #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 std::string CSVFilter::FilterCreator::getNamespace() const
{ {
switch (mScope->currentIndex()) switch (mScope->currentIndex())
@ -28,6 +33,15 @@ std::string CSVFilter::FilterCreator::getId() const
return getNamespace() + GenericCreator::getId(); return getNamespace() + GenericCreator::getId();
} }
void CSVFilter::FilterCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const
{
int index =
dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId())).
findColumnIndex (CSMWorld::Columns::ColumnId_Scope);
command.addValue (index, mScope->currentIndex());
}
CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack, CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id) const CSMWorld::UniversalId& id)
: GenericCreator (data, undoStack, id) : GenericCreator (data, undoStack, id)
@ -39,7 +53,7 @@ CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoS
mScope->addItem ("Project"); mScope->addItem ("Project");
mScope->addItem ("Session"); mScope->addItem ("Session");
/// \ŧodo re-enable for OpenMW 1.1 /// \todo re-enable for OpenMW 1.1
// mScope->addItem ("Content"); // mScope->addItem ("Content");
connect (mScope, SIGNAL (currentIndexChanged (int)), this, SLOT (setScope (int))); connect (mScope, SIGNAL (currentIndexChanged (int)), this, SLOT (setScope (int)));

@ -25,6 +25,8 @@ namespace CSVFilter
virtual std::string getId() const; virtual std::string getId() const;
virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const;
public: public:
FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack, FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack,

@ -0,0 +1,35 @@
#include "dialoguecreator.hpp"
#include <components/esm/loaddial.hpp>
#include "../../model/world/data.hpp"
#include "../../model/world/commands.hpp"
#include "../../model/world/columns.hpp"
#include "../../model/world/idtable.hpp"
void CSVWorld::DialogueCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const
{
int index =
dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId())).
findColumnIndex (CSMWorld::Columns::ColumnId_DialogueType);
command.addValue (index, mType);
}
CSVWorld::DialogueCreator::DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id, int type)
: GenericCreator (data, undoStack, id), mType (type)
{}
CSVWorld::Creator *CSVWorld::TopicCreatorFactory::makeCreator (CSMWorld::Data& data,
QUndoStack& undoStack, const CSMWorld::UniversalId& id) const
{
return new DialogueCreator (data, undoStack, id, ESM::Dialogue::Topic);
}
CSVWorld::Creator *CSVWorld::JournalCreatorFactory::makeCreator (CSMWorld::Data& data,
QUndoStack& undoStack, const CSMWorld::UniversalId& id) const
{
return new DialogueCreator (data, undoStack, id, ESM::Dialogue::Journal);
}

@ -0,0 +1,41 @@
#ifndef CSV_WORLD_DIALOGUECREATOR_H
#define CSV_WORLD_DIALOGUECREATOR_H
#include "genericcreator.hpp"
namespace CSVWorld
{
class DialogueCreator : public GenericCreator
{
int mType;
protected:
virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const;
public:
DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id, int type);
};
class TopicCreatorFactory : public CreatorFactoryBase
{
public:
virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id) const;
///< The ownership of the returned Creator is transferred to the caller.
};
class JournalCreatorFactory : public CreatorFactoryBase
{
public:
virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id) const;
///< The ownership of the returned Creator is transferred to the caller.
};
}
#endif

@ -14,6 +14,7 @@
#include "referenceablecreator.hpp" #include "referenceablecreator.hpp"
#include "referencecreator.hpp" #include "referencecreator.hpp"
#include "scenesubview.hpp" #include "scenesubview.hpp"
#include "dialoguecreator.hpp"
void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
{ {
@ -53,6 +54,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_References, manager.add (CSMWorld::UniversalId::Type_References,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<ReferenceCreator> >); new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<ReferenceCreator> >);
manager.add (CSMWorld::UniversalId::Type_Topics,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, TopicCreatorFactory>);
manager.add (CSMWorld::UniversalId::Type_Journal,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, JournalCreatorFactory>);
// Subviews for editing/viewing individual records // Subviews for editing/viewing individual records
manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory<ScriptSubView>); manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory<ScriptSubView>);

@ -87,19 +87,33 @@ std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const
{ {
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
// check record state
CSMWorld::RecordBase::State state = CSMWorld::RecordBase::State state =
static_cast<CSMWorld::RecordBase::State> ( static_cast<CSMWorld::RecordBase::State> (
mModel->data (mModel->index (index.row(), 1)).toInt()); mModel->data (mModel->index (index.row(), 1)).toInt());
if (state!=CSMWorld::RecordBase::State_Deleted) if (state==CSMWorld::RecordBase::State_Deleted)
{ continue;
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
std::string id = mModel->data (mModel->index (index.row(), columnIndex)). // check other columns (only relevant for a subset of the tables)
toString().toUtf8().constData(); int dialogueTypeIndex =
mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_DialogueType);
deletableIds.push_back (id); if (dialogueTypeIndex!=-1)
{
int type = mModel->data (mModel->index (index.row(), dialogueTypeIndex)).toInt();
if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal)
continue;
} }
// add the id to the collection
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
std::string id = mModel->data (mModel->index (index.row(), columnIndex)).
toString().toUtf8().constData();
deletableIds.push_back (id);
} }
} }

@ -58,6 +58,7 @@ add_openmw_dir (mwworld
cells localscripts customdata weather inventorystore ptr actionopen actionread cells localscripts customdata weather inventorystore ptr actionopen actionread
actionequip timestamp actionalchemy cellstore actionapply actioneat actionequip timestamp actionalchemy cellstore actionapply actioneat
esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
contentloader esmloader omwloader
) )
add_openmw_dir (mwclass add_openmw_dir (mwclass

@ -261,34 +261,14 @@ void OMW::Engine::setCell (const std::string& cellName)
mCellName = cellName; mCellName = cellName;
} }
// Set master file (esm) void OMW::Engine::addContentFile(const std::string& file)
// - If the given name does not have an extension, ".esm" is added automatically
void OMW::Engine::addMaster (const std::string& master)
{ {
mMaster.push_back(master); if (file.find_last_of(".") == std::string::npos)
std::string &str = mMaster.back(); {
throw std::runtime_error("Missing extension in content file!");
// Append .esm if not already there }
std::string::size_type sep = str.find_last_of (".");
if (sep == std::string::npos)
{
str += ".esm";
}
}
// Add plugin file (esp) mContentFiles.push_back(file);
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";
}
} }
void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity) void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity)
@ -399,7 +379,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mEnvironment.setWindowManager (window); mEnvironment.setWindowManager (window);
// Create the world // 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, mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap,
mActivationDistanceOverride)); mActivationDistanceOverride));
MWBase::Environment::get().getWorld()->setupPlayer(); MWBase::Environment::get().getWorld()->setupPlayer();
@ -414,8 +394,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
//Load translation data //Load translation data
mTranslationDataStorage.setEncoder(mEncoder); mTranslationDataStorage.setEncoder(mEncoder);
for (size_t i = 0; i < mMaster.size(); i++) for (size_t i = 0; i < mContentFiles.size(); i++)
mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster[i]); mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]);
Compiler::registerExtensions (mExtensions); Compiler::registerExtensions (mExtensions);
@ -480,7 +460,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
void OMW::Engine::go() void OMW::Engine::go()
{ {
assert (!mCellName.empty()); assert (!mCellName.empty());
assert (!mMaster.empty()); assert (!mContentFiles.empty());
assert (!mOgre); assert (!mOgre);
Settings::Manager settings; Settings::Manager settings;

@ -68,8 +68,7 @@ namespace OMW
boost::filesystem::path mResDir; boost::filesystem::path mResDir;
OEngine::Render::OgreRenderer *mOgre; OEngine::Render::OgreRenderer *mOgre;
std::string mCellName; std::string mCellName;
std::vector<std::string> mMaster; std::vector<std::string> mContentFiles;
std::vector<std::string> mPlugins;
int mFpsLevel; int mFpsLevel;
bool mVerboseScripts; bool mVerboseScripts;
bool mNewGame; bool mNewGame;
@ -135,13 +134,11 @@ namespace OMW
/// Set start cell name (only interiors for now) /// Set start cell name (only interiors for now)
void setCell(const std::string& cellName); void setCell(const std::string& cellName);
/// Set master file (esm) /**
/// - If the given name does not have an extension, ".esm" is added automatically * @brief addContentFile - Adds content file (ie. esm/esp, or omwgame/omwaddon) to the content files container.
void addMaster(const std::string& master); * @param file - filename (extension is required)
*/
/// Same as "addMaster", but for plugin files (esp) void addContentFile(const std::string& file);
/// - If the given name does not have an extension, ".esp" is added automatically
void addPlugin(const std::string& plugin);
/// Enable fps counter /// Enable fps counter
void showFPS(int level); void showFPS(int level);

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save