diff options
author | Bret Curtis <psi29a@gmail.com> | 2013-11-15 13:24:12 +0100 |
---|---|---|
committer | Bret Curtis <psi29a@gmail.com> | 2013-11-15 13:24:12 +0100 |
commit | 4dca3c5ce1400f655b3a7184e21d23460447a521 (patch) | |
tree | 27b89b26b8fa039d70b2b8bd7cf77fdd91bf2ec8 | |
parent | 9a2b6c69b6064cd7e6fe12f901b11521afbc6dfe (diff) |
Imported Upstream version 0.27.0upstream/0.27.0
433 files changed, 8108 insertions, 4729 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..f22f1bd49c --- /dev/null +++ b/.gitignore @@ -0,0 +1,53 @@ +## make +CMakeFiles +*/CMakeFiles +CMakeCache.txt +cmake_install.cmake +CMakeLists.txt.user +Makefile +makefile +build +prebuilt + +## doxygen +Doxygen + +## ides/editors +*~ +*.kdev4 +*.swp +*.swo +*.kate-swp +.cproject +.project +.settings +.directory + +## resources +data +resources +/*.cfg +/*.desktop +/*.install + +## binaries +/esmtool +/mwiniimport +/omwlauncher +/openmw +/opencs + +## generated objects +apps/openmw/config.hpp +Docs/mainpage.hpp +moc_*.cxx +*.cxx_parameters +*qrc_launcher.cxx +*qrc_resources.cxx +*__* +*ui_datafilespage.h +*ui_graphicspage.h +*ui_mainwindow.h +*ui_playpage.h +*.[ao] +*.so diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f95f2d4e6..01f02ddb96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,7 +19,7 @@ include (OpenMWMacros) # Version set (OPENMW_VERSION_MAJOR 0) -set (OPENMW_VERSION_MINOR 26) +set (OPENMW_VERSION_MINOR 27) set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") @@ -161,6 +161,20 @@ if (NOT FFMPEG_FOUND) message(WARNING "--------------------") endif (NOT FFMPEG_FOUND) +# TinyXML +option(USE_SYSTEM_TINYXML "Use system TinyXML library instead of internal." OFF) +if(USE_SYSTEM_TINYXML) + find_library(TINYXML_LIBRARIES tinyxml) + find_path(TINYXML_INCLUDE_DIR tinyxml.h) + message(STATUS "Found TinyXML: ${TINYXML_LIBRARIES} ${TINYXML_INCLUDE_DIR}") + add_definitions (-DTIXML_USE_STL) + if(TINYXML_LIBRARIES AND TINYXML_INCLUDE_DIR) + include_directories(${TINYXML_INCLUDE_DIR}) + message(STATUS "Using system TinyXML library.") + else() + message(FATAL_ERROR "Detection of system TinyXML incomplete.") + endif() +endif() # Platform specific if (WIN32) @@ -305,6 +319,9 @@ configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg configure_file(${OpenMW_SOURCE_DIR}/files/opencs.cfg "${OpenMW_BINARY_DIR}/opencs.cfg") + +configure_file(${OpenMW_SOURCE_DIR}/files/opencs/defaultfilters + "${OpenMW_BINARY_DIR}/resources/defaultfilters" COPYONLY) if (NOT WIN32 AND NOT APPLE) configure_file(${OpenMW_SOURCE_DIR}/files/openmw.desktop @@ -366,7 +383,6 @@ IF(NOT WIN32 AND NOT APPLE) # Install licenses INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" ) - INSTALL(FILES "Daedric Font License.txt" DESTINATION "${LICDIR}" ) INSTALL(FILES "OFL.txt" DESTINATION "${LICDIR}" ) INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" ) ENDIF (DPKG_PROGRAM) @@ -414,7 +430,7 @@ IF(NOT WIN32 AND NOT APPLE) Data files from the original game is required to run it.") SET(CPACK_DEBIAN_PACKAGE_NAME "openmw") SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}") - SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW bsatool;Bsatool esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter") + SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW opencs;OpenCS bsatool;Bsatool esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter") SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)") SET(CPACK_DEBIAN_PACKAGE_SECTION "Games") @@ -441,13 +457,22 @@ if(WIN32) "${OpenMW_SOURCE_DIR}/GPL3.txt" "${OpenMW_SOURCE_DIR}/OFL.txt" "${OpenMW_SOURCE_DIR}/DejaVu Font License.txt" - "${OpenMW_SOURCE_DIR}/Daedric Font License.txt" "${OpenMW_BINARY_DIR}/settings-default.cfg" "${OpenMW_BINARY_DIR}/transparency-overrides.cfg" - "${OpenMW_BINARY_DIR}/Release/mwiniimport.exe" - "${OpenMW_BINARY_DIR}/Release/omwlauncher.exe" "${OpenMW_BINARY_DIR}/Release/openmw.exe" DESTINATION ".") + + IF(BUILD_LAUNCHER) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/omwlauncher.exe" DESTINATION ".") + ENDIF(BUILD_LAUNCHER) + IF(BUILD_MWINIIMPORTER) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/mwiniimport.exe" DESTINATION ".") + ENDIF(BUILD_MWINIIMPORTER) + IF(BUILD_OPENCS) + INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/Release/opencs.exe" DESTINATION ".") + INSTALL(FILES "${OpenMW_BINARY_DIR}/opencs.cfg" DESTINATION ".") + ENDIF(BUILD_OPENCS) + INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION ".") SET(CPACK_GENERATOR "NSIS") @@ -457,7 +482,13 @@ if(WIN32) SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR}) SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) - SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW;omwlauncher;OpenMW Launcher") + SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW") + IF(BUILD_LAUNCHER) + SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};omwlauncher;OpenMW Launcher") + ENDIF(BUILD_LAUNCHER) + IF(BUILD_OPENCS) + SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};opencs;OpenMW Construction Set") + ENDIF(BUILD_OPENCS) SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'") SET(CPACK_NSIS_DELETE_ICONS_EXTRA " !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP @@ -654,7 +685,10 @@ if (APPLE) set(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINO}) set(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) - set(APPS "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}") + set(OPENMW_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/${APP_BUNDLE_NAME}") + + set(OPENCS_APP "\${CMAKE_INSTALL_PREFIX}/${INSTALL_SUBDIR}/OpenCS.app") + set(PLUGINS "") set(ABSOLUTE_PLUGINS "") @@ -715,7 +749,8 @@ if (APPLE) cmake_policy(SET CMP0009 OLD) set(BU_CHMOD_BUNDLE_ITEMS ON) include(BundleUtilities) - fixup_bundle(\"${APPS}\" \"${PLUGINS}\" \"${DIRS}\") + fixup_bundle(\"${OPENMW_APP}\" \"${PLUGINS}\" \"${DIRS}\") + fixup_bundle(\"${OPENCS_APP}\" \"\" \"${DIRS}\") " COMPONENT Runtime) include(CPack) endif (APPLE) diff --git a/Daedric Font License.txt b/Daedric Font License.txt deleted file mode 100644 index a1553d0b04..0000000000 --- a/Daedric Font License.txt +++ /dev/null @@ -1,10 +0,0 @@ -Dongle's Oblivion Daedric font set -http://www.uesp.net/wiki/Lore:Daedric_Alphabet#Daedric_Font - ---------------------------------------------------- - -This was done entirely as a personal project. Bethesda Softworks graciously granted me the permission for it. I am not connected with them in any way. -You may freely use these fonts to create anything you'd like. You may re-distribute the fonts freely, over the Internet, or by any other means. Always keep the .zip file intact, and this read me included. -Please do not modify and redistribute the fonts without my permission. -You may NOT sell any of these fonts under any circumstances. This includes putting them on compilation font CDs for sale, putting them in a "members only" pay-area of a website, or any other means of financial gain connected in ANY way with the redistribution of any of these fonts. -You have my permission to create and sell any artwork made with these fonts, however you may need to contact Bethesda Softworks before doing so. diff --git a/apps/esmtool/.gitignore b/apps/esmtool/.gitignore new file mode 100644 index 0000000000..d20f8fd880 --- /dev/null +++ b/apps/esmtool/.gitignore @@ -0,0 +1,6 @@ +*.esp +*.esm +*.ess +*_test +esmtool +*_raw.txt diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index a60e9f0e20..6ccf9c3f3b 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -305,14 +305,14 @@ int load(Arguments& info) info.data.author = esm.getAuthor(); info.data.description = esm.getDesc(); - info.data.masters = esm.getMasters(); + info.data.masters = esm.getGameFiles(); if (!quiet) { std::cout << "Author: " << esm.getAuthor() << std::endl << "Description: " << esm.getDesc() << std::endl << "File format version: " << esm.getFVer() << std::endl; - std::vector<ESM::Header::MasterData> m = esm.getMasters(); + std::vector<ESM::Header::MasterData> m = esm.getGameFiles(); if (!m.empty()) { std::cout << "Masters:" << std::endl; diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 5908deb907..18c555a249 100644 --- a/apps/launcher/CMakeLists.txt +++ b/apps/launcher/CMakeLists.txt @@ -11,7 +11,9 @@ set(LAUNCHER settings/launchersettings.cpp utils/checkablemessagebox.cpp + utils/profilescombobox.cpp utils/textinputdialog.cpp + utils/lineedit.cpp ${CMAKE_SOURCE_DIR}/files/launcher/launcher.rc ) @@ -24,7 +26,6 @@ set(LAUNCHER_HEADER graphicspage.hpp maindialog.hpp playpage.hpp - unshieldthread.hpp textslotmsgbox.hpp settings/gamesettings.hpp @@ -33,8 +34,9 @@ set(LAUNCHER_HEADER settings/settingsbase.hpp utils/checkablemessagebox.hpp + utils/profilescombobox.hpp utils/textinputdialog.hpp - + utils/lineedit.hpp ) if(NOT WIN32) LIST(APPEND LAUNCHER_HEADER unshieldthread.hpp) @@ -47,11 +49,13 @@ set(LAUNCHER_HEADER_MOC graphicspage.hpp maindialog.hpp playpage.hpp - unshieldthread.hpp textslotmsgbox.hpp - utils/checkablemessagebox.hpp utils/textinputdialog.hpp + utils/checkablemessagebox.hpp + utils/profilescombobox.hpp + utils/lineedit.hpp + ) if(NOT WIN32) @@ -64,6 +68,7 @@ set(LAUNCHER_UI ${CMAKE_SOURCE_DIR}/files/ui/graphicspage.ui ${CMAKE_SOURCE_DIR}/files/ui/mainwindow.ui ${CMAKE_SOURCE_DIR}/files/ui/playpage.ui + ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui ) source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER}) diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index add3dea40e..734277b97d 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -4,548 +4,307 @@ #include <QMessageBox> #include <QCheckBox> #include <QMenu> +#include <QSortFilterProxyModel> #include <components/files/configurationmanager.hpp> -#include <components/fileorderlist/model/datafilesmodel.hpp> -#include <components/fileorderlist/model/pluginsproxymodel.hpp> -#include <components/fileorderlist/model/esm/esmfile.hpp> +#include <components/contentselector/model/esmfile.hpp> -#include <components/fileorderlist/utils/lineedit.hpp> -#include <components/fileorderlist/utils/naturalsort.hpp> -#include <components/fileorderlist/utils/profilescombobox.hpp> +#include <components/contentselector/model/naturalsort.hpp> + +#include "utils/textinputdialog.hpp" +#include "utils/profilescombobox.hpp" #include "settings/gamesettings.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) , mGameSettings(gameSettings) , mLauncherSettings(launcherSettings) , QWidget(parent) { - setupUi(this); - - // Models - 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))); + ui.setupUi (this); + setObjectName ("DataFilesPage"); + mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); - connect(splitter, SIGNAL(splitterMoved(int,int)), this, SLOT(updateSplitter())); - - createActions(); + buildView(); setupDataFiles(); } -void DataFilesPage::createActions() -{ - - // Add the actions to the toolbuttons - newProfileButton->setDefaultAction(newProfileAction); - deleteProfileButton->setDefaultAction(deleteProfileAction); - - // Context menu actions - mContextMenu = new QMenu(this); - mContextMenu->addAction(checkAction); - mContextMenu->addAction(uncheckAction); -} - -void DataFilesPage::setupDataFiles() +void Launcher::DataFilesPage::loadSettings() { - // 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(); + paths.insert (0, mDataLocal); + PathIterator pathIterator (paths); - foreach (const QString &path, paths) { - mDataFilesModel->addFiles(path); - } - - QString dataLocal = mGameSettings.getDataLocal(); - if (!dataLocal.isEmpty()) - mDataFilesModel->addFiles(dataLocal); + QString profileName = ui.profilesComboBox->currentText(); - // Sort by date accessed for now - mDataFilesModel->sort(3); + QStringList files = mLauncherSettings.values(QString("Profiles/") + profileName, Qt::MatchExactly); - QStringList profiles = mLauncherSettings.subKeys(QString("Profiles/")); - QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); - - if (!profiles.isEmpty()) - profilesComboBox->addItems(profiles); - - // Add the current profile if empty - if (profilesComboBox->findText(profile) == -1 && !profile.isEmpty()) - profilesComboBox->addItem(profile); - - if (profilesComboBox->findText(QString("Default")) == -1) - profilesComboBox->addItem(QString("Default")); - - if (profile.isEmpty() || profile == QLatin1String("Default")) { - deleteProfileAction->setEnabled(false); - profilesComboBox->setEditEnabled(false); - profilesComboBox->setCurrentIndex(profilesComboBox->findText(QString("Default"))); - } else { - profilesComboBox->setEditEnabled(true); - profilesComboBox->setCurrentIndex(profilesComboBox->findText(profile)); - } - - // We do this here to prevent deletion of profiles when initializing the combobox - connect(profilesComboBox, SIGNAL(profileRenamed(QString,QString)), this, SLOT(profileRenamed(QString,QString))); - connect(profilesComboBox, SIGNAL(profileChanged(QString,QString)), this, SLOT(profileChanged(QString,QString))); - - loadSettings(); - -} + QStringList filepaths; -void DataFilesPage::loadSettings() -{ - QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); - - 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 &file, files) + { + QString filepath = pathIterator.findFirstPath (file); - foreach (const QString &master, masters) { - QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(master)); - if (index.isValid()) - mDataFilesModel->setCheckState(index, Qt::Checked); + if (!filepath.isEmpty()) + filepaths << filepath; } - foreach (const QString &plugin, plugins) { - QModelIndex index = mDataFilesModel->indexFromItem(mDataFilesModel->findItem(plugin)); - if (index.isValid()) - mDataFilesModel->setCheckState(index, Qt::Checked); - } + mSelector->setProfileContent (filepaths); } -void DataFilesPage::saveSettings() +void Launcher::DataFilesPage::saveSettings(const QString &profile) { - if (mDataFilesModel->rowCount() < 1) - return; - - QString profile = mLauncherSettings.value(QString("Profiles/currentprofile")); + QString profileName = profile; - if (profile.isEmpty()) { - profile = profilesComboBox->currentText(); - mLauncherSettings.setValue(QString("Profiles/currentprofile"), profile); - } + if (profileName.isEmpty()) + profileName = ui.profilesComboBox->currentText(); - mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master")); - mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin")); + //retrieve the files selected for the profile + ContentSelectorModel::ContentFileList items = mSelector->selectedFiles(); - mGameSettings.remove(QString("master")); - mGameSettings.remove(QString("plugin")); + removeProfile (profileName); - 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)) { - mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/master"), item); - mGameSettings.setMultiValue(QString("master"), item); + foreach(const ContentSelectorModel::EsmFile *item, items) { - } else if (item.endsWith(QString(".esp"), Qt::CaseInsensitive)) { - mLauncherSettings.setMultiValue(QString("Profiles/") + profile + QString("/plugin"), item); - mGameSettings.setMultiValue(QString("plugin"), item); + if (item->gameFiles().size() == 0) { + mLauncherSettings.setMultiValue(QString("Profiles/") + profileName, item->fileName()); + 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 - if (text.isEmpty()) { - mNewProfileDialog->setOkButtonEnabled(false); - return; - } + ui.verticalLayout->insertWidget (0, mSelector->uiWidget()); - (profilesComboBox->findText(text) == -1) - ? mNewProfileDialog->setOkButtonEnabled(true) - : mNewProfileDialog->setOkButtonEnabled(false); -} + //tool buttons + ui.newProfileButton->setToolTip ("Create a new profile"); + ui.deleteProfileButton->setToolTip ("Delete an existing profile"); -void DataFilesPage::updateSplitter() -{ - // Sigh, update the saved splitter size in settings only when moved - // Since getting mSplitter->sizes() if page is hidden returns invalid values - QList<int> sizes = splitter->sizes(); + //combo box + ui.profilesComboBox->addItem ("Default"); + ui.profilesComboBox->setPlaceholderText (QString("Select a profile...")); - mLauncherSettings.setValue(QString("General/MastersTable/width"), QString::number(sizes.at(0))); - mLauncherSettings.setValue(QString("General/PluginsTable/width"), QString::number(sizes.at(1))); -} + // Add the actions to the toolbuttons + ui.newProfileButton->setDefaultAction (ui.newProfileAction); + ui.deleteProfileButton->setDefaultAction (ui.deleteProfileAction); -void DataFilesPage::updateViews() -{ - // Ensure the columns are hidden because sort() re-enables them - mastersTable->setColumnHidden(1, true); - mastersTable->setColumnHidden(2, true); - mastersTable->setColumnHidden(3, true); - mastersTable->setColumnHidden(4, true); - mastersTable->setColumnHidden(5, true); - mastersTable->setColumnHidden(6, true); - mastersTable->setColumnHidden(7, true); - mastersTable->setColumnHidden(8, true); - - pluginsTable->setColumnHidden(1, true); - pluginsTable->setColumnHidden(2, true); - pluginsTable->setColumnHidden(3, true); - pluginsTable->setColumnHidden(4, true); - pluginsTable->setColumnHidden(5, true); - pluginsTable->setColumnHidden(6, true); - pluginsTable->setColumnHidden(7, true); - pluginsTable->setColumnHidden(8, true); -} + //establish connections + connect (ui.profilesComboBox, SIGNAL (currentIndexChanged(int)), + this, SLOT (slotProfileChanged(int))); -void DataFilesPage::setProfilesComboBoxIndex(int index) -{ - profilesComboBox->setCurrentIndex(index); + connect (ui.profilesComboBox, SIGNAL (profileRenamed(QString, QString)), + this, SLOT (slotProfileRenamed(QString, QString))); + + connect (ui.profilesComboBox, SIGNAL (signalProfileChanged(QString, QString)), + this, SLOT (slotProfileChangedByUser(QString, QString))); } -void DataFilesPage::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) { - QString profile = mNewProfileDialog->lineEdit()->text(); - profilesComboBox->addItem(profile); - profilesComboBox->setCurrentIndex(profilesComboBox->findText(profile)); + if (index >= -1 && index < ui.profilesComboBox->count()) + { + QString previous = ui.profilesComboBox->itemText(ui.profilesComboBox->currentIndex()); + QString current = ui.profilesComboBox->itemText(index); + + setProfile (previous, current, savePrevious); } } -void DataFilesPage::on_deleteProfileAction_triggered() +void Launcher::DataFilesPage::setProfile (const QString &previous, const QString ¤t, bool savePrevious) { - QString profile = profilesComboBox->currentText(); - - if (profile.isEmpty()) - return; - - QMessageBox msgBox(this); - msgBox.setWindowTitle(tr("Delete Profile")); - msgBox.setIcon(QMessageBox::Warning); - msgBox.setStandardButtons(QMessageBox::Cancel); - msgBox.setText(tr("Are you sure you want to delete <b>%0</b>?").arg(profile)); + //abort if no change (poss. duplicate signal) + if (previous == current) + return; - QAbstractButton *deleteButton = - msgBox.addButton(tr("Delete"), QMessageBox::ActionRole); + if (!previous.isEmpty() && savePrevious) + saveSettings (previous); - msgBox.exec(); + ui.profilesComboBox->setCurrentProfile (ui.profilesComboBox->findText (current)); - if (msgBox.clickedButton() == deleteButton) { - mLauncherSettings.remove(QString("Profiles/") + profile + QString("/master")); - mLauncherSettings.remove(QString("Profiles/") + profile + QString("/plugin")); + loadSettings(); - // Remove the profile from the combobox - profilesComboBox->removeItem(profilesComboBox->findText(profile)); - } + checkForDefaultProfile(); } -void DataFilesPage::on_checkAction_triggered() +void Launcher::DataFilesPage::slotProfileDeleted (const QString &item) { - if (pluginsTable->hasFocus()) - setPluginsCheckstates(Qt::Checked); - - if (mastersTable->hasFocus()) - setMastersCheckstates(Qt::Checked); - + removeProfile (item); } -void DataFilesPage::on_uncheckAction_triggered() +void Launcher::DataFilesPage::slotProfileChangedByUser(const QString &previous, const QString ¤t) { - if (pluginsTable->hasFocus()) - setPluginsCheckstates(Qt::Unchecked); - - if (mastersTable->hasFocus()) - setMastersCheckstates(Qt::Unchecked); + setProfile(previous, current, true); + emit signalProfileChanged (ui.profilesComboBox->findText(current)); } -void DataFilesPage::setMastersCheckstates(Qt::CheckState state) +void Launcher::DataFilesPage::slotProfileRenamed(const QString &previous, const QString ¤t) { - if (!mastersTable->selectionModel()->hasSelection()) { + if (previous.isEmpty()) 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()) - return; + // Remove the old one + removeProfile (previous); - mDataFilesModel->setCheckState(sourceIndex, state); - } + loadSettings(); } -void DataFilesPage::setPluginsCheckstates(Qt::CheckState state) +void Launcher::DataFilesPage::slotProfileChanged(int index) { - if (!pluginsTable->selectionModel()->hasSelection()) { - 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); - } + setProfile (index, true); } -void DataFilesPage::setCheckState(QModelIndex index) +void Launcher::DataFilesPage::setupDataFiles() { - if (!index.isValid()) - return; + QStringList paths = mGameSettings.getDataDirs(); - QObject *object = QObject::sender(); + foreach (const QString &path, paths) + mSelector->addFiles(path); - // Not a signal-slot call - if (!object) - return; + mDataLocal = mGameSettings.getDataLocal(); + if (!mDataLocal.isEmpty()) + mSelector->addFiles(mDataLocal); - if (object->objectName() == QLatin1String("PluginsTable")) { - QModelIndex sourceIndex = mPluginsProxyModel->mapToSource( - mFilterProxyModel->mapToSource(index)); + QStringList profiles; + QString currentProfile = mLauncherSettings.getSettings().value("Profiles/currentprofile"); - if (sourceIndex.isValid()) { - (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) - ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) - : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); + foreach (QString key, mLauncherSettings.getSettings().keys()) + { + if (key.contains("Profiles/")) + { + QString profile = key.mid (9); + if (profile != "currentprofile") + { + if (!profiles.contains(profile)) + profiles << profile; + } } } - if (object->objectName() == QLatin1String("MastersTable")) { - QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index); - - if (sourceIndex.isValid()) { - (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) - ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) - : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); - } - } + foreach (const QString &item, profiles) + addProfile (item, false); - return; -} + setProfile (ui.profilesComboBox->findText(currentProfile), false); -void DataFilesPage::filterChanged(const QString filter) -{ - QRegExp regExp(filter, Qt::CaseInsensitive, QRegExp::FixedString); - mFilterProxyModel->setFilterRegExp(regExp); + loadSettings(); } -void DataFilesPage::profileChanged(const QString &previous, const QString ¤t) +void Launcher::DataFilesPage::on_newProfileAction_triggered() { - // Prevent the deletion of the default profile - if (current == QLatin1String("Default")) { - deleteProfileAction->setEnabled(false); - profilesComboBox->setEditEnabled(false); - } else { - deleteProfileAction->setEnabled(true); - profilesComboBox->setEditEnabled(true); - } + TextInputDialog newDialog (tr("New Profile"), tr("Profile name:"), this); - if (previous.isEmpty()) + if (newDialog.exec() != QDialog::Accepted) return; - if (profilesComboBox->findText(previous) == -1) - return; // Profile was deleted - - // Store the previous profile - mLauncherSettings.setValue(QString("Profiles/currentprofile"), previous); - saveSettings(); - mLauncherSettings.setValue(QString("Profiles/currentprofile"), current); - - loadSettings(); -} + QString profile = newDialog.getText(); -void DataFilesPage::profileRenamed(const QString &previous, const QString ¤t) -{ - if (previous.isEmpty()) - return; + if (profile.isEmpty()) + return; - // Save the new profile name - mLauncherSettings.setValue(QString("Profiles/currentprofile"), current); saveSettings(); - // Remove the old one - mLauncherSettings.remove(QString("Profiles/") + previous + QString("/master")); - mLauncherSettings.remove(QString("Profiles/") + previous + QString("/plugin")); + mSelector->clearCheckStates(); - // Remove the profile from the combobox - profilesComboBox->removeItem(profilesComboBox->findText(previous)); + addProfile(profile, true); - 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 (!object) + if (ui.profilesComboBox->findText (profile) != -1) return; - if (object->objectName() == QLatin1String("PluginsTable")) { - if (!pluginsTable->selectionModel()->hasSelection()) - return; + ui.profilesComboBox->addItem (profile); - QPoint globalPos = pluginsTable->mapToGlobal(point); - QModelIndexList indexes = pluginsTable->selectionModel()->selectedIndexes(); + if (setAsCurrent) + setProfile (ui.profilesComboBox->findText (profile), false); +} - // Show the check/uncheck actions depending on the state of the selected items - uncheckAction->setEnabled(false); - checkAction->setEnabled(false); +void Launcher::DataFilesPage::on_deleteProfileAction_triggered() +{ + QString profile = ui.profilesComboBox->currentText(); - foreach (const QModelIndex &index, indexes) - { - if (!index.isValid()) - return; + if (profile.isEmpty()) + return; - QModelIndex sourceIndex = mPluginsProxyModel->mapToSource( - mFilterProxyModel->mapToSource(index)); + if (!showDeleteMessageBox (profile)) + return; - if (!sourceIndex.isValid()) - return; + // Remove the profile from the combobox + ui.profilesComboBox->removeItem (ui.profilesComboBox->findText (profile)); - (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) - ? uncheckAction->setEnabled(true) - : checkAction->setEnabled(true); - } + removeProfile(profile); - // Show menu - mContextMenu->exec(globalPos); - } + saveSettings(); - if (object->objectName() == QLatin1String("MastersTable")) { - if (!mastersTable->selectionModel()->hasSelection()) - return; + loadSettings(); - QPoint globalPos = mastersTable->mapToGlobal(point); - QModelIndexList indexes = mastersTable->selectionModel()->selectedIndexes(); + checkForDefaultProfile(); +} - // Show the check/uncheck actions depending on the state of the selected items - uncheckAction->setEnabled(false); - checkAction->setEnabled(false); +void Launcher::DataFilesPage::checkForDefaultProfile() +{ + //don't allow deleting "Default" profile + bool success = (ui.profilesComboBox->currentText() != "Default"); - foreach (const QModelIndex &index, indexes) - { - if (!index.isValid()) - return; + ui.deleteProfileAction->setEnabled (success); + ui.profilesComboBox->setEditEnabled (success); +} - 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()) - return; + QAbstractButton *deleteButton = + msgBox.addButton(tr("Delete"), QMessageBox::ActionRole); - (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) - ? uncheckAction->setEnabled(true) - : checkAction->setEnabled(true); - } + msgBox.exec(); - mContextMenu->exec(globalPos); - } + return (msgBox.clickedButton() == deleteButton); } diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index a0b0293309..37603a2106 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -1,88 +1,136 @@ #ifndef DATAFILESPAGE_H #define DATAFILESPAGE_H +#include "ui_datafilespage.h" #include <QWidget> -#include <QModelIndex> -#include "ui_datafilespage.h" + +#include <QDir> +#include <QFile> class QSortFilterProxyModel; class QAbstractItemModel; -class QAction; class QMenu; -class DataFilesModel; -class TextInputDialog; -class GameSettings; -class LauncherSettings; -class PluginsProxyModel; - 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 ¤t); + void slotProfileRenamed(const QString &previous, const QString ¤t); + void slotProfileDeleted(const QString &item); -public: - DataFilesPage(Files::ConfigurationManager &cfg, GameSettings &gameSettings, LauncherSettings &launcherSettings, QWidget *parent = 0); + void on_newProfileAction_triggered(); + void on_deleteProfileAction_triggered(); - QAbstractItemModel* profilesComboBoxModel(); - int profilesComboBoxIndex(); + private: - void writeConfig(QString profile = QString()); - void saveSettings(); + QMenu *mContextMenu; -signals: - void profileChanged(int index); + Files::ConfigurationManager &mCfgMgr; -public slots: - void setCheckState(QModelIndex index); - void setProfilesComboBoxIndex(int index); + GameSettings &mGameSettings; + LauncherSettings &mLauncherSettings; - void filterChanged(const QString filter); - void showContextMenu(const QPoint &point); - void profileChanged(const QString &previous, const QString ¤t); - void profileRenamed(const QString &previous, const QString ¤t); - void updateOkButton(const QString &text); - void updateSplitter(); - void updateViews(); + QString mDataLocal; - // Action slots - void on_newProfileAction_triggered(); - void on_deleteProfileAction_triggered(); - void on_checkAction_triggered(); - void on_uncheckAction_triggered(); + void setPluginsCheckstates(Qt::CheckState state); -private slots: - void slotCurrentIndexChanged(int index); + void buildView(); + void setupDataFiles(); + void setupConfig(); + void readConfig(); + void setProfile (int index, bool savePrevious); + void setProfile (const QString &previous, const QString ¤t, bool savePrevious); + void removeProfile (const QString &profile); + bool showDeleteMessageBox (const QString &text); + void addProfile (const QString &profile, bool setAsCurrent); + void checkForDefaultProfile(); -private: - DataFilesModel *mDataFilesModel; + class PathIterator + { + QStringList::ConstIterator mCitEnd; + QStringList::ConstIterator mCitCurrent; + QStringList::ConstIterator mCitBegin; + QString mFile; + QString mFilePath; - PluginsProxyModel *mPluginsProxyModel; - QSortFilterProxyModel *mMastersProxyModel; + public: + 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; - LauncherSettings &mLauncherSettings; + QString path () + { + bool success = false; + QDir dir; + QFileInfo file; - TextInputDialog *mNewProfileDialog; + while (!success) + { + if (mCitCurrent == mCitEnd) + break; - void setMastersCheckstates(Qt::CheckState state); - void setPluginsCheckstates(Qt::CheckState state); + dir.setPath (*(mCitCurrent++)); + file.setFile (dir.absoluteFilePath (mFile)); - void createActions(); - void setupDataFiles(); - void setupConfig(); - void readConfig(); + success = file.exists(); + } - void loadSettings(); + if (success) + return file.absoluteFilePath(); -}; + return ""; + } + }; + }; +} #endif diff --git a/apps/launcher/graphicspage.cpp b/apps/launcher/graphicspage.cpp index 9308c1d572..516c3d8233 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -4,10 +4,12 @@ #include <QMessageBox> #include <QDir> -#ifdef __APPLE__ +#ifdef MAC_OS_X_VERSION_MIN_REQUIRED +#undef MAC_OS_X_VERSION_MIN_REQUIRED // We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154 #define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ -#endif +#endif // MAC_OS_X_VERSION_MIN_REQUIRED + #include <SDL.h> #include <cstdlib> @@ -17,7 +19,7 @@ #include <components/files/configurationmanager.hpp> #include <components/files/ogreplugin.hpp> -#include <components/fileorderlist/utils/naturalsort.hpp> +#include <components/contentselector/model/naturalsort.hpp> #include "settings/graphicssettings.hpp" @@ -33,11 +35,12 @@ QString getAspect(int x, int y) return QString(QString::number(xaspect) + ":" + QString::number(yaspect)); } -GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSetting, QWidget *parent) +Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSetting, QWidget *parent) : mCfgMgr(cfg) , mGraphicsSettings(graphicsSetting) , QWidget(parent) { + setObjectName ("GraphicsPage"); setupUi(this); // Set the maximum res we can set in windowed mode @@ -52,7 +55,7 @@ GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &g } -bool GraphicsPage::setupOgre() +bool Launcher::GraphicsPage::setupOgre() { // Create a log manager so we can surpress debug text to stdout/stderr Ogre::LogManager* logMgr = OGRE_NEW Ogre::LogManager; @@ -133,7 +136,7 @@ bool GraphicsPage::setupOgre() msgBox.setIcon(QMessageBox::Critical); msgBox.setStandardButtons(QMessageBox::Ok); msgBox.setText(tr("<br><b>Could not select a valid render system</b><br><br> \ - Please make sure the plugins.cfg file exists and contains a valid rendering plugin.<br>")); + Please make sure Ogre plugins were installed correctly.<br>")); msgBox.exec(); return false; } @@ -156,7 +159,7 @@ bool GraphicsPage::setupOgre() return true; } -bool GraphicsPage::setupSDL() +bool Launcher::GraphicsPage::setupSDL() { int displays = SDL_GetNumVideoDisplays(); @@ -179,7 +182,7 @@ bool GraphicsPage::setupSDL() return true; } -bool GraphicsPage::loadSettings() +bool Launcher::GraphicsPage::loadSettings() { if (!setupSDL()) return false; @@ -218,7 +221,7 @@ bool GraphicsPage::loadSettings() return true; } -void GraphicsPage::saveSettings() +void Launcher::GraphicsPage::saveSettings() { vSyncCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/vsync"), QString("true")) : mGraphicsSettings.setValue(QString("Video/vsync"), QString("false")); @@ -245,7 +248,7 @@ void GraphicsPage::saveSettings() mGraphicsSettings.setValue(QString("Video/screen"), QString::number(screenComboBox->currentIndex())); } -QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer) +QStringList Launcher::GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer) { QStringList result; @@ -278,7 +281,7 @@ QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSy return result; } -QStringList GraphicsPage::getAvailableResolutions(int screen) +QStringList Launcher::GraphicsPage::getAvailableResolutions(int screen) { QStringList result; SDL_DisplayMode mode; @@ -325,7 +328,7 @@ QStringList GraphicsPage::getAvailableResolutions(int screen) return result; } -QRect GraphicsPage::getMaximumResolution() +QRect Launcher::GraphicsPage::getMaximumResolution() { QRect max; int screens = QApplication::desktop()->screenCount(); @@ -340,7 +343,7 @@ QRect GraphicsPage::getMaximumResolution() return max; } -void GraphicsPage::rendererChanged(const QString &renderer) +void Launcher::GraphicsPage::rendererChanged(const QString &renderer) { mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString()); @@ -349,7 +352,7 @@ void GraphicsPage::rendererChanged(const QString &renderer) antiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mSelectedRenderSystem)); } -void GraphicsPage::screenChanged(int screen) +void Launcher::GraphicsPage::screenChanged(int screen) { if (screen >= 0) { resolutionComboBox->clear(); @@ -357,7 +360,7 @@ void GraphicsPage::screenChanged(int screen) } } -void GraphicsPage::slotFullScreenChanged(int state) +void Launcher::GraphicsPage::slotFullScreenChanged(int state) { if (state == Qt::Checked) { standardRadioButton->toggle(); @@ -371,7 +374,7 @@ void GraphicsPage::slotFullScreenChanged(int state) } } -void GraphicsPage::slotStandardToggled(bool checked) +void Launcher::GraphicsPage::slotStandardToggled(bool checked) { if (checked) { resolutionComboBox->setEnabled(true); diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index d233ea12e2..7f5dcae1ee 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -18,49 +18,52 @@ #include "ui_graphicspage.h" -class GraphicsSettings; namespace Files { struct ConfigurationManager; } -class GraphicsPage : public QWidget, private Ui::GraphicsPage +namespace Launcher { - Q_OBJECT - -public: - GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0); - - void saveSettings(); - bool loadSettings(); - -public slots: - void rendererChanged(const QString &renderer); - void screenChanged(int screen); - -private slots: - void slotFullScreenChanged(int state); - void slotStandardToggled(bool checked); - -private: - Ogre::Root *mOgre; - Ogre::RenderSystem *mSelectedRenderSystem; - Ogre::RenderSystem *mOpenGLRenderSystem; - Ogre::RenderSystem *mDirect3DRenderSystem; - #ifdef ENABLE_PLUGIN_GL - Ogre::GLPlugin* mGLPlugin; - #endif - #ifdef ENABLE_PLUGIN_Direct3D9 - Ogre::D3D9Plugin* mD3D9Plugin; - #endif - - Files::ConfigurationManager &mCfgMgr; - GraphicsSettings &mGraphicsSettings; - - QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer); - QStringList getAvailableResolutions(int screen); - QRect getMaximumResolution(); - - bool setupOgre(); - bool setupSDL(); -}; - + class GraphicsSettings; + + class GraphicsPage : public QWidget, private Ui::GraphicsPage + { + Q_OBJECT + + public: + GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0); + + void saveSettings(); + bool loadSettings(); + + public slots: + void rendererChanged(const QString &renderer); + void screenChanged(int screen); + + private slots: + void slotFullScreenChanged(int state); + void slotStandardToggled(bool checked); + + private: + Ogre::Root *mOgre; + Ogre::RenderSystem *mSelectedRenderSystem; + Ogre::RenderSystem *mOpenGLRenderSystem; + Ogre::RenderSystem *mDirect3DRenderSystem; + #ifdef ENABLE_PLUGIN_GL + Ogre::GLPlugin* mGLPlugin; + #endif + #ifdef ENABLE_PLUGIN_Direct3D9 + Ogre::D3D9Plugin* mD3D9Plugin; + #endif + + Files::ConfigurationManager &mCfgMgr; + GraphicsSettings &mGraphicsSettings; + + QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer); + QStringList getAvailableResolutions(int screen); + QRect getMaximumResolution(); + + bool setupOgre(); + bool setupSDL(); + }; +} #endif diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index f67f5edcff..9f89f28102 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -3,10 +3,12 @@ #include <QDir> #include <QDebug> -#ifdef __APPLE__ +#ifdef MAC_OS_X_VERSION_MIN_REQUIRED +#undef MAC_OS_X_VERSION_MIN_REQUIRED // We need to do this because of Qt: https://bugreports.qt-project.org/browse/QTBUG-22154 #define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ -#endif +#endif // MAC_OS_X_VERSION_MIN_REQUIRED + #include <SDL.h> #include "maindialog.hpp" @@ -49,7 +51,7 @@ int main(int argc, char *argv[]) // Support non-latin characters QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); - MainDialog mainWin; + Launcher::MainDialog mainWin; if (mainWin.setup()) { mainWin.show(); @@ -61,4 +63,3 @@ int main(int argc, char *argv[]) SDL_Quit(); return returnValue; } - diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 032f70916f..4012a1fbd5 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -1,5 +1,6 @@ #include "maindialog.hpp" +#include <QPushButton> #include <QFontDatabase> #include <QInputDialog> #include <QFileDialog> @@ -23,8 +24,8 @@ #include "graphicspage.hpp" #include "datafilespage.hpp" -MainDialog::MainDialog() - : mGameSettings(mCfgMgr) +Launcher::MainDialog::MainDialog(QWidget *parent) + : mGameSettings(mCfgMgr), QMainWindow (parent) { // Install the stylesheet font QFile file; @@ -69,7 +70,7 @@ MainDialog::MainDialog() createIcons(); } -void MainDialog::createIcons() +void Launcher::MainDialog::createIcons() { if (!QIcon::hasThemeIcon("document-new")) QIcon::setThemeName("tango"); @@ -101,15 +102,15 @@ void MainDialog::createIcons() } -void MainDialog::createPages() +void Launcher::MainDialog::createPages() { mPlayPage = new PlayPage(this); mGraphicsPage = new GraphicsPage(mCfgMgr, mGraphicsSettings, this); mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this); // Set the combobox of the play page to imitate the combobox on the datafilespage - mPlayPage->setProfilesComboBoxModel(mDataFilesPage->profilesComboBoxModel()); - mPlayPage->setProfilesComboBoxIndex(mDataFilesPage->profilesComboBoxIndex()); + mPlayPage->setProfilesModel(mDataFilesPage->profilesModel()); + mPlayPage->setProfilesIndex(mDataFilesPage->profilesIndex()); // Add the pages to the stacked widget pagesWidget->addWidget(mPlayPage); @@ -121,12 +122,12 @@ void MainDialog::createPages() connect(mPlayPage, SIGNAL(playButtonClicked()), this, SLOT(play())); - connect(mPlayPage, SIGNAL(profileChanged(int)), mDataFilesPage, SLOT(setProfilesComboBoxIndex(int))); - connect(mDataFilesPage, SIGNAL(profileChanged(int)), mPlayPage, SLOT(setProfilesComboBoxIndex(int))); + connect(mPlayPage, SIGNAL(signalProfileChanged(int)), mDataFilesPage, SLOT(slotProfileChanged(int))); + connect(mDataFilesPage, SIGNAL(signalProfileChanged(int)), mPlayPage, SLOT(setProfilesIndex(int))); } -bool MainDialog::showFirstRunDialog() +bool Launcher::MainDialog::showFirstRunDialog() { QStringList iniPaths; @@ -261,19 +262,11 @@ bool MainDialog::showFirstRunDialog() // Add a new profile if (msgBox.isChecked()) { mLauncherSettings.setValue(QString("Profiles/currentprofile"), QString("Imported")); + mLauncherSettings.remove(QString("Profiles/Imported/content")); - mLauncherSettings.remove(QString("Profiles/Imported/master")); - mLauncherSettings.remove(QString("Profiles/Imported/plugin")); - - QStringList masters = mGameSettings.values(QString("master")); - QStringList plugins = mGameSettings.values(QString("plugin")); - - foreach (const QString &master, masters) { - mLauncherSettings.setMultiValue(QString("Profiles/Imported/master"), master); - } - - foreach (const QString &plugin, plugins) { - mLauncherSettings.setMultiValue(QString("Profiles/Imported/plugin"), plugin); + QStringList contents = mGameSettings.values(QString("content")); + foreach (const QString &content, contents) { + mLauncherSettings.setMultiValue(QString("Profiles/Imported/content"), content); } } @@ -282,7 +275,7 @@ bool MainDialog::showFirstRunDialog() return true; } -bool MainDialog::setup() +bool Launcher::MainDialog::setup() { if (!setupLauncherSettings()) return false; @@ -311,15 +304,33 @@ bool MainDialog::setup() return true; } -void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) +void Launcher::MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous) { if (!current) current = previous; - pagesWidget->setCurrentIndex(iconWidget->row(current)); + int currentIndex = iconWidget->row(current); + int previousIndex = iconWidget->row(previous); + + pagesWidget->setCurrentIndex(currentIndex); + + DataFilesPage *previousPage = dynamic_cast<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); @@ -356,7 +367,7 @@ bool MainDialog::setupLauncherSettings() } #ifndef WIN32 -bool expansions(UnshieldThread& cd) +bool Launcher::expansions(Launcher::UnshieldThread& cd) { if(cd.BloodmoonDone()) { @@ -367,7 +378,7 @@ bool expansions(UnshieldThread& cd) QMessageBox expansionsBox; 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>")); - + QAbstractButton* tribunalButton = NULL; if(!cd.TribunalDone()) tribunalButton = expansionsBox.addButton(QObject::tr("&Tribunal"), QMessageBox::ActionRole); @@ -386,7 +397,7 @@ bool expansions(UnshieldThread& cd) { TextSlotMsgBox cdbox; - cdbox.setStandardButtons(QMessageBox::Cancel); + cdbox.setStandardButtons(QMessageBox::Cancel); QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&))); QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject())); @@ -405,7 +416,7 @@ bool expansions(UnshieldThread& cd) { TextSlotMsgBox cdbox; - cdbox.setStandardButtons(QMessageBox::Cancel); + cdbox.setStandardButtons(QMessageBox::Cancel); QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&))); QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject())); @@ -427,7 +438,7 @@ bool expansions(UnshieldThread& cd) } #endif // WIN32 -bool MainDialog::setupGameSettings() +bool Launcher::MainDialog::setupGameSettings() { QString userPath = QString::fromStdString(mCfgMgr.getUserPath().string()); QString globalPath = QString::fromStdString(mCfgMgr.getGlobalPath().string()); @@ -467,7 +478,7 @@ bool MainDialog::setupGameSettings() foreach (const QString path, mGameSettings.getDataDirs()) { QDir dir(path); QStringList filters; - filters << "*.esp" << "*.esm"; + filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon"; if (!dir.entryList(filters).isEmpty()) dataDirs.append(path); @@ -485,12 +496,12 @@ bool MainDialog::setupGameSettings() QAbstractButton *dirSelectButton = msgBox.addButton(QObject::tr("Browse to &Install..."), QMessageBox::ActionRole); - + #ifndef WIN32 - QAbstractButton *cdSelectButton = + QAbstractButton *cdSelectButton = msgBox.addButton(QObject::tr("Browse to &CD..."), QMessageBox::ActionRole); #endif - + msgBox.exec(); @@ -505,14 +516,14 @@ bool MainDialog::setupGameSettings() #ifndef WIN32 else if(msgBox.clickedButton() == cdSelectButton) { UnshieldThread cd; - + { TextSlotMsgBox cdbox; - cdbox.setStandardButtons(QMessageBox::Cancel); + cdbox.setStandardButtons(QMessageBox::Cancel); QObject::connect(&cd,SIGNAL(signalGUI(const QString&)), &cdbox, SLOT(setTextSlot(const QString&))); QObject::connect(&cd,SIGNAL(close()), &cdbox, SLOT(reject())); - + cd.SetMorrowindPath( QFileDialog::getOpenFileName( NULL, @@ -526,11 +537,11 @@ bool MainDialog::setupGameSettings() QObject::tr("Select where to extract files to"), QDir::currentPath(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks).toUtf8().constData()); - + cd.start(); cdbox.exec(); } - + while(expansions(cd)); selectedFile = QString::fromStdString(cd.GetMWEsmPath()); @@ -550,7 +561,7 @@ bool MainDialog::setupGameSettings() return true; } -bool MainDialog::setupGraphicsSettings() +bool Launcher::MainDialog::setupGraphicsSettings() { mGraphicsSettings.setMultiValueEnabled(false); @@ -604,7 +615,7 @@ bool MainDialog::setupGraphicsSettings() return true; } -void MainDialog::loadSettings() +void Launcher::MainDialog::loadSettings() { int width = mLauncherSettings.value(QString("General/MainWindow/width")).toInt(); int height = mLauncherSettings.value(QString("General/MainWindow/height")).toInt(); @@ -616,7 +627,7 @@ void MainDialog::loadSettings() move(posX, posY); } -void MainDialog::saveSettings() +void Launcher::MainDialog::saveSettings() { QString width = QString::number(this->width()); QString height = QString::number(this->height()); @@ -634,7 +645,7 @@ void MainDialog::saveSettings() } -bool MainDialog::writeSettings() +bool Launcher::MainDialog::writeSettings() { // Now write all config files saveSettings(); @@ -727,13 +738,13 @@ bool MainDialog::writeSettings() return true; } -void MainDialog::closeEvent(QCloseEvent *event) +void Launcher::MainDialog::closeEvent(QCloseEvent *event) { writeSettings(); event->accept(); } -void MainDialog::play() +void Launcher::MainDialog::play() { if (!writeSettings()) { qApp->quit(); @@ -742,11 +753,11 @@ void MainDialog::play() if(!mGameSettings.hasMaster()) { QMessageBox msgBox; - msgBox.setWindowTitle(tr("No master file selected")); + msgBox.setWindowTitle(tr("No game file selected")); msgBox.setIcon(QMessageBox::Warning); msgBox.setStandardButtons(QMessageBox::Ok); - msgBox.setText(tr("<br><b>You do not have any master files selected.</b><br><br> \ - OpenMW will not start without a master file selected.<br>")); + msgBox.setText(tr("<br><b>You do not have no game file selected.</b><br><br> \ + OpenMW will not start without a game file selected.<br>")); msgBox.exec(); return; } @@ -756,7 +767,7 @@ void MainDialog::play() qApp->quit(); } -bool MainDialog::startProgram(const QString &name, const QStringList &arguments, bool detached) +bool Launcher::MainDialog::startProgram(const QString &name, const QStringList &arguments, bool detached) { QString path = name; #ifdef Q_OS_WIN diff --git a/apps/launcher/maindialog.hpp b/apps/launcher/maindialog.hpp index 824dff6e82..5b8e4908e7 100644 --- a/apps/launcher/maindialog.hpp +++ b/apps/launcher/maindialog.hpp @@ -11,57 +11,59 @@ #include "ui_mainwindow.h" -class QListWidget; class QListWidgetItem; -class QStackedWidget; -class QStringList; -class QStringListModel; -class QString; -class PlayPage; -class GraphicsPage; -class DataFilesPage; - -class MainDialog : public QMainWindow, private Ui::MainWindow +namespace Launcher { - Q_OBJECT + class PlayPage; + class GraphicsPage; + class DataFilesPage; + class UnshieldThread; + +#ifndef WIN32 + bool expansions(Launcher::UnshieldThread& cd); +#endif -public: - MainDialog(); - bool setup(); - bool showFirstRunDialog(); + class MainDialog : public QMainWindow, private Ui::MainWindow + { + Q_OBJECT -public slots: - void changePage(QListWidgetItem *current, QListWidgetItem *previous); - void play(); + public: + explicit MainDialog(QWidget *parent = 0); + bool setup(); + bool showFirstRunDialog(); -private: - void createIcons(); - void createPages(); + public slots: + void changePage(QListWidgetItem *current, QListWidgetItem *previous); + void play(); - bool setupLauncherSettings(); - bool setupGameSettings(); - bool setupGraphicsSettings(); + private: + void createIcons(); + void createPages(); - void loadSettings(); - void saveSettings(); - bool writeSettings(); + bool setupLauncherSettings(); + bool setupGameSettings(); + bool setupGraphicsSettings(); - inline bool startProgram(const QString &name, bool detached = false) { return startProgram(name, QStringList(), detached); } - bool startProgram(const QString &name, const QStringList &arguments, bool detached = false); + void loadSettings(); + void saveSettings(); + bool writeSettings(); - void closeEvent(QCloseEvent *event); + inline bool startProgram(const QString &name, bool detached = false) { return startProgram(name, QStringList(), detached); } + bool startProgram(const QString &name, const QStringList &arguments, bool detached = false); - PlayPage *mPlayPage; - GraphicsPage *mGraphicsPage; - DataFilesPage *mDataFilesPage; + void closeEvent(QCloseEvent *event); - Files::ConfigurationManager mCfgMgr; + PlayPage *mPlayPage; + GraphicsPage *mGraphicsPage; + DataFilesPage *mDataFilesPage; - GameSettings mGameSettings; - GraphicsSettings mGraphicsSettings; - LauncherSettings mLauncherSettings; + Files::ConfigurationManager mCfgMgr; -}; + GameSettings mGameSettings; + GraphicsSettings mGraphicsSettings; + LauncherSettings mLauncherSettings; + }; +} #endif diff --git a/apps/launcher/playpage.cpp b/apps/launcher/playpage.cpp index 46900c5958..6cfb9686fa 100644 --- a/apps/launcher/playpage.cpp +++ b/apps/launcher/playpage.cpp @@ -6,8 +6,9 @@ #include <QPlastiqueStyle> #endif -PlayPage::PlayPage(QWidget *parent) : QWidget(parent) +Launcher::PlayPage::PlayPage(QWidget *parent) : QWidget(parent) { + setObjectName ("PlayPage"); setupUi(this); // Hacks to get the stylesheet look properly @@ -17,27 +18,22 @@ PlayPage::PlayPage(QWidget *parent) : QWidget(parent) #endif profilesComboBox->setView(new QListView()); - connect(profilesComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentIndexChanged(int))); + connect(profilesComboBox, SIGNAL(activated(int)), this, SIGNAL (signalProfileChanged(int))); connect(playButton, SIGNAL(clicked()), this, SLOT(slotPlayClicked())); } -void PlayPage::setProfilesComboBoxModel(QAbstractItemModel *model) +void Launcher::PlayPage::setProfilesModel(QAbstractItemModel *model) { profilesComboBox->setModel(model); } -void PlayPage::setProfilesComboBoxIndex(int index) +void Launcher::PlayPage::setProfilesIndex(int index) { profilesComboBox->setCurrentIndex(index); } -void PlayPage::slotCurrentIndexChanged(int index) -{ - emit profileChanged(index); -} - -void PlayPage::slotPlayClicked() +void Launcher::PlayPage::slotPlayClicked() { emit playButtonClicked(); } diff --git a/apps/launcher/playpage.hpp b/apps/launcher/playpage.hpp index 4306396bd2..1dc5bb0fe0 100644 --- a/apps/launcher/playpage.hpp +++ b/apps/launcher/playpage.hpp @@ -9,27 +9,28 @@ class QComboBox; class QPushButton; class QAbstractItemModel; -class PlayPage : public QWidget, private Ui::PlayPage +namespace Launcher { - Q_OBJECT + class PlayPage : public QWidget, private Ui::PlayPage + { + Q_OBJECT -public: - PlayPage(QWidget *parent = 0); - void setProfilesComboBoxModel(QAbstractItemModel *model); + public: + PlayPage(QWidget *parent = 0); + void setProfilesModel(QAbstractItemModel *model); -signals: - void profileChanged(int index); - void playButtonClicked(); + signals: + void signalProfileChanged(int index); + void playButtonClicked(); -public slots: - void setProfilesComboBoxIndex(int index); + public slots: + void setProfilesIndex(int index); -private slots: - void slotCurrentIndexChanged(int index); - void slotPlayClicked(); + private slots: + void slotPlayClicked(); -}; - + }; +} #endif diff --git a/apps/launcher/settings/gamesettings.cpp b/apps/launcher/settings/gamesettings.cpp index 205879bc37..41113c35aa 100644 --- a/apps/launcher/settings/gamesettings.cpp +++ b/apps/launcher/settings/gamesettings.cpp @@ -9,6 +9,7 @@ #include <components/files/configurationmanager.hpp> #include <boost/version.hpp> + /** * Workaround for problems with whitespaces in paths in older versions of Boost library */ @@ -26,16 +27,16 @@ namespace boost #endif /* (BOOST_VERSION <= 104600) */ -GameSettings::GameSettings(Files::ConfigurationManager &cfg) +Launcher::GameSettings::GameSettings(Files::ConfigurationManager &cfg) : mCfgMgr(cfg) { } -GameSettings::~GameSettings() +Launcher::GameSettings::~GameSettings() { } -void GameSettings::validatePaths() +void Launcher::GameSettings::validatePaths() { if (mSettings.isEmpty() || !mDataDirs.isEmpty()) return; // Don't re-validate paths if they are already parsed @@ -81,14 +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()) return mSettings.values(key); return defaultValues; } -bool GameSettings::readFile(QTextStream &stream) +bool Launcher::GameSettings::readFile(QTextStream &stream) { QMap<QString, QString> cache; QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); @@ -130,7 +131,7 @@ bool GameSettings::readFile(QTextStream &stream) return true; } -bool GameSettings::writeFile(QTextStream &stream) +bool Launcher::GameSettings::writeFile(QTextStream &stream) { // Iterate in reverse order to preserve insertion order QMapIterator<QString, QString> i(mSettings); @@ -139,13 +140,13 @@ bool GameSettings::writeFile(QTextStream &stream) while (i.hasPrevious()) { i.previous(); - if (i.key() == QLatin1String("master") || i.key() == QLatin1String("plugin")) + if (i.key() == QLatin1String("content")) continue; // Quote paths with spaces if (i.key() == QLatin1String("data") - || i.key() == QLatin1String("data-local") - || i.key() == QLatin1String("resources")) + || i.key() == QLatin1String("data-local") + || i.key() == QLatin1String("resources")) { if (i.value().contains(QChar(' '))) { @@ -161,15 +162,24 @@ bool GameSettings::writeFile(QTextStream &stream) } - QStringList masters = mSettings.values(QString("master")); - for (int i = masters.count(); i--;) { - stream << "master=" << masters.at(i) << "\n"; + QStringList content = mSettings.values(QString("content")); + for (int i = content.count(); i--;) { + stream << "content=" << content.at(i) << "\n"; } - QStringList plugins = mSettings.values(QString("plugin")); - for (int i = plugins.count(); i--;) { - stream << "plugin=" << plugins.at(i) << "\n"; + return true; +} + +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; } diff --git a/apps/launcher/settings/gamesettings.hpp b/apps/launcher/settings/gamesettings.hpp index 55b2107e2a..60236200a9 100644 --- a/apps/launcher/settings/gamesettings.hpp +++ b/apps/launcher/settings/gamesettings.hpp @@ -8,55 +8,61 @@ #include <boost/filesystem/path.hpp> -namespace Files { typedef std::vector<boost::filesystem::path> PathContainer; - struct ConfigurationManager;} - -class GameSettings +namespace Files { -public: - GameSettings(Files::ConfigurationManager &cfg); - ~GameSettings(); + typedef std::vector<boost::filesystem::path> PathContainer; + struct ConfigurationManager; +} - 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) - { - QStringList values = mSettings.values(key); - if (!values.contains(value)) - mSettings.insertMulti(key, value); - } + inline void setValue(const QString &key, const QString &value) + { + mSettings.insert(key, value); + } - inline void remove(const QString &key) - { - mSettings.remove(key); - } + inline void setMultiValue(const QString &key, const QString &value) + { + QStringList values = mSettings.values(key); + if (!values.contains(value)) + mSettings.insertMulti(key, value); + } + + inline void remove(const QString &key) + { + mSettings.remove(key); + } - inline QStringList getDataDirs() { return mDataDirs; } - inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); } - inline QString getDataLocal() {return mDataLocal; } - inline bool hasMaster() { return mSettings.count(QString("master")) > 0; } + inline QStringList getDataDirs() { return mDataDirs; } + inline void addDataDir(const QString &dir) { if(!dir.isEmpty()) mDataDirs.append(dir); } + inline QString getDataLocal() {return mDataLocal; } - QStringList values(const QString &key, const QStringList &defaultValues = QStringList()); - bool readFile(QTextStream &stream); - bool writeFile(QTextStream &stream); + bool hasMaster(); -private: - Files::ConfigurationManager &mCfgMgr; + QStringList values(const QString &key, const QStringList &defaultValues = QStringList()); + bool readFile(QTextStream &stream); + bool writeFile(QTextStream &stream); - void validatePaths(); - QMap<QString, QString> mSettings; + private: + Files::ConfigurationManager &mCfgMgr; - QStringList mDataDirs; - QString mDataLocal; -}; + void validatePaths(); + QMap<QString, QString> mSettings; + QStringList mDataDirs; + QString mDataLocal; + }; +} #endif // GAMESETTINGS_HPP diff --git a/apps/launcher/settings/graphicssettings.cpp b/apps/launcher/settings/graphicssettings.cpp index 0c55800917..9dad3dee6b 100644 --- a/apps/launcher/settings/graphicssettings.cpp +++ b/apps/launcher/settings/graphicssettings.cpp @@ -5,15 +5,15 @@ #include <QRegExp> #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; QRegExp sectionRe("([^/]+)/(.+)$"); diff --git a/apps/launcher/settings/graphicssettings.hpp b/apps/launcher/settings/graphicssettings.hpp index 3e8617849e..6f7c135473 100644 --- a/apps/launcher/settings/graphicssettings.hpp +++ b/apps/launcher/settings/graphicssettings.hpp @@ -3,14 +3,16 @@ #include "settingsbase.hpp" -class GraphicsSettings : public SettingsBase<QMap<QString, QString> > +namespace Launcher { -public: - GraphicsSettings(); - ~GraphicsSettings(); + class GraphicsSettings : public SettingsBase<QMap<QString, QString> > + { + public: + GraphicsSettings(); + ~GraphicsSettings(); - bool writeFile(QTextStream &stream); - -}; + bool writeFile(QTextStream &stream); + }; +} #endif // GRAPHICSSETTINGS_HPP diff --git a/apps/launcher/settings/launchersettings.cpp b/apps/launcher/settings/launchersettings.cpp index 5d298e814e..705453555d 100644 --- a/apps/launcher/settings/launchersettings.cpp +++ b/apps/launcher/settings/launchersettings.cpp @@ -5,15 +5,17 @@ #include <QRegExp> #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(); @@ -34,7 +36,7 @@ QStringList LauncherSettings::values(const QString &key, Qt::MatchFlags flags) return result; } -QStringList LauncherSettings::subKeys(const QString &key) +QStringList Launcher::LauncherSettings::subKeys(const QString &key) { QMap<QString, QString> settings = SettingsBase::getSettings(); QStringList keys = settings.uniqueKeys(); @@ -44,12 +46,9 @@ QStringList LauncherSettings::subKeys(const QString &key) QStringList result; foreach (const QString ¤tKey, keys) { - if (keyRe.indexIn(currentKey) != -1) { - QString prefixedKey = keyRe.cap(1); if(prefixedKey.startsWith(key)) { - QString subKey = prefixedKey.remove(key); if (!subKey.isEmpty()) result.append(subKey); @@ -61,7 +60,7 @@ QStringList LauncherSettings::subKeys(const QString &key) return result; } -bool LauncherSettings::writeFile(QTextStream &stream) +bool Launcher::LauncherSettings::writeFile(QTextStream &stream) { QString sectionPrefix; QRegExp sectionRe("([^/]+)/(.+)$"); diff --git a/apps/launcher/settings/launchersettings.hpp b/apps/launcher/settings/launchersettings.hpp index 60c6f86bc7..8acc389a9d 100644 --- a/apps/launcher/settings/launchersettings.hpp +++ b/apps/launcher/settings/launchersettings.hpp @@ -3,17 +3,19 @@ #include "settingsbase.hpp" -class LauncherSettings : public SettingsBase<QMap<QString, QString> > +namespace Launcher { -public: - LauncherSettings(); - ~LauncherSettings(); + class LauncherSettings : public SettingsBase<QMap<QString, QString> > + { + public: + LauncherSettings(); + ~LauncherSettings(); - QStringList subKeys(const QString &key); - QStringList values(const QString &key, Qt::MatchFlags flags = Qt::MatchExactly); + QStringList subKeys(const QString &key); + QStringList values(const QString &key, Qt::MatchFlags flags = Qt::MatchExactly); - bool writeFile(QTextStream &stream); - -}; + bool writeFile(QTextStream &stream); + }; +} #endif // LAUNCHERSETTINGS_HPP diff --git a/apps/launcher/settings/settingsbase.hpp b/apps/launcher/settings/settingsbase.hpp index ed8ada56c3..3a1cf8e30e 100644 --- a/apps/launcher/settings/settingsbase.hpp +++ b/apps/launcher/settings/settingsbase.hpp @@ -7,103 +7,105 @@ #include <QRegExp> #include <QMap> -template <class Map> -class SettingsBase +namespace Launcher { + template <class Map> + class SettingsBase + { -public: - SettingsBase() { mMultiValue = false; } - ~SettingsBase() {} + public: + SettingsBase() { mMultiValue = false; } + ~SettingsBase() {} - inline QString value(const QString &key, const QString &defaultValue = QString()) - { - return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key); - } + inline QString value(const QString &key, const QString &defaultValue = QString()) + { + return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key); + } - inline void setValue(const QString &key, const QString &value) - { - QStringList values = mSettings.values(key); - if (!values.contains(value)) - mSettings.insert(key, value); - } + inline void setValue(const QString &key, const QString &value) + { + QStringList values = mSettings.values(key); + if (!values.contains(value)) + mSettings.insert(key, value); + } - inline void setMultiValue(const QString &key, const QString &value) - { - QStringList values = mSettings.values(key); - if (!values.contains(value)) - mSettings.insertMulti(key, value); - } + inline void setMultiValue(const QString &key, const QString &value) + { + QStringList values = mSettings.values(key); + if (!values.contains(value)) + mSettings.insertMulti(key, value); + } - inline void setMultiValueEnabled(bool enable) - { - mMultiValue = enable; - } + inline void setMultiValueEnabled(bool enable) + { + mMultiValue = enable; + } - inline void remove(const QString &key) - { - mSettings.remove(key); - } + inline void remove(const QString &key) + { + mSettings.remove(key); + } - Map getSettings() {return mSettings;} + Map getSettings() {return mSettings;} - bool readFile(QTextStream &stream) - { - mCache.clear(); + bool readFile(QTextStream &stream) + { + mCache.clear(); - QString sectionPrefix; + QString sectionPrefix; - QRegExp sectionRe("^\\[([^]]+)\\]"); - QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); + QRegExp sectionRe("^\\[([^]]+)\\]"); + QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); - while (!stream.atEnd()) { - QString line = stream.readLine(); + while (!stream.atEnd()) { + QString line = stream.readLine(); - if (line.isEmpty() || line.startsWith("#")) - continue; + if (line.isEmpty() || line.startsWith("#")) + continue; - if (sectionRe.exactMatch(line)) { - sectionPrefix = sectionRe.cap(1); - sectionPrefix.append("/"); - continue; - } + if (sectionRe.exactMatch(line)) { + sectionPrefix = sectionRe.cap(1); + sectionPrefix.append("/"); + continue; + } - if (keyRe.indexIn(line) != -1) { + if (keyRe.indexIn(line) != -1) { - QString key = keyRe.cap(1).trimmed(); - QString value = keyRe.cap(2).trimmed(); + QString key = keyRe.cap(1).trimmed(); + QString value = keyRe.cap(2).trimmed(); - if (!sectionPrefix.isEmpty()) - key.prepend(sectionPrefix); + if (!sectionPrefix.isEmpty()) + key.prepend(sectionPrefix); - mSettings.remove(key); + mSettings.remove(key); - QStringList values = mCache.values(key); + QStringList values = mCache.values(key); - if (!values.contains(value)) { - if (mMultiValue) { - mCache.insertMulti(key, value); - } else { - mCache.insert(key, value); + if (!values.contains(value)) { + if (mMultiValue) { + mCache.insertMulti(key, value); + } else { + mCache.insert(key, value); + } } } } - } - if (mSettings.isEmpty()) { - mSettings = mCache; // This is the first time we read a file + if (mSettings.isEmpty()) { + mSettings = mCache; // This is the first time we read a file + return true; + } + + // Merge the changed keys with those which didn't + mSettings.unite(mCache); return true; } - // Merge the changed keys with those which didn't - mSettings.unite(mCache); - return true; - } - -private: - Map mSettings; - Map mCache; - - bool mMultiValue; -}; + private: + Map mSettings; + Map mCache; + bool mMultiValue; + }; +} #endif // SETTINGSBASE_HPP diff --git a/apps/launcher/textslotmsgbox.cpp b/apps/launcher/textslotmsgbox.cpp index 0607d1cc6e..62d9cf5761 100644 --- a/apps/launcher/textslotmsgbox.cpp +++ b/apps/launcher/textslotmsgbox.cpp @@ -1,6 +1,6 @@ #include "textslotmsgbox.hpp" -void TextSlotMsgBox::setTextSlot(const QString& string) +void Launcher::TextSlotMsgBox::setTextSlot(const QString& string) { setText(string); } diff --git a/apps/launcher/textslotmsgbox.hpp b/apps/launcher/textslotmsgbox.hpp index a29e2c3543..a0fefaa253 100644 --- a/apps/launcher/textslotmsgbox.hpp +++ b/apps/launcher/textslotmsgbox.hpp @@ -3,11 +3,13 @@ #include <QMessageBox> -class TextSlotMsgBox : public QMessageBox +namespace Launcher { -Q_OBJECT - public slots: - void setTextSlot(const QString& string); -}; - + class TextSlotMsgBox : public QMessageBox + { + Q_OBJECT + public slots: + void setTextSlot(const QString& string); + }; +} #endif diff --git a/apps/launcher/unshieldthread.cpp b/apps/launcher/unshieldthread.cpp index ab9d984e1a..d0dbeb1bdb 100644 --- a/apps/launcher/unshieldthread.cpp +++ b/apps/launcher/unshieldthread.cpp @@ -77,6 +77,8 @@ namespace ini.insert(loc, setting + "=" + val + "\r\n"); } + #define FIX(setting) add_setting(category, setting, get_setting(category, setting, inx), ini) + void bloodmoon_fix_ini(std::string& ini, const bfs::path inxPath) { std::string inx = read_to_string(inxPath); @@ -88,95 +90,94 @@ namespace ini.erase(start, end-start); std::string category; - std::string setting; category = "General"; { - setting = "Werewolf FOV"; add_setting(category, setting, get_setting(category, setting, inx), ini); + FIX("Werewolf FOV"); } category = "Moons"; { - setting = "Script Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); + FIX("Script Color"); } category = "Weather"; { - setting = "Snow Ripples"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Snow Ripple Radius"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Snow Ripples Per Flake"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Snow Ripple Scale"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Snow Ripple Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Snow Gravity Scale"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Snow High Kill"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Snow Low Kill"; add_setting(category, setting, get_setting(category, setting, inx), ini); + FIX("Snow Ripples"); + FIX("Snow Ripple Radius"); + FIX("Snow Ripples Per Flake"); + FIX("Snow Ripple Scale"); + FIX("Snow Ripple Speed"); + FIX("Snow Gravity Scale"); + FIX("Snow High Kill"); + FIX("Snow Low Kill"); } category = "Weather Blight"; { - setting = "Ambient Loop Sound ID"; add_setting(category, setting, get_setting(category, setting, inx), ini); + FIX("Ambient Loop Sound ID"); } category = "Weather Snow"; { - setting = "Sky Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Sky Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Sky Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Sky Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Fog Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Fog Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Fog Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Fog Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Ambient Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Ambient Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Ambient Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Ambient Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Sun Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Sun Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Sun Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Sun Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Sun Disc Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Transition Delta"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Land Fog Day Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Land Fog Night Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Clouds Maximum Percent"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Wind Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Cloud Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Glare View"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Cloud Texture"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Ambient Loop Sound ID"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Snow Threshold"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Snow Diameter"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Snow Height Min"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Snow Height Max"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Snow Entrance Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Max Snowflakes"; add_setting(category, setting, get_setting(category, setting, inx), ini); + FIX("Sky Sunrise Color"); + FIX("Sky Day Color"); + FIX("Sky Sunset Color"); + FIX("Sky Night Color"); + FIX("Fog Sunrise Color"); + FIX("Fog Day Color"); + FIX("Fog Sunset Color"); + FIX("Fog Night Color"); + FIX("Ambient Sunrise Color"); + FIX("Ambient Day Color"); + FIX("Ambient Sunset Color"); + FIX("Ambient Night Color"); + FIX("Sun Sunrise Color"); + FIX("Sun Day Color"); + FIX("Sun Sunset Color"); + FIX("Sun Night Color"); + FIX("Sun Disc Sunset Color"); + FIX("Transition Delta"); + FIX("Land Fog Day Depth"); + FIX("Land Fog Night Depth"); + FIX("Clouds Maximum Percent"); + FIX("Wind Speed"); + FIX("Cloud Speed"); + FIX("Glare View"); + FIX("Cloud Texture"); + FIX("Ambient Loop Sound ID"); + FIX("Snow Threshold"); + FIX("Snow Diameter"); + FIX("Snow Height Min"); + FIX("Snow Height Max"); + FIX("Snow Entrance Speed"); + FIX("Max Snowflakes"); } category = "Weather Blizzard"; { - setting = "Sky Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Sky Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Sky Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Sky Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Fog Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Fog Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Fog Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Fog Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Ambient Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Ambient Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Ambient Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Ambient Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Sun Sunrise Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Sun Day Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Sun Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Sun Night Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Sun Disc Sunset Color"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Transition Delta"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Land Fog Day Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Land Fog Night Depth"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Clouds Maximum Percent"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Wind Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Cloud Speed"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Glare View"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Cloud Texture"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Ambient Loop Sound ID"; add_setting(category, setting, get_setting(category, setting, inx), ini); - setting = "Storm Threshold"; add_setting(category, setting, get_setting(category, setting, inx), ini); + FIX("Sky Sunrise Color"); + FIX("Sky Day Color"); + FIX("Sky Sunset Color"); + FIX("Sky Night Color"); + FIX("Fog Sunrise Color"); + FIX("Fog Day Color"); + FIX("Fog Sunset Color"); + FIX("Fog Night Color"); + FIX("Ambient Sunrise Color"); + FIX("Ambient Day Color"); + FIX("Ambient Sunset Color"); + FIX("Ambient Night Color"); + FIX("Sun Sunrise Color"); + FIX("Sun Day Color"); + FIX("Sun Sunset Color"); + FIX("Sun Night Color"); + FIX("Sun Disc Sunset Color"); + FIX("Transition Delta"); + FIX("Land Fog Day Depth"); + FIX("Land Fog Night Depth"); + FIX("Clouds Maximum Percent"); + FIX("Wind Speed"); + FIX("Cloud Speed"); + FIX("Glare View"); + FIX("Cloud Texture"); + FIX("Ambient Loop Sound ID"); + FIX("Storm Threshold"); } } @@ -216,7 +217,12 @@ namespace else { if(copy) - bfs::copy_file(dir->path(), to / dir->path().filename()); + { + bfs::path dest = to / dir->path().filename(); + if(bfs::exists(dest)) + bfs::remove_all(dest); + bfs::copy_file(dir->path(), dest); + } else bfs::rename(dir->path(), to / dir->path().filename()); } @@ -263,32 +269,53 @@ namespace strptime(time, "%d %B %Y", &tms); return mktime(&tms); } + + // Some cds have cab files which have the Data Files subfolders outside the Data Files folder + void install_dfiles_outside(const bfs::path& from, const bfs::path& dFiles) + { + bfs::path fonts = findFile(from, "fonts", false); + if(fonts.string() != "") + installToPath(fonts, dFiles / "Fonts"); + + bfs::path music = findFile(from, "music", false); + if(music.string() != "") + installToPath(music, dFiles / "Music"); + + bfs::path sound = findFile(from, "sound", false); + if(sound.string() != "") + installToPath(sound, dFiles / "Sound"); + + bfs::path splash = findFile(from, "splash", false); + if(splash.string() != "") + installToPath(splash, dFiles / "Splash"); + } + } -bool UnshieldThread::SetMorrowindPath(const std::string& path) +bool Launcher::UnshieldThread::SetMorrowindPath(const std::string& path) { mMorrowindPath = path; return true; } -bool UnshieldThread::SetTribunalPath(const std::string& path) +bool Launcher::UnshieldThread::SetTribunalPath(const std::string& path) { mTribunalPath = path; return true; } -bool UnshieldThread::SetBloodmoonPath(const std::string& path) +bool Launcher::UnshieldThread::SetBloodmoonPath(const std::string& path) { mBloodmoonPath = path; return true; } -void UnshieldThread::SetOutputPath(const std::string& path) +void Launcher::UnshieldThread::SetOutputPath(const std::string& path) { mOutputPath = path; } -bool UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, const char* prefix, int index) +bool Launcher::UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, const char* prefix, int index) { bool success; bfs::path dirname; @@ -322,7 +349,7 @@ bool UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, cons return success; } -void UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_dir, bool extract_ini) +void Launcher::UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_dir, bool extract_ini) { Unshield * unshield; unshield = unshield_open(cab.c_str()); @@ -342,7 +369,7 @@ void UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_d } -bool UnshieldThread::extract() +bool Launcher::UnshieldThread::extract() { bfs::path outputDataFilesDir = mOutputPath; outputDataFilesDir /= "Data Files"; @@ -360,6 +387,8 @@ bool UnshieldThread::extract() installToPath(dFilesDir, outputDataFilesDir); + install_dfiles_outside(mwExtractPath, outputDataFilesDir); + // Videos are often kept uncompressed on the cd bfs::path videosPath = findFile(mMorrowindPath.parent_path(), "video", false); if(videosPath.string() != "") @@ -394,6 +423,8 @@ bool UnshieldThread::extract() installToPath(dFilesDir, outputDataFilesDir); + install_dfiles_outside(tbExtractPath, outputDataFilesDir); + // Mt GOTY CD has Sounds in a seperate folder from the rest of the data files bfs::path soundsPath = findFile(tbExtractPath, "sounds", false); if(soundsPath.string() != "") @@ -421,6 +452,8 @@ bool UnshieldThread::extract() bfs::path dFilesDir = findFile(bmExtractPath, "bloodmoon.esm").parent_path(); installToPath(dFilesDir, outputDataFilesDir); + + install_dfiles_outside(bmExtractPath, outputDataFilesDir); // My GOTY CD contains a folder within cab files called Tribunal patch, // which contains Tribunal.esm @@ -442,7 +475,7 @@ bool UnshieldThread::extract() return true; } -void UnshieldThread::Done() +void Launcher::UnshieldThread::Done() { // Get rid of unnecessary files bfs::remove_all(mOutputPath / "extract-temp"); @@ -458,29 +491,30 @@ void UnshieldThread::Done() bfs::last_write_time(findFile(mOutputPath, "bloodmoon.esm"), getTime("3 June 2003")); } -std::string UnshieldThread::GetMWEsmPath() +std::string Launcher::UnshieldThread::GetMWEsmPath() { return findFile(mOutputPath / "Data Files", "morrowind.esm").string(); } -bool UnshieldThread::TribunalDone() +bool Launcher::UnshieldThread::TribunalDone() { return mTribunalDone; } -bool UnshieldThread::BloodmoonDone() +bool Launcher::UnshieldThread::BloodmoonDone() { return mBloodmoonDone; } -void UnshieldThread::run() +void Launcher::UnshieldThread::run() { extract(); emit close(); } -UnshieldThread::UnshieldThread() +Launcher::UnshieldThread::UnshieldThread() { + unshield_set_log_level(0); mMorrowindDone = false; mTribunalDone = false; mBloodmoonDone = false; diff --git a/apps/launcher/unshieldthread.hpp b/apps/launcher/unshieldthread.hpp index b48d3d9878..de6a32b442 100644 --- a/apps/launcher/unshieldthread.hpp +++ b/apps/launcher/unshieldthread.hpp @@ -7,51 +7,52 @@ #include <libunshield.h> - -class UnshieldThread : public QThread +namespace Launcher { - Q_OBJECT + class UnshieldThread : public QThread + { + Q_OBJECT + + public: + bool SetMorrowindPath(const std::string& path); + bool SetTribunalPath(const std::string& path); + bool SetBloodmoonPath(const std::string& path); - public: - bool SetMorrowindPath(const std::string& path); - bool SetTribunalPath(const std::string& path); - bool SetBloodmoonPath(const std::string& path); + void SetOutputPath(const std::string& path); - void SetOutputPath(const std::string& path); - - bool extract(); + bool extract(); - bool TribunalDone(); - bool BloodmoonDone(); + bool TribunalDone(); + bool BloodmoonDone(); - void Done(); + void Done(); - std::string GetMWEsmPath(); + std::string GetMWEsmPath(); - UnshieldThread(); + UnshieldThread(); - private: + private: - void extract_cab(const boost::filesystem::path& cab, const boost::filesystem::path& output_dir, bool extract_ini = false); - bool extract_file(Unshield* unshield, boost::filesystem::path output_dir, const char* prefix, int index); - - boost::filesystem::path mMorrowindPath; - boost::filesystem::path mTribunalPath; - boost::filesystem::path mBloodmoonPath; + void extract_cab(const boost::filesystem::path& cab, const boost::filesystem::path& output_dir, bool extract_ini = false); + bool extract_file(Unshield* unshield, boost::filesystem::path output_dir, const char* prefix, int index); - bool mMorrowindDone; - bool mTribunalDone; - bool mBloodmoonDone; + boost::filesystem::path mMorrowindPath; + boost::filesystem::path mTribunalPath; + boost::filesystem::path mBloodmoonPath; - boost::filesystem::path mOutputPath; + bool mMorrowindDone; + bool mTribunalDone; + bool mBloodmoonDone; + boost::filesystem::path mOutputPath; - protected: - virtual void run(); - signals: - void signalGUI(QString); - void close(); -}; + protected: + virtual void run(); + signals: + void signalGUI(QString); + void close(); + }; +} #endif diff --git a/apps/launcher/utils/checkablemessagebox.cpp b/apps/launcher/utils/checkablemessagebox.cpp index 41207a8ded..2f775af57a 100644 --- a/apps/launcher/utils/checkablemessagebox.cpp +++ b/apps/launcher/utils/checkablemessagebox.cpp @@ -54,72 +54,61 @@ Emulates the QMessageBox API with static conveniences. The message label can open external URLs. */ - -class CheckableMessageBoxPrivate -{ -public: - CheckableMessageBoxPrivate(QDialog *q) +Launcher::CheckableMessageBoxPrivate::CheckableMessageBoxPrivate(QDialog *q) : clickedButton(0) - { - QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); - - pixmapLabel = new QLabel(q); - sizePolicy.setHorizontalStretch(0); - sizePolicy.setVerticalStretch(0); - sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth()); - pixmapLabel->setSizePolicy(sizePolicy); - pixmapLabel->setVisible(false); - - QSpacerItem *pixmapSpacer = - new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); - - messageLabel = new QLabel(q); - messageLabel->setMinimumSize(QSize(300, 0)); - messageLabel->setWordWrap(true); - messageLabel->setOpenExternalLinks(true); - messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse); - - QSpacerItem *checkBoxRightSpacer = - new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); - QSpacerItem *buttonSpacer = - new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum); - - checkBox = new QCheckBox(q); - checkBox->setText(CheckableMessageBox::tr("Do not ask again")); - - buttonBox = new QDialogButtonBox(q); - buttonBox->setOrientation(Qt::Horizontal); - buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); - - QVBoxLayout *verticalLayout = new QVBoxLayout(); - verticalLayout->addWidget(pixmapLabel); - verticalLayout->addItem(pixmapSpacer); - - QHBoxLayout *horizontalLayout_2 = new QHBoxLayout(); - horizontalLayout_2->addLayout(verticalLayout); - horizontalLayout_2->addWidget(messageLabel); - - QHBoxLayout *horizontalLayout = new QHBoxLayout(); - horizontalLayout->addWidget(checkBox); - horizontalLayout->addItem(checkBoxRightSpacer); - - QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q); - verticalLayout_2->addLayout(horizontalLayout_2); - verticalLayout_2->addLayout(horizontalLayout); - verticalLayout_2->addItem(buttonSpacer); - verticalLayout_2->addWidget(buttonBox); - } - - QLabel *pixmapLabel; - QLabel *messageLabel; - QCheckBox *checkBox; - QDialogButtonBox *buttonBox; - QAbstractButton *clickedButton; -}; +{ + QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + + pixmapLabel = new QLabel(q); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth()); + pixmapLabel->setSizePolicy(sizePolicy); + pixmapLabel->setVisible(false); + + QSpacerItem *pixmapSpacer = + new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); + + messageLabel = new QLabel(q); + messageLabel->setMinimumSize(QSize(300, 0)); + messageLabel->setWordWrap(true); + messageLabel->setOpenExternalLinks(true); + messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse); + + QSpacerItem *checkBoxRightSpacer = + new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); + QSpacerItem *buttonSpacer = + new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum); + + checkBox = new QCheckBox(q); + checkBox->setText(Launcher::CheckableMessageBox::tr("Do not ask again")); + + buttonBox = new QDialogButtonBox(q); + buttonBox->setOrientation(Qt::Horizontal); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + + QVBoxLayout *verticalLayout = new QVBoxLayout(); + verticalLayout->addWidget(pixmapLabel); + verticalLayout->addItem(pixmapSpacer); + + QHBoxLayout *horizontalLayout_2 = new QHBoxLayout(); + horizontalLayout_2->addLayout(verticalLayout); + horizontalLayout_2->addWidget(messageLabel); + + QHBoxLayout *horizontalLayout = new QHBoxLayout(); + horizontalLayout->addWidget(checkBox); + horizontalLayout->addItem(checkBoxRightSpacer); + + QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q); + verticalLayout_2->addLayout(horizontalLayout_2); + verticalLayout_2->addLayout(horizontalLayout); + verticalLayout_2->addItem(buttonSpacer); + verticalLayout_2->addWidget(buttonBox); +} -CheckableMessageBox::CheckableMessageBox(QWidget *parent) : +Launcher::CheckableMessageBox::CheckableMessageBox(QWidget *parent) : QDialog(parent), - d(new CheckableMessageBoxPrivate(this)) + d(new Launcher::CheckableMessageBoxPrivate(this)) { setModal(true); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); @@ -129,102 +118,102 @@ CheckableMessageBox::CheckableMessageBox(QWidget *parent) : SLOT(slotClicked(QAbstractButton*))); } -CheckableMessageBox::~CheckableMessageBox() +Launcher::CheckableMessageBox::~CheckableMessageBox() { delete d; } -void CheckableMessageBox::slotClicked(QAbstractButton *b) +void Launcher::CheckableMessageBox::slotClicked(QAbstractButton *b) { d->clickedButton = b; } -QAbstractButton *CheckableMessageBox::clickedButton() const +QAbstractButton *Launcher::CheckableMessageBox::clickedButton() const { return d->clickedButton; } -QDialogButtonBox::StandardButton CheckableMessageBox::clickedStandardButton() const +QDialogButtonBox::StandardButton Launcher::CheckableMessageBox::clickedStandardButton() const { if (d->clickedButton) return d->buttonBox->standardButton(d->clickedButton); return QDialogButtonBox::NoButton; } -QString CheckableMessageBox::text() const +QString Launcher::CheckableMessageBox::text() const { return d->messageLabel->text(); } -void CheckableMessageBox::setText(const QString &t) +void Launcher::CheckableMessageBox::setText(const QString &t) { d->messageLabel->setText(t); } -QPixmap CheckableMessageBox::iconPixmap() const +QPixmap Launcher::CheckableMessageBox::iconPixmap() const { if (const QPixmap *p = d->pixmapLabel->pixmap()) return QPixmap(*p); return QPixmap(); } -void CheckableMessageBox::setIconPixmap(const QPixmap &p) +void Launcher::CheckableMessageBox::setIconPixmap(const QPixmap &p) { d->pixmapLabel->setPixmap(p); d->pixmapLabel->setVisible(!p.isNull()); } -bool CheckableMessageBox::isChecked() const +bool Launcher::CheckableMessageBox::isChecked() const { return d->checkBox->isChecked(); } -void CheckableMessageBox::setChecked(bool s) +void Launcher::CheckableMessageBox::setChecked(bool s) { d->checkBox->setChecked(s); } -QString CheckableMessageBox::checkBoxText() const +QString Launcher::CheckableMessageBox::checkBoxText() const { return d->checkBox->text(); } -void CheckableMessageBox::setCheckBoxText(const QString &t) +void Launcher::CheckableMessageBox::setCheckBoxText(const QString &t) { d->checkBox->setText(t); } -bool CheckableMessageBox::isCheckBoxVisible() const +bool Launcher::CheckableMessageBox::isCheckBoxVisible() const { return d->checkBox->isVisible(); } -void CheckableMessageBox::setCheckBoxVisible(bool v) +void Launcher::CheckableMessageBox::setCheckBoxVisible(bool v) { d->checkBox->setVisible(v); } -QDialogButtonBox::StandardButtons CheckableMessageBox::standardButtons() const +QDialogButtonBox::StandardButtons Launcher::CheckableMessageBox::standardButtons() const { return d->buttonBox->standardButtons(); } -void CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s) +void Launcher::CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s) { d->buttonBox->setStandardButtons(s); } -QPushButton *CheckableMessageBox::button(QDialogButtonBox::StandardButton b) const +QPushButton *Launcher::CheckableMessageBox::button(QDialogButtonBox::StandardButton b) const { return d->buttonBox->button(b); } -QPushButton *CheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role) +QPushButton *Launcher::CheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role) { return d->buttonBox->addButton(text, role); } -QDialogButtonBox::StandardButton CheckableMessageBox::defaultButton() const +QDialogButtonBox::StandardButton Launcher::CheckableMessageBox::defaultButton() const { foreach (QAbstractButton *b, d->buttonBox->buttons()) if (QPushButton *pb = qobject_cast<QPushButton *>(b)) @@ -233,7 +222,7 @@ QDialogButtonBox::StandardButton CheckableMessageBox::defaultButton() const return QDialogButtonBox::NoButton; } -void CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s) +void Launcher::CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s) { if (QPushButton *b = d->buttonBox->button(s)) { b->setDefault(true); @@ -242,7 +231,7 @@ void CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s) } QDialogButtonBox::StandardButton -CheckableMessageBox::question(QWidget *parent, +Launcher::CheckableMessageBox::question(QWidget *parent, const QString &title, const QString &question, const QString &checkBoxText, @@ -263,7 +252,7 @@ CheckableMessageBox::question(QWidget *parent, return mb.clickedStandardButton(); } -QMessageBox::StandardButton CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db) +QMessageBox::StandardButton Launcher::CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db) { return static_cast<QMessageBox::StandardButton>(int(db)); } diff --git a/apps/launcher/utils/checkablemessagebox.hpp b/apps/launcher/utils/checkablemessagebox.hpp index 93fd43fe1f..09a501b9c2 100644 --- a/apps/launcher/utils/checkablemessagebox.hpp +++ b/apps/launcher/utils/checkablemessagebox.hpp @@ -34,67 +34,83 @@ #include <QMessageBox> #include <QDialog> -class CheckableMessageBoxPrivate; +class QCheckBox; -class CheckableMessageBox : public QDialog +namespace Launcher { - Q_OBJECT - Q_PROPERTY(QString text READ text WRITE setText) - Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap) - Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked) - Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText) - Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons) - Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton) - -public: - explicit CheckableMessageBox(QWidget *parent); - virtual ~CheckableMessageBox(); - - static QDialogButtonBox::StandardButton - question(QWidget *parent, - const QString &title, - const QString &question, - const QString &checkBoxText, - bool *checkBoxSetting, - QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No, - QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No); - - QString text() const; - void setText(const QString &); - - bool isChecked() const; - void setChecked(bool s); - - QString checkBoxText() const; - void setCheckBoxText(const QString &); - - bool isCheckBoxVisible() const; - void setCheckBoxVisible(bool); - - QDialogButtonBox::StandardButtons standardButtons() const; - void setStandardButtons(QDialogButtonBox::StandardButtons s); - QPushButton *button(QDialogButtonBox::StandardButton b) const; - QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role); - - QDialogButtonBox::StandardButton defaultButton() const; - void setDefaultButton(QDialogButtonBox::StandardButton s); - - // See static QMessageBox::standardPixmap() - QPixmap iconPixmap() const; - void setIconPixmap (const QPixmap &p); - - // Query the result - QAbstractButton *clickedButton() const; - QDialogButtonBox::StandardButton clickedStandardButton() const; - - // Conversion convenience - static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton); - -private slots: - void slotClicked(QAbstractButton *b); - -private: - CheckableMessageBoxPrivate *d; -}; - + class CheckableMessageBoxPrivate + { + public: + + QLabel *pixmapLabel; + QLabel *messageLabel; + QCheckBox *checkBox; + QDialogButtonBox *buttonBox; + QAbstractButton *clickedButton; + + public: + CheckableMessageBoxPrivate(QDialog *q); + }; + + class CheckableMessageBox : public QDialog + { + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap) + Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked) + Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText) + Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons) + Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton) + + public: + explicit CheckableMessageBox(QWidget *parent); + virtual ~CheckableMessageBox(); + + static QDialogButtonBox::StandardButton + question(QWidget *parent, + const QString &title, + const QString &question, + const QString &checkBoxText, + bool *checkBoxSetting, + QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No, + QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No); + + QString text() const; + void setText(const QString &); + + bool isChecked() const; + void setChecked(bool s); + + QString checkBoxText() const; + void setCheckBoxText(const QString &); + + bool isCheckBoxVisible() const; + void setCheckBoxVisible(bool); + + QDialogButtonBox::StandardButtons standardButtons() const; + void setStandardButtons(QDialogButtonBox::StandardButtons s); + QPushButton *button(QDialogButtonBox::StandardButton b) const; + QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role); + + QDialogButtonBox::StandardButton defaultButton() const; + void setDefaultButton(QDialogButtonBox::StandardButton s); + + // See static QMessageBox::standardPixmap() + QPixmap iconPixmap() const; + void setIconPixmap (const QPixmap &p); + + // Query the result + QAbstractButton *clickedButton() const; + QDialogButtonBox::StandardButton clickedStandardButton() const; + + // Conversion convenience + static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton); + + private slots: + void slotClicked(QAbstractButton *b); + + private: + CheckableMessageBoxPrivate *d; + }; +} #endif // CHECKABLEMESSAGEBOX_HPP diff --git a/components/fileorderlist/utils/lineedit.cpp b/apps/launcher/utils/lineedit.cpp index b0f3395897..3487075808 100644 --- a/components/fileorderlist/utils/lineedit.cpp +++ b/apps/launcher/utils/lineedit.cpp @@ -1,11 +1,13 @@ -#include <QToolButton> -#include <QStyle> - #include "lineedit.hpp" LineEdit::LineEdit(QWidget *parent) : QLineEdit(parent) { + setupClearButton(); +} + +void LineEdit::setupClearButton() +{ mClearButton = new QToolButton(this); QPixmap pixmap(":images/clear.png"); mClearButton->setIcon(QIcon(pixmap)); @@ -15,13 +17,6 @@ LineEdit::LineEdit(QWidget *parent) mClearButton->hide(); connect(mClearButton, SIGNAL(clicked()), this, SLOT(clear())); connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateClearButton(const QString&))); - int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); - - setObjectName(QString("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 *) diff --git a/components/fileorderlist/utils/lineedit.hpp b/apps/launcher/utils/lineedit.hpp index 14bd7b1b4c..2dd7da32bd 100644 --- a/components/fileorderlist/utils/lineedit.hpp +++ b/apps/launcher/utils/lineedit.hpp @@ -11,6 +11,9 @@ #define LINEEDIT_H #include <QLineEdit> +#include <QStyle> +#include <QStylePainter> +#include <QToolButton> class QToolButton; @@ -18,6 +21,8 @@ class LineEdit : public QLineEdit { Q_OBJECT + QString mPlaceholderText; + public: LineEdit(QWidget *parent = 0); @@ -27,8 +32,10 @@ protected: private slots: void updateClearButton(const QString &text); -private: +protected: QToolButton *mClearButton; + + void setupClearButton(); }; #endif // LIENEDIT_H diff --git a/components/fileorderlist/utils/profilescombobox.cpp b/apps/launcher/utils/profilescombobox.cpp index c3ff953ae0..c143307249 100644 --- a/components/fileorderlist/utils/profilescombobox.cpp +++ b/apps/launcher/utils/profilescombobox.cpp @@ -5,18 +5,12 @@ #include <QKeyEvent> #include "profilescombobox.hpp" -#include "comboboxlineedit.hpp" ProfilesComboBox::ProfilesComboBox(QWidget *parent) : - QComboBox(parent) + ContentSelectorView::ComboBox(parent) { - mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore - setEditEnabled(true); - setValidator(mValidator); - setCompleter(0); - - connect(this, SIGNAL(currentIndexChanged(int)), this, - SLOT(slotIndexChanged(int))); + connect(this, SIGNAL(activated(int)), this, + SLOT(slotIndexChangedByUser(int))); setInsertPolicy(QComboBox::NoInsert); } @@ -37,6 +31,7 @@ void ProfilesComboBox::setEditEnabled(bool editable) setValidator(mValidator); ComboBoxLineEdit *edit = new ComboBoxLineEdit(this); + setLineEdit(edit); setCompleter(0); @@ -45,6 +40,9 @@ void ProfilesComboBox::setEditEnabled(bool editable) connect(lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString))); + + connect (lineEdit(), SIGNAL(textChanged(QString)), this, + SIGNAL (signalProfileTextChanged (QString))); } void ProfilesComboBox::slotTextChanged(const QString &text) @@ -82,11 +80,20 @@ void ProfilesComboBox::slotEditingFinished() emit(profileRenamed(previous, current)); } -void ProfilesComboBox::slotIndexChanged(int index) +void ProfilesComboBox::slotIndexChangedByUser(int index) { if (index == -1) return; - emit(profileChanged(mOldProfile, currentText())); - mOldProfile = itemText(index); + emit (signalProfileChanged(mOldProfile, currentText())); + 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)); } diff --git a/apps/launcher/utils/profilescombobox.hpp b/apps/launcher/utils/profilescombobox.hpp new file mode 100644 index 0000000000..7b83c41b2f --- /dev/null +++ b/apps/launcher/utils/profilescombobox.hpp @@ -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 ¤t); + void signalProfileChanged(int index); + void profileRenamed(const QString &oldName, const QString &newName); + +private slots: + + void slotEditingFinished(); + void slotIndexChangedByUser(int index); + void slotTextChanged(const QString &text); + +private: + QString mOldProfile; +}; +#endif // PROFILESCOMBOBOX_HPP diff --git a/apps/launcher/utils/textinputdialog.cpp b/apps/launcher/utils/textinputdialog.cpp index a4b36b95ea..76cbe32d01 100644 --- a/apps/launcher/utils/textinputdialog.cpp +++ b/apps/launcher/utils/textinputdialog.cpp @@ -7,19 +7,18 @@ #include <QValidator> #include <QLabel> -#include <components/fileorderlist/utils/lineedit.hpp> - -TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) : +Launcher::TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) : QDialog(parent) { setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); mButtonBox = new QDialogButtonBox(this); mButtonBox->addButton(QDialogButtonBox::Ok); mButtonBox->addButton(QDialogButtonBox::Cancel); + mButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false); // Line edit QValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore - mLineEdit = new LineEdit(this); + mLineEdit = new DialogLineEdit(this); mLineEdit->setValidator(validator); mLineEdit->setCompleter(0); @@ -38,34 +37,51 @@ TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWid Q_UNUSED(title); #endif - setOkButtonEnabled(false); setModal(true); connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject())); + connect(mLineEdit, SIGNAL(textChanged(QString)), this, SLOT(slotUpdateOkButton(QString))); } -int TextInputDialog::exec() +int Launcher::TextInputDialog::exec() { mLineEdit->clear(); mLineEdit->setFocus(); return QDialog::exec(); } -void TextInputDialog::setOkButtonEnabled(bool enabled) +QString Launcher::TextInputDialog::getText() const { - QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok); - okButton->setEnabled(enabled); + return mLineEdit->text(); +} - QPalette *palette = new QPalette(); - palette->setColor(QPalette::Text,Qt::red); +void Launcher::TextInputDialog::slotUpdateOkButton(QString text) +{ + bool enabled = !(text.isEmpty()); + mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(enabled); - if (enabled) { + if (enabled) mLineEdit->setPalette(QApplication::palette()); - } else { + else + { // Existing profile name, make the text red + QPalette *palette = new QPalette(); + palette->setColor(QPalette::Text,Qt::red); mLineEdit->setPalette(*palette); } +} + +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)); } diff --git a/apps/launcher/utils/textinputdialog.hpp b/apps/launcher/utils/textinputdialog.hpp index cbb453ac83..bb01778be3 100644 --- a/apps/launcher/utils/textinputdialog.hpp +++ b/apps/launcher/utils/textinputdialog.hpp @@ -2,27 +2,39 @@ #define TEXTINPUTDIALOG_HPP #include <QDialog> -//#include "lineedit.hpp" + +#include "lineedit.hpp" class QDialogButtonBox; -class LineEdit; -class TextInputDialog : public QDialog +namespace Launcher { - Q_OBJECT -public: - explicit TextInputDialog(const QString& title, const QString &text, QWidget *parent = 0); - inline LineEdit *lineEdit() { return mLineEdit; } - void setOkButtonEnabled(bool enabled); + class TextInputDialog : public QDialog + { + Q_OBJECT + + 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: - QDialogButtonBox *mButtonBox; + private slots: + void slotUpdateOkButton(QString text); - -}; + }; +} #endif // TEXTINPUTDIALOG_HPP diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index 8732b3eab3..b8b7e4c9da 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -813,8 +813,7 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con } void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) const { - std::vector<std::string> esmFiles; - std::vector<std::string> espFiles; + std::vector<std::string> contentFiles; std::string baseGameFile("Game Files:GameFile"); std::string gameFile(""); @@ -832,29 +831,19 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) co std::string filetype(entry->substr(entry->length()-3)); Misc::StringUtils::toLower(filetype); - if(filetype.compare("esm") == 0) { - esmFiles.push_back(*entry); - } - else if(filetype.compare("esp") == 0) { - espFiles.push_back(*entry); + if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) { + contentFiles.push_back(*entry); } } gameFile = ""; } - cfg.erase("master"); - cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("master", 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>() ) ); + cfg.erase("content"); + cfg.insert( std::make_pair("content", std::vector<std::string>() ) ); - for(std::vector<std::string>::const_iterator it=espFiles.begin(); it!=espFiles.end(); ++it) { - cfg["plugin"].push_back(*it); + for(std::vector<std::string>::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) { + cfg["content"].push_back(*it); } } diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index fe0415ac08..f87650b331 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -5,11 +5,11 @@ opencs_units (. editor) set (CMAKE_BUILD_TYPE DEBUG) opencs_units (model/doc - document + document operation saving ) opencs_units_noqt (model/doc - documentmanager + documentmanager stage savingstate savingstages ) opencs_hdrs_noqt (model/doc @@ -18,12 +18,12 @@ opencs_hdrs_noqt (model/doc opencs_units (model/world - idtable idtableproxymodel regionmap + idtable idtableproxymodel regionmap data ) opencs_units_noqt (model/world - universalid data record commands columnbase scriptcontext cell refidcollection + universalid record commands columnbase scriptcontext cell refidcollection refidadapter refiddata refidadapterimp ref collectionbase refcollection columns ) @@ -33,17 +33,18 @@ opencs_hdrs_noqt (model/world opencs_units (model/tools - tools operation reportmodel + tools reportmodel ) opencs_units_noqt (model/tools - stage verifier mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck + mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck birthsigncheck spellcheck ) opencs_units (view/doc - viewmanager view operations operation subview startup filedialog + viewmanager view operations operation subview startup filedialog newgame + filewidget adjusterwidget ) @@ -58,13 +59,14 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator - cellcreator referenceablecreator referencecreator + cellcreator referenceablecreator referencecreator scenesubview scenetoolbar scenetool + scenetoolmode ) opencs_units_noqt (view/world dialoguesubview subviews - enumdelegate vartypedelegate recordstatusdelegate refidtypedelegate datadisplaydelegate - scripthighlighter idvalidator + enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate + scripthighlighter idvalidator dialoguecreator ) @@ -122,11 +124,13 @@ opencs_units (view/filter set (OPENCS_US ) -set (OPENCS_RES ../../files/opencs/resources.qrc - ../../files/launcher/launcher.qrc +set (OPENCS_RES ${CMAKE_SOURCE_DIR}/files/opencs/resources.qrc + ${CMAKE_SOURCE_DIR}/files/launcher/launcher.qrc ) -set (OPENCS_UI ../../files/ui/datafilespage.ui +set (OPENCS_UI + ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui + ${CMAKE_SOURCE_DIR}/files/ui/filedialog.ui ) source_group (opencs FILES ${OPENCS_SRC} ${OPENCS_HDR}) @@ -144,15 +148,46 @@ qt4_add_resources(OPENCS_RES_SRC ${OPENCS_RES}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) +if(APPLE) + set (OPENCS_MAC_ICON ${CMAKE_SOURCE_DIR}/files/mac/opencs.icns) +else() + set (OPENCS_MAC_ICON "") +endif(APPLE) + add_executable(opencs + MACOSX_BUNDLE ${OPENCS_SRC} ${OPENCS_UI_HDR} ${OPENCS_MOC_SRC} ${OPENCS_RES_SRC} + ${OPENCS_MAC_ICON} ) +if(APPLE) + set_target_properties(opencs PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${OpenMW_BINARY_DIR}" + OUTPUT_NAME "OpenCS" + MACOSX_BUNDLE_ICON_FILE "opencs.icns" + MACOSX_BUNDLE_BUNDLE_NAME "OpenCS" + MACOSX_BUNDLE_GUI_IDENTIFIER "org.openmw.opencs" + MACOSX_BUNDLE_SHORT_VERSION_STRING ${OPENMW_VERSION} + MACOSX_BUNDLE_BUNDLE_VERSION ${OPENMW_VERSION} + ) + + set_source_files_properties(${OPENCS_MAC_ICON} PROPERTIES + MACOSX_PACKAGE_LOCATION Resources) +endif(APPLE) + target_link_libraries(opencs ${Boost_LIBRARIES} ${QT_LIBRARIES} components ) + +if(DPKG_PROGRAM) + INSTALL(TARGETS opencs RUNTIME DESTINATION games COMPONENT opencs) +endif() + +if(APPLE) + INSTALL(TARGETS opencs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE) +endif() diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 36d4f9735c..e879cb25e8 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -4,64 +4,97 @@ #include <QApplication> #include <QLocalServer> #include <QLocalSocket> +#include <QMessageBox> #include "model/doc/document.hpp" #include "model/world/data.hpp" +#include <iostream> -CS::Editor::Editor() : mViewManager (mDocumentManager) +CS::Editor::Editor() + : mDocumentManager (mCfgMgr), mViewManager (mDocumentManager) { - mIpcServerName = "org.openmw.OpenCS"; + mIpcServerName = "org.openmw.OpenCS"; - connect (&mViewManager, SIGNAL (newDocumentRequest ()), this, SLOT (createDocument ())); + setupDataFiles(); + + mNewGame.setLocalData (mLocal); + mFileDialog.setLocalData (mLocal); + + connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ())); + connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ())); connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ())); + connect (&mViewManager, SIGNAL (editSettingsRequest()), this, SLOT (showSettings ())); - connect (&mStartup, SIGNAL (createDocument()), this, SLOT (createDocument ())); + connect (&mStartup, SIGNAL (createGame()), this, SLOT (createGame ())); + connect (&mStartup, SIGNAL (createAddon()), this, SLOT (createAddon ())); connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ())); + connect (&mStartup, SIGNAL (editConfig()), this, SLOT (showSettings ())); - connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles())); - connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile())); + connect (&mFileDialog, SIGNAL(signalOpenFiles (const boost::filesystem::path&)), + this, SLOT(openFiles (const boost::filesystem::path&))); - setupDataFiles(); + connect (&mFileDialog, SIGNAL(signalCreateNewFile (const boost::filesystem::path&)), + this, SLOT(createNewFile (const boost::filesystem::path&))); + + connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)), + this, SLOT (createNewGame (const boost::filesystem::path&))); } void CS::Editor::setupDataFiles() { 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() ("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken()) ("data-local", boost::program_options::value<std::string>()->default_value("")) ("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); mCfgMgr.readConfiguration(variables, desc); - Files::PathContainer mDataDirs, mDataLocal; + Files::PathContainer dataDirs, dataLocal; if (!variables["data"].empty()) { - mDataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>()); + dataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>()); } std::string local = variables["data-local"].as<std::string>(); if (!local.empty()) { - mDataLocal.push_back(Files::PathContainer::value_type(local)); + dataLocal.push_back(Files::PathContainer::value_type(local)); } - mCfgMgr.processPaths(mDataDirs); - mCfgMgr.processPaths(mDataLocal); + mCfgMgr.processPaths (dataDirs); + mCfgMgr.processPaths (dataLocal, true); + + if (!dataLocal.empty()) + mLocal = dataLocal[0]; + else + { + QMessageBox messageBox; + messageBox.setWindowTitle (tr ("No local data path available")); + messageBox.setIcon (QMessageBox::Critical); + messageBox.setStandardButtons (QMessageBox::Ok); + messageBox.setText(tr("<br><b>OpenCS is unable to access the local data directory. This may indicate a faulty configuration or a broken install.</b>")); + messageBox.exec(); + + QApplication::exit (1); + return; + } // Set the charset for reading the esm/esp files - QString encoding = QString::fromStdString(variables["encoding"].as<std::string>()); - mFileDialog.setEncoding(encoding); + // QString encoding = QString::fromStdString(variables["encoding"].as<std::string>()); + //mFileDialog.setEncoding(encoding); + + dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); - Files::PathContainer dataDirs; - dataDirs.insert(dataDirs.end(), mDataDirs.begin(), mDataDirs.end()); - dataDirs.insert(dataDirs.end(), mDataLocal.begin(), mDataLocal.end()); + mDocumentManager.setResourceDir (variables["resources"].as<std::string>()); for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) { + QString path = QString::fromStdString(iter->string()); mFileDialog.addFiles(path); } @@ -69,86 +102,116 @@ void CS::Editor::setupDataFiles() //load the settings into the userSettings instance. const QString settingFileName = "opencs.cfg"; CSMSettings::UserSettings::instance().loadSettings(settingFileName); - } -void CS::Editor::createDocument() +void CS::Editor::createGame() { mStartup.hide(); - mFileDialog.newFile(); + if (mNewGame.isHidden()) + mNewGame.show(); + + mNewGame.raise(); + mNewGame.activateWindow(); } -void CS::Editor::loadDocument() +void CS::Editor::createAddon() { mStartup.hide(); + mFileDialog.showDialog (CSVDoc::ContentAction_New); +} - mFileDialog.openFile(); +void CS::Editor::loadDocument() +{ + mStartup.hide(); + mFileDialog.showDialog (CSVDoc::ContentAction_Edit); } -void CS::Editor::openFiles() +void CS::Editor::openFiles (const boost::filesystem::path &savePath) { 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()); - } - CSMDoc::Document *document = mDocumentManager.addDocument(files, false); + CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, false); mViewManager.addView (document); mFileDialog.hide(); } -void CS::Editor::createNewFile() +void CS::Editor::createNewFile (const boost::filesystem::path &savePath) { 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(mFileDialog.fileName().toStdString()); + files.push_back(mFileDialog.filename().toStdString()); - CSMDoc::Document *document = mDocumentManager.addDocument (files, true); + CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, true); mViewManager.addView (document); mFileDialog.hide(); } +void CS::Editor::createNewGame (const boost::filesystem::path& file) +{ + std::vector<boost::filesystem::path> files; + + files.push_back (file); + + CSMDoc::Document *document = mDocumentManager.addDocument (files, file, true); + + mViewManager.addView (document); + + mNewGame.hide(); +} + void CS::Editor::showStartup() { - if(mStartup.isHidden()) - mStartup.show(); - mStartup.raise(); - mStartup.activateWindow(); + if(mStartup.isHidden()) + mStartup.show(); + mStartup.raise(); + mStartup.activateWindow(); +} + +void CS::Editor::showSettings() +{ + if (mSettings.isHidden()) + mSettings.show(); + + mSettings.raise(); + mSettings.activateWindow(); } bool CS::Editor::makeIPCServer() { - mServer = new QLocalServer(this); + mServer = new QLocalServer(this); - if(mServer->listen(mIpcServerName)) - { - connect(mServer, SIGNAL(newConnection()), this, SLOT(showStartup())); - return true; - } + if(mServer->listen(mIpcServerName)) + { + connect(mServer, SIGNAL(newConnection()), this, SLOT(showStartup())); + return true; + } - mServer->close(); - return false; + mServer->close(); + return false; } void CS::Editor::connectToIPCServer() { - mClientSocket = new QLocalSocket(this); - mClientSocket->connectToServer(mIpcServerName); - mClientSocket->close(); + mClientSocket = new QLocalSocket(this); + mClientSocket->connectToServer(mIpcServerName); + mClientSocket->close(); } int CS::Editor::run() { + if (mLocal.empty()) + return 1; + mStartup.show(); QApplication::setQuitOnLastWindowClosed (true); diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index 80336d66f8..930aa9d643 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -9,12 +9,16 @@ #ifndef Q_MOC_RUN #include <components/files/configurationmanager.hpp> #endif + +#include "model/settings/usersettings.hpp" #include "model/doc/documentmanager.hpp" #include "view/doc/viewmanager.hpp" #include "view/doc/startup.hpp" #include "view/doc/filedialog.hpp" -#include "model/settings/usersettings.hpp" +#include "view/doc/newgame.hpp" + +#include "view/settings/usersettingsdialog.hpp" namespace CS { @@ -22,13 +26,17 @@ namespace CS { Q_OBJECT + Files::ConfigurationManager mCfgMgr; CSMSettings::UserSettings mUserSettings; CSMDoc::DocumentManager mDocumentManager; CSVDoc::ViewManager mViewManager; CSVDoc::StartupDialogue mStartup; - FileDialog mFileDialog; + CSVDoc::NewGameDialogue mNewGame; + CSVSettings::UserSettingsDialog mSettings; + CSVDoc::FileDialog mFileDialog; + + boost::filesystem::path mLocal; - Files::ConfigurationManager mCfgMgr; void setupDataFiles(); // not implemented @@ -47,14 +55,18 @@ namespace CS private slots: - void createDocument(); + void createGame(); + void createAddon(); void loadDocument(); - void openFiles(); - void createNewFile(); + void openFiles (const boost::filesystem::path &path); + void createNewFile (const boost::filesystem::path& path); + void createNewGame (const boost::filesystem::path& file); void showStartup(); + void showSettings(); + private: QString mIpcServerName; diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index eddeb1983b..344a9360f6 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -7,6 +7,10 @@ #include <QApplication> #include <QIcon> +#ifdef Q_OS_MAC +#include <QDir> +#endif + class Application : public QApplication { private: @@ -35,14 +39,33 @@ int main(int argc, char *argv[]) Q_INIT_RESOURCE (resources); Application mApplication (argc, argv); +#ifdef Q_OS_MAC + QDir dir(QCoreApplication::applicationDirPath()); + if (dir.dirName() == "MacOS") { + dir.cdUp(); + dir.cdUp(); + dir.cdUp(); + } + QDir::setCurrent(dir.absolutePath()); + + // force Qt to load only LOCAL plugins, don't touch system Qt installation + QDir pluginsPath(QCoreApplication::applicationDirPath()); + pluginsPath.cdUp(); + pluginsPath.cd("Plugins"); + + QStringList libraryPaths; + libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath(); + mApplication.setLibraryPaths(libraryPaths); +#endif + mApplication.setWindowIcon (QIcon (":./opencs.png")); CS::Editor editor; if(!editor.makeIPCServer()) { - editor.connectToIPCServer(); - return 0; + editor.connectToIPCServer(); + // return 0; } return editor.run(); diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 30e9c21d12..27f4f498a4 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -1,8 +1,15 @@ #include "document.hpp" + #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, - 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); @@ -12,10 +19,10 @@ void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_i --end2; for (std::vector<boost::filesystem::path>::const_iterator iter (begin); iter!=end2; ++iter) - getData().loadFile (*iter, true); + getData().loadFile (*iter, true, false); if (lastAsModified) - getData().loadFile (*end2, false); + getData().loadFile (*end2, false, false); } void CSMDoc::Document::addGmsts() @@ -2058,9 +2065,9 @@ void CSMDoc::Document::addOptionalGlobals() { static const char *sGlobals[] = { - "dayspassed", - "pcwerewolf", - "pcyear", + "DaysPassed", + "PCWerewolf", + "PCYear", 0 }; @@ -2137,20 +2144,90 @@ void CSMDoc::Document::createBase() getData().getSkills().add (record); } + + static const char *sVoice[] = + { + "Intruder", + "Attack", + "Hello", + "Thief", + "Alarm", + "Idle", + "Flee", + "Hit", + 0 + }; + + for (int i=0; sVoice[i]; ++i) + { + ESM::Dialogue record; + record.mId = sVoice[i]; + record.mType = ESM::Dialogue::Voice; + record.blank(); + + getData().getTopics().add (record); + } + + static const char *sGreetings[] = + { + "Greeting 0", + "Greeting 1", + "Greeting 2", + "Greeting 3", + "Greeting 4", + "Greeting 5", + "Greeting 6", + "Greeting 7", + "Greeting 8", + "Greeting 9", + 0 + }; + + for (int i=0; sGreetings[i]; ++i) + { + ESM::Dialogue record; + record.mId = sGreetings[i]; + record.mType = ESM::Dialogue::Greeting; + record.blank(); + + getData().getTopics().add (record); + } + + static const char *sPersuasion[] = + { + "Intimidate Success", + "Intimidate Fail", + "Service Refusal", + "Admire Success", + "Taunt Success", + "Bribe Success", + "Info Refusal", + "Admire Fail", + "Taunt Fail", + "Bribe Fail", + 0 + }; + + for (int i=0; sPersuasion[i]; ++i) + { + ESM::Dialogue record; + record.mId = sPersuasion[i]; + record.mType = ESM::Dialogue::Persuasion; + record.blank(); + + getData().getTopics().add (record); + } } -CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files, bool new_) -: mTools (mData) +CSMDoc::Document::Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_) + : mSavePath (savePath), mContentFiles (files), mTools (mData), mResDir(resDir), + mProjectPath ((configuration.getUserPath() / "projects") / + (savePath.filename().string() + ".project")), + mSaving (*this, mProjectPath) { if (files.empty()) throw std::runtime_error ("Empty content file sequence"); - /// \todo adjust last file name: - /// \li make sure it is located in the data-local directory (adjust path if necessary) - /// \li make sure the extension matches the new scheme (change it if necesarry) - - mName = files.back().filename().string(); - if (new_ && files.size()==1) createBase(); else @@ -2163,6 +2240,44 @@ CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files, b load (files.begin(), end, !new_); } + if (new_) + { + mData.setDescription (""); + mData.setAuthor (""); + } + + bool filtersFound = false; + + if (boost::filesystem::exists (mProjectPath)) + { + filtersFound = true; + } + else + { + boost::filesystem::path locCustomFiltersPath (configuration.getUserPath()); + locCustomFiltersPath /= "defaultfilters"; + + if (boost::filesystem::exists(locCustomFiltersPath)) + { + boost::filesystem::copy_file (locCustomFiltersPath, mProjectPath); + filtersFound = true; + } + else + { + boost::filesystem::path filters(mResDir); + filters /= "defaultfilters"; + + if (boost::filesystem::exists(filters)) + { + boost::filesystem::copy_file(filters, mProjectPath); + filtersFound = true; + } + } + } + + if (filtersFound) + getData().loadFile (mProjectPath, false, true); + addOptionalGmsts(); addOptionalGlobals(); @@ -2171,9 +2286,10 @@ CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files, b connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); connect (&mTools, SIGNAL (done (int)), this, SLOT (operationDone (int))); - // dummy implementation -> remove when proper save is implemented. - mSaveCount = 0; - connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving())); + connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); + connect (&mSaving, SIGNAL (done (int)), this, SLOT (operationDone (int))); + connect (&mSaving, SIGNAL (reportMessage (const QString&, int)), + this, SLOT (reportMessage (const QString&, int))); } CSMDoc::Document::~Document() @@ -2192,7 +2308,7 @@ int CSMDoc::Document::getState() const if (!mUndoStack.isClean()) state |= State_Modified; - if (mSaveCount) + if (mSaving.isRunning()) state |= State_Locked | State_Saving | State_Operation; if (int operations = mTools.getRunningOperations()) @@ -2201,17 +2317,25 @@ int CSMDoc::Document::getState() const return state; } -const std::string& CSMDoc::Document::getName() const +const boost::filesystem::path& CSMDoc::Document::getSavePath() const { - return mName; + return mSavePath; +} + +const std::vector<boost::filesystem::path>& CSMDoc::Document::getContentFiles() const +{ + return mContentFiles; } void CSMDoc::Document::save() { - mSaveCount = 1; - mSaveTimer.start (500); + if (mSaving.isRunning()) + throw std::logic_error ( + "Failed to initiate save, because a save operation is already running."); + + mSaving.start(); + emit stateChanged (getState(), this); - emit progress (1, 16, State_Saving, 1, this); } CSMWorld::UniversalId CSMDoc::Document::verify() @@ -2223,44 +2347,26 @@ CSMWorld::UniversalId CSMDoc::Document::verify() void CSMDoc::Document::abortOperation (int type) { - mTools.abortOperation (type); - if (type==State_Saving) - { - mSaveCount=0; - mSaveTimer.stop(); - emit stateChanged (getState(), this); - } + mSaving.abort(); + else + mTools.abortOperation (type); } - void CSMDoc::Document::modificationStateChanged (bool clean) { emit stateChanged (getState(), this); } - -void CSMDoc::Document::operationDone (int type) +void CSMDoc::Document::reportMessage (const QString& message, 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 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); - } + emit stateChanged (getState(), this); } const CSMWorld::Data& CSMDoc::Document::getData() const diff --git a/apps/opencs/model/doc/document.hpp b/apps/opencs/model/doc/document.hpp index 1c6d9db2a9..437b0c5131 100644 --- a/apps/opencs/model/doc/document.hpp +++ b/apps/opencs/model/doc/document.hpp @@ -14,6 +14,7 @@ #include "../tools/tools.hpp" #include "state.hpp" +#include "saving.hpp" class QAbstractItemModel; @@ -23,6 +24,11 @@ namespace ESM struct Global; } +namespace Files +{ + class ConfigurationManager; +} + namespace CSMDoc { class Document : public QObject @@ -31,17 +37,18 @@ namespace CSMDoc private: - std::string mName; ///< \todo replace name with ESX list + boost::filesystem::path mSavePath; + std::vector<boost::filesystem::path> mContentFiles; CSMWorld::Data mData; CSMTools::Tools mTools; + boost::filesystem::path mProjectPath; + Saving mSaving; + boost::filesystem::path mResDir; // It is important that the undo stack is declared last, because on desctruction it fires a signal, that is connected to a slot, that is // using other member variables. Unfortunately this connection is cut only in the QObject destructor, which is way too late. QUndoStack mUndoStack; - int mSaveCount; ///< dummy implementation -> remove when proper save is implemented. - QTimer mSaveTimer; ///< dummy implementation -> remove when proper save is implemented. - // not implemented Document (const Document&); Document& operator= (const Document&); @@ -64,15 +71,19 @@ namespace CSMDoc public: - Document (const std::vector<boost::filesystem::path>& files, bool new_); + Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_); + ~Document(); QUndoStack& getUndoStack(); int getState() const; - const std::string& getName() const; - ///< \todo replace with ESX list + 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(); @@ -97,10 +108,9 @@ namespace CSMDoc void modificationStateChanged (bool clean); - void operationDone (int type); + void reportMessage (const QString& message, int type); - void saving(); - ///< dummy implementation -> remove when proper save is implemented. + void operationDone (int type); public slots: @@ -109,3 +119,4 @@ namespace CSMDoc } #endif + diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp index 740c0b5827..024c46beae 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -4,9 +4,22 @@ #include <algorithm> #include <stdexcept> +#include <boost/filesystem.hpp> + +#ifndef Q_MOC_RUN +#include <components/files/configurationmanager.hpp> +#endif + #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() { @@ -14,10 +27,10 @@ CSMDoc::DocumentManager::~DocumentManager() delete *iter; } -CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, +CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files, const boost::filesystem::path& savePath, bool new_) { - Document *document = new Document (files, new_); + Document *document = new Document (mConfiguration, files, savePath, mResDir, new_); mDocuments.push_back (document); @@ -35,4 +48,9 @@ bool CSMDoc::DocumentManager::removeDocument (Document *document) delete document; return mDocuments.empty(); +} + +void CSMDoc::DocumentManager::setResourceDir (const boost::filesystem::path& parResDir) +{ + mResDir = boost::filesystem::system_complete(parResDir); }
\ No newline at end of file diff --git a/apps/opencs/model/doc/documentmanager.hpp b/apps/opencs/model/doc/documentmanager.hpp index a307b76a57..b80a186429 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -6,6 +6,11 @@ #include <boost/filesystem/path.hpp> +namespace Files +{ + class ConfigurationManager; +} + namespace CSMDoc { class Document; @@ -13,17 +18,18 @@ namespace CSMDoc class DocumentManager { std::vector<Document *> mDocuments; + const Files::ConfigurationManager& mConfiguration; DocumentManager (const DocumentManager&); DocumentManager& operator= (const DocumentManager&); public: - DocumentManager(); + DocumentManager (const Files::ConfigurationManager& configuration); ~DocumentManager(); - Document *addDocument (const std::vector<boost::filesystem::path>& files, bool new_); + Document *addDocument (const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, bool new_); ///< The ownership of the returned document is not transferred to the caller. /// /// \param new_ Do not load the last content file in \a files and instead create in an @@ -31,6 +37,10 @@ namespace CSMDoc bool removeDocument (Document *document); ///< \return last document removed? + void setResourceDir (const boost::filesystem::path& parResDir); + + private: + boost::filesystem::path mResDir; }; } diff --git a/apps/opencs/model/tools/operation.cpp b/apps/opencs/model/doc/operation.cpp index 71761cdaea..d29cc2631b 100644 --- a/apps/opencs/model/tools/operation.cpp +++ b/apps/opencs/model/doc/operation.cpp @@ -6,16 +6,16 @@ #include <QTimer> -#include "../doc/state.hpp" - +#include "state.hpp" #include "stage.hpp" -void CSMTools::Operation::prepareStages() +void CSMDoc::Operation::prepareStages() { mCurrentStage = mStages.begin(); mCurrentStep = 0; mCurrentStepTotal = 0; mTotalSteps = 0; + mError = false; 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) delete iter->first; } -void CSMTools::Operation::run() +void CSMDoc::Operation::run() { prepareStages(); QTimer timer; - timer.connect (&timer, SIGNAL (timeout()), this, SLOT (verify())); + timer.connect (&timer, SIGNAL (timeout()), this, SLOT (executeStage())); timer.start (0); exec(); } -void CSMTools::Operation::appendStage (Stage *stage) +void CSMDoc::Operation::appendStage (Stage *stage) { mStages.push_back (std::make_pair (stage, 0)); } -void CSMTools::Operation::abort() +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; @@ -68,7 +91,16 @@ void CSMTools::Operation::verify() } else { - mCurrentStage->first->perform (mCurrentStep++, messages); + try + { + mCurrentStage->first->perform (mCurrentStep++, messages); + } + catch (const std::exception& e) + { + emit reportMessage (e.what(), mType); + abort(); + } + ++mCurrentStepTotal; break; } @@ -81,4 +113,9 @@ void CSMTools::Operation::verify() if (mCurrentStage==mStages.end()) exit(); +} + +void CSMDoc::Operation::operationDone() +{ + emit done (mType); }
\ No newline at end of file diff --git a/apps/opencs/model/tools/operation.hpp b/apps/opencs/model/doc/operation.hpp index 4731c58fa8..316eda78fd 100644 --- a/apps/opencs/model/tools/operation.hpp +++ b/apps/opencs/model/doc/operation.hpp @@ -1,11 +1,11 @@ -#ifndef CSM_TOOLS_OPERATION_H -#define CSM_TOOLS_OPERATION_H +#ifndef CSM_DOC_OPERATION_H +#define CSM_DOC_OPERATION_H #include <vector> #include <QThread> -namespace CSMTools +namespace CSMDoc { class Stage; @@ -19,12 +19,17 @@ namespace CSMTools int mCurrentStep; int mCurrentStepTotal; int mTotalSteps; + int mOrdered; + bool mFinalAlways; + bool mError; void prepareStages(); 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(); @@ -35,19 +40,25 @@ namespace CSMTools /// /// \attention Do no call this function while this Operation is running. + bool hasError() const; + signals: void progress (int current, int max, int type); void reportMessage (const QString& message, int type); + void done (int type); + public slots: void abort(); private slots: - void verify(); + void executeStage(); + + void operationDone(); }; } diff --git a/apps/opencs/model/doc/saving.cpp b/apps/opencs/model/doc/saving.cpp new file mode 100644 index 0000000000..b756a9dc19 --- /dev/null +++ b/apps/opencs/model/doc/saving.cpp @@ -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)); +}
\ No newline at end of file diff --git a/apps/opencs/model/doc/saving.hpp b/apps/opencs/model/doc/saving.hpp new file mode 100644 index 0000000000..cd1bbef980 --- /dev/null +++ b/apps/opencs/model/doc/saving.hpp @@ -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 diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp new file mode 100644 index 0000000000..d68c723179 --- /dev/null +++ b/apps/opencs/model/doc/savingstages.cpp @@ -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(); + } +}
\ No newline at end of file diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp new file mode 100644 index 0000000000..ff94116fdb --- /dev/null +++ b/apps/opencs/model/doc/savingstages.hpp @@ -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 diff --git a/apps/opencs/model/doc/savingstate.cpp b/apps/opencs/model/doc/savingstate.cpp new file mode 100644 index 0000000000..4a1abb8883 --- /dev/null +++ b/apps/opencs/model/doc/savingstate.cpp @@ -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; +}
\ No newline at end of file diff --git a/apps/opencs/model/doc/savingstate.hpp b/apps/opencs/model/doc/savingstate.hpp new file mode 100644 index 0000000000..8cf7883e50 --- /dev/null +++ b/apps/opencs/model/doc/savingstate.hpp @@ -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
\ No newline at end of file diff --git a/apps/opencs/model/doc/stage.cpp b/apps/opencs/model/doc/stage.cpp new file mode 100644 index 0000000000..99b7657709 --- /dev/null +++ b/apps/opencs/model/doc/stage.cpp @@ -0,0 +1,4 @@ + +#include "stage.hpp" + +CSMDoc::Stage::~Stage() {}
\ No newline at end of file diff --git a/apps/opencs/model/tools/stage.hpp b/apps/opencs/model/doc/stage.hpp index 3020936f32..1f96c60b43 100644 --- a/apps/opencs/model/tools/stage.hpp +++ b/apps/opencs/model/doc/stage.hpp @@ -1,10 +1,10 @@ -#ifndef CSM_TOOLS_STAGE_H -#define CSM_TOOLS_STAGE_H +#ifndef CSM_DOC_STAGE_H +#define CSM_DOC_STAGE_H #include <vector> #include <string> -namespace CSMTools +namespace CSMDoc { class Stage { @@ -16,7 +16,7 @@ namespace CSMTools ///< \return number of steps 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. }; } diff --git a/apps/opencs/model/filter/parser.cpp b/apps/opencs/model/filter/parser.cpp index d334a7f634..8f4fcb70c9 100644 --- a/apps/opencs/model/filter/parser.cpp +++ b/apps/opencs/model/filter/parser.cpp @@ -49,19 +49,31 @@ namespace CSMFilter Token (Type type = Type_None); + Token (Type type, const std::string& string); + ///< Non-string type that can also be interpreted as a string. + Token (const std::string& string); Token (double number); operator bool() const; + + bool isString() const; }; Token::Token (Type type) : mType (type) {} + Token::Token (Type type, const std::string& string) : mType (type), mString (string) {} + Token::Token (const std::string& string) : mType (Type_String), mString (string) {} Token::Token (double number) : mType (Type_Number), mNumber (number) {} + bool Token::isString() const + { + return mType==Type_String || mType>=Type_Keyword_True; + } + Token::operator bool() const { return mType!=Type_None; @@ -120,7 +132,7 @@ CSMFilter::Token CSMFilter::Parser::getStringToken() } if (string[0]=='"') - string = string.substr (1, string.size()-2); + return string.substr (1, string.size()-2); } return checkKeywords (string); @@ -182,7 +194,7 @@ CSMFilter::Token CSMFilter::Parser::checkKeywords (const Token& token) for (int i=0; sKeywords[i]; ++i) if (sKeywords[i]==string || (string.size()==1 && sKeywords[i][0]==string[0])) - return Token (static_cast<Token::Type> (i+Token::Type_Keyword_True)); + return Token (static_cast<Token::Type> (i+Token::Type_Keyword_True), token.mString); return token; } @@ -351,7 +363,7 @@ boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseText() if (static_cast<int> (token.mNumber)==token.mNumber) columnId = static_cast<int> (token.mNumber); } - else if (token.mType==Token::Type_String) + else if (token.isString()) { columnId = CSMWorld::Columns::getId (token.mString); } @@ -373,7 +385,7 @@ boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseText() // parse text pattern token = getNextToken(); - if (token.mType!=Token::Type_String) + if (!token.isString()) { error(); return boost::shared_ptr<Node>(); @@ -415,7 +427,7 @@ boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseValue() if (static_cast<int> (token.mNumber)==token.mNumber) columnId = static_cast<int> (token.mNumber); } - else if (token.mType==Token::Type_String) + else if (token.isString()) { columnId = CSMWorld::Columns::getId (token.mString); } @@ -437,22 +449,22 @@ boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseValue() // parse value double lower = 0; double upper = 0; - bool min = false; - bool max = false; + ValueNode::Type lowerType = ValueNode::Type_Open; + ValueNode::Type upperType = ValueNode::Type_Open; token = getNextToken(); if (token.mType==Token::Type_Number) { // single value - min = max = true; lower = upper = token.mNumber; + lowerType = upperType = ValueNode::Type_Closed; } else { // interval if (token.mType==Token::Type_OpenSquare) - min = true; + lowerType = ValueNode::Type_Closed; else if (token.mType!=Token::Type_CloseSquare && token.mType!=Token::Type_Open) { error(); @@ -461,17 +473,23 @@ boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseValue() token = getNextToken(); - if (token.mType!=Token::Type_Number) + if (token.mType==Token::Type_Number) { - error(); - return boost::shared_ptr<Node>(); - } + lower = token.mNumber; - lower = token.mNumber; - - token = getNextToken(); + token = getNextToken(); - if (token.mType!=Token::Type_Comma) + if (token.mType!=Token::Type_Comma) + { + error(); + return boost::shared_ptr<Node>(); + } + } + else if (token.mType==Token::Type_Comma) + { + lowerType = ValueNode::Type_Infinite; + } + else { error(); return boost::shared_ptr<Node>(); @@ -479,18 +497,20 @@ boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseValue() token = getNextToken(); - if (token.mType!=Token::Type_Number) + if (token.mType==Token::Type_Number) { - error(); - return boost::shared_ptr<Node>(); - } + upper = token.mNumber; - upper = token.mNumber; - - token = getNextToken(); + token = getNextToken(); + } + else + upperType = ValueNode::Type_Infinite; if (token.mType==Token::Type_CloseSquare) - max = true; + { + if (upperType!=ValueNode::Type_Infinite) + upperType = ValueNode::Type_Closed; + } else if (token.mType!=Token::Type_OpenSquare && token.mType!=Token::Type_Close) { error(); @@ -506,7 +526,7 @@ boost::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseValue() return boost::shared_ptr<Node>(); } - return boost::shared_ptr<Node> (new ValueNode (columnId, lower, upper, min, max)); + return boost::shared_ptr<Node> (new ValueNode (columnId, lowerType, upperType, lower, upper)); } void CSMFilter::Parser::error() @@ -553,6 +573,8 @@ bool CSMFilter::Parser::parse (const std::string& filter, bool allowPredefined) return true; } + // We do not use isString() here, because there could be a pre-defined filter with an ID that is + // equal a filter keyword. else if (token.mType==Token::Type_String && allowPredefined) { if (getNextToken()!=Token (Token::Type_EOS)) diff --git a/apps/opencs/model/filter/textnode.cpp b/apps/opencs/model/filter/textnode.cpp index 9987c66d22..7d1a4845f6 100644 --- a/apps/opencs/model/filter/textnode.cpp +++ b/apps/opencs/model/filter/textnode.cpp @@ -28,13 +28,36 @@ bool CSMFilter::TextNode::test (const CSMWorld::IdTable& table, int row, QVariant data = table.data (index); - if (data.type()!=QVariant::String) + QString string; + + if (data.type()==QVariant::String) + { + string = data.toString(); + } + else if ((data.type()==QVariant::Int || data.type()==QVariant::UInt) && + CSMWorld::Columns::hasEnums (static_cast<CSMWorld::Columns::ColumnId> (mColumnId))) + { + int value = data.toInt(); + + std::vector<std::string> enums = + CSMWorld::Columns::getEnums (static_cast<CSMWorld::Columns::ColumnId> (mColumnId)); + + if (value>=0 && value<static_cast<int> (enums.size())) + string = QString::fromUtf8 (enums[value].c_str()); + } + else if (data.type()==QVariant::Bool) + { + string = data.toBool() ? "true" : "false"; + } + else if (mText.empty() && !data.isValid()) + return true; + else return false; /// \todo make pattern syntax configurable QRegExp regExp (QString::fromUtf8 (mText.c_str()), Qt::CaseInsensitive); - return regExp.exactMatch (data.toString()); + return regExp.exactMatch (string); } std::vector<int> CSMFilter::TextNode::getReferencedColumns() const diff --git a/apps/opencs/model/filter/valuenode.cpp b/apps/opencs/model/filter/valuenode.cpp index f6cb20e4cb..fdcce00ab3 100644 --- a/apps/opencs/model/filter/valuenode.cpp +++ b/apps/opencs/model/filter/valuenode.cpp @@ -7,10 +7,9 @@ #include "../world/columns.hpp" #include "../world/idtable.hpp" -CSMFilter::ValueNode::ValueNode (int columnId, - double lower, double upper, bool min, bool max) -: mColumnId (columnId), mLower (lower), mUpper (upper), mMin (min), mMax (max) -{} +CSMFilter::ValueNode::ValueNode (int columnId, Type lowerType, Type upperType, + double lower, double upper) +: mColumnId (columnId), mLowerType (lowerType), mUpperType (upperType), mLower (lower), mUpper (upper){} bool CSMFilter::ValueNode::test (const CSMWorld::IdTable& table, int row, const std::map<int, int>& columns) const @@ -18,7 +17,7 @@ bool CSMFilter::ValueNode::test (const CSMWorld::IdTable& table, int row, const std::map<int, int>::const_iterator iter = columns.find (mColumnId); if (iter==columns.end()) - throw std::logic_error ("invalid column in test value test"); + throw std::logic_error ("invalid column in value node test"); if (iter->second==-1) return true; @@ -28,15 +27,26 @@ bool CSMFilter::ValueNode::test (const CSMWorld::IdTable& table, int row, QVariant data = table.data (index); if (data.type()!=QVariant::Double && data.type()!=QVariant::Bool && data.type()!=QVariant::Int && - data.type()!=QVariant::UInt) + data.type()!=QVariant::UInt && data.type()!=static_cast<QVariant::Type> (QMetaType::Float)) return false; double value = data.toDouble(); - if (mLower==mUpper && mMin && mMax) - return value==mLower; - - return (mMin ? value>=mLower : value>mLower) && (mMax ? value<=mUpper : value<mUpper); + switch (mLowerType) + { + case Type_Closed: if (value<mLower) return false; break; + case Type_Open: if (value<=mLower) return false; break; + case Type_Infinite: break; + } + + switch (mUpperType) + { + case Type_Closed: if (value>mUpper) return false; break; + case Type_Open: if (value>=mUpper) return false; break; + case Type_Infinite: break; + } + + return true; } std::vector<int> CSMFilter::ValueNode::getReferencedColumns() const @@ -58,12 +68,28 @@ std::string CSMFilter::ValueNode::toString (bool numericColumns) const << CSMWorld::Columns::getName (static_cast<CSMWorld::Columns::ColumnId> (mColumnId)) << "\""; - stream << ", \""; + stream << ", "; - if (mLower==mUpper && mMin && mMax) + if (mLower==mUpper && mLowerType!=Type_Infinite && mUpperType!=Type_Infinite) stream << mLower; else - stream << (mMin ? "[" : "(") << mLower << ", " << mUpper << (mMax ? "]" : ")"); + { + switch (mLowerType) + { + case Type_Closed: stream << "[" << mLower; break; + case Type_Open: stream << "(" << mLower; break; + case Type_Infinite: stream << "("; break; + } + + stream << ", "; + + switch (mUpperType) + { + case Type_Closed: stream << mUpper << "]"; break; + case Type_Open: stream << mUpper << ")"; break; + case Type_Infinite: stream << ")"; break; + } + } stream << ")"; diff --git a/apps/opencs/model/filter/valuenode.hpp b/apps/opencs/model/filter/valuenode.hpp index faaa1e2ff6..b1050709d0 100644 --- a/apps/opencs/model/filter/valuenode.hpp +++ b/apps/opencs/model/filter/valuenode.hpp @@ -7,16 +7,25 @@ namespace CSMFilter { class ValueNode : public LeafNode { + public: + + enum Type + { + Type_Closed, Type_Open, Type_Infinite + }; + + private: + int mColumnId; std::string mText; double mLower; double mUpper; - bool mMin; - bool mMax; + Type mLowerType; + Type mUpperType; public: - ValueNode (int columnId, double lower, double upper, bool min, bool max); + ValueNode (int columnId, Type lowerType, Type upperType, double lower, double upper); virtual bool test (const CSMWorld::IdTable& table, int row, const std::map<int, int>& columns) const; diff --git a/apps/opencs/model/tools/birthsigncheck.cpp b/apps/opencs/model/tools/birthsigncheck.cpp index b673c93ded..59c65086ed 100644 --- a/apps/opencs/model/tools/birthsigncheck.cpp +++ b/apps/opencs/model/tools/birthsigncheck.cpp @@ -19,7 +19,12 @@ int CSMTools::BirthsignCheckStage::setup() void CSMTools::BirthsignCheckStage::perform (int stage, std::vector<std::string>& messages) { - const ESM::BirthSign& birthsign = mBirthsigns.getRecord (stage).get(); + const CSMWorld::Record<ESM::BirthSign>& record = mBirthsigns.getRecord (stage); + + if (record.isDeleted()) + return; + + const ESM::BirthSign& birthsign = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Birthsign, birthsign.mId); diff --git a/apps/opencs/model/tools/birthsigncheck.hpp b/apps/opencs/model/tools/birthsigncheck.hpp index 42b5a6b244..bdd65b44ab 100644 --- a/apps/opencs/model/tools/birthsigncheck.hpp +++ b/apps/opencs/model/tools/birthsigncheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that birthsign records are internally consistent - class BirthsignCheckStage : public Stage + class BirthsignCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection<ESM::BirthSign>& mBirthsigns; diff --git a/apps/opencs/model/tools/classcheck.cpp b/apps/opencs/model/tools/classcheck.cpp index da2e9f19a6..6923b31535 100644 --- a/apps/opencs/model/tools/classcheck.cpp +++ b/apps/opencs/model/tools/classcheck.cpp @@ -20,7 +20,12 @@ int CSMTools::ClassCheckStage::setup() void CSMTools::ClassCheckStage::perform (int stage, std::vector<std::string>& messages) { - const ESM::Class& class_= mClasses.getRecord (stage).get(); + const CSMWorld::Record<ESM::Class>& record = mClasses.getRecord (stage); + + if (record.isDeleted()) + return; + + const ESM::Class& class_ = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Class, class_.mId); diff --git a/apps/opencs/model/tools/classcheck.hpp b/apps/opencs/model/tools/classcheck.hpp index a29d7c8b78..3604b451c5 100644 --- a/apps/opencs/model/tools/classcheck.hpp +++ b/apps/opencs/model/tools/classcheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that class records are internally consistent - class ClassCheckStage : public Stage + class ClassCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection<ESM::Class>& mClasses; diff --git a/apps/opencs/model/tools/factioncheck.cpp b/apps/opencs/model/tools/factioncheck.cpp index af26904efa..c219e56102 100644 --- a/apps/opencs/model/tools/factioncheck.cpp +++ b/apps/opencs/model/tools/factioncheck.cpp @@ -20,7 +20,12 @@ int CSMTools::FactionCheckStage::setup() void CSMTools::FactionCheckStage::perform (int stage, std::vector<std::string>& messages) { - const ESM::Faction& faction = mFactions.getRecord (stage).get(); + const CSMWorld::Record<ESM::Faction>& record = mFactions.getRecord (stage); + + if (record.isDeleted()) + return; + + const ESM::Faction& faction = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Faction, faction.mId); diff --git a/apps/opencs/model/tools/factioncheck.hpp b/apps/opencs/model/tools/factioncheck.hpp index 8686505727..7cd80347db 100644 --- a/apps/opencs/model/tools/factioncheck.hpp +++ b/apps/opencs/model/tools/factioncheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that faction records are internally consistent - class FactionCheckStage : public Stage + class FactionCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection<ESM::Faction>& mFactions; diff --git a/apps/opencs/model/tools/mandatoryid.hpp b/apps/opencs/model/tools/mandatoryid.hpp index 342e2d7540..5fddf08d32 100644 --- a/apps/opencs/model/tools/mandatoryid.hpp +++ b/apps/opencs/model/tools/mandatoryid.hpp @@ -6,7 +6,7 @@ #include "../world/universalid.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMWorld { @@ -16,7 +16,7 @@ namespace CSMWorld namespace CSMTools { /// \brief Verify stage: make sure that records with specific IDs exist. - class MandatoryIdStage : public Stage + class MandatoryIdStage : public CSMDoc::Stage { const CSMWorld::CollectionBase& mIdCollection; CSMWorld::UniversalId mCollectionId; diff --git a/apps/opencs/model/tools/racecheck.cpp b/apps/opencs/model/tools/racecheck.cpp index 1e7a4cab45..413de5ef01 100644 --- a/apps/opencs/model/tools/racecheck.cpp +++ b/apps/opencs/model/tools/racecheck.cpp @@ -9,7 +9,12 @@ void CSMTools::RaceCheckStage::performPerRecord (int stage, std::vector<std::string>& messages) { - const ESM::Race& race = mRaces.getRecord (stage).get(); + const CSMWorld::Record<ESM::Race>& record = mRaces.getRecord (stage); + + if (record.isDeleted()) + return; + + const ESM::Race& race = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId); diff --git a/apps/opencs/model/tools/racecheck.hpp b/apps/opencs/model/tools/racecheck.hpp index 155f799021..ff9948bf6d 100644 --- a/apps/opencs/model/tools/racecheck.hpp +++ b/apps/opencs/model/tools/racecheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that race records are internally consistent - class RaceCheckStage : public Stage + class RaceCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection<ESM::Race>& mRaces; bool mPlayable; diff --git a/apps/opencs/model/tools/regioncheck.cpp b/apps/opencs/model/tools/regioncheck.cpp index ac64ac0271..4398e00ef6 100644 --- a/apps/opencs/model/tools/regioncheck.cpp +++ b/apps/opencs/model/tools/regioncheck.cpp @@ -19,7 +19,12 @@ int CSMTools::RegionCheckStage::setup() void CSMTools::RegionCheckStage::perform (int stage, std::vector<std::string>& messages) { - const ESM::Region& region = mRegions.getRecord (stage).get(); + const CSMWorld::Record<ESM::Region>& record = mRegions.getRecord (stage); + + if (record.isDeleted()) + return; + + const ESM::Region& region = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Region, region.mId); diff --git a/apps/opencs/model/tools/regioncheck.hpp b/apps/opencs/model/tools/regioncheck.hpp index b421356514..c8c437cbd2 100644 --- a/apps/opencs/model/tools/regioncheck.hpp +++ b/apps/opencs/model/tools/regioncheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that region records are internally consistent - class RegionCheckStage : public Stage + class RegionCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection<ESM::Region>& mRegions; diff --git a/apps/opencs/model/tools/skillcheck.cpp b/apps/opencs/model/tools/skillcheck.cpp index 897aeab473..28fc24fd39 100644 --- a/apps/opencs/model/tools/skillcheck.cpp +++ b/apps/opencs/model/tools/skillcheck.cpp @@ -18,7 +18,12 @@ int CSMTools::SkillCheckStage::setup() void CSMTools::SkillCheckStage::perform (int stage, std::vector<std::string>& messages) { - const ESM::Skill& skill = mSkills.getRecord (stage).get(); + const CSMWorld::Record<ESM::Skill>& record = mSkills.getRecord (stage); + + if (record.isDeleted()) + return; + + const ESM::Skill& skill = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Skill, skill.mId); diff --git a/apps/opencs/model/tools/skillcheck.hpp b/apps/opencs/model/tools/skillcheck.hpp index 30a3f01cad..662bdadee1 100644 --- a/apps/opencs/model/tools/skillcheck.hpp +++ b/apps/opencs/model/tools/skillcheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that skill records are internally consistent - class SkillCheckStage : public Stage + class SkillCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection<ESM::Skill>& mSkills; diff --git a/apps/opencs/model/tools/soundcheck.cpp b/apps/opencs/model/tools/soundcheck.cpp index 52834e6594..dce2d2b6fa 100644 --- a/apps/opencs/model/tools/soundcheck.cpp +++ b/apps/opencs/model/tools/soundcheck.cpp @@ -18,7 +18,12 @@ int CSMTools::SoundCheckStage::setup() void CSMTools::SoundCheckStage::perform (int stage, std::vector<std::string>& messages) { - const ESM::Sound& sound = mSounds.getRecord (stage).get(); + const CSMWorld::Record<ESM::Sound>& record = mSounds.getRecord (stage); + + if (record.isDeleted()) + return; + + const ESM::Sound& sound = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Sound, sound.mId); diff --git a/apps/opencs/model/tools/soundcheck.hpp b/apps/opencs/model/tools/soundcheck.hpp index a309763a12..00b45cd935 100644 --- a/apps/opencs/model/tools/soundcheck.hpp +++ b/apps/opencs/model/tools/soundcheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that sound records are internally consistent - class SoundCheckStage : public Stage + class SoundCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection<ESM::Sound>& mSounds; diff --git a/apps/opencs/model/tools/spellcheck.cpp b/apps/opencs/model/tools/spellcheck.cpp index 3adee0a4ef..a2cc7c8d25 100644 --- a/apps/opencs/model/tools/spellcheck.cpp +++ b/apps/opencs/model/tools/spellcheck.cpp @@ -19,7 +19,12 @@ int CSMTools::SpellCheckStage::setup() void CSMTools::SpellCheckStage::perform (int stage, std::vector<std::string>& messages) { - const ESM::Spell& spell = mSpells.getRecord (stage).get(); + const CSMWorld::Record<ESM::Spell>& record = mSpells.getRecord (stage); + + if (record.isDeleted()) + return; + + const ESM::Spell& spell = record.get(); CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Spell, spell.mId); diff --git a/apps/opencs/model/tools/spellcheck.hpp b/apps/opencs/model/tools/spellcheck.hpp index 0566392193..880ddafcd2 100644 --- a/apps/opencs/model/tools/spellcheck.hpp +++ b/apps/opencs/model/tools/spellcheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that spell records are internally consistent - class SpellCheckStage : public Stage + class SpellCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection<ESM::Spell>& mSpells; diff --git a/apps/opencs/model/tools/stage.cpp b/apps/opencs/model/tools/stage.cpp deleted file mode 100644 index 6f4567e579..0000000000 --- a/apps/opencs/model/tools/stage.cpp +++ /dev/null @@ -1,4 +0,0 @@ - -#include "stage.hpp" - -CSMTools::Stage::~Stage() {}
\ No newline at end of file diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 803861203c..cd4653280e 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -3,9 +3,8 @@ #include <QThreadPool> -#include "verifier.hpp" - #include "../doc/state.hpp" +#include "../doc/operation.hpp" #include "../world/data.hpp" #include "../world/universalid.hpp" @@ -21,7 +20,7 @@ #include "birthsigncheck.hpp" #include "spellcheck.hpp" -CSMTools::Operation *CSMTools::Tools::get (int type) +CSMDoc::Operation *CSMTools::Tools::get (int type) { switch (type) { @@ -31,19 +30,19 @@ CSMTools::Operation *CSMTools::Tools::get (int type) return 0; } -const CSMTools::Operation *CSMTools::Tools::get (int type) const +const CSMDoc::Operation *CSMTools::Tools::get (int type) const { return const_cast<Tools *> (this)->get (type); } -CSMTools::Verifier *CSMTools::Tools::getVerifier() +CSMDoc::Operation *CSMTools::Tools::getVerifier() { if (!mVerifier) { - mVerifier = new Verifier; + mVerifier = new CSMDoc::Operation (CSMDoc::State_Verifying, false); connect (mVerifier, SIGNAL (progress (int, int, int)), this, SIGNAL (progress (int, int, int))); - connect (mVerifier, SIGNAL (finished()), this, SLOT (verifierDone())); + connect (mVerifier, SIGNAL (done (int)), this, SIGNAL (done (int))); connect (mVerifier, SIGNAL (reportMessage (const QString&, int)), this, SLOT (verifierMessage (const QString&, int))); @@ -103,7 +102,7 @@ CSMWorld::UniversalId CSMTools::Tools::runVerifier() void CSMTools::Tools::abortOperation (int type) { - if (Operation *operation = get (type)) + if (CSMDoc::Operation *operation = get (type)) operation->abort(); } @@ -118,7 +117,7 @@ int CSMTools::Tools::getRunningOperations() const int result = 0; for (int i=0; sOperations[i]!=-1; ++i) - if (const Operation *operation = get (sOperations[i])) + if (const CSMDoc::Operation *operation = get (sOperations[i])) if (operation->isRunning()) result |= sOperations[i]; @@ -133,11 +132,6 @@ CSMTools::ReportModel *CSMTools::Tools::getReport (const CSMWorld::UniversalId& return mReports.at (id.getIndex()); } -void CSMTools::Tools::verifierDone() -{ - emit done (CSMDoc::State_Verifying); -} - void CSMTools::Tools::verifierMessage (const QString& message, int type) { std::map<int, int>::iterator iter = mActiveReports.find (type); diff --git a/apps/opencs/model/tools/tools.hpp b/apps/opencs/model/tools/tools.hpp index 652345c6da..0079fab34e 100644 --- a/apps/opencs/model/tools/tools.hpp +++ b/apps/opencs/model/tools/tools.hpp @@ -11,10 +11,13 @@ namespace CSMWorld class UniversalId; } -namespace CSMTools +namespace CSMDoc { - class Verifier; class Operation; +} + +namespace CSMTools +{ class ReportModel; class Tools : public QObject @@ -22,7 +25,7 @@ namespace CSMTools Q_OBJECT CSMWorld::Data& mData; - Verifier *mVerifier; + CSMDoc::Operation *mVerifier; std::map<int, ReportModel *> mReports; int mNextReportNumber; std::map<int, int> mActiveReports; // type, report number @@ -31,12 +34,12 @@ namespace CSMTools Tools (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. - const Operation *get (int type) const; + const CSMDoc::Operation *get (int type) const; ///< Returns a 0-pointer, if operation hasn't been used yet. public: @@ -58,8 +61,6 @@ namespace CSMTools private slots: - void verifierDone(); - void verifierMessage (const QString& message, int type); signals: diff --git a/apps/opencs/model/tools/verifier.cpp b/apps/opencs/model/tools/verifier.cpp deleted file mode 100644 index 9c00d4ea7e..0000000000 --- a/apps/opencs/model/tools/verifier.cpp +++ /dev/null @@ -1,7 +0,0 @@ - -#include "verifier.hpp" - -#include "../doc/state.hpp" - -CSMTools::Verifier::Verifier() : Operation (CSMDoc::State_Verifying) -{} diff --git a/apps/opencs/model/tools/verifier.hpp b/apps/opencs/model/tools/verifier.hpp deleted file mode 100644 index 054f87169c..0000000000 --- a/apps/opencs/model/tools/verifier.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef CSM_TOOLS_VERIFIER_H -#define CSM_TOOLS_VERIFIER_H - -#include "operation.hpp" - -namespace CSMTools -{ - class Verifier : public Operation - { - public: - - Verifier(); - - }; -} - -#endif diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 6cf31d0a4f..84a00cef82 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -107,6 +107,11 @@ namespace CSMWorld virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const; ///< \param type Will be ignored, unless the collection supports multiple record types + virtual std::vector<std::string> getIds (bool listDeleted = true) const; + ///< Return a sorted collection of all IDs + /// + /// \param listDeleted include deleted record in the list + void addColumn (Column<ESXRecordT> *column); void setRecord (int index, const Record<ESXRecordT>& record); @@ -294,6 +299,21 @@ namespace CSMWorld } template<typename ESXRecordT, typename IdAccessorT> + std::vector<std::string> Collection<ESXRecordT, IdAccessorT>::getIds (bool listDeleted) const + { + std::vector<std::string> ids; + + for (typename std::map<std::string, int>::const_iterator iter = mIndex.begin(); + iter!=mIndex.end(); ++iter) + { + if (listDeleted || !mRecords[iter->second].isDeleted()) + ids.push_back (IdAccessorT().getId (mRecords[iter->second].get())); + } + + return ids; + } + + template<typename ESXRecordT, typename IdAccessorT> const Record<ESXRecordT>& Collection<ESXRecordT, IdAccessorT>::getRecord (const std::string& id) const { int index = getIndex (id); diff --git a/apps/opencs/model/world/collectionbase.cpp b/apps/opencs/model/world/collectionbase.cpp index 932ea27b58..241f198cb2 100644 --- a/apps/opencs/model/world/collectionbase.cpp +++ b/apps/opencs/model/world/collectionbase.cpp @@ -1,6 +1,31 @@ #include "collectionbase.hpp" +#include <stdexcept> + +#include "columnbase.hpp" + CSMWorld::CollectionBase::CollectionBase() {} CSMWorld::CollectionBase::~CollectionBase() {} + +int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const +{ + int columns = getColumns(); + + for (int i=0; i<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; +}
\ No newline at end of file diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index 1700a68ecd..1056a961d5 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -4,6 +4,7 @@ #include <string> #include "universalid.hpp" +#include "columns.hpp" class QVariant; @@ -78,8 +79,19 @@ namespace CSMWorld virtual int getAppendIndex (UniversalId::Type type = UniversalId::Type_None) const = 0; ///< \param type Will be ignored, unless the collection supports multiple record types - }; + virtual std::vector<std::string> getIds (bool listDeleted = true) const = 0; + ///< Return a sorted collection of all IDs + /// + /// \param listDeleted include deleted record in the list + + int searchColumnIndex (Columns::ColumnId id) const; + ///< Return index of column with the given \a id. If no such column exists, -1 is returned. + + int findColumnIndex (Columns::ColumnId id) const; + ///< Return index of column with the given \a id. If no such column exists, an exception is + /// thrown. + }; } #endif
\ No newline at end of file diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index c1b423c94c..9b8d7dafb8 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -43,7 +43,8 @@ namespace CSMWorld Display_CreatureType, Display_WeaponType, Display_RecordState, - Display_RefRecordType + Display_RefRecordType, + Display_DialogueType }; int mColumnId; diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 1a2bf9df13..8575480fd4 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -1216,6 +1216,138 @@ namespace CSMWorld return true; } }; + + 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> + struct PosColumn : public Column<ESXRecordT> + { + ESM::Position ESXRecordT::* mPosition; + int mIndex; + + PosColumn (ESM::Position ESXRecordT::* position, int index, bool door) + : Column<ESXRecordT> ( + (door ? Columns::ColumnId_DoorPositionXPos : Columns::ColumnId_PositionXPos)+index, + ColumnBase::Display_Float), mPosition (position), mIndex (index) {} + + virtual QVariant get (const Record<ESXRecordT>& record) const + { + const ESM::Position& position = record.get().*mPosition; + return position.pos[mIndex]; + } + + virtual void set (Record<ESXRecordT>& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + ESM::Position& position = record.get().*mPosition; + + position.pos[mIndex] = data.toFloat(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + }; + + template<typename ESXRecordT> + struct RotColumn : public Column<ESXRecordT> + { + ESM::Position ESXRecordT::* mPosition; + int mIndex; + + RotColumn (ESM::Position ESXRecordT::* position, int index, bool door) + : Column<ESXRecordT> ( + (door ? Columns::ColumnId_DoorPositionXRot : Columns::ColumnId_PositionXRot)+index, + ColumnBase::Display_Float), mPosition (position), mIndex (index) {} + + virtual QVariant get (const Record<ESXRecordT>& record) const + { + const ESM::Position& position = record.get().*mPosition; + return position.rot[mIndex]; + } + + virtual void set (Record<ESXRecordT>& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + ESM::Position& position = record.get().*mPosition; + + position.rot[mIndex] = data.toFloat(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + 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 diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index b206322582..a842e7b8de 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -3,6 +3,8 @@ #include <components/misc/stringops.hpp> +#include "universalid.hpp" + namespace CSMWorld { namespace Columns @@ -49,7 +51,7 @@ namespace CSMWorld { ColumnId_FactionIndex, "Faction Index" }, { ColumnId_Charges, "Charges" }, { ColumnId_Enchantment, "Enchantment" }, - { ColumnId_Value, "Coin Value" }, + { ColumnId_CoinValue, "Coin Value" }, { ColumnId_Teleport, "Teleport" }, { ColumnId_TeleportCell, "Teleport Cell" }, { ColumnId_LockLevel, "Lock Level" }, @@ -145,6 +147,20 @@ namespace CSMWorld { ColumnId_Magical, "Magical" }, { ColumnId_Silver, "Silver" }, { ColumnId_Filter, "Filter" }, + { ColumnId_PositionXPos, "Pos X" }, + { ColumnId_PositionYPos, "Pos Y" }, + { ColumnId_PositionZPos, "Pos Z" }, + { ColumnId_PositionXRot, "Rot X" }, + { ColumnId_PositionYRot, "Rot Y" }, + { ColumnId_PositionZRot, "Rot Z" }, + { ColumnId_DoorPositionXPos, "Teleport Pos X" }, + { ColumnId_DoorPositionYPos, "Teleport Pos Y" }, + { ColumnId_DoorPositionZPos, "Teleport Pos Z" }, + { ColumnId_DoorPositionXRot, "Teleport Rot X" }, + { ColumnId_DoorPositionYRot, "Teleport Rot Y" }, + { ColumnId_DoorPositionZRot, "Teleport Rot Z" }, + { ColumnId_DialogueType, "Dialogue Type" }, + { ColumnId_Scope, "Scope", }, { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, @@ -196,4 +212,110 @@ int CSMWorld::Columns::getId (const std::string& name) return sNames[i].mId; return -1; +} + +namespace +{ + static const char *sSpecialisations[] = + { + "Combat", "Magic", "Stealth", 0 + }; + + static const char *sAttributes[] = + { + "Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality", + "Luck", 0 + }; + + static const char *sSpellTypes[] = + { + "Spell", "Ability", "Blight", "Disease", "Curse", "Power", 0 + }; + + static const char *sApparatusTypes[] = + { + "Mortar & Pestle", "Albemic", "Calcinator", "Retort", 0 + }; + + static const char *sArmorTypes[] = + { + "Helmet", "Cuirass", "Left Pauldron", "Right Pauldron", "Greaves", "Boots", "Left Gauntlet", + "Right Gauntlet", "Shield", "Left Bracer", "Right Bracer", 0 + }; + + static const char *sClothingTypes[] = + { + "Pants", "Shoes", "Shirt", "Belt", "Robe", "Right Glove", "Left Glove", "Skirt", "Ring", + "Amulet", 0 + }; + + static const char *sCreatureTypes[] = + { + "Creature", "Deadra", "Undead", "Humanoid", 0 + }; + + static const char *sWeaponTypes[] = + { + "Short Blade 1H", "Long Blade 1H", "Long Blade 2H", "Blunt 1H", "Blunt 2H Close", + "Blunt 2H Wide", "Spear 2H", "Axe 1H", "Axe 2H", "Bow", "Crossbow", "Thrown", "Arrow", + "Bolt", 0 + }; + + static const char *sModificationEnums[] = + { + "Base", "Modified", "Added", "Deleted", "Deleted", 0 + }; + + static const char *sVarTypeEnums[] = + { + "unknown", "none", "short", "integer", "long", "float", "string", 0 + }; + + static const char *sDialogueTypeEnums[] = + { + "Topic", "Voice", "Greeting", "Persuasion", 0 + }; + + const char **getEnumNames (CSMWorld::Columns::ColumnId column) + { + switch (column) + { + case CSMWorld::Columns::ColumnId_Specialisation: return sSpecialisations; + case CSMWorld::Columns::ColumnId_Attribute: return sAttributes; + case CSMWorld::Columns::ColumnId_SpellType: return sSpellTypes; + case CSMWorld::Columns::ColumnId_ApparatusType: return sApparatusTypes; + case CSMWorld::Columns::ColumnId_ArmorType: return sArmorTypes; + case CSMWorld::Columns::ColumnId_ClothingType: return sClothingTypes; + case CSMWorld::Columns::ColumnId_CreatureType: return sCreatureTypes; + case CSMWorld::Columns::ColumnId_WeaponType: return sWeaponTypes; + case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums; + case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums; + case CSMWorld::Columns::ColumnId_DialogueType: return sDialogueTypeEnums; + + default: return 0; + } + } +} + +bool CSMWorld::Columns::hasEnums (ColumnId column) +{ + return getEnumNames (column)!=0 || column==ColumnId_RecordType; +} + +std::vector<std::string> CSMWorld::Columns::getEnums (ColumnId column) +{ + std::vector<std::string> enums; + + if (const char **table = getEnumNames (column)) + for (int i=0; table[i]; ++i) + enums.push_back (table[i]); + else if (column==ColumnId_RecordType) + { + enums.push_back (""); // none + + for (int i=UniversalId::Type_None+1; i<UniversalId::NumberOfTypes; ++i) + enums.push_back (UniversalId (static_cast<UniversalId::Type> (i)).getTypeName()); + } + + return enums; }
\ No newline at end of file diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 9a39e16780..55d085a96f 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -2,6 +2,7 @@ #define CSM_WOLRD_COLUMNS_H #include <string> +#include <vector> namespace CSMWorld { @@ -44,101 +45,115 @@ namespace CSMWorld ColumnId_Charges = 32, ColumnId_Enchantment = 33, ColumnId_CoinValue = 34, - ColumnId_Teleport = 25, - ColumnId_TeleportCell = 26, - ColumnId_LockLevel = 27, - ColumnId_Key = 28, - ColumnId_Trap = 29, - ColumnId_BeastRace = 30, - ColumnId_AutoCalc = 31, - ColumnId_StarterSpell = 32, - ColumnId_AlwaysSucceeds = 33, - ColumnId_SleepForbidden = 34, - ColumnId_InteriorWater = 35, - ColumnId_InteriorSky = 36, - ColumnId_Model = 37, - ColumnId_Script = 38, - ColumnId_Icon = 39, - ColumnId_Weight = 40, - ColumnId_EnchantmentPoints = 31, - ColumnId_Quality = 32, - ColumnId_Ai = 33, - ColumnId_AiHello = 34, - ColumnId_AiFlee = 35, - ColumnId_AiFight = 36, - ColumnId_AiAlarm = 37, - ColumnId_BuysWeapons = 38, - ColumnId_BuysArmor = 39, - ColumnId_BuysClothing = 40, - ColumnId_BuysBooks = 41, - ColumnId_BuysIngredients = 42, - ColumnId_BuysLockpicks = 43, - ColumnId_BuysProbes = 44, - ColumnId_BuysLights = 45, - ColumnId_BuysApparati = 46, - ColumnId_BuysRepairItems = 47, - ColumnId_BuysMiscItems = 48, - ColumnId_BuysPotions = 49, - ColumnId_BuysMagicItems = 50, - ColumnId_SellsSpells = 51, - ColumnId_Trainer = 52, - ColumnId_Spellmaking = 53, - ColumnId_EnchantingService = 54, - ColumnId_RepairService = 55, - ColumnId_ApparatusType = 56, - ColumnId_ArmorType = 57, - ColumnId_Health = 58, - ColumnId_ArmorValue = 59, - ColumnId_Scroll = 60, - ColumnId_ClothingType = 61, - ColumnId_WeightCapacity = 62, - ColumnId_OrganicContainer = 63, - ColumnId_Respawn = 64, - ColumnId_CreatureType = 65, - ColumnId_SoulPoints = 66, - ColumnId_OriginalCreature = 67, - ColumnId_Biped = 68, - ColumnId_HasWeapon = 69, - ColumnId_NoMovement = 70, - ColumnId_Swims = 71, - ColumnId_Flies = 72, - ColumnId_Walks = 73, - ColumnId_Essential = 74, - ColumnId_SkeletonBlood = 75, - ColumnId_MetalBlood = 76, - ColumnId_OpenSound = 77, - ColumnId_CloseSound = 78, - ColumnId_Duration = 79, - ColumnId_Radius = 80, - ColumnId_Colour = 81, - ColumnId_Sound = 82, - ColumnId_Dynamic = 83, - ColumnId_Portable = 84, - ColumnId_NegativeLight = 85, - ColumnId_Flickering = 86, - ColumnId_SlowFlickering = 87, - ColumnId_Pulsing = 88, - ColumnId_SlowPulsing = 89, - ColumnId_Fire = 90, - ColumnId_OffByDefault = 91, - ColumnId_IsKey = 92, - ColumnId_Race = 93, - ColumnId_Class = 94, - Columnid_Hair = 95, - ColumnId_Head = 96, - ColumnId_Female = 97, - ColumnId_WeaponType = 98, - ColumnId_WeaponSpeed = 99, - ColumnId_WeaponReach = 100, - ColumnId_MinChop = 101, - ColumnId_MaxChip = 102, - Columnid_MinSlash = 103, - ColumnId_MaxSlash = 104, - ColumnId_MinThrust = 105, - ColumnId_MaxThrust = 106, - ColumnId_Magical = 107, - ColumnId_Silver = 108, - ColumnId_Filter = 109, + ColumnId_Teleport = 35, + ColumnId_TeleportCell = 36, + ColumnId_LockLevel = 37, + ColumnId_Key = 38, + ColumnId_Trap = 39, + ColumnId_BeastRace = 40, + ColumnId_AutoCalc = 41, + ColumnId_StarterSpell = 42, + ColumnId_AlwaysSucceeds = 43, + ColumnId_SleepForbidden = 44, + ColumnId_InteriorWater = 45, + ColumnId_InteriorSky = 46, + ColumnId_Model = 47, + ColumnId_Script = 48, + ColumnId_Icon = 49, + ColumnId_Weight = 50, + ColumnId_EnchantmentPoints = 51, + ColumnId_Quality = 52, + ColumnId_Ai = 53, + ColumnId_AiHello = 54, + ColumnId_AiFlee = 55, + ColumnId_AiFight = 56, + ColumnId_AiAlarm = 57, + ColumnId_BuysWeapons = 58, + ColumnId_BuysArmor = 59, + ColumnId_BuysClothing = 60, + ColumnId_BuysBooks = 61, + ColumnId_BuysIngredients = 62, + ColumnId_BuysLockpicks = 63, + ColumnId_BuysProbes = 64, + ColumnId_BuysLights = 65, + ColumnId_BuysApparati = 66, + ColumnId_BuysRepairItems = 67, + ColumnId_BuysMiscItems = 68, + ColumnId_BuysPotions = 69, + ColumnId_BuysMagicItems = 70, + ColumnId_SellsSpells = 71, + ColumnId_Trainer = 72, + ColumnId_Spellmaking = 73, + ColumnId_EnchantingService = 74, + ColumnId_RepairService = 75, + ColumnId_ApparatusType = 76, + ColumnId_ArmorType = 77, + ColumnId_Health = 78, + ColumnId_ArmorValue = 79, + ColumnId_Scroll = 80, + ColumnId_ClothingType = 81, + ColumnId_WeightCapacity = 82, + ColumnId_OrganicContainer = 83, + ColumnId_Respawn = 84, + ColumnId_CreatureType = 85, + ColumnId_SoulPoints = 86, + ColumnId_OriginalCreature = 87, + ColumnId_Biped = 88, + ColumnId_HasWeapon = 89, + ColumnId_NoMovement = 90, + ColumnId_Swims = 91, + ColumnId_Flies = 92, + ColumnId_Walks = 93, + ColumnId_Essential = 94, + ColumnId_SkeletonBlood = 95, + ColumnId_MetalBlood = 96, + ColumnId_OpenSound = 97, + ColumnId_CloseSound = 98, + ColumnId_Duration = 99, + ColumnId_Radius = 100, + ColumnId_Colour = 101, + ColumnId_Sound = 102, + ColumnId_Dynamic = 103, + ColumnId_Portable = 104, + ColumnId_NegativeLight = 105, + ColumnId_Flickering = 106, + ColumnId_SlowFlickering = 107, + ColumnId_Pulsing = 108, + ColumnId_SlowPulsing = 109, + ColumnId_Fire = 110, + ColumnId_OffByDefault = 111, + ColumnId_IsKey = 112, + ColumnId_Race = 113, + ColumnId_Class = 114, + Columnid_Hair = 115, + ColumnId_Head = 116, + ColumnId_Female = 117, + ColumnId_WeaponType = 118, + ColumnId_WeaponSpeed = 119, + ColumnId_WeaponReach = 120, + ColumnId_MinChop = 121, + ColumnId_MaxChip = 122, + Columnid_MinSlash = 123, + ColumnId_MaxSlash = 124, + ColumnId_MinThrust = 125, + ColumnId_MaxThrust = 126, + ColumnId_Magical = 127, + ColumnId_Silver = 128, + ColumnId_Filter = 129, + ColumnId_PositionXPos = 130, + ColumnId_PositionYPos = 131, + ColumnId_PositionZPos = 132, + ColumnId_PositionXRot = 133, + ColumnId_PositionYRot = 134, + ColumnId_PositionZRot = 135, + ColumnId_DoorPositionXPos = 136, + ColumnId_DoorPositionYPos = 137, + ColumnId_DoorPositionZPos = 138, + ColumnId_DoorPositionXRot = 139, + ColumnId_DoorPositionYRot = 140, + 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 // to extend the number of use values. @@ -180,6 +195,11 @@ namespace CSMWorld int getId (const std::string& name); ///< Will return -1 for an invalid name. + + bool hasEnums (ColumnId column); + + std::vector<std::string> getEnums (ColumnId column); + ///< Returns an empty vector, if \æ column isn't an enum type column. } } diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index fbdbb44136..10e56765fa 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -2,12 +2,14 @@ #include "data.hpp" #include <stdexcept> +#include <algorithm> #include <QAbstractItemModel> #include <components/esm/esmreader.hpp> #include <components/esm/defs.hpp> #include <components/esm/loadglob.hpp> +#include <components/esm/cellref.hpp> #include "idtable.hpp" #include "columnimp.hpp" @@ -15,13 +17,42 @@ #include "columns.hpp" void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type1, - UniversalId::Type type2) + UniversalId::Type type2, bool update) { mModels.push_back (model); mModelIndex.insert (std::make_pair (type1, model)); if (type2!=UniversalId::Type_None) mModelIndex.insert (std::make_pair (type2, model)); + + if (update) + { + connect (model, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (dataChanged (const QModelIndex&, const QModelIndex&))); + connect (model, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (rowsChanged (const QModelIndex&, int, int))); + connect (model, SIGNAL (rowsRemoved (const QModelIndex&, int, int)), + this, SLOT (rowsChanged (const QModelIndex&, int, int))); + } +} + +void CSMWorld::Data::appendIds (std::vector<std::string>& ids, const CollectionBase& collection, + bool listDeleted) +{ + std::vector<std::string> ids2 = collection.getIds (listDeleted); + + 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) @@ -121,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_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 RecordStateColumn<Cell>); mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_Cell)); @@ -134,6 +173,12 @@ CSMWorld::Data::Data() : mRefs (mCells) mRefs.addColumn (new RecordStateColumn<CellRef>); mRefs.addColumn (new CellColumn<CellRef>); mRefs.addColumn (new IdColumn<CellRef>); + mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 0, false)); + mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 1, false)); + mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 2, false)); + mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mPos, 0, false)); + mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mPos, 1, false)); + mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mPos, 2, false)); mRefs.addColumn (new ScaleColumn<CellRef>); mRefs.addColumn (new OwnerColumn<CellRef>); mRefs.addColumn (new SoulColumn<CellRef>); @@ -144,6 +189,12 @@ CSMWorld::Data::Data() : mRefs (mCells) mRefs.addColumn (new GoldValueColumn<CellRef>); mRefs.addColumn (new TeleportColumn<CellRef>); mRefs.addColumn (new TeleportCellColumn<CellRef>); + mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mDoorDest, 0, true)); + mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mDoorDest, 1, true)); + mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mDoorDest, 2, true)); + mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mDoorDest, 0, true)); + mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mDoorDest, 1, true)); + mRefs.addColumn (new RotColumn<CellRef> (&CellRef::mDoorDest, 2, true)); mRefs.addColumn (new LockLevelColumn<CellRef>); mRefs.addColumn (new KeyColumn<CellRef>); mRefs.addColumn (new TrapColumn<CellRef>); @@ -152,10 +203,11 @@ CSMWorld::Data::Data() : mRefs (mCells) mFilters.addColumn (new RecordStateColumn<CSMFilter::Filter>); mFilters.addColumn (new FilterColumn<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 (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst); - addModel (new IdTable (&mSkills), UniversalId::Type_Skills, UniversalId::Type_Skill); + addModel (new IdTable (&mSkills), UniversalId::Type_Skills, UniversalId::Type_Skill, false); addModel (new IdTable (&mClasses), UniversalId::Type_Classes, UniversalId::Type_Class); addModel (new IdTable (&mFactions), UniversalId::Type_Factions, UniversalId::Type_Faction); addModel (new IdTable (&mRaces), UniversalId::Type_Races, UniversalId::Type_Race); @@ -164,11 +216,13 @@ CSMWorld::Data::Data() : mRefs (mCells) addModel (new IdTable (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region); addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign); addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell); + addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic); + addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal); addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell); addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, UniversalId::Type_Referenceable); - addModel (new IdTable (&mRefs), UniversalId::Type_References, UniversalId::Type_Reference); - addModel (new IdTable (&mFilters), UniversalId::Type_Filters, UniversalId::Type_Filter); + addModel (new IdTable (&mRefs), UniversalId::Type_References, UniversalId::Type_Reference, false); + addModel (new IdTable (&mFilters), UniversalId::Type_Filters, UniversalId::Type_Filter, false); } CSMWorld::Data::~Data() @@ -287,6 +341,28 @@ CSMWorld::IdCollection<ESM::Spell>& CSMWorld::Data::getSpells() 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 { return mCells; @@ -341,7 +417,7 @@ QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id) { RegionMap *table = 0; addModel (table = new RegionMap (*this), UniversalId::Type_RegionMap, - UniversalId::Type_None); + UniversalId::Type_None, false); return table; } throw std::logic_error ("No table model available for " + id.toString()); @@ -355,7 +431,7 @@ void CSMWorld::Data::merge() mGlobals.merge(); } -void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) +void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base, bool project) { ESM::ESMReader reader; @@ -365,6 +441,9 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) reader.open (path.string()); + mAuthor = reader.getAuthor(); + mDescription = reader.getDesc(); + // Note: We do not need to send update signals here, because at this point the model is not connected // to any view. while (reader.hasMoreRecs()) @@ -415,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_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: /// \todo throw an exception instead, once all records are implemented @@ -437,6 +564,81 @@ bool CSMWorld::Data::hasId (const std::string& id) const getRegions().searchId (id)!=-1 || getBirthsigns().searchId (id)!=-1 || getSpells().searchId (id)!=-1 || + getTopics().searchId (id)!=-1 || + getJournals().searchId (id)!=-1 || getCells().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> ids; + + appendIds (ids, mGlobals, listDeleted); + appendIds (ids, mGmsts, listDeleted); + appendIds (ids, mClasses, listDeleted); + appendIds (ids, mFactions, listDeleted); + appendIds (ids, mRaces, listDeleted); + appendIds (ids, mSounds, listDeleted); + appendIds (ids, mScripts, listDeleted); + appendIds (ids, mRegions, listDeleted); + appendIds (ids, mBirthsigns, listDeleted); + appendIds (ids, mSpells, listDeleted); + appendIds (ids, mTopics, listDeleted); + appendIds (ids, mJournals, listDeleted); + appendIds (ids, mCells, listDeleted); + appendIds (ids, mReferenceables, listDeleted); + + std::sort (ids.begin(), ids.end()); + + return ids; +} + +void CSMWorld::Data::dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + if (topLeft.column()<=0) + emit idListChanged(); +} + +void CSMWorld::Data::rowsChanged (const QModelIndex& parent, int start, int end) +{ + emit idListChanged(); +}
\ No newline at end of file diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 2f8a2117e0..1a9eae2d26 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -6,6 +6,9 @@ #include <boost/filesystem/path.hpp> +#include <QObject> +#include <QModelIndex> + #include <components/esm/loadglob.hpp> #include <components/esm/loadgmst.hpp> #include <components/esm/loadskil.hpp> @@ -17,6 +20,7 @@ #include <components/esm/loadregn.hpp> #include <components/esm/loadbsgn.hpp> #include <components/esm/loadspel.hpp> +#include <components/esm/loaddial.hpp> #include "../filter/filter.hpp" @@ -30,8 +34,10 @@ class QAbstractItemModel; namespace CSMWorld { - class Data + class Data : public QObject { + Q_OBJECT + IdCollection<ESM::Global> mGlobals; IdCollection<ESM::GameSetting> mGmsts; IdCollection<ESM::Skill> mSkills; @@ -43,25 +49,35 @@ namespace CSMWorld IdCollection<ESM::Region> mRegions; IdCollection<ESM::BirthSign> mBirthsigns; IdCollection<ESM::Spell> mSpells; + IdCollection<ESM::Dialogue> mTopics; + IdCollection<ESM::Dialogue> mJournals; IdCollection<Cell> mCells; RefIdCollection mReferenceables; RefCollection mRefs; IdCollection<CSMFilter::Filter> mFilters; std::vector<QAbstractItemModel *> mModels; std::map<UniversalId::Type, QAbstractItemModel *> mModelIndex; + std::string mAuthor; + std::string mDescription; // not implemented Data (const Data&); Data& operator= (const Data&); void addModel (QAbstractItemModel *model, UniversalId::Type type1, - UniversalId::Type type2 = UniversalId::Type_None); + UniversalId::Type type2 = UniversalId::Type_None, bool update = true); + + static void appendIds (std::vector<std::string>& ids, const CollectionBase& collection, + bool listDeleted); + ///< Append all IDs from collection to \a ids. + + static int count (RecordBase::State state, const CollectionBase& collection); public: Data(); - ~Data(); + virtual ~Data(); const IdCollection<ESM::Global>& getGlobals() const; @@ -107,6 +123,14 @@ namespace CSMWorld 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; IdCollection<Cell>& getCells(); @@ -132,10 +156,38 @@ namespace CSMWorld void merge(); ///< Merge modified into base. - void loadFile (const boost::filesystem::path& path, bool base); + void loadFile (const boost::filesystem::path& path, bool base, bool project); ///< Merging content of a file into base or modified. + /// + /// \param project load project file instead of content file bool hasId (const std::string& id) const; + + std::vector<std::string> getIds (bool listDeleted = true) const; + ///< Return a sorted collection of all IDs that are not internal to the editor. + /// + /// \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: + + void idListChanged(); + + private slots: + + void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void rowsChanged (const QModelIndex& parent, int start, int end); }; } diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 04e65eea7b..a7b37be5bc 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -7,21 +7,24 @@ namespace CSMWorld { - /// \brief Single type collection of top level records template<typename ESXRecordT, typename IdAccessorT = IdAccessor<ESXRecordT> > class IdCollection : public Collection<ESXRecordT, IdAccessorT> { public: - void load (ESM::ESMReader& reader, bool base, - UniversalId::Type type = UniversalId::Type_None); - ///< \param type Will be ignored, unless the collection supports multiple record types + void load (ESM::ESMReader& reader, bool base); + + 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> - void IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base, - UniversalId::Type type) + void IdCollection<ESXRecordT, IdAccessorT>::load (ESM::ESMReader& reader, bool base) { std::string id = reader.getHNOString ("NAME"); @@ -56,30 +59,62 @@ namespace CSMWorld IdAccessorT().getId (record) = id; record.load (reader); - int index = this->searchId (IdAccessorT().getId (record)); + load (record, base); + } + } - if (index==-1) - { - // new record - Record<ESXRecordT> record2; - record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; - (base ? record2.mBase : record2.mModified) = record; + template<typename ESXRecordT, typename IdAccessorT> + void IdCollection<ESXRecordT, IdAccessorT>::load (const ESXRecordT& record, bool base) + { + int index = this->searchId (IdAccessorT().getId (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 - { - // old record - Record<ESXRecordT> record2 = Collection<ESXRecordT, IdAccessorT>::getRecord (index); + record2.setModified (record); - if (base) - record2.mBase = record; - else - record2.setModified (record); + this->setRecord (index, record2); + } + } - 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; } } diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index baaf75289c..b7b1a9db05 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -161,21 +161,10 @@ const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id) int CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const { - int columns = mIdCollection->getColumns(); - - for (int i=0; i<columns; ++i) - if (mIdCollection->getColumn (i).mColumnId==id) - return i; - - return -1; + return mIdCollection->searchColumnIndex (id); } int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const { - int index = searchColumnIndex (id); - - if (index==-1) - throw std::logic_error ("invalid column index"); - - return index; + return mIdCollection->findColumnIndex (id); }
\ No newline at end of file diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 085817753e..696aeefaa2 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -6,10 +6,6 @@ #include "ref.hpp" #include "cell.hpp" -CSMWorld::RefCollection::RefCollection (Collection<Cell>& cells) -: mCells (cells), mNextId (0) -{} - void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base) { Record<Cell> cell = mCells.getRecord (cellIndex); diff --git a/apps/opencs/model/world/refcollection.hpp b/apps/opencs/model/world/refcollection.hpp index 895315a179..b5f8c8064a 100644 --- a/apps/opencs/model/world/refcollection.hpp +++ b/apps/opencs/model/world/refcollection.hpp @@ -16,8 +16,10 @@ namespace CSMWorld int mNextId; public: - - RefCollection (Collection<Cell>& cells); + // MSVC needs the constructor for a class inheriting a template to be defined in header + RefCollection (Collection<Cell>& cells) + : mCells (cells), mNextId (0) + {} void load (ESM::ESMReader& reader, int cellIndex, bool base); ///< Load a sequence of references. diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 343cbe302a..3a6f70d310 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -534,3 +534,13 @@ int CSMWorld::RefIdCollection::getAppendIndex (UniversalId::Type type) const { return mData.getAppendIndex (type); } + +std::vector<std::string> CSMWorld::RefIdCollection::getIds (bool listDeleted) const +{ + return mData.getIds (listDeleted); +} + +void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const +{ + mData.save (index, writer); +}
\ No newline at end of file diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index c10d1d2d0f..a479735db5 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -9,6 +9,11 @@ #include "collectionbase.hpp" #include "refiddata.hpp" +namespace ESM +{ + class ESMWriter; +} + namespace CSMWorld { class RefIdAdapter; @@ -89,6 +94,13 @@ namespace CSMWorld virtual int getAppendIndex (UniversalId::Type type) const; ///< \param type Will be ignored, unless the collection supports multiple record types + + virtual std::vector<std::string> getIds (bool listDeleted) const; + ///< Return a sorted collection of all IDs + /// + /// \param listDeleted include deleted record in the list + + void save (int index, ESM::ESMWriter& writer) const; }; } diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index c95db045f5..8f59b0fe74 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -196,3 +196,38 @@ int CSMWorld::RefIdData::getSize() const { return mIndex.size(); } + +std::vector<std::string> CSMWorld::RefIdData::getIds (bool listDeleted) const +{ + std::vector<std::string> ids; + + for (std::map<std::string, LocalIndex>::const_iterator iter (mIndex.begin()); iter!=mIndex.end(); + ++iter) + { + if (listDeleted || !getRecord (iter->second).isDeleted()) + { + std::map<UniversalId::Type, RefIdDataContainerBase *>::const_iterator container = + mRecordContainers.find (iter->second.second); + + if (container==mRecordContainers.end()) + throw std::logic_error ("Invalid referenceable ID type"); + + ids.push_back (container->second->getId (iter->second.first)); + } + } + + 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); +}
\ No newline at end of file diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 475566fb54..9595ab23b5 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -23,6 +23,7 @@ #include <components/esm/loadweap.hpp> #include <components/esm/loadnpc.hpp> #include <components/esm/loadmisc.hpp> +#include <components/esm/esmwriter.hpp> #include "record.hpp" #include "universalid.hpp" @@ -51,6 +52,8 @@ namespace CSMWorld virtual void erase (int index, int count) = 0; virtual std::string getId (int index) const = 0; + + virtual void save (int index, ESM::ESMWriter& writer) const = 0; }; template<typename RecordT> @@ -71,6 +74,8 @@ namespace CSMWorld virtual void erase (int index, int count); virtual std::string getId (int index) const; + + virtual void save (int index, ESM::ESMWriter& writer) const; }; template<typename RecordT> @@ -123,6 +128,31 @@ namespace CSMWorld 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 { public: @@ -182,6 +212,13 @@ namespace CSMWorld void load (const LocalIndex& index, ESM::ESMReader& reader, bool base); int getSize() const; + + std::vector<std::string> getIds (bool listDeleted = true) const; + ///< Return a sorted collection of all IDs + /// + /// \param listDeleted include deleted record in the list + + void save (int index, ESM::ESMWriter& writer) const; }; } diff --git a/apps/opencs/model/world/scriptcontext.cpp b/apps/opencs/model/world/scriptcontext.cpp index 69b72abf26..86689d823b 100644 --- a/apps/opencs/model/world/scriptcontext.cpp +++ b/apps/opencs/model/world/scriptcontext.cpp @@ -1,6 +1,14 @@ #include "scriptcontext.hpp" +#include <algorithm> + +#include <components/misc/stringops.hpp> + +#include "data.hpp" + +CSMWorld::ScriptContext::ScriptContext (const Data& data) : mData (data), mIdsUpdated (false) {} + bool CSMWorld::ScriptContext::canDeclareLocals() const { return false; @@ -18,5 +26,19 @@ char CSMWorld::ScriptContext::getMemberType (const std::string& name, const std: bool CSMWorld::ScriptContext::isId (const std::string& name) const { - return false; + if (!mIdsUpdated) + { + mIds = mData.getIds(); + + std::for_each (mIds.begin(), mIds.end(), &Misc::StringUtils::lowerCase); + + mIdsUpdated = true; + } + + return std::binary_search (mIds.begin(), mIds.end(), Misc::StringUtils::lowerCase (name)); +} + +void CSMWorld::ScriptContext::invalidateIds() +{ + mIdsUpdated = false; }
\ No newline at end of file diff --git a/apps/opencs/model/world/scriptcontext.hpp b/apps/opencs/model/world/scriptcontext.hpp index 1231aea649..b839b5a432 100644 --- a/apps/opencs/model/world/scriptcontext.hpp +++ b/apps/opencs/model/world/scriptcontext.hpp @@ -1,14 +1,25 @@ #ifndef CSM_WORLD_SCRIPTCONTEXT_H #define CSM_WORLD_SCRIPTCONTEXT_H +#include <string> +#include <vector> + #include <components/compiler/context.hpp> namespace CSMWorld { + class Data; + class ScriptContext : public Compiler::Context { + const Data& mData; + mutable std::vector<std::string> mIds; + mutable bool mIdsUpdated; + public: + ScriptContext (const Data& data); + virtual bool canDeclareLocals() const; ///< Is the compiler allowed to declare local variables? @@ -20,6 +31,8 @@ namespace CSMWorld virtual bool isId (const std::string& name) const; ///< Does \a name match an ID, that can be referenced? + + void invalidateIds(); }; } diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 42ebd1f807..6201a3cdaa 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -29,6 +29,8 @@ namespace { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, "Referenceables", 0 }, @@ -43,18 +45,20 @@ namespace static const TypeData sIdArg[] = { - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable", 0 }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting", 0 }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill", 0 }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class", 0 }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction", 0 }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race", 0 }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound", 0 }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script", 0 }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", 0 }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", 0 }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", 0 }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable", ":./globvar.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting", ":./GMST.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill", ":./skill.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class", ":./class.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction", ":./faction.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race", ":./race.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound", ":./sound.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script", ":./script.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_Spell, "Spell", ":./spell.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" }, @@ -80,18 +84,17 @@ namespace { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" }, { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Reference", 0 }, - + { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; static const TypeData sIndexArg[] = { { CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 }, + { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker }; - - static const unsigned int IDARG_SIZE = sizeof (sIdArg) / sizeof (TypeData); } CSMWorld::UniversalId::UniversalId (const std::string& universalId) @@ -151,6 +154,22 @@ CSMWorld::UniversalId::UniversalId (Type type) : mArgumentType (ArgumentType_Non return; } + for (int i=0; sIdArg[i].mName; ++i) + if (type==sIdArg[i].mType) + { + mArgumentType = ArgumentType_Id; + mClass = sIdArg[i].mClass; + return; + } + + for (int i=0; sIndexArg[i].mName; ++i) + if (type==sIndexArg[i].mType) + { + mArgumentType = ArgumentType_Index; + mClass = sIndexArg[i].mClass; + return; + } + throw std::logic_error ("invalid argument-less UniversalId type"); } @@ -293,25 +312,6 @@ std::vector<CSMWorld::UniversalId::Type> CSMWorld::UniversalId::listReferenceabl return list; } -std::pair<int, const char *> CSMWorld::UniversalId::getIdArgPair (unsigned int index) -{ - std::pair<int, const char *> retPair; - - if ( index < IDARG_SIZE ) - { - retPair.first = sIdArg[index].mType; - retPair.second = sIdArg[index].mName; - } - - return retPair; -} - -unsigned int CSMWorld::UniversalId::getIdArgSize() -{ - return IDARG_SIZE; -} - - bool CSMWorld::operator== (const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right) { return left.isEqual (right); diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 8042c3dfdf..ffd99e572f 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -34,7 +34,7 @@ namespace CSMWorld enum Type { - Type_None, + Type_None = 0, Type_Globals, Type_Global, Type_VerificationResults, @@ -86,9 +86,16 @@ namespace CSMWorld Type_Reference, Type_RegionMap, Type_Filter, - Type_Filters + Type_Filters, + Type_Topics, + Type_Topic, + Type_Journals, + Type_Journal, + Type_Scene }; + enum { NumberOfTypes = Type_Scene+1 }; + private: Class mClass; @@ -102,7 +109,6 @@ namespace CSMWorld UniversalId (const std::string& universalId); UniversalId (Type type = Type_None); - ///< Using a type for a non-argument-less UniversalId will throw an exception. UniversalId (Type type, const std::string& id); ///< Using a type for a non-ID-argument UniversalId will throw an exception. @@ -134,9 +140,6 @@ namespace CSMWorld ///< Will return an empty string, if no icon is available. static std::vector<Type> listReferenceableTypes(); - - static std::pair<int, const char *> getIdArgPair (unsigned int index); - static unsigned int getIdArgSize (); }; bool operator== (const UniversalId& left, const UniversalId& right); diff --git a/apps/opencs/ocspropertywidget.cpp b/apps/opencs/ocspropertywidget.cpp deleted file mode 100644 index 68315201ae..0000000000 --- a/apps/opencs/ocspropertywidget.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "ocspropertywidget.hpp" - -OcsPropertyWidget::OcsPropertyWidget(QObject *parent) : - QObject(parent) -{ -} diff --git a/apps/opencs/ocspropertywidget.hpp b/apps/opencs/ocspropertywidget.hpp deleted file mode 100644 index fc64a0a692..0000000000 --- a/apps/opencs/ocspropertywidget.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef OCSPROPERTYWIDGET_HPP -#define OCSPROPERTYWIDGET_HPP - -#include <QObject> - -class OcsPropertyWidget : public QObject -{ - Q_OBJECT -public: - explicit OcsPropertyWidget(QObject *parent = 0); - -signals: - -public slots: - -}; - -#endif // OCSPROPERTYWIDGET_HPP diff --git a/apps/opencs/view/doc/adjusterwidget.cpp b/apps/opencs/view/doc/adjusterwidget.cpp new file mode 100644 index 0000000000..09e58690fc --- /dev/null +++ b/apps/opencs/view/doc/adjusterwidget.cpp @@ -0,0 +1,115 @@ + +#include "adjusterwidget.hpp" + +#include <stdexcept> +#include <string> + +#include <boost/filesystem.hpp> + +#include <QHBoxLayout> +#include <QLabel> +#include <QStyle> + +CSVDoc::AdjusterWidget::AdjusterWidget (QWidget *parent) + : QWidget (parent), mValid (false), mAction (ContentAction_Undefined) +{ + QHBoxLayout *layout = new QHBoxLayout (this); + + mIcon = new QLabel (this); + + layout->addWidget (mIcon, 0); + + mMessage = new QLabel (this); + mMessage->setWordWrap (true); + mMessage->setSizePolicy (QSizePolicy (QSizePolicy::Minimum, QSizePolicy::Minimum)); + + layout->addWidget (mMessage, 1); + + setName ("", false); + + setLayout (layout); +} + +void CSVDoc::AdjusterWidget::setAction (ContentAction action) +{ + mAction = action; +} + +void CSVDoc::AdjusterWidget::setLocalData (const boost::filesystem::path& localData) +{ + mLocalData = localData; +} + +boost::filesystem::path CSVDoc::AdjusterWidget::getPath() const +{ + if (!mValid) + throw std::logic_error ("invalid content file path"); + + 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) +{ + QString message; + + mValid = (!name.isEmpty()); + + if (!mValid) + { + message = "No name."; + } + else + { + boost::filesystem::path path (name.toUtf8().data()); + + bool isLegacyPath = (path.extension() == ".esm" || + path.extension() == ".esp"); + + bool isFilePathChanged = (path.parent_path().string() != mLocalData.string()); + + if (isLegacyPath) + path.replace_extension (addon ? ".omwaddon" : ".omwgame"); + + //if the file came from data-local and is not a legacy file to be converted, + //don't worry about doing a file check. + if (!isFilePathChanged && !isLegacyPath) + { + // path already points to the local data directory + message = QString::fromUtf8 (("Will be saved as: " + path.string()).c_str()); + mResultPath = path; + } + //in all other cases, ensure the path points to data-local and do an existing file check + else + { + // path points somewhere else or is a leaf name. + if (isFilePathChanged) + path = mLocalData / path.filename(); + + message = QString::fromUtf8 (("Will be saved as: " + path.string()).c_str()); + mResultPath = path; + + if (boost::filesystem::exists (path)) + { + /// \todo add an user setting to make this an error. + message += "<p>A file with the same name already exists. If you continue, it will be overwritten."; + } + } + } + + mMessage->setText (message); + mIcon->setPixmap (style()->standardIcon ( + mValid ? QStyle::SP_MessageBoxInformation : QStyle::SP_MessageBoxWarning). + pixmap (QSize (16, 16))); + + emit stateChanged (mValid); +} diff --git a/apps/opencs/view/doc/adjusterwidget.hpp b/apps/opencs/view/doc/adjusterwidget.hpp new file mode 100644 index 0000000000..91e308236f --- /dev/null +++ b/apps/opencs/view/doc/adjusterwidget.hpp @@ -0,0 +1,56 @@ +#ifndef CSV_DOC_ADJUSTERWIDGET_H +#define CSV_DOC_ADJUSTERWIDGET_H + +#include <boost/filesystem/path.hpp> + +#include <QWidget> + +class QLabel; + +namespace CSVDoc +{ + enum ContentAction + { + ContentAction_New, + ContentAction_Edit, + ContentAction_Undefined + }; + + class AdjusterWidget : public QWidget + { + Q_OBJECT + + public: + + boost::filesystem::path mLocalData; + QLabel *mMessage; + QLabel *mIcon; + bool mValid; + boost::filesystem::path mResultPath; + ContentAction mAction; + bool mDoFilenameCheck; + + public: + + AdjusterWidget (QWidget *parent = 0); + + void setLocalData (const boost::filesystem::path& localData); + void setAction (ContentAction action); + + void setFilenameCheck (bool doCheck); + bool isValid() const; + + boost::filesystem::path getPath() const; + ///< This function must not be called if there is no valid path. + + public slots: + + void setName (const QString& name, bool addon); + + signals: + + void stateChanged (bool valid); + }; +} + +#endif diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index f956317a71..ab56415a14 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -9,264 +9,166 @@ #include <QSpacerItem> #include <QPushButton> #include <QLabel> +#include <QGroupBox> -#include <components/fileorderlist/model/datafilesmodel.hpp> -#include <components/fileorderlist/model/pluginsproxymodel.hpp> -#include <components/fileorderlist/model/esm/esmfile.hpp> +#include "components/contentselector/model/esmfile.hpp" +#include "components/contentselector/view/contentselector.hpp" -#include <components/fileorderlist/utils/lineedit.hpp> +#include "filewidget.hpp" +#include "adjusterwidget.hpp" -FileDialog::FileDialog(QWidget *parent) : - QDialog(parent) +CSVDoc::FileDialog::FileDialog(QWidget *parent) : + QDialog(parent), mSelector (0), mFileWidget (0), mAdjusterWidget (0) { - setupUi(this); + ui.setupUi (this); + resize(400, 400); - // Models - 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; + setObjectName ("FileDialog"); + mSelector = new ContentSelectorView::ContentSelector (ui.contentSelectorWidget); + mAdjusterWidget = new AdjusterWidget (this); +} - 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); +void CSVDoc::FileDialog::addFiles(const QString &path) +{ + mSelector->addFiles(path); +} - // Set the row height to the size of the checkboxes - mastersTable->verticalHeader()->setDefaultSectionSize(height); - mastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); - mastersTable->verticalHeader()->hide(); +QStringList CSVDoc::FileDialog::selectedFilePaths() +{ + QStringList filePaths; - 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); + foreach (ContentSelectorModel::EsmFile *file, mSelector->selectedFiles() ) + filePaths.append(file->filePath()); - pluginsTable->verticalHeader()->setDefaultSectionSize(height); - pluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed); + return filePaths; +} - // Hide the profile elements - profileLabel->hide(); - profilesComboBox->hide(); - newProfileButton->hide(); - deleteProfileButton->hide(); +void CSVDoc::FileDialog::setLocalData (const boost::filesystem::path& localData) +{ + mAdjusterWidget->setLocalData (localData); +} - // Add some extra widgets - QHBoxLayout *nameLayout = new QHBoxLayout(); - QSpacerItem *spacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); +void CSVDoc::FileDialog::showDialog (ContentAction action) +{ + mAction = action; - mNameLabel = new QLabel(tr("File Name:"), this); + ui.projectGroupBoxLayout->insertWidget (0, mAdjusterWidget); - QRegExpValidator *validator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9\\s]*$")); - mNameLineEdit = new LineEdit(this); - mNameLineEdit->setValidator(validator); + switch (mAction) + { + case ContentAction_New: + buildNewFileView(); + break; - nameLayout->addSpacerItem(spacer); - nameLayout->addWidget(mNameLabel); - nameLayout->addWidget(mNameLineEdit); + case ContentAction_Edit: + buildOpenFileView(); + break; - mButtonBox = new QDialogButtonBox(this); + default: + break; + } - mCreateButton = new QPushButton(tr("Create"), this); - mCreateButton->setEnabled(false); + mAdjusterWidget->setFilenameCheck (mAction == ContentAction_New); - verticalLayout->addLayout(nameLayout); - verticalLayout->addWidget(mButtonBox); + //connections common to both dialog view flavors + connect (mSelector, SIGNAL (signalCurrentGamefileIndexChanged (int)), + this, SLOT (slotUpdateAcceptButton (int))); - // Set sizes - QList<int> sizeList; - sizeList << 175; - sizeList << 200; + connect (ui.projectButtonBox, SIGNAL (rejected()), this, SLOT (slotRejected())); - 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())); - connect(mDataFilesModel, SIGNAL(checkedItemsChanged(QStringList)), this, SLOT(updateOpenButton(QStringList))); - connect(mNameLineEdit, SIGNAL(textChanged(QString)), this, SLOT(updateCreateButton(QString))); + QPushButton* createButton = ui.projectButtonBox->button (QDialogButtonBox::Ok); + createButton->setText ("Create"); + 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))); - connect(mastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex))); + mFileWidget->setType (true); + mFileWidget->extensionLabelIsVisible(true); - connect(mCreateButton, SIGNAL(clicked()), this, SLOT(createButtonClicked())); + ui.projectGroupBoxLayout->insertWidget (0, mFileWidget); - connect(mButtonBox, SIGNAL(accepted()), this, SLOT(accept())); - connect(mButtonBox, SIGNAL(rejected()), this, SLOT(reject())); -} + connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)), + mAdjusterWidget, SLOT (setName (const QString&, bool))); -void FileDialog::updateViews() -{ - // Ensure the columns are hidden because sort() re-enables them - mastersTable->setColumnHidden(1, true); - mastersTable->setColumnHidden(3, true); - mastersTable->setColumnHidden(4, true); - mastersTable->setColumnHidden(5, true); - mastersTable->setColumnHidden(6, true); - mastersTable->setColumnHidden(7, true); - mastersTable->setColumnHidden(8, true); - mastersTable->resizeColumnsToContents(); - - pluginsTable->setColumnHidden(1, true); - pluginsTable->setColumnHidden(3, true); - pluginsTable->setColumnHidden(4, true); - pluginsTable->setColumnHidden(5, true); - pluginsTable->setColumnHidden(6, true); - pluginsTable->setColumnHidden(7, true); - pluginsTable->setColumnHidden(8, true); - pluginsTable->resizeColumnsToContents(); + connect (mFileWidget, SIGNAL (nameChanged(const QString &, bool)), + this, SLOT (slotUpdateAcceptButton(const QString &, bool))); + connect (ui.projectButtonBox, SIGNAL (accepted()), this, SLOT (slotNewFile())); } -void FileDialog::updateOpenButton(const QStringList &items) +void CSVDoc::FileDialog::buildOpenFileView() { - QPushButton *openButton = mButtonBox->button(QDialogButtonBox::Open); - - if (!openButton) - return; + setWindowTitle(tr("Open")); + ui.projectGroupBox->setTitle (QString("")); - openButton->setEnabled(!items.isEmpty()); -} + ui.projectButtonBox->button(QDialogButtonBox::Ok)->setEnabled (false); -void FileDialog::updateCreateButton(const QString &name) -{ - if (!mCreateButton->isVisible()) - return; + connect (mSelector, SIGNAL (signalAddonFileSelected (int)), this, SLOT (slotUpdateAcceptButton (int))); + connect (mSelector, SIGNAL (signalAddonFileUnselected (int)), this, SLOT (slotUpdateAcceptButton (int))); - 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); - mFilterProxyModel->setFilterRegExp(filterRe); -} + QString name = ""; -void FileDialog::addFiles(const QString &path) -{ - mDataFilesModel->addFiles(path); - mDataFilesModel->sort(3); // Sort by date accessed -} + if (mAction == ContentAction_New) + name = mFileWidget->getName(); -void FileDialog::setEncoding(const QString &encoding) -{ - mDataFilesModel->setEncoding(encoding); + slotUpdateAcceptButton (name, true); } -void FileDialog::setCheckState(QModelIndex index) +void CSVDoc::FileDialog::slotUpdateAcceptButton(const QString &name, bool) { - if (!index.isValid()) - return; - - QObject *object = QObject::sender(); - - // Not a signal-slot call - if (!object) - return; + bool success = (mSelector->selectedFiles().size() > 0); + bool isNew = (mAction == ContentAction_New); - if (object->objectName() == QLatin1String("PluginsTable")) { - QModelIndex sourceIndex = mPluginsProxyModel->mapToSource( - mFilterProxyModel->mapToSource(index)); - - if (sourceIndex.isValid()) { - (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) - ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) - : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); - } + if (isNew) + success = success && !(name.isEmpty()); + else + { + ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back(); + mAdjusterWidget->setName (file->filePath(), !file->isGameFile()); } - if (object->objectName() == QLatin1String("MastersTable")) { - QModelIndex sourceIndex = mMastersProxyModel->mapToSource(index); - - if (sourceIndex.isValid()) { - (mDataFilesModel->checkState(sourceIndex) == Qt::Checked) - ? mDataFilesModel->setCheckState(sourceIndex, Qt::Unchecked) - : mDataFilesModel->setCheckState(sourceIndex, Qt::Checked); - } - } - - return; + ui.projectButtonBox->button (QDialogButtonBox::Ok)->setEnabled (success); } -QStringList FileDialog::checkedItemsPaths() +QString CSVDoc::FileDialog::filename() const { - return mDataFilesModel->checkedItemsPaths(); -} + if (mAction == ContentAction_New) + return ""; -QString FileDialog::fileName() -{ - return mNameLineEdit->text(); + return mSelector->currentFile(); } -void FileDialog::openFile() +void CSVDoc::FileDialog::slotRejected() { - setWindowTitle(tr("Open")); - - mNameLabel->hide(); - mNameLineEdit->hide(); - mCreateButton->hide(); - - mButtonBox->removeButton(mCreateButton); - mButtonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Open); - QPushButton *openButton = mButtonBox->button(QDialogButtonBox::Open); - openButton->setEnabled(false); - - show(); - raise(); - activateWindow(); + emit rejected(); + close(); } -void FileDialog::newFile() +void CSVDoc::FileDialog::slotNewFile() { - setWindowTitle(tr("New")); - - mNameLabel->show(); - mNameLineEdit->clear(); - mNameLineEdit->show(); - mCreateButton->show(); - - mButtonBox->setStandardButtons(QDialogButtonBox::Cancel); - mButtonBox->addButton(mCreateButton, QDialogButtonBox::ActionRole); - - show(); - raise(); - activateWindow(); + emit signalCreateNewFile (mAdjusterWidget->getPath()); } -void FileDialog::accept() +void CSVDoc::FileDialog::slotOpenFile() { - emit openFiles(); -} + ContentSelectorModel::EsmFile *file = mSelector->selectedFiles().back(); -void FileDialog::createButtonClicked() -{ - emit createNewFile(); + mAdjusterWidget->setName (file->filePath(), !file->isGameFile()); + + emit signalOpenFiles (mAdjusterWidget->getPath()); } diff --git a/apps/opencs/view/doc/filedialog.hpp b/apps/opencs/view/doc/filedialog.hpp index b21618d5de..d9fd569435 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -4,63 +4,71 @@ #include <QDialog> #include <QModelIndex> -#include "ui_datafilespage.h" +#include <boost/filesystem/path.hpp> +#include "adjusterwidget.hpp" -class QDialogButtonBox; -class QSortFilterProxyModel; -class QAbstractItemModel; -class QPushButton; -class QStringList; -class QString; -class QMenu; +#ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED +#define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED +Q_DECLARE_METATYPE (boost::filesystem::path) +#endif + +#include "ui_filedialog.h" class DataFilesModel; class PluginsProxyModel; -class FileDialog : public QDialog, private Ui::DataFilesPage +namespace ContentSelectorView +{ + class ContentSelector; +} + +namespace CSVDoc { - Q_OBJECT -public: - explicit FileDialog(QWidget *parent = 0); - void addFiles(const QString &path); - void setEncoding(const QString &encoding); + class FileWidget; + + class FileDialog : public QDialog + { + Q_OBJECT + + private: + + ContentSelectorView::ContentSelector *mSelector; + Ui::FileDialog ui; + ContentAction mAction; + FileWidget *mFileWidget; + AdjusterWidget *mAdjusterWidget; + + public: - void openFile(); - void newFile(); - void accepted(); + explicit FileDialog(QWidget *parent = 0); + void showDialog (ContentAction action); - QStringList checkedItemsPaths(); - QString fileName(); + void addFiles (const QString &path); -signals: - void openFiles(); - void createNewFile(); - -public slots: - void accept(); + QString filename() const; + QStringList selectedFilePaths(); -private slots: - void updateViews(); - void updateOpenButton(const QStringList &items); - void updateCreateButton(const QString &name); - void setCheckState(QModelIndex index); + void setLocalData (const boost::filesystem::path& localData); - void filterChanged(const QString &filter); + private: - void createButtonClicked(); + void buildNewFileView(); + void buildOpenFileView(); -private: - QLabel *mNameLabel; - LineEdit *mNameLineEdit; + signals: - QPushButton *mCreateButton; - QDialogButtonBox *mButtonBox; + void signalOpenFiles (const boost::filesystem::path &path); + void signalCreateNewFile (const boost::filesystem::path &path); - DataFilesModel *mDataFilesModel; + void signalUpdateAcceptButton (bool, int); - PluginsProxyModel *mPluginsProxyModel; - QSortFilterProxyModel *mMastersProxyModel; - QSortFilterProxyModel *mFilterProxyModel; -}; + private slots: + void slotNewFile(); + void slotOpenFile(); + void slotUpdateAcceptButton (int); + void slotUpdateAcceptButton (const QString &, bool); + void slotRejected(); + }; +} #endif // FILEDIALOG_HPP diff --git a/apps/opencs/view/doc/filewidget.cpp b/apps/opencs/view/doc/filewidget.cpp new file mode 100644 index 0000000000..9cd2fad422 --- /dev/null +++ b/apps/opencs/view/doc/filewidget.cpp @@ -0,0 +1,58 @@ + +#include "filewidget.hpp" + +#include <QHBoxLayout> +#include <QLineEdit> +#include <QLabel> +#include <QRegExpValidator> +#include <QRegExp> + +QString CSVDoc::FileWidget::getExtension() const +{ + return mAddon ? ".omwaddon" : ".omwgame"; +} + +CSVDoc::FileWidget::FileWidget (QWidget *parent) : QWidget (parent), mAddon (false) +{ + QHBoxLayout *layout = new QHBoxLayout (this); + + mInput = new QLineEdit (this); + mInput->setValidator (new QRegExpValidator(QRegExp("^[a-zA-Z0-9\\s]*$"))); + + layout->addWidget (mInput, 1); + + mType = new QLabel (this); + + layout ->addWidget (mType); + + connect (mInput, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&))); + + setLayout (layout); +} + +void CSVDoc::FileWidget::setType (bool addon) +{ + mAddon = addon; + + mType->setText (getExtension()); +} + +QString CSVDoc::FileWidget::getName() const +{ + QString text = mInput->text(); + + if (text.isEmpty()) + return ""; + + return text + getExtension(); +} + +void CSVDoc::FileWidget::textChanged (const QString& text) +{ + emit nameChanged (getName(), mAddon); +} + +void CSVDoc::FileWidget::extensionLabelIsVisible(bool visible) +{ + mType->setVisible(visible); +} diff --git a/apps/opencs/view/doc/filewidget.hpp b/apps/opencs/view/doc/filewidget.hpp new file mode 100644 index 0000000000..ff09d71a39 --- /dev/null +++ b/apps/opencs/view/doc/filewidget.hpp @@ -0,0 +1,42 @@ +#ifndef CSV_DOC_FILEWIDGET_H +#define CSV_DOC_FILEWIDGET_H + +#include <QWidget> + +class QLabel; +class QString; +class QLineEdit; + +namespace CSVDoc +{ + class FileWidget : public QWidget + { + Q_OBJECT + + bool mAddon; + QLineEdit *mInput; + QLabel *mType; + + QString getExtension() const; + + public: + + FileWidget (QWidget *parent = 0); + + void setType (bool addon); + + QString getName() const; + + void extensionLabelIsVisible(bool visible); + + private slots: + + void textChanged (const QString& text); + + signals: + + void nameChanged (const QString& file, bool addon); + }; +} + +#endif diff --git a/apps/opencs/view/doc/newgame.cpp b/apps/opencs/view/doc/newgame.cpp new file mode 100644 index 0000000000..98681c499d --- /dev/null +++ b/apps/opencs/view/doc/newgame.cpp @@ -0,0 +1,68 @@ + +#include "newgame.hpp" + +#include <QApplication> +#include <QDesktopWidget> +#include <QVBoxLayout> +#include <QDialogButtonBox> +#include <QPushButton> + +#include "filewidget.hpp" +#include "adjusterwidget.hpp" + +CSVDoc::NewGameDialogue::NewGameDialogue() +{ + setWindowTitle ("Create New Game"); + + QVBoxLayout *layout = new QVBoxLayout (this); + + mFileWidget = new FileWidget (this); + mFileWidget->setType (false); + + layout->addWidget (mFileWidget, 1); + + mAdjusterWidget = new AdjusterWidget (this); + + layout->addWidget (mAdjusterWidget, 1); + + QDialogButtonBox *buttons = new QDialogButtonBox (this); + + mCreate = new QPushButton ("Create", this); + mCreate->setDefault (true); + mCreate->setEnabled (false); + + buttons->addButton (mCreate, QDialogButtonBox::AcceptRole); + + QPushButton *cancel = new QPushButton ("Cancel", this); + + buttons->addButton (cancel, QDialogButtonBox::RejectRole); + + layout->addWidget (buttons); + + setLayout (layout); + + connect (mAdjusterWidget, SIGNAL (stateChanged (bool)), this, SLOT (stateChanged (bool))); + connect (mCreate, SIGNAL (clicked()), this, SLOT (create())); + connect (cancel, SIGNAL (clicked()), this, SLOT (reject())); + connect (mFileWidget, SIGNAL (nameChanged (const QString&, bool)), + mAdjusterWidget, SLOT (setName (const QString&, bool))); + + QRect scr = QApplication::desktop()->screenGeometry(); + QRect rect = geometry(); + move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); +} + +void CSVDoc::NewGameDialogue::setLocalData (const boost::filesystem::path& localData) +{ + mAdjusterWidget->setLocalData (localData); +} + +void CSVDoc::NewGameDialogue::stateChanged (bool valid) +{ + mCreate->setEnabled (valid); +} + +void CSVDoc::NewGameDialogue::create() +{ + emit createRequest (mAdjusterWidget->getPath()); +} diff --git a/apps/opencs/view/doc/newgame.hpp b/apps/opencs/view/doc/newgame.hpp new file mode 100644 index 0000000000..9ad7ea1690 --- /dev/null +++ b/apps/opencs/view/doc/newgame.hpp @@ -0,0 +1,47 @@ +#ifndef CSV_DOC_NEWGAME_H +#define CSV_DOC_NEWGAME_H + +#include <boost/filesystem/path.hpp> + +#include <QDialog> +#include <QMetaType> + +#ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED +#define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED +Q_DECLARE_METATYPE (boost::filesystem::path) +#endif + +class QPushButton; + +namespace CSVDoc +{ + class FileWidget; + class AdjusterWidget; + + class NewGameDialogue : public QDialog + { + Q_OBJECT + + QPushButton *mCreate; + FileWidget *mFileWidget; + AdjusterWidget *mAdjusterWidget; + + public: + + NewGameDialogue(); + + void setLocalData (const boost::filesystem::path& localData); + + signals: + + void createRequest (const boost::filesystem::path& file); + + private slots: + + void stateChanged (bool valid); + + void create(); + }; +} + +#endif diff --git a/apps/opencs/view/doc/startup.cpp b/apps/opencs/view/doc/startup.cpp index 6c1e740589..5d59492c64 100644 --- a/apps/opencs/view/doc/startup.cpp +++ b/apps/opencs/view/doc/startup.cpp @@ -3,21 +3,117 @@ #include <QApplication> #include <QDesktopWidget> -#include <QPushButton> +#include <QVBoxLayout> #include <QHBoxLayout> #include <QRect> +#include <QGridLayout> +#include <QLabel> +#include <QIcon> +#include <QPushButton> + +QPushButton *CSVDoc::StartupDialogue::addButton (const QString& label, const QIcon& icon) +{ + int column = mColumn--; + + QPushButton *button = new QPushButton (this); + + button->setIcon (QIcon (icon)); + + button->setSizePolicy (QSizePolicy (QSizePolicy::Preferred, QSizePolicy::Preferred)); + + mLayout->addWidget (button, 0, column); + + mLayout->addWidget (new QLabel (label, this), 1, column, Qt::AlignCenter); + + int width = mLayout->itemAtPosition (1, column)->widget()->sizeHint().width(); + + if (width>mWidth) + mWidth = width; -CSVDoc::StartupDialogue::StartupDialogue() + return button; +} + + +QWidget *CSVDoc::StartupDialogue::createButtons() { - QHBoxLayout *layout = new QHBoxLayout (this); + QWidget *widget = new QWidget (this); - QPushButton *createDocument = new QPushButton ("new", this); - connect (createDocument, SIGNAL (clicked()), this, SIGNAL (createDocument())); - layout->addWidget (createDocument); + mLayout = new QGridLayout (widget); - QPushButton *loadDocument = new QPushButton ("load", this); + /// \todo add icons + QPushButton *loadDocument = addButton ("Edit A Content File", QIcon (":startup/edit-content")); connect (loadDocument, SIGNAL (clicked()), this, SIGNAL (loadDocument())); - layout->addWidget (loadDocument); + + QPushButton *createAddon = addButton ("Create A New Addon", QIcon (":startup/create-addon")); + connect (createAddon, SIGNAL (clicked()), this, SIGNAL (createAddon())); + + QPushButton *createGame = addButton ("Create A New Game", QIcon (":startup/create-game")); + connect (createGame, SIGNAL (clicked()), this, SIGNAL (createGame())); + + for (int i=0; i<3; ++i) + mLayout->setColumnMinimumWidth (i, mWidth); + + mLayout->setRowMinimumHeight (0, mWidth); + + mLayout->setSizeConstraint (QLayout::SetMinimumSize); + mLayout->setHorizontalSpacing (32); + + mLayout->setContentsMargins (16, 16, 16, 8); + + loadDocument->setIconSize (QSize (mWidth, mWidth)); + createGame->setIconSize (QSize (mWidth, mWidth)); + createAddon->setIconSize (QSize (mWidth, mWidth)); + + widget->setLayout (mLayout); + + return widget; +} + +QWidget *CSVDoc::StartupDialogue::createTools() +{ + QWidget *widget = new QWidget (this); + + QHBoxLayout *layout = new QHBoxLayout (widget); + layout->setDirection (QBoxLayout::RightToLeft); + layout->setContentsMargins (4, 4, 4, 4); + + QPushButton *config = new QPushButton (widget); + + config->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); + config->setIcon (QIcon (":startup/configure")); + + layout->addWidget (config); + + layout->addWidget (new QWidget, 1); // dummy widget; stops buttons from taking all the space + + widget->setLayout (layout); + + connect (config, SIGNAL (clicked()), this, SIGNAL (editConfig())); + + return widget; +} + +CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2) +{ + setWindowTitle ("Open CS"); + + QVBoxLayout *layout = new QVBoxLayout (this); + + layout->setContentsMargins (0, 0, 0, 0); + + layout->addWidget (createButtons()); + 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); diff --git a/apps/opencs/view/doc/startup.hpp b/apps/opencs/view/doc/startup.hpp index f24d2a64ba..f059a44e5c 100644 --- a/apps/opencs/view/doc/startup.hpp +++ b/apps/opencs/view/doc/startup.hpp @@ -3,21 +3,43 @@ #include <QWidget> +class QGridLayout; +class QString; +class QPushButton; +class QWidget; +class QIcon; + namespace CSVDoc { class StartupDialogue : public QWidget { Q_OBJECT + private: + + int mWidth; + int mColumn; + QGridLayout *mLayout; + + QPushButton *addButton (const QString& label, const QIcon& icon); + + QWidget *createButtons(); + + QWidget *createTools(); + public: StartupDialogue(); signals: - void createDocument(); + void createGame(); + + void createAddon(); void loadDocument(); + + void editConfig(); }; } diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index 6801ea20df..5713449f23 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -27,9 +27,13 @@ void CSVDoc::View::setupFileMenu() { QMenu *file = menuBar()->addMenu (tr ("&File")); - QAction *new_ = new QAction (tr ("New"), this); - connect (new_, SIGNAL (triggered()), this, SIGNAL (newDocumentRequest())); - file->addAction (new_); + QAction *newGame = new QAction (tr ("New Game"), this); + connect (newGame, SIGNAL (triggered()), this, SIGNAL (newGameRequest())); + file->addAction (newGame); + + QAction *newAddon = new QAction (tr ("New Addon"), this); + connect (newAddon, SIGNAL (triggered()), this, SIGNAL (newAddonRequest())); + file->addAction (newAddon); QAction *open = new QAction (tr ("&Open"), this); connect (open, SIGNAL (triggered()), this, SIGNAL (loadDocumentRequest())); @@ -67,7 +71,7 @@ void CSVDoc::View::setupEditMenu() edit->addAction (mRedo); QAction *userSettings = new QAction (tr ("&Preferences"), this); - connect (userSettings, SIGNAL (triggered()), this, SLOT (showUserSettings())); + connect (userSettings, SIGNAL (triggered()), this, SIGNAL (editSettingsRequest())); edit->addAction (userSettings); } @@ -111,6 +115,10 @@ void CSVDoc::View::setupWorldMenu() world->addSeparator(); // items that don't represent single record lists follow here + QAction *scene = new QAction (tr ("Scene"), this); + connect (scene, SIGNAL (triggered()), this, SLOT (addSceneSubView())); + world->addAction (scene); + QAction *regionMap = new QAction (tr ("Region Map"), this); connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView())); world->addAction (regionMap); @@ -155,6 +163,14 @@ void CSVDoc::View::setupMechanicsMenu() QAction *spells = new QAction (tr ("Spells"), this); connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView())); mechanics->addAction (spells); + + QAction *topics = new QAction (tr ("Topics"), this); + connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView())); + mechanics->addAction (topics); + + QAction *journals = new QAction (tr ("Journals"), this); + connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView())); + mechanics->addAction (journals); } void CSVDoc::View::setupAssetsMenu() @@ -180,7 +196,7 @@ void CSVDoc::View::updateTitle() { std::ostringstream stream; - stream << mDocument->getName(); + stream << mDocument->getSavePath().filename().string(); if (mDocument->getState() & CSMDoc::State_Modified) stream << " *"; @@ -399,6 +415,21 @@ void CSVDoc::View::addFiltersSubView() addSubView (CSMWorld::UniversalId::Type_Filters); } +void CSVDoc::View::addSceneSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Scene); +} + +void CSVDoc::View::addTopicsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Topics); +} + +void CSVDoc::View::addJournalsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Journals); +} + void CSVDoc::View::abortOperation (int type) { mDocument->abortOperation (type); @@ -415,13 +446,6 @@ void CSVDoc::View::exit() emit exitApplicationRequest (this); } -void CSVDoc::View::showUserSettings() -{ - CSVSettings::UserSettingsDialog *settingsDialog = new CSVSettings::UserSettingsDialog(this); - - settingsDialog->show(); -} - void CSVDoc::View::resizeViewWidth (int width) { if (width >= 0) diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 56c0b3edd4..2a31d9d807 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -106,12 +106,16 @@ namespace CSVDoc signals: - void newDocumentRequest(); + void newGameRequest(); + + void newAddonRequest(); void loadDocumentRequest(); void exitApplicationRequest (CSVDoc::View *view); + void editSettingsRequest(); + public slots: void addSubView (const CSMWorld::UniversalId& id); @@ -160,7 +164,11 @@ namespace CSVDoc void addFiltersSubView(); - void showUserSettings(); + void addSceneSubView(); + + void addTopicsSubView(); + + void addJournalsSubView(); void toggleShowStatusBar (bool show); }; diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 6d06e42488..a4849795b2 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -8,12 +8,13 @@ #include "../../model/doc/documentmanager.hpp" #include "../../model/doc/document.hpp" +#include "../../model/world/columns.hpp" #include "../world/util.hpp" #include "../world/enumdelegate.hpp" #include "../world/vartypedelegate.hpp" #include "../world/recordstatusdelegate.hpp" -#include "../world/refidtypedelegate.hpp" +#include "../world/idtypedelegate.hpp" #include "../settings/usersettingsdialog.hpp" #include "view.hpp" @@ -43,51 +44,6 @@ void CSVDoc::ViewManager::updateIndices() CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) : mDocumentManager (documentManager), mExitOnSaveStateChange(false), mUserWarned(false) { - static const char *sSpecialisations[] = - { - "Combat", "Magic", "Stealth", 0 - }; - - static const char *sAttributes[] = - { - "Strength", "Intelligence", "Willpower", "Agility", "Speed", "Endurance", "Personality", - "Luck", 0 - }; - - static const char *sSpellTypes[] = - { - "Spell", "Ability", "Blight", "Disease", "Curse", "Power", 0 - }; - - static const char *sApparatusTypes[] = - { - "Mortar & Pestle", "Albemic", "Calcinator", "Retort", 0 - }; - - static const char *sArmorTypes[] = - { - "Helmet", "Cuirass", "Left Pauldron", "Right Pauldron", "Greaves", "Boots", "Left Gauntlet", - "Right Gauntlet", "Shield", "Left Bracer", "Right Bracer", 0 - }; - - static const char *sClothingTypes[] = - { - "Pants", "Shoes", "Shirt", "Belt", "Robe", "Right Glove", "Left Glove", "Skirt", "Ring", - "Amulet", 0 - }; - - static const char *sCreatureTypes[] = - { - "Creature", "Deadra", "Undead", "Humanoid", 0 - }; - - static const char *sWeaponTypes[] = - { - "Short Blade 1H", "Long Blade 1H", "Long Blade 2H", "Blunt 1H", "Blunt 2H Close", - "Blunt 2H Wide", "Spear 2H", "Axe 1H", "Axe 2H", "Bow", "Crossbow", "Thrown", "Arrow", - "Bolt", 0 - }; - mDelegateFactories = new CSVWorld::CommandDelegateFactoryCollection; mDelegateFactories->add (CSMWorld::ColumnBase::Display_GmstVarType, @@ -96,38 +52,38 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) mDelegateFactories->add (CSMWorld::ColumnBase::Display_GlobalVarType, new CSVWorld::VarTypeDelegateFactory (ESM::VT_Short, ESM::VT_Long, ESM::VT_Float)); - mDelegateFactories->add (CSMWorld::ColumnBase::Display_Specialisation, - new CSVWorld::EnumDelegateFactory (sSpecialisations)); - - mDelegateFactories->add (CSMWorld::ColumnBase::Display_Attribute, - new CSVWorld::EnumDelegateFactory (sAttributes, true)); - - mDelegateFactories->add (CSMWorld::ColumnBase::Display_SpellType, - new CSVWorld::EnumDelegateFactory (sSpellTypes)); - - mDelegateFactories->add (CSMWorld::ColumnBase::Display_ApparatusType, - new CSVWorld::EnumDelegateFactory (sApparatusTypes)); - - mDelegateFactories->add (CSMWorld::ColumnBase::Display_ArmorType, - new CSVWorld::EnumDelegateFactory (sArmorTypes)); - - mDelegateFactories->add (CSMWorld::ColumnBase::Display_ClothingType, - new CSVWorld::EnumDelegateFactory (sClothingTypes)); + mDelegateFactories->add (CSMWorld::ColumnBase::Display_RecordState, + new CSVWorld::RecordStatusDelegateFactory()); - mDelegateFactories->add (CSMWorld::ColumnBase::Display_CreatureType, - new CSVWorld::EnumDelegateFactory (sCreatureTypes)); + mDelegateFactories->add (CSMWorld::ColumnBase::Display_RefRecordType, + new CSVWorld::IdTypeDelegateFactory()); - mDelegateFactories->add (CSMWorld::ColumnBase::Display_WeaponType, - new CSVWorld::EnumDelegateFactory (sWeaponTypes)); + struct Mapping + { + CSMWorld::ColumnBase::Display mDisplay; + CSMWorld::Columns::ColumnId mColumnId; + bool mAllowNone; + }; - mDelegateFactories->add (CSMWorld::ColumnBase::Display_RecordState, - new CSVWorld::RecordStatusDelegateFactory() ); + static const Mapping sMapping[] = + { + { CSMWorld::ColumnBase::Display_Specialisation, CSMWorld::Columns::ColumnId_Specialisation, false }, + { CSMWorld::ColumnBase::Display_Attribute, CSMWorld::Columns::ColumnId_Attribute, true }, + { CSMWorld::ColumnBase::Display_SpellType, CSMWorld::Columns::ColumnId_SpellType, false }, + { CSMWorld::ColumnBase::Display_ApparatusType, CSMWorld::Columns::ColumnId_ApparatusType, false }, + { CSMWorld::ColumnBase::Display_ArmorType, CSMWorld::Columns::ColumnId_ArmorType, false }, + { CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false }, + { CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, false }, + { CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false }, + { CSMWorld::ColumnBase::Display_DialogueType, CSMWorld::Columns::ColumnId_DialogueType, false } + }; - mDelegateFactories->add (CSMWorld::ColumnBase::Display_RefRecordType, - new CSVWorld::RefIdTypeDelegateFactory() ); + for (std::size_t i=0; i<sizeof (sMapping)/sizeof (Mapping); ++i) + mDelegateFactories->add (sMapping[i].mDisplay, new CSVWorld::EnumDelegateFactory ( + CSMWorld::Columns::getEnums (sMapping[i].mColumnId), sMapping[i].mAllowNone)); connect (&CSMSettings::UserSettings::instance(), SIGNAL (signalUpdateEditorSetting (const QString &, const QString &)), - this, SLOT (slotUpdateEditorSetting (const QString &, const QString &))); + this, SLOT (slotUpdateEditorSetting (const QString &, const QString &))); } CSVDoc::ViewManager::~ViewManager() @@ -152,13 +108,14 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document) View *view = new View (*this, document, countViews (document)+1); - mViews.push_back (view); view->show(); - connect (view, SIGNAL (newDocumentRequest ()), this, SIGNAL (newDocumentRequest())); + connect (view, SIGNAL (newGameRequest ()), this, SIGNAL (newGameRequest())); + connect (view, SIGNAL (newAddonRequest ()), this, SIGNAL (newAddonRequest())); connect (view, SIGNAL (loadDocumentRequest ()), this, SIGNAL (loadDocumentRequest())); + connect (view, SIGNAL (editSettingsRequest()), this, SIGNAL (editSettingsRequest())); updateIndices(); diff --git a/apps/opencs/view/doc/viewmanager.hpp b/apps/opencs/view/doc/viewmanager.hpp index 1f4dcd51b1..01f4951864 100644 --- a/apps/opencs/view/doc/viewmanager.hpp +++ b/apps/opencs/view/doc/viewmanager.hpp @@ -55,12 +55,16 @@ namespace CSVDoc signals: - void newDocumentRequest(); + void newGameRequest(); + + void newAddonRequest(); void loadDocumentRequest(); void closeMessageBox(); + void editSettingsRequest(); + public slots: void exitApplication (CSVDoc::View *view); diff --git a/apps/opencs/view/filter/filtercreator.cpp b/apps/opencs/view/filter/filtercreator.cpp index 47925ea57a..640c9fe785 100644 --- a/apps/opencs/view/filter/filtercreator.cpp +++ b/apps/opencs/view/filter/filtercreator.cpp @@ -6,6 +6,11 @@ #include "../../model/filter/filter.hpp" +#include "../../model/world/data.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/columns.hpp" +#include "../../model/world/idtable.hpp" + std::string CSVFilter::FilterCreator::getNamespace() const { switch (mScope->currentIndex()) @@ -28,6 +33,15 @@ std::string CSVFilter::FilterCreator::getId() const return getNamespace() + GenericCreator::getId(); } +void CSVFilter::FilterCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const +{ + int index = + dynamic_cast<CSMWorld::IdTable&> (*getData().getTableModel (getCollectionId())). + findColumnIndex (CSMWorld::Columns::ColumnId_Scope); + + command.addValue (index, mScope->currentIndex()); +} + CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) : GenericCreator (data, undoStack, id) @@ -39,7 +53,7 @@ CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoS mScope->addItem ("Project"); mScope->addItem ("Session"); - /// \ŧodo re-enable for OpenMW 1.1 + /// \todo re-enable for OpenMW 1.1 // mScope->addItem ("Content"); connect (mScope, SIGNAL (currentIndexChanged (int)), this, SLOT (setScope (int))); diff --git a/apps/opencs/view/filter/filtercreator.hpp b/apps/opencs/view/filter/filtercreator.hpp index 82d38d22c7..437d01c8da 100644 --- a/apps/opencs/view/filter/filtercreator.hpp +++ b/apps/opencs/view/filter/filtercreator.hpp @@ -25,6 +25,8 @@ namespace CSVFilter virtual std::string getId() const; + virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + public: FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack, diff --git a/apps/opencs/view/settings/usersettingsdialog.cpp b/apps/opencs/view/settings/usersettingsdialog.cpp index 21311c2dab..e73e24dcb4 100644 --- a/apps/opencs/view/settings/usersettingsdialog.cpp +++ b/apps/opencs/view/settings/usersettingsdialog.cpp @@ -1,5 +1,7 @@ #include "usersettingsdialog.hpp" +#include <boost/filesystem/path.hpp> + #include <QApplication> #include <QDesktopWidget> #include <QWidget> @@ -9,14 +11,14 @@ #include <QFile> #include <QPushButton> #include <QDockWidget> - #include <QGridLayout> +#include <QApplication> +#include <QDesktopWidget> + +#include "../../model/settings/support.hpp" #include "datadisplayformatpage.hpp" #include "windowpage.hpp" - -#include "../../model/settings/support.hpp" -#include <boost/filesystem/path.hpp> #include "settingwidget.hpp" CSVSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) : @@ -29,7 +31,11 @@ CSVSettings::UserSettingsDialog::UserSettingsDialog(QMainWindow *parent) : connect (mListWidget, SIGNAL (currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, - SLOT (slotChangePage (QListWidgetItem*, QListWidgetItem*))); + SLOT (slotChangePage (QListWidgetItem*, QListWidgetItem*))); + + QRect scr = QApplication::desktop()->screenGeometry(); + QRect rect = geometry(); + move (scr.center().x() - rect.center().x(), scr.center().y() - rect.center().y()); } CSVSettings::UserSettingsDialog::~UserSettingsDialog() diff --git a/apps/opencs/view/tools/reportsubview.cpp b/apps/opencs/view/tools/reportsubview.cpp index fe1be85d79..182d1cdd6c 100644 --- a/apps/opencs/view/tools/reportsubview.cpp +++ b/apps/opencs/view/tools/reportsubview.cpp @@ -6,6 +6,8 @@ #include "../../model/tools/reportmodel.hpp" +#include "../../view/world/idtypedelegate.hpp" + CSVTools::ReportSubView::ReportSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) : CSVDoc::SubView (id), mModel (document.getReport (id)) { @@ -18,6 +20,11 @@ CSVTools::ReportSubView::ReportSubView (const CSMWorld::UniversalId& id, CSMDoc: mTable->setSelectionBehavior (QAbstractItemView::SelectRows); mTable->setSelectionMode (QAbstractItemView::ExtendedSelection); + mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate ( + document.getUndoStack(), this); + + mTable->setItemDelegateForColumn (0, mIdTypeDelegate); + connect (mTable, SIGNAL (doubleClicked (const QModelIndex&)), this, SLOT (show (const QModelIndex&))); } @@ -26,6 +33,11 @@ void CSVTools::ReportSubView::setEditLock (bool locked) // ignored. We don't change document state anyway. } +void CSVTools::ReportSubView::updateEditorSetting (const QString& key, const QString& value) +{ + mIdTypeDelegate->updateEditorSetting (key, value); +} + void CSVTools::ReportSubView::show (const QModelIndex& index) { focusId (mModel->getUniversalId (index.row())); diff --git a/apps/opencs/view/tools/reportsubview.hpp b/apps/opencs/view/tools/reportsubview.hpp index 626ceb663d..6503ebd278 100644 --- a/apps/opencs/view/tools/reportsubview.hpp +++ b/apps/opencs/view/tools/reportsubview.hpp @@ -16,6 +16,11 @@ namespace CSMTools class ReportModel; } +namespace CSVWorld +{ + class CommandDelegate; +} + namespace CSVTools { class Table; @@ -26,6 +31,7 @@ namespace CSVTools CSMTools::ReportModel *mModel; QTableView *mTable; + CSVWorld::CommandDelegate *mIdTypeDelegate; public: @@ -33,6 +39,8 @@ namespace CSVTools virtual void setEditLock (bool locked); + virtual void updateEditorSetting (const QString&, const QString&); + private slots: void show (const QModelIndex& index); diff --git a/apps/opencs/view/world/dialoguecreator.cpp b/apps/opencs/view/world/dialoguecreator.cpp new file mode 100644 index 0000000000..c16214283e --- /dev/null +++ b/apps/opencs/view/world/dialoguecreator.cpp @@ -0,0 +1,35 @@ + +#include "dialoguecreator.hpp" + +#include <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); +}
\ No newline at end of file diff --git a/apps/opencs/view/world/dialoguecreator.hpp b/apps/opencs/view/world/dialoguecreator.hpp new file mode 100644 index 0000000000..26f866909a --- /dev/null +++ b/apps/opencs/view/world/dialoguecreator.hpp @@ -0,0 +1,41 @@ +#ifndef CSV_WORLD_DIALOGUECREATOR_H +#define CSV_WORLD_DIALOGUECREATOR_H + +#include "genericcreator.hpp" + +namespace CSVWorld +{ + class DialogueCreator : public GenericCreator + { + int mType; + + protected: + + virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + + public: + + DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id, int type); + }; + + class TopicCreatorFactory : public CreatorFactoryBase + { + public: + + virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) const; + ///< The ownership of the returned Creator is transferred to the caller. + }; + + class JournalCreatorFactory : public CreatorFactoryBase + { + public: + + virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id) const; + ///< The ownership of the returned Creator is transferred to the caller. + }; +} + +#endif diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index dd194abe9a..fc9b7ee3b3 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -109,6 +109,18 @@ CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const char **names, bool all add (i, names[i]); } +CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const std::vector<std::string>& names, + bool allowNone) +{ + if (allowNone) + add (-1, ""); + + int size = static_cast<int> (names.size()); + + for (int i=0; i<size; ++i) + add (i, names[i].c_str()); +} + CSVWorld::CommandDelegate *CSVWorld::EnumDelegateFactory::makeDelegate (QUndoStack& undoStack, QObject *parent) const { diff --git a/apps/opencs/view/world/enumdelegate.hpp b/apps/opencs/view/world/enumdelegate.hpp index b79516a09b..606f9278a1 100644 --- a/apps/opencs/view/world/enumdelegate.hpp +++ b/apps/opencs/view/world/enumdelegate.hpp @@ -54,6 +54,9 @@ namespace CSVWorld ///< \param names Array of char pointer with a 0-pointer as end mark /// \param allowNone Use value of -1 for "none selected" (empty string) + EnumDelegateFactory (const std::vector<std::string>& names, bool allowNone = false); + /// \param allowNone Use value of -1 for "none selected" (empty string) + virtual CommandDelegate *makeDelegate (QUndoStack& undoStack, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. diff --git a/apps/opencs/view/world/idtypedelegate.cpp b/apps/opencs/view/world/idtypedelegate.cpp new file mode 100755 index 0000000000..ce4e8f0148 --- /dev/null +++ b/apps/opencs/view/world/idtypedelegate.cpp @@ -0,0 +1,46 @@ +#include "idtypedelegate.hpp" + +#include "../../model/world/universalid.hpp" + +CSVWorld::IdTypeDelegate::IdTypeDelegate + (const ValueList &values, const IconList &icons, QUndoStack& undoStack, QObject *parent) + : DataDisplayDelegate (values, icons, undoStack, parent) +{} + +bool CSVWorld::IdTypeDelegate::updateEditorSetting (const QString &settingName, const QString &settingValue) +{ + /// \todo make the setting key a member variable, that is initialised from a constructor argument + if (settingName == "Referenceable ID Type Display") + { + if (settingValue == "Icon and Text") + mDisplayMode = Mode_IconAndText; + + else if (settingValue == "Icon Only") + mDisplayMode = Mode_IconOnly; + + else if (settingValue == "Text Only") + mDisplayMode = Mode_TextOnly; + + return true; + } + + return false; +} + + +CSVWorld::IdTypeDelegateFactory::IdTypeDelegateFactory() +{ + for (int i=0; i<CSMWorld::UniversalId::NumberOfTypes; ++i) + { + CSMWorld::UniversalId id (static_cast<CSMWorld::UniversalId::Type> (i)); + + DataDisplayDelegateFactory::add (id.getType(), QString::fromUtf8 (id.getTypeName().c_str()), + QString::fromUtf8 (id.getIcon().c_str())); + } +} + +CSVWorld::CommandDelegate *CSVWorld::IdTypeDelegateFactory::makeDelegate (QUndoStack& undoStack, + QObject *parent) const +{ + return new IdTypeDelegate (mValues, mIcons, undoStack, parent); +} diff --git a/apps/opencs/view/world/refidtypedelegate.hpp b/apps/opencs/view/world/idtypedelegate.hpp index 384aebb98d..ea80fd0d92 100755 --- a/apps/opencs/view/world/refidtypedelegate.hpp +++ b/apps/opencs/view/world/idtypedelegate.hpp @@ -1,5 +1,5 @@ -#ifndef REFIDTYPEDELEGATE_HPP -#define REFIDTYPEDELEGATE_HPP +#ifndef IDTYPEDELEGATE_HPP +#define IDTYPEDELEGATE_HPP #include "enumdelegate.hpp" #include "util.hpp" @@ -8,29 +8,23 @@ namespace CSVWorld { - class RefIdTypeDelegate : public DataDisplayDelegate + class IdTypeDelegate : public DataDisplayDelegate { public: - RefIdTypeDelegate (const ValueList &mValues, const IconList &icons, QUndoStack& undoStack, QObject *parent); + IdTypeDelegate (const ValueList &mValues, const IconList &icons, QUndoStack& undoStack, QObject *parent); virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue); }; - class RefIdTypeDelegateFactory : public DataDisplayDelegateFactory + class IdTypeDelegateFactory : public DataDisplayDelegateFactory { - - typedef std::vector < std::pair <CSMWorld::UniversalId::Type, QString> > UidTypeList; - public: - RefIdTypeDelegateFactory(); + + IdTypeDelegateFactory(); virtual CommandDelegate *makeDelegate (QUndoStack& undoStack, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. - - private: - UidTypeList buildUidTypeList () const; - }; } diff --git a/apps/opencs/view/world/recordstatusdelegate.cpp b/apps/opencs/view/world/recordstatusdelegate.cpp index a0ffd3063d..8085ec7be4 100644 --- a/apps/opencs/view/world/recordstatusdelegate.cpp +++ b/apps/opencs/view/world/recordstatusdelegate.cpp @@ -1,8 +1,11 @@ #include "recordstatusdelegate.hpp" + #include <QPainter> #include <QApplication> #include <QUndoStack> + #include "../../model/settings/usersettings.hpp" +#include "../../model/world/columns.hpp" CSVWorld::RecordStatusDelegate::RecordStatusDelegate(const ValueList& values, const IconList & icons, @@ -37,9 +40,14 @@ bool CSVWorld::RecordStatusDelegate::updateEditorSetting (const QString &setting CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory() { - DataDisplayDelegateFactory::add ( CSMWorld::RecordBase::State_BaseOnly, "Base", ":./base.png"); - DataDisplayDelegateFactory::add ( CSMWorld::RecordBase::State_Deleted, "Deleted", ":./removed.png"); - DataDisplayDelegateFactory::add ( CSMWorld::RecordBase::State_Erased, "Deleted", ":./removed.png"); - DataDisplayDelegateFactory::add ( CSMWorld::RecordBase::State_Modified, "Modified", ":./modified.png"); - DataDisplayDelegateFactory::add ( CSMWorld::RecordBase::State_ModifiedOnly, "Added", ":./added.png"); + std::vector<std::string> enums = + CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_Modification); + + static const char *sIcons[] = + { + ":./base.png", ":./modified.png", ":./added.png", ":./removed.png", ":./removed.png", 0 + }; + + for (int i=0; sIcons[i]; ++i) + add (i, enums.at (i).c_str(), sIcons[i]); } diff --git a/apps/opencs/view/world/refidtypedelegate.cpp b/apps/opencs/view/world/refidtypedelegate.cpp deleted file mode 100755 index bf3acbb206..0000000000 --- a/apps/opencs/view/world/refidtypedelegate.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "refidtypedelegate.hpp" -#include "../../model/world/universalid.hpp" - -CSVWorld::RefIdTypeDelegate::RefIdTypeDelegate - (const ValueList &values, const IconList &icons, QUndoStack& undoStack, QObject *parent) - : DataDisplayDelegate (values, icons, undoStack, parent) -{} - -CSVWorld::RefIdTypeDelegateFactory::RefIdTypeDelegateFactory() -{ - UidTypeList uIdList = buildUidTypeList(); - - for (UidTypeList::const_iterator it = uIdList.begin(); it != uIdList.end(); it++) - { - int i = it->first; - DataDisplayDelegateFactory::add (i, QString::fromStdString(CSMWorld::UniversalId(it->first, "").getTypeName()), it->second); - } -} - -CSVWorld::CommandDelegate *CSVWorld::RefIdTypeDelegateFactory::makeDelegate (QUndoStack& undoStack, - QObject *parent) const -{ - return new RefIdTypeDelegate (mValues, mIcons, undoStack, parent); -} - -CSVWorld::RefIdTypeDelegateFactory::UidTypeList CSVWorld::RefIdTypeDelegateFactory::buildUidTypeList() const -{ - UidTypeList list; - - std::vector<CSMWorld::UniversalId::Type> types = CSMWorld::UniversalId::listReferenceableTypes(); - - for (std::vector<CSMWorld::UniversalId::Type>::const_iterator iter (types.begin()); - iter!=types.end(); ++iter) - { - CSMWorld::UniversalId id (*iter, ""); - - list.push_back (std::make_pair (id.getType(), id.getIcon().c_str())); - } - - return list; -} - -bool CSVWorld::RefIdTypeDelegate::updateEditorSetting (const QString &settingName, const QString &settingValue) -{ - if (settingName == "Referenceable ID Type Display") - { - if (settingValue == "Icon and Text") - mDisplayMode = Mode_IconAndText; - - else if (settingValue == "Icon Only") - mDisplayMode = Mode_IconOnly; - - else if (settingValue == "Text Only") - mDisplayMode = Mode_TextOnly; - - return true; - } - - return false; -} diff --git a/apps/opencs/view/world/refrecordtypedelegate.cpp b/apps/opencs/view/world/refrecordtypedelegate.cpp deleted file mode 100644 index 2bcb7ca50e..0000000000 --- a/apps/opencs/view/world/refrecordtypedelegate.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "refrecordtypedelegate.hpp" -#include "../../model/world/universalid.hpp" - -CSVWorld::RefRecordTypeDelegate::RefRecordTypeDelegate - (const std::vector<std::pair<int, QString> > &values, QUndoStack& undoStack, QObject *parent) - : EnumDelegate (values, undoStack, parent) -{} - -CSVWorld::RefRecordTypeDelegateFactory::RefRecordTypeDelegateFactory() -{ - unsigned int argSize = CSMWorld::UniversalId::getIdArgSize(); - - for (unsigned int i = 0; i < argSize; i++) - { - std::pair<int, const char *> idPair = CSMWorld::UniversalId::getIdArgPair(i); - - mValues.push_back (std::pair<int, QString>(idPair.first, QString::fromUtf8(idPair.second))); - } -} - -CSVWorld::CommandDelegate *CSVWorld::RefRecordTypeDelegateFactory::makeDelegate (QUndoStack& undoStack, - QObject *parent) const -{ - return new RefRecordTypeDelegate (mValues, undoStack, parent); -} diff --git a/apps/opencs/view/world/refrecordtypedelegate.hpp b/apps/opencs/view/world/refrecordtypedelegate.hpp deleted file mode 100644 index baec2cc2e9..0000000000 --- a/apps/opencs/view/world/refrecordtypedelegate.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef REFRECORDTYPEDELEGATE_HPP -#define REFRECORDTYPEDELEGATE_HPP - -#include "enumdelegate.hpp" -#include "util.hpp" - -namespace CSVWorld -{ - class RefRecordTypeDelegate : public EnumDelegate - { - public: - RefRecordTypeDelegate (const std::vector<std::pair<int, QString> > &mValues, QUndoStack& undoStack, QObject *parent); - }; - - class RefRecordTypeDelegateFactory : public CommandDelegateFactory - { - - std::vector<std::pair<int, QString> > mValues; - - public: - RefRecordTypeDelegateFactory(); - - virtual CommandDelegate *makeDelegate (QUndoStack& undoStack, QObject *parent) const; - ///< The ownership of the returned CommandDelegate is transferred to the caller. - }; -} -/* - class VarTypeDelegate : public EnumDelegate - { - private: - - virtual void addCommands (QAbstractItemModel *model, - const QModelIndex& index, int type) const; - - public: - - VarTypeDelegate (const std::vector<std::pair<int, QString> >& values, - QUndoStack& undoStack, QObject *parent); - }; - - class VarTypeDelegateFactory : public CommandDelegateFactory - { - std::vector<std::pair<int, QString> > mValues; - - public: - - VarTypeDelegateFactory (ESM::VarType type0 = ESM::VT_Unknown, - ESM::VarType type1 = ESM::VT_Unknown, ESM::VarType type2 = ESM::VT_Unknown, - ESM::VarType type3 = ESM::VT_Unknown); - - virtual CommandDelegate *makeDelegate (QUndoStack& undoStack, QObject *parent) const; - ///< The ownership of the returned CommandDelegate is transferred to the caller. - - void add (ESM::VarType type); - }; -*/ - -#endif // REFRECORDTYPEDELEGATE_HPP diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp new file mode 100644 index 0000000000..e3618c5493 --- /dev/null +++ b/apps/opencs/view/world/scenesubview.cpp @@ -0,0 +1,82 @@ + +#include "scenesubview.hpp" + +#include <QVBoxLayout> +#include <QHBoxLayout> +#include <QLabel> + +#include "../../model/doc/document.hpp" + +#include "../filter/filterbox.hpp" + +#include "tablebottombox.hpp" +#include "creator.hpp" +#include "scenetoolbar.hpp" +#include "scenetoolmode.hpp" + +CSVWorld::SceneSubView::SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document) +: SubView (id) +{ + QVBoxLayout *layout = new QVBoxLayout; + + layout->setContentsMargins (QMargins (0, 0, 0, 0)); + + layout->addWidget (mBottom = + new TableBottomBox (NullCreatorFactory(), document.getData(), document.getUndoStack(), id, + this), 0); + + QHBoxLayout *layout2 = new QHBoxLayout; + + layout2->setContentsMargins (QMargins (0, 0, 0, 0)); + + SceneToolbar *toolbar = new SceneToolbar (48, this); +// test +SceneToolMode *tool = new SceneToolMode (toolbar); +tool->addButton (":door.png", "a"); +tool->addButton (":GMST.png", "b"); +tool->addButton (":Info.png", "c"); +toolbar->addTool (tool); +toolbar->addTool (new SceneToolMode (toolbar)); +toolbar->addTool (new SceneToolMode (toolbar)); +toolbar->addTool (new SceneToolMode (toolbar)); + layout2->addWidget (toolbar, 0); + + /// \todo replace with rendering widget + QPalette palette2 (palette()); + palette2.setColor (QPalette::Background, Qt::white); + QLabel *placeholder = new QLabel ("Here goes the 3D scene", this); + placeholder->setAutoFillBackground (true); + placeholder->setPalette (palette2); + placeholder->setAlignment (Qt::AlignHCenter); + + layout2->addWidget (placeholder, 1); + + layout->insertLayout (0, layout2, 1); + + CSVFilter::FilterBox *filterBox = new CSVFilter::FilterBox (document.getData(), this); + + layout->insertWidget (0, filterBox); + + QWidget *widget = new QWidget; + + widget->setLayout (layout); + + setWidget (widget); +} + +void CSVWorld::SceneSubView::setEditLock (bool locked) +{ + + +} + +void CSVWorld::SceneSubView::updateEditorSetting(const QString &settingName, const QString &settingValue) +{ + + +} + +void CSVWorld::SceneSubView::setStatusBar (bool show) +{ + mBottom->setStatusBar (show); +}
\ No newline at end of file diff --git a/apps/opencs/view/world/scenesubview.hpp b/apps/opencs/view/world/scenesubview.hpp new file mode 100644 index 0000000000..a0fed908df --- /dev/null +++ b/apps/opencs/view/world/scenesubview.hpp @@ -0,0 +1,37 @@ +#ifndef CSV_WORLD_SCENESUBVIEW_H +#define CSV_WORLD_SCENESUBVIEW_H + +#include "../doc/subview.hpp" + +class QModelIndex; + +namespace CSMDoc +{ + class Document; +} + +namespace CSVWorld +{ + class Table; + class TableBottomBox; + class CreatorFactoryBase; + + class SceneSubView : public CSVDoc::SubView + { + Q_OBJECT + + TableBottomBox *mBottom; + + public: + + SceneSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); + + virtual void setEditLock (bool locked); + + virtual void updateEditorSetting (const QString& key, const QString& value); + + virtual void setStatusBar (bool show); + }; +} + +#endif diff --git a/apps/opencs/view/world/scenetool.cpp b/apps/opencs/view/world/scenetool.cpp new file mode 100644 index 0000000000..320deb1ba9 --- /dev/null +++ b/apps/opencs/view/world/scenetool.cpp @@ -0,0 +1,17 @@ + +#include "scenetool.hpp" + +#include "scenetoolbar.hpp" + +CSVWorld::SceneTool::SceneTool (SceneToolbar *parent) : QPushButton (parent) +{ + setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); + setFixedSize (parent->getButtonSize(), parent->getButtonSize()); + + connect (this, SIGNAL (clicked()), this, SLOT (openRequest())); +} + +void CSVWorld::SceneTool::openRequest() +{ + showPanel (parentWidget()->mapToGlobal (pos())); +} diff --git a/apps/opencs/view/world/scenetool.hpp b/apps/opencs/view/world/scenetool.hpp new file mode 100644 index 0000000000..07e8b58d72 --- /dev/null +++ b/apps/opencs/view/world/scenetool.hpp @@ -0,0 +1,27 @@ +#ifndef CSV_WORLD_SCENETOOL_H +#define CSV_WORLD_SCENETOOL_H + +#include <QPushButton> + +namespace CSVWorld +{ + class SceneToolbar; + + ///< \brief Tool base class + class SceneTool : public QPushButton + { + Q_OBJECT + + public: + + SceneTool (SceneToolbar *parent); + + virtual void showPanel (const QPoint& position) = 0; + + private slots: + + void openRequest(); + }; +} + +#endif diff --git a/apps/opencs/view/world/scenetoolbar.cpp b/apps/opencs/view/world/scenetoolbar.cpp new file mode 100644 index 0000000000..2972c53913 --- /dev/null +++ b/apps/opencs/view/world/scenetoolbar.cpp @@ -0,0 +1,29 @@ + +#include "scenetoolbar.hpp" + +#include <QVBoxLayout> + +#include "scenetool.hpp" + +CSVWorld::SceneToolbar::SceneToolbar (int buttonSize, QWidget *parent) +: QWidget (parent), mButtonSize (buttonSize) +{ + setFixedWidth (mButtonSize); + + mLayout = new QVBoxLayout (this); + mLayout->setAlignment (Qt::AlignTop); + + mLayout->setContentsMargins (QMargins (0, 0, 0, 0)); + + setLayout (mLayout); +} + +void CSVWorld::SceneToolbar::addTool (SceneTool *tool) +{ + mLayout->addWidget (tool, 0, Qt::AlignTop); +} + +int CSVWorld::SceneToolbar::getButtonSize() const +{ + return mButtonSize; +}
\ No newline at end of file diff --git a/apps/opencs/view/world/scenetoolbar.hpp b/apps/opencs/view/world/scenetoolbar.hpp new file mode 100644 index 0000000000..f713ca3dff --- /dev/null +++ b/apps/opencs/view/world/scenetoolbar.hpp @@ -0,0 +1,29 @@ +#ifndef CSV_WORLD_SCENETOOLBAR_H +#define CSV_WORLD_SCENETOOLBAR_H + +#include <QWidget> + +class QVBoxLayout; + +namespace CSVWorld +{ + class SceneTool; + + class SceneToolbar : public QWidget + { + Q_OBJECT + + QVBoxLayout *mLayout; + int mButtonSize; + + public: + + SceneToolbar (int buttonSize, QWidget *parent = 0); + + void addTool (SceneTool *tool); + + int getButtonSize() const; + }; +} + +#endif diff --git a/apps/opencs/view/world/scenetoolmode.cpp b/apps/opencs/view/world/scenetoolmode.cpp new file mode 100644 index 0000000000..281d703b65 --- /dev/null +++ b/apps/opencs/view/world/scenetoolmode.cpp @@ -0,0 +1,56 @@ + +#include "scenetoolmode.hpp" + +#include <QHBoxLayout> +#include <QFrame> +#include <QSignalMapper> + +#include "scenetoolbar.hpp" + +CSVWorld::SceneToolMode::SceneToolMode (SceneToolbar *parent) +: SceneTool (parent), mButtonSize (parent->getButtonSize()) +{ + mPanel = new QFrame (this, Qt::Popup); + + mLayout = new QHBoxLayout (mPanel); + + mLayout->setContentsMargins (QMargins (0, 0, 0, 0)); + + mPanel->setLayout (mLayout); +} + +void CSVWorld::SceneToolMode::showPanel (const QPoint& position) +{ + mPanel->move (position); + mPanel->show(); +} + +void CSVWorld::SceneToolMode::addButton (const std::string& icon, const std::string& id) +{ + QPushButton *button = new QPushButton (QIcon (QPixmap (icon.c_str())), "", mPanel); + button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed)); + button->setFixedSize (mButtonSize, mButtonSize); + + mLayout->addWidget (button); + + mButtons.insert (std::make_pair (button, id)); + + connect (button, SIGNAL (clicked()), this, SLOT (selected())); + + if (mButtons.size()==1) + setIcon (button->icon()); +} + +void CSVWorld::SceneToolMode::selected() +{ + std::map<QPushButton *, std::string>::const_iterator iter = + mButtons.find (dynamic_cast<QPushButton *> (sender())); + + if (iter!=mButtons.end()) + { + mPanel->hide(); + + setIcon (iter->first->icon()); + emit modeChanged (iter->second); + } +}
\ No newline at end of file diff --git a/apps/opencs/view/world/scenetoolmode.hpp b/apps/opencs/view/world/scenetoolmode.hpp new file mode 100644 index 0000000000..a8fe2b5a6c --- /dev/null +++ b/apps/opencs/view/world/scenetoolmode.hpp @@ -0,0 +1,42 @@ +#ifndef CSV_WORLD_SCENETOOL_MODE_H +#define CSV_WORLD_SCENETOOL_MODE_H + +#include "scenetool.hpp" + +#include <map> + +class QHBoxLayout; + +namespace CSVWorld +{ + class SceneToolbar; + + ///< \brief Mode selector tool + class SceneToolMode : public SceneTool + { + Q_OBJECT + + QWidget *mPanel; + QHBoxLayout *mLayout; + std::map<QPushButton *, std::string> mButtons; // widget, id + int mButtonSize; + + public: + + SceneToolMode (SceneToolbar *parent); + + virtual void showPanel (const QPoint& position); + + void addButton (const std::string& icon, const std::string& id); + + signals: + + void modeChanged (const std::string& id); + + private slots: + + void selected(); + }; +} + +#endif diff --git a/apps/opencs/view/world/scripthighlighter.cpp b/apps/opencs/view/world/scripthighlighter.cpp index 288a3d12ac..e06dab3727 100644 --- a/apps/opencs/view/world/scripthighlighter.cpp +++ b/apps/opencs/view/world/scripthighlighter.cpp @@ -4,6 +4,7 @@ #include <sstream> #include <components/compiler/scanner.hpp> +#include <components/compiler/extensions0.hpp> bool CSVWorld::ScriptHighlighter::parseInt (int value, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner) @@ -22,7 +23,7 @@ bool CSVWorld::ScriptHighlighter::parseFloat (float value, const Compiler::Token bool CSVWorld::ScriptHighlighter::parseName (const std::string& name, const Compiler::TokenLoc& loc, Compiler::Scanner& scanner) { - highlight (loc, Type_Name); + highlight (loc, mContext.isId (name) ? Type_Id : Type_Name); return true; } @@ -62,10 +63,10 @@ void CSVWorld::ScriptHighlighter::highlight (const Compiler::TokenLoc& loc, Type setFormat (index, length, mScheme[type]); } -CSVWorld::ScriptHighlighter::ScriptHighlighter (QTextDocument *parent) -: QSyntaxHighlighter (parent), Compiler::Parser (mErrorHandler, mContext) +CSVWorld::ScriptHighlighter::ScriptHighlighter (const CSMWorld::Data& data, QTextDocument *parent) +: QSyntaxHighlighter (parent), Compiler::Parser (mErrorHandler, mContext), mContext (data) { - /// \ŧodo replace this with user settings + /// \todo replace this with user settings { QTextCharFormat format; format.setForeground (Qt::darkMagenta); @@ -101,6 +102,16 @@ CSVWorld::ScriptHighlighter::ScriptHighlighter (QTextDocument *parent) format.setForeground (Qt::green); mScheme.insert (std::make_pair (Type_Comment, format)); } + + { + QTextCharFormat format; + format.setForeground (Qt::blue); + mScheme.insert (std::make_pair (Type_Id, format)); + } + + // configure compiler + Compiler::registerExtensions (mExtensions); + mContext.setExtensions (&mExtensions); } void CSVWorld::ScriptHighlighter::highlightBlock (const QString& text) @@ -114,5 +125,9 @@ void CSVWorld::ScriptHighlighter::highlightBlock (const QString& text) scanner.scan (*this); } catch (...) {} // ignore syntax errors +} +void CSVWorld::ScriptHighlighter::invalidateIds() +{ + mContext.invalidateIds(); }
\ No newline at end of file diff --git a/apps/opencs/view/world/scripthighlighter.hpp b/apps/opencs/view/world/scripthighlighter.hpp index 3ef6978097..495c2e6a3a 100644 --- a/apps/opencs/view/world/scripthighlighter.hpp +++ b/apps/opencs/view/world/scripthighlighter.hpp @@ -7,6 +7,7 @@ #include <components/compiler/nullerrorhandler.hpp> #include <components/compiler/parser.hpp> +#include <components/compiler/extensions.hpp> #include "../../model/world/scriptcontext.hpp" @@ -23,12 +24,14 @@ namespace CSVWorld Type_Name, Type_Keyword, Type_Special, - Type_Comment + Type_Comment, + Type_Id }; private: Compiler::NullErrorHandler mErrorHandler; + Compiler::Extensions mExtensions; CSMWorld::ScriptContext mContext; std::map<Type, QTextCharFormat> mScheme; @@ -71,9 +74,11 @@ namespace CSVWorld public: - ScriptHighlighter (QTextDocument *parent); + ScriptHighlighter (const CSMWorld::Data& data, QTextDocument *parent); virtual void highlightBlock (const QString& text); + + void invalidateIds(); }; } diff --git a/apps/opencs/view/world/scriptsubview.cpp b/apps/opencs/view/world/scriptsubview.cpp index ab1c2d57c6..446c34e5f0 100644 --- a/apps/opencs/view/world/scriptsubview.cpp +++ b/apps/opencs/view/world/scriptsubview.cpp @@ -58,7 +58,13 @@ CSVWorld::ScriptSubView::ScriptSubView (const CSMWorld::UniversalId& id, CSMDoc: connect (mModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), this, SLOT (rowsAboutToBeRemoved (const QModelIndex&, int, int))); - new ScriptHighlighter (mEditor->document()); + connect (&document.getData(), SIGNAL (idListChanged()), this, SLOT (idListChanged())); + + mHighlighter = new ScriptHighlighter (document.getData(), mEditor->document()); + + connect (&mUpdateTimer, SIGNAL (timeout()), this, SLOT (updateHighlighting())); + + mUpdateTimer.setSingleShot (true); } void CSVWorld::ScriptSubView::setEditLock (bool locked) @@ -66,8 +72,19 @@ void CSVWorld::ScriptSubView::setEditLock (bool locked) mEditor->setReadOnly (locked); } +void CSVWorld::ScriptSubView::idListChanged() +{ + mHighlighter->invalidateIds(); + + if (!mUpdateTimer.isActive()) + mUpdateTimer.start (0); +} + void CSVWorld::ScriptSubView::textChanged() { + if (mChangeLocked) + return; + ChangeLock lock (*this); mDocument.getUndoStack().push (new CSMWorld::ModifyCommand (*mModel, @@ -79,6 +96,8 @@ void CSVWorld::ScriptSubView::dataChanged (const QModelIndex& topLeft, const QMo if (mChangeLocked) return; + ChangeLock lock (*this); + QModelIndex index = mModel->getModelIndex (getUniversalId().getId(), mColumn); if (index.row()>=topLeft.row() && index.row()<=bottomRight.row() && @@ -96,4 +115,14 @@ void CSVWorld::ScriptSubView::rowsAboutToBeRemoved (const QModelIndex& parent, i if (!parent.isValid() && index.row()>=start && index.row()<=end) deleteLater(); +} + +void CSVWorld::ScriptSubView::updateHighlighting() +{ + if (mChangeLocked) + return; + + ChangeLock lock (*this); + + mHighlighter->rehighlight(); }
\ No newline at end of file diff --git a/apps/opencs/view/world/scriptsubview.hpp b/apps/opencs/view/world/scriptsubview.hpp index 07d87d9476..7ceab70bae 100644 --- a/apps/opencs/view/world/scriptsubview.hpp +++ b/apps/opencs/view/world/scriptsubview.hpp @@ -3,6 +3,8 @@ #include "../doc/subview.hpp" +#include <QTimer> + class QTextEdit; class QModelIndex; @@ -18,6 +20,8 @@ namespace CSMWorld namespace CSVWorld { + class ScriptHighlighter; + class ScriptSubView : public CSVDoc::SubView { Q_OBJECT @@ -27,6 +31,8 @@ namespace CSVWorld CSMWorld::IdTable *mModel; int mColumn; int mChangeLocked; + ScriptHighlighter *mHighlighter; + QTimer mUpdateTimer; class ChangeLock { @@ -49,13 +55,19 @@ namespace CSVWorld virtual void setEditLock (bool locked); - private slots: + public slots: + + void idListChanged(); void textChanged(); void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void rowsAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + private slots: + + void updateHighlighting(); }; } diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index d22e07d89c..3d98cf73ce 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -13,6 +13,8 @@ #include "cellcreator.hpp" #include "referenceablecreator.hpp" #include "referencecreator.hpp" +#include "scenesubview.hpp" +#include "dialoguecreator.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { @@ -52,6 +54,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_References, 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 manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory<ScriptSubView>); @@ -62,4 +70,5 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<CSVFilter::FilterCreator> >); + manager.add (CSMWorld::UniversalId::Type_Scene, new CSVDoc::SubViewFactory<SceneSubView>); }
\ No newline at end of file diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 72e78c738e..a58eb873f3 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -12,8 +12,8 @@ #include "../../model/world/idtableproxymodel.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/record.hpp" + #include "recordstatusdelegate.hpp" -#include "refidtypedelegate.hpp" #include "util.hpp" void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) @@ -87,19 +87,33 @@ std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const { QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); + // check record state CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State> ( mModel->data (mModel->index (index.row(), 1)).toInt()); - if (state!=CSMWorld::RecordBase::State_Deleted) - { - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + if (state==CSMWorld::RecordBase::State_Deleted) + continue; - std::string id = mModel->data (mModel->index (index.row(), columnIndex)). - toString().toUtf8().constData(); + // check other columns (only relevant for a subset of the tables) + 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); } } diff --git a/apps/opencs/view/world/vartypedelegate.cpp b/apps/opencs/view/world/vartypedelegate.cpp index 72cbaae428..15ce2dbaf2 100644 --- a/apps/opencs/view/world/vartypedelegate.cpp +++ b/apps/opencs/view/world/vartypedelegate.cpp @@ -4,6 +4,7 @@ #include <QUndoStack> #include "../../model/world/commands.hpp" +#include "../../model/world/columns.hpp" void CSVWorld::VarTypeDelegate::addCommands (QAbstractItemModel *model, const QModelIndex& index, int type) const @@ -75,29 +76,11 @@ CSVWorld::CommandDelegate *CSVWorld::VarTypeDelegateFactory::makeDelegate (QUndo void CSVWorld::VarTypeDelegateFactory::add (ESM::VarType type) { - struct Name - { - ESM::VarType mType; - const char *mName; - }; + std::vector<std::string> enums = + CSMWorld::Columns::getEnums (CSMWorld::Columns::ColumnId_ValueType); - static const Name sNames[] = - { - { ESM::VT_None, "empty" }, - { ESM::VT_Short, "short" }, - { ESM::VT_Int, "integer" }, - { ESM::VT_Long, "long" }, - { ESM::VT_Float, "float" }, - { ESM::VT_String, "string" }, - { ESM::VT_Unknown, 0 } // end marker - }; - - for (int i=0; sNames[i].mName; ++i) - if (sNames[i].mType==type) - { - mValues.push_back (std::make_pair (type, sNames[i].mName)); - return; - } - - throw std::logic_error ("Unsupported variable type"); + if (type<0 && type>=enums.size()) + throw std::logic_error ("Unsupported variable type"); + + mValues.push_back (std::make_pair (type, QString::fromUtf8 (enums[type].c_str()))); } diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index a44fd4b343..807b1b5ff1 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -58,6 +58,7 @@ add_openmw_dir (mwworld cells localscripts customdata weather inventorystore ptr actionopen actionread actionequip timestamp actionalchemy cellstore actionapply actioneat esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor + contentloader esmloader omwloader ) add_openmw_dir (mwclass @@ -119,6 +120,10 @@ target_link_libraries(openmw components ) +if (USE_SYSTEM_TINYXML) + target_link_libraries(openmw ${TINYXML_LIBRARIES}) +endif() + if (NOT UNIX) target_link_libraries(openmw ${SDL2MAIN_LIBRARY}) endif() diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index a2eccbaf9a..e9c70e2aa1 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -261,34 +261,14 @@ void OMW::Engine::setCell (const std::string& cellName) mCellName = cellName; } -// Set master file (esm) -// - If the given name does not have an extension, ".esm" is added automatically - -void OMW::Engine::addMaster (const std::string& master) +void OMW::Engine::addContentFile(const std::string& file) { - mMaster.push_back(master); - std::string &str = mMaster.back(); + if (file.find_last_of(".") == std::string::npos) + { + throw std::runtime_error("Missing extension in content file!"); + } - // Append .esm if not already there - std::string::size_type sep = str.find_last_of ("."); - if (sep == std::string::npos) - { - str += ".esm"; - } -} - -// Add plugin file (esp) -void OMW::Engine::addPlugin (const std::string& plugin) -{ - mPlugins.push_back(plugin); - std::string &str = mPlugins.back(); - - // Append .esp if not already there - std::string::size_type sep = str.find_last_of ("."); - if (sep == std::string::npos) - { - str += ".esp"; - } + mContentFiles.push_back(file); } void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity) @@ -370,7 +350,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) addResourcesDirectory(mResDir / "mygui"); addResourcesDirectory(mResDir / "water"); addResourcesDirectory(mResDir / "shadows"); - addZipResource(mResDir / "mygui" / "Obliviontt.zip"); OEngine::Render::WindowSettings windowSettings; windowSettings.fullscreen = settings.getBool("fullscreen", "Video"); @@ -399,23 +378,25 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mExtensions, mFpsLevel, mOgre, mCfgMgr.getLogPath().string() + std::string("/"), mCfgMgr.getCachePath ().string(), mScriptConsoleMode, mTranslationDataStorage, mEncoding); mEnvironment.setWindowManager (window); - if (mNewGame) - mEnvironment.getWindowManager()->setNewGame(true); // Create the world - mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins, + mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mContentFiles, mResDir, mCfgMgr.getCachePath(), mEncoder, mFallbackMap, mActivationDistanceOverride)); MWBase::Environment::get().getWorld()->setupPlayer(); input->setPlayer(&mEnvironment.getWorld()->getPlayer()); window->initUI(); + if (mNewGame) + // still redundant work here: recreate CharacterCreation(), + // double update visibility etc. + window->setNewGame(true); window->renderWorldMap(); //Load translation data mTranslationDataStorage.setEncoder(mEncoder); - for (size_t i = 0; i < mMaster.size(); i++) - mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster[i]); + for (size_t i = 0; i < mContentFiles.size(); i++) + mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]); Compiler::registerExtensions (mExtensions); @@ -480,7 +461,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) void OMW::Engine::go() { assert (!mCellName.empty()); - assert (!mMaster.empty()); + assert (!mContentFiles.empty()); assert (!mOgre); Settings::Manager settings; diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 665b0094c1..553d290687 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -68,8 +68,7 @@ namespace OMW boost::filesystem::path mResDir; OEngine::Render::OgreRenderer *mOgre; std::string mCellName; - std::vector<std::string> mMaster; - std::vector<std::string> mPlugins; + std::vector<std::string> mContentFiles; int mFpsLevel; bool mVerboseScripts; bool mNewGame; @@ -135,13 +134,11 @@ namespace OMW /// Set start cell name (only interiors for now) void setCell(const std::string& cellName); - /// Set master file (esm) - /// - If the given name does not have an extension, ".esm" is added automatically - void addMaster(const std::string& master); - - /// Same as "addMaster", but for plugin files (esp) - /// - If the given name does not have an extension, ".esp" is added automatically - void addPlugin(const std::string& plugin); + /** + * @brief addContentFile - Adds content file (ie. esm/esp, or omwgame/omwaddon) to the content files container. + * @param file - filename (extension is required) + */ + void addContentFile(const std::string& file); /// Enable fps counter void showFPS(int level); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 27afd734ae..33f740b311 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -110,11 +110,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("start", bpo::value<std::string>()->default_value("Beshara"), "set initial cell") - ("master", bpo::value<StringsVector>()->default_value(StringsVector(), "") - ->multitoken(), "master file(s)") - - ("plugin", bpo::value<StringsVector>()->default_value(StringsVector(), "") - ->multitoken(), "plugin file(s)") + ("content", bpo::value<StringsVector>()->default_value(StringsVector(), "") + ->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon") ("anim-verbose", bpo::value<bool>()->implicit_value(true) ->default_value(false), "output animation indices files") @@ -152,8 +149,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("activate-dist", bpo::value <int> ()->default_value (-1), "activation distance override"); - ; - bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv) .options(desc).allow_unregistered().run(); @@ -211,29 +206,18 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setResourceDir(variables["resources"].as<std::string>()); - // master and plugin - StringsVector master = variables["master"].as<StringsVector>(); - if (master.empty()) + StringsVector content = variables["content"].as<StringsVector>(); + if (content.empty()) { - std::cout << "No master file given. Aborting...\n"; - return false; + std::cout << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting..." << std::endl; + return false; } - StringsVector plugin = variables["plugin"].as<StringsVector>(); - // Removed check for 255 files, which would be the hard-coded limit in Morrowind. - // I'll keep the following variable in, maybe we can use it for something different. - // Say, a feedback like "loading file x/cnt". - // Commenting this out for now to silence compiler warning. - //int cnt = master.size() + plugin.size(); - - // Prepare loading master/plugin files (i.e. send filenames to engine) - for (std::vector<std::string>::size_type i = 0; i < master.size(); i++) - { - engine.addMaster(master[i]); - } - for (std::vector<std::string>::size_type i = 0; i < plugin.size(); i++) + StringsVector::const_iterator it(content.begin()); + StringsVector::const_iterator end(content.end()); + for (; it != end; ++it) { - engine.addPlugin(plugin[i]); + engine.addContentFile(*it); } // startup-settings diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 6ca900a4d3..c39e878268 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -324,6 +324,7 @@ namespace MWBase virtual void processChangedSettings (const Settings::CategorySettingVector& settings) = 0; virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0; + virtual bool isSlowFalling(const MWWorld::Ptr &ptr) const = 0; virtual bool isSwimming(const MWWorld::Ptr &object) const = 0; ///Is the head of the creature underwater? virtual bool isSubmerged(const MWWorld::Ptr &object) const = 0; @@ -389,12 +390,22 @@ namespace MWBase /// Returns true if teleport spell effects are allowed. virtual bool isTeleportingEnabled() const = 0; + /// Enables or disables use of levitation spell effect. + virtual void enableLevitation(bool enable) = 0; + + /// Returns true if levitation spell effect is allowed. + virtual bool isLevitationEnabled() const = 0; + /// Turn actor into werewolf or normal form. virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf) = 0; /// Sets the NPC's Acrobatics skill to match the fWerewolfAcrobatics GMST. /// It only applies to the current form the NPC is in. virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor) = 0; + + virtual bool getGodModeState() = 0; + + virtual bool toggleGodMode() = 0; }; } diff --git a/apps/openmw/mwclass/light.cpp b/apps/openmw/mwclass/light.cpp index 7eefc6167c..a593eb295a 100644 --- a/apps/openmw/mwclass/light.cpp +++ b/apps/openmw/mwclass/light.cpp @@ -16,12 +16,34 @@ #include "../mwworld/inventorystore.hpp" #include "../mwworld/cellstore.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/customdata.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" +namespace +{ + struct CustomData : public MWWorld::CustomData + { + float mTime; + ///< Time remaining + + CustomData(MWWorld::Ptr ptr) + { + MWWorld::LiveCellRef<ESM::Light> *ref = ptr.get<ESM::Light>(); + mTime = ref->mBase->mData.mTime; + } + ///< Constructs this CustomData from the base values for Ptr. + + virtual MWWorld::CustomData *clone() const + { + return new CustomData (*this); + } + }; +} + namespace MWClass { void Light::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -182,6 +204,21 @@ namespace MWClass return action; } + void Light::setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const + { + ensureCustomData(ptr); + + float &timeRemaining = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime; + timeRemaining = duration; + } + + float Light::getRemainingUsageTime (const MWWorld::Ptr& ptr) const + { + ensureCustomData(ptr); + + return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime; + } + MWWorld::Ptr Light::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { @@ -191,6 +228,12 @@ namespace MWClass return MWWorld::Ptr(&cell.mLights.insert(*ref), &cell); } + void Light::ensureCustomData (const MWWorld::Ptr& ptr) const + { + if (!ptr.getRefData().getCustomData()) + ptr.getRefData().setCustomData(new CustomData(ptr)); + } + bool Light::canSell (const MWWorld::Ptr& item, int npcServices) const { return npcServices & ESM::NPC::Lights; diff --git a/apps/openmw/mwclass/light.hpp b/apps/openmw/mwclass/light.hpp index 79d662763b..c15228a6a0 100644 --- a/apps/openmw/mwclass/light.hpp +++ b/apps/openmw/mwclass/light.hpp @@ -10,6 +10,8 @@ namespace MWClass virtual MWWorld::Ptr copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; + void ensureCustomData (const MWWorld::Ptr& ptr) const; + public: virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; @@ -56,6 +58,12 @@ namespace MWClass const; ///< Generate action for using via inventory menu + virtual void setRemainingUsageTime (const MWWorld::Ptr& ptr, float duration) const; + ///< Sets the remaining duration of the object. + + virtual float getRemainingUsageTime (const MWWorld::Ptr& ptr) const; + ///< Returns the remaining duration of the object. + virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual float getWeight (const MWWorld::Ptr& ptr) const; diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 073d1b1b92..01a0c0a6f9 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -7,6 +7,7 @@ #include <OgreSceneNode.h> +#include <components/esm/loadmgef.hpp> #include <components/esm/loadnpc.hpp> #include "../mwbase/environment.hpp" @@ -396,9 +397,10 @@ namespace MWClass MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); } - weapon.getCellRef().mCharge -= std::min(std::max(1, - (int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), - weapon.getCellRef().mCharge); + + if (!MWBase::Environment::get().getWorld()->getGodModeState()) + weapon.getCellRef().mCharge -= std::min(std::max(1, + (int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weapon.getCellRef().mCharge); } healthdmg = true; } @@ -769,6 +771,37 @@ namespace MWClass return x; } + float Npc::getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>(); + + const float fallDistanceMin = gmst.find("fFallDamageDistanceMin")->getFloat(); + + if (fallHeight >= fallDistanceMin) + { + const float acrobaticsSkill = MWWorld::Class::get(ptr).getNpcStats (ptr).getSkill(ESM::Skill::Acrobatics).getModified(); + const CustomData *npcdata = static_cast<const CustomData*>(ptr.getRefData().getCustomData()); + const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::Jump)).mMagnitude; + const float fallAcroBase = gmst.find("fFallAcroBase")->getFloat(); + const float fallAcroMult = gmst.find("fFallAcroMult")->getFloat(); + const float fallDistanceBase = gmst.find("fFallDistanceBase")->getFloat(); + const float fallDistanceMult = gmst.find("fFallDistanceMult")->getFloat(); + + float x = fallHeight - fallDistanceMin; + x -= (1.5 * acrobaticsSkill) + jumpSpellBonus; + x = std::max(0.0f, x); + + float a = fallAcroBase + fallAcroMult * (100 - acrobaticsSkill); + x = fallDistanceBase + fallDistanceMult * x; + x *= a; + + return x; + } + + return 0; + } + MWMechanics::Movement& Npc::getMovementSettings (const MWWorld::Ptr& ptr) const { ensureCustomData (ptr); diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 3591d7c688..c39ca42ef4 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -97,6 +97,9 @@ namespace MWClass virtual float getJump(const MWWorld::Ptr &ptr) const; ///< Return jump velocity (not accounting for movement) + virtual float getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const; + ///< Return amount of health points lost when falling + virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const; ///< Return desired movement. diff --git a/apps/openmw/mwgui/fontloader.cpp b/apps/openmw/mwgui/fontloader.cpp index ff160105a3..7a6317c163 100644 --- a/apps/openmw/mwgui/fontloader.cpp +++ b/apps/openmw/mwgui/fontloader.cpp @@ -9,6 +9,7 @@ #include <MyGUI_XmlDocument.h> #include <MyGUI_FactoryManager.h> + #include <components/misc/stringops.hpp> namespace @@ -62,6 +63,58 @@ namespace return unicode; } + + std::string getUtf8 (unsigned char c, ToUTF8::Utf8Encoder& encoder, ToUTF8::FromType encoding) + { + if (encoding == ToUTF8::WINDOWS_1250) + { + unsigned char win1250; + std::map<unsigned char, unsigned char> conv; + conv[0x80] = 0xc6; + conv[0x81] = 0x9c; + conv[0x82] = 0xe6; + conv[0x83] = 0xb3; + conv[0x84] = 0xf1; + conv[0x85] = 0xb9; + conv[0x86] = 0xbf; + conv[0x87] = 0x9f; + conv[0x88] = 0xea; + conv[0x89] = 0xea; + conv[0x8a] = 0x0; // not contained in win1250 + conv[0x8b] = 0x0; // not contained in win1250 + conv[0x8c] = 0x8f; + conv[0x8d] = 0xaf; + conv[0x8e] = 0xa5; + conv[0x8f] = 0x8c; + conv[0x90] = 0xca; + conv[0x93] = 0xa3; + conv[0x94] = 0xf6; + conv[0x95] = 0xf3; + conv[0x96] = 0xaf; + conv[0x97] = 0x8f; + conv[0x99] = 0xd3; + conv[0x9a] = 0xd1; + conv[0x9c] = 0x0; // not contained in win1250 + conv[0xa0] = 0xb9; + conv[0xa1] = 0xaf; + conv[0xa2] = 0xf3; + conv[0xa3] = 0xbf; + conv[0xa4] = 0x0; // not contained in win1250 + conv[0xe1] = 0x8c; + conv[0xe1] = 0x8c; + conv[0xe3] = 0x0; // not contained in win1250 + conv[0xf5] = 0x0; // not contained in win1250 + + if (conv.find(c) != conv.end()) + win1250 = conv[c]; + else + win1250 = c; + return encoder.getUtf8(std::string(1, win1250)); + } + else + return encoder.getUtf8(std::string(1, c)); + } + } namespace MWGui @@ -184,7 +237,7 @@ namespace MWGui int h = data[i].bottom_left.y*height - y1; ToUTF8::Utf8Encoder encoder(mEncoding); - unsigned long unicodeVal = utf8ToUnicode(encoder.getUtf8(std::string(1, (unsigned char)(i)))); + unsigned long unicodeVal = utf8ToUnicode(getUtf8(i, encoder, mEncoding)); MyGUI::xml::ElementPtr code = codes->createChild("Code"); code->addAttribute("index", unicodeVal); diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index f9d31bdcd6..e7b9f9c015 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -28,6 +28,7 @@ namespace MWGui , mStamina(NULL) , mDrowning(NULL) , mDrowningFrame(NULL) + , mDrowningFlash(NULL) , mWeapImage(NULL) , mSpellImage(NULL) , mWeapStatus(NULL) @@ -53,6 +54,7 @@ namespace MWGui , mSpellVisible(true) , mWorldMouseOver(false) , mEnemyHealthTimer(0) + , mIsDrowning(false) { setCoord(0,0, width, height); @@ -75,6 +77,7 @@ namespace MWGui //Drowning bar getWidget(mDrowningFrame, "DrowningFrame"); getWidget(mDrowning, "Drowning"); + getWidget(mDrowningFlash, "Flash"); mDrowning->setProgressRange(200); const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); @@ -207,7 +210,15 @@ namespace MWGui void HUD::setDrowningTimeLeft(float time) { - mDrowning->setProgressPosition(time/20.0*200.0); + size_t progress = time/20.0*200.0; + mDrowning->setProgressPosition(progress); + + bool isDrowning = (progress == 0); + if (isDrowning && !mIsDrowning) // Just started drowning + mDrowningFlashTheta = 0.0f; // Start out on bright red every time. + + mDrowningFlash->setVisible(isDrowning); + mIsDrowning = isDrowning; } void HUD::setDrowningBarVisible(bool visible) @@ -250,6 +261,7 @@ namespace MWGui // remove object from the container it was coming from mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount); mDragAndDrop->finish(); + mDragAndDrop->mSourceModel->update(); } else { @@ -366,6 +378,9 @@ namespace MWGui mEnemyHealth->setVisible(false); mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() + MyGUI::IntPoint(0,20)); } + + if (mIsDrowning) + mDrowningFlashTheta += dt * Ogre::Math::TWO_PI; } void HUD::onResChange(int width, int height) @@ -608,6 +623,12 @@ namespace MWGui mEnemyHealth->setProgressRange(100); mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100); } + + if (mIsDrowning) + { + float intensity = (cos(mDrowningFlashTheta) + 1.0f) / 2.0f; + mDrowningFlash->setColour(MyGUI::Colour(intensity, intensity, intensity)); + } } void HUD::setEnemy(const MWWorld::Ptr &enemy) diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index c40742a603..04206fbc88 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -67,7 +67,7 @@ namespace MWGui MyGUI::ImageBox* mCrosshair; MyGUI::TextBox* mCellNameBox; MyGUI::TextBox* mWeaponSpellBox; - MyGUI::Widget* mDrowningFrame; + MyGUI::Widget *mDrowningFrame, *mDrowningFlash; MyGUI::Widget* mDummy; @@ -101,6 +101,9 @@ namespace MWGui MWWorld::Ptr mEnemy; float mEnemyHealthTimer; + bool mIsDrowning; + float mDrowningFlashTheta; + void onWorldClicked(MyGUI::Widget* _sender); void onWorldMouseOver(MyGUI::Widget* _sender, int x, int y); void onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new); diff --git a/apps/openmw/mwgui/loadingscreen.cpp b/apps/openmw/mwgui/loadingscreen.cpp index 9b63dfa76d..4bd383c2f3 100644 --- a/apps/openmw/mwgui/loadingscreen.cpp +++ b/apps/openmw/mwgui/loadingscreen.cpp @@ -23,6 +23,7 @@ namespace MWGui , mLastWallpaperChangeTime(0.f) , mFirstLoad(true) , mProgress(0) + , mVSyncWasEnabled(false) { getWidget(mLoadingText, "LoadingText"); getWidget(mProgressBar, "ProgressBar"); @@ -67,6 +68,14 @@ namespace MWGui void LoadingScreen::loadingOn() { + // Temporarily turn off VSync, we want to do actual loading rather than waiting for the screen to sync. + // Threaded loading would be even better, of course - especially because some drivers force VSync to on and we can't change it. + // In Ogre 1.8, the swapBuffers argument is useless and setVSyncEnabled is bugged with GLX, nothing we can do :/ + mVSyncWasEnabled = mWindow->isVSyncEnabled(); + #if OGRE_VERSION >= (1 << 16 | 9 << 8 | 0) + mWindow->setVSyncEnabled(false); + #endif + setVisible(true); if (mFirstLoad) @@ -83,6 +92,12 @@ namespace MWGui void LoadingScreen::loadingOff() { + // Re-enable vsync now. + // In Ogre 1.8, the swapBuffers argument is useless and setVSyncEnabled is bugged with GLX, nothing we can do :/ + #if OGRE_VERSION >= (1 << 16 | 9 << 8 | 0) + mWindow->setVSyncEnabled(mVSyncWasEnabled); + #endif + setVisible(false); MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_Loading); @@ -212,7 +227,8 @@ namespace MWGui // caused a sync / flush and would be expensive). // We're doing this so we can do some actual loading while the GPU is busy with the render. // This means the render is lagging a frame behind, but this is hardly noticable. - mWindow->swapBuffers(false); // never Vsync, makes no sense here + mWindow->swapBuffers(); + mWindow->update(false); if (!hasCompositor) diff --git a/apps/openmw/mwgui/loadingscreen.hpp b/apps/openmw/mwgui/loadingscreen.hpp index dde8ff63aa..2d1d7431f8 100644 --- a/apps/openmw/mwgui/loadingscreen.hpp +++ b/apps/openmw/mwgui/loadingscreen.hpp @@ -57,6 +57,8 @@ namespace MWGui Ogre::StringVector mResources; + bool mVSyncWasEnabled; + void changeWallpaper(); void draw(); diff --git a/apps/openmw/mwgui/mainmenu.cpp b/apps/openmw/mwgui/mainmenu.cpp index 88227c7512..1db6e9ecd2 100644 --- a/apps/openmw/mwgui/mainmenu.cpp +++ b/apps/openmw/mwgui/mainmenu.cpp @@ -70,7 +70,10 @@ namespace MWGui { MWBase::Environment::get().getSoundManager()->playSound("Menu Click", 1.f, 1.f); if (sender == mButtons["return"]) + { + MWBase::Environment::get().getSoundManager ()->resumeSounds (MWBase::SoundManager::Play_TypeSfx); MWBase::Environment::get().getWindowManager ()->removeGuiMode (GM_MainMenu); + } else if (sender == mButtons["options"]) MWBase::Environment::get().getWindowManager ()->pushGuiMode (GM_Settings); else if (sender == mButtons["exitgame"]) diff --git a/apps/openmw/mwgui/messagebox.cpp b/apps/openmw/mwgui/messagebox.cpp index 45da1bf17e..48d7ec1717 100644 --- a/apps/openmw/mwgui/messagebox.cpp +++ b/apps/openmw/mwgui/messagebox.cpp @@ -14,6 +14,7 @@ namespace MWGui mMessageBoxSpeed = 0.1; mInterMessageBoxe = NULL; mStaticMessageBox = NULL; + mLastButtonPressed = -1; } void MessageBoxManager::onFrame (float frameDuration) @@ -62,6 +63,7 @@ namespace MWGui } if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) { + mLastButtonPressed = mInterMessageBoxe->readPressedButton(); delete mInterMessageBoxe; mInterMessageBoxe = NULL; MWBase::Environment::get().getInputManager()->changeInputMode( @@ -107,6 +109,7 @@ namespace MWGui } mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons); + mLastButtonPressed = -1; return true; } @@ -154,11 +157,9 @@ namespace MWGui int MessageBoxManager::readPressedButton () { - if(mInterMessageBoxe != NULL) - { - return mInterMessageBoxe->readPressedButton(); - } - return -1; + int pressed = mLastButtonPressed; + mLastButtonPressed = -1; + return pressed; } @@ -421,9 +422,7 @@ namespace MWGui int InteractiveMessageBox::readPressedButton () { - int pressed = mButtonPressed; - mButtonPressed = -1; - return pressed; + return mButtonPressed; } } diff --git a/apps/openmw/mwgui/messagebox.hpp b/apps/openmw/mwgui/messagebox.hpp index 4ef645f5e6..63840cfe2c 100644 --- a/apps/openmw/mwgui/messagebox.hpp +++ b/apps/openmw/mwgui/messagebox.hpp @@ -56,6 +56,7 @@ namespace MWGui MessageBox* mStaticMessageBox; std::vector<MessageBoxManagerTimer> mTimers; float mMessageBoxSpeed; + int mLastButtonPressed; }; class MessageBox : public OEngine::GUI::Layout diff --git a/apps/openmw/mwgui/settingswindow.cpp b/apps/openmw/mwgui/settingswindow.cpp index 3dfa17badc..923b9d01d6 100644 --- a/apps/openmw/mwgui/settingswindow.cpp +++ b/apps/openmw/mwgui/settingswindow.cpp @@ -363,8 +363,12 @@ namespace MWGui else if (_sender == mVSyncButton) { Settings::Manager::setBool("vsync", "Video", newState); + // Ogre::Window::setVSyncEnabled is bugged in 1.8 +#if OGRE_VERSION < (1 << 16 | 9 << 8 | 0) MWBase::Environment::get().getWindowManager()-> messageBox("VSync will be applied after a restart", std::vector<std::string>()); +#endif + apply(); } else { diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index 9812c0f8a9..0c303485af 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -2,6 +2,9 @@ #include <boost/lexical_cast.hpp> +#include <sstream> +#include <iomanip> + #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" @@ -169,13 +172,34 @@ namespace MWGui MWBase::Environment::get().getWindowManager()->getGameSettingString( ESM::Attribute::sGmstAttributeIds[effectIt->mKey.mArg], "") + ")"; - if (!(effect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) + ESM::MagicEffect::MagnitudeDisplayType displayType = effect->getMagnitudeDisplayType(); + if (displayType == ESM::MagicEffect::MDT_TimesInt) + { + std::string timesInt = MWBase::Environment::get().getWindowManager()->getGameSettingString("sXTimesINT", ""); + std::stringstream formatter; + formatter << std::fixed << std::setprecision(1) << " " << (effectIt->mMagnitude / 10.0f) << timesInt; + sourcesDescription += formatter.str(); + } + else if ( displayType != ESM::MagicEffect::MDT_None ) { - std::string pt = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", ""); - std::string pts = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoints", ""); - sourcesDescription += ": " + boost::lexical_cast<std::string>(effectIt->mMagnitude); - sourcesDescription += " " + ((effectIt->mMagnitude > 1) ? pts : pt); + + if ( displayType == ESM::MagicEffect::MDT_Percentage ) + sourcesDescription += MWBase::Environment::get().getWindowManager()->getGameSettingString("spercent", ""); + else if ( displayType == ESM::MagicEffect::MDT_Feet ) + sourcesDescription += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sfeet", ""); + else if ( displayType == ESM::MagicEffect::MDT_Level ) + { + sourcesDescription += " " + ((effectIt->mMagnitude > 1) ? + MWBase::Environment::get().getWindowManager()->getGameSettingString("sLevels", "") : + MWBase::Environment::get().getWindowManager()->getGameSettingString("sLevel", "") ); + } + else // ESM::MagicEffect::MDT_Points + { + sourcesDescription += " " + ((effectIt->mMagnitude > 1) ? + MWBase::Environment::get().getWindowManager()->getGameSettingString("spoints", "") : + MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", "") ); + } } } diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index dea64ae8cf..3fc3187e80 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -2,6 +2,9 @@ #include <boost/lexical_cast.hpp> +#include <sstream> +#include <iomanip> + #include <MyGUI_ProgressBar.h> #include <MyGUI_ImageBox.h> #include <MyGUI_ControllerManager.h> @@ -405,6 +408,10 @@ namespace MWGui std::string pt = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoint", ""); std::string pts = MWBase::Environment::get().getWindowManager()->getGameSettingString("spoints", ""); + std::string pct = MWBase::Environment::get().getWindowManager()->getGameSettingString("spercent", ""); + std::string ft = MWBase::Environment::get().getWindowManager()->getGameSettingString("sfeet", ""); + std::string lvl = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLevel", ""); + std::string lvls = MWBase::Environment::get().getWindowManager()->getGameSettingString("sLevels", ""); std::string to = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sTo", "") + " "; std::string sec = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("ssecond", ""); std::string secs = " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sseconds", ""); @@ -421,13 +428,32 @@ namespace MWGui spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString(ESM::Attribute::sGmstAttributeIds[mEffectParams.mAttribute], ""); } - if ((mEffectParams.mMagnMin >= 0 || mEffectParams.mMagnMax >= 0) && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) - { - if (mEffectParams.mMagnMin == mEffectParams.mMagnMax) - spellLine += " " + boost::lexical_cast<std::string>(mEffectParams.mMagnMin) + " " + ((mEffectParams.mMagnMin == 1) ? pt : pts); - else - { - spellLine += " " + boost::lexical_cast<std::string>(mEffectParams.mMagnMin) + to + boost::lexical_cast<std::string>(mEffectParams.mMagnMax) + " " + pts; + if (mEffectParams.mMagnMin >= 0 || mEffectParams.mMagnMax >= 0) { + ESM::MagicEffect::MagnitudeDisplayType displayType = magicEffect->getMagnitudeDisplayType(); + if ( displayType == ESM::MagicEffect::MDT_TimesInt ) { + std::string timesInt = MWBase::Environment::get().getWindowManager()->getGameSettingString("sXTimesINT", ""); + std::stringstream formatter; + + formatter << std::fixed << std::setprecision(1) << " " << (mEffectParams.mMagnMin / 10.0f); + if (mEffectParams.mMagnMin != mEffectParams.mMagnMax) + formatter << to << (mEffectParams.mMagnMax / 10.0f); + formatter << timesInt; + + spellLine += formatter.str(); + } + else if ( displayType != ESM::MagicEffect::MDT_None ) { + spellLine += " " + boost::lexical_cast<std::string>(mEffectParams.mMagnMin); + if (mEffectParams.mMagnMin != mEffectParams.mMagnMax) + spellLine += to + boost::lexical_cast<std::string>(mEffectParams.mMagnMax); + + if ( displayType == ESM::MagicEffect::MDT_Percentage ) + spellLine += pct; + else if ( displayType == ESM::MagicEffect::MDT_Feet ) + spellLine += " " + ft; + else if ( displayType == ESM::MagicEffect::MDT_Level ) + spellLine += " " + ((mEffectParams.mMagnMin == 1 && mEffectParams.mMagnMax == 1) ? lvl : lvls ); + else // ESM::MagicEffect::MDT_Points + spellLine += " " + ((mEffectParams.mMagnMin == 1 && mEffectParams.mMagnMax == 1) ? pt : pts ); } } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index bf8b664daa..4b4d2dfd19 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -61,20 +61,23 @@ namespace MWGui const Compiler::Extensions& extensions, int fpsLevel, OEngine::Render::OgreRenderer *ogre, const std::string& logpath, const std::string& cacheDir, bool consoleOnlyScripts, Translation::Storage& translationDataStorage, ToUTF8::FromType encoding) - : mGuiManager(NULL) - , mConsoleOnlyScripts(consoleOnlyScripts) + : mConsoleOnlyScripts(consoleOnlyScripts) + , mGuiManager(NULL) , mRendering(ogre) , mHud(NULL) , mMap(NULL) , mMenu(NULL) - , mStatsWindow(NULL) , mToolTips(NULL) + , mStatsWindow(NULL) , mMessageBoxManager(NULL) , mConsole(NULL) , mJournal(NULL) , mDialogueWindow(NULL) - , mBookWindow(NULL) + , mContainerWindow(NULL) + , mDragAndDrop(NULL) + , mInventoryWindow(NULL) , mScrollWindow(NULL) + , mBookWindow(NULL) , mCountDialog(NULL) , mTradeWindow(NULL) , mSpellBuyingWindow(NULL) @@ -83,27 +86,37 @@ namespace MWGui , mConfirmationDialog(NULL) , mAlchemyWindow(NULL) , mSpellWindow(NULL) + , mQuickKeysMenu(NULL) , mLoadingScreen(NULL) - , mCharGen(NULL) , mLevelupDialog(NULL) , mWaitDialog(NULL) , mSpellCreationDialog(NULL) , mEnchantingDialog(NULL) , mTrainingWindow(NULL) , mMerchantRepair(NULL) - , mRepair(NULL) , mSoulgemDialog(NULL) + , mRepair(NULL) , mCompanionWindow(NULL) + , mTranslationDataStorage (translationDataStorage) + , mSoftwareCursor(NULL) + , mCharGen(NULL) + , mInputBlocker(NULL) + , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) + , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) + , mHudEnabled(true) + , mCursorVisible(true) , mPlayerName() , mPlayerRaceId() , mPlayerAttributes() - , mPlayerMajorSkills() , mPlayerMinorSkills() + , mPlayerMajorSkills() , mPlayerSkillValues() , mPlayerHealth() , mPlayerMagicka() , mPlayerFatigue() , mGui(NULL) + , mGuiModes() + , mCursorManager(NULL) , mGarbageDialogs() , mShown(GW_ALL) , mForceHidden(GW_None) @@ -113,13 +126,7 @@ namespace MWGui , mFPS(0.0f) , mTriangleCount(0) , mBatchCount(0) - , mCrosshairEnabled(Settings::Manager::getBool ("crosshair", "HUD")) - , mSubtitlesEnabled(Settings::Manager::getBool ("subtitles", "GUI")) - , mHudEnabled(true) - , mTranslationDataStorage (translationDataStorage) - , mCursorManager(NULL) , mUseHardwareCursors(Settings::Manager::getBool("hardware cursors", "GUI")) - , mCursorVisible(true) { // Set up the GUI system mGuiManager = new OEngine::GUI::MyGUIManager(mRendering->getWindow(), mRendering->getScene(), false, logpath); diff --git a/apps/openmw/mwinput/inputmanagerimp.cpp b/apps/openmw/mwinput/inputmanagerimp.cpp index 1039a0dced..4746260edf 100644 --- a/apps/openmw/mwinput/inputmanagerimp.cpp +++ b/apps/openmw/mwinput/inputmanagerimp.cpp @@ -468,6 +468,41 @@ namespace MWInput bool InputManager::keyPressed( const SDL_KeyboardEvent &arg ) { + // Cut, copy & paste + MyGUI::Widget* focus = MyGUI::InputManager::getInstance().getKeyFocusWidget(); + if (focus) + { + MyGUI::EditBox* edit = focus->castType<MyGUI::EditBox>(false); + if (edit && !edit->getEditReadOnly()) + { + if (arg.keysym.sym == SDLK_v && (arg.keysym.mod & SDL_Keymod(KMOD_CTRL))) + { + char* text = SDL_GetClipboardText(); + + if (text) + { + edit->addText(MyGUI::UString(text)); + SDL_free(text); + } + } + if (arg.keysym.sym == SDLK_x && (arg.keysym.mod & SDL_Keymod(KMOD_CTRL))) + { + std::string text = edit->getTextSelection(); + if (text.length()) + { + SDL_SetClipboardText(text.c_str()); + edit->deleteTextSelection(); + } + } + if (arg.keysym.sym == SDLK_c && (arg.keysym.mod & SDL_Keymod(KMOD_CTRL))) + { + std::string text = edit->getTextSelection(); + if (text.length()) + SDL_SetClipboardText(text.c_str()); + } + } + } + mInputBinder->keyPressed (arg); if(arg.keysym.sym == SDLK_RETURN @@ -577,15 +612,15 @@ namespace MWInput rot[0] = -y; rot[1] = 0.0f; rot[2] = x; - - // Only actually turn player when we're not in vanity mode + + // Only actually turn player when we're not in vanity mode if(!MWBase::Environment::get().getWorld()->vanityRotateCamera(rot)) { mPlayer->yaw(x/scale); mPlayer->pitch(-y/scale); } - if (arg.zrel) + if (arg.zrel && mControlSwitch["playerviewswitch"]) //Check to make sure you are allowed to zoomout and there is a change { MWBase::Environment::get().getWorld()->changeVanityModeScale(arg.zrel); MWBase::Environment::get().getWorld()->setCameraDistance(arg.zrel, true, true); @@ -617,9 +652,15 @@ namespace MWInput if (MWBase::Environment::get().getWindowManager()->isGuiMode () && MWBase::Environment::get().getWindowManager()->getMode () == MWGui::GM_Video) MWBase::Environment::get().getWorld ()->stopVideo (); else if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu)) + { MWBase::Environment::get().getWindowManager()->popGuiMode(); + MWBase::Environment::get().getSoundManager()->resumeSounds (MWBase::SoundManager::Play_TypeSfx); + } else + { MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu); + MWBase::Environment::get().getSoundManager()->pauseSounds (MWBase::SoundManager::Play_TypeSfx); + } } void InputManager::toggleSpell() diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 566d4bc505..42851dea39 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -31,18 +31,24 @@ namespace MWMechanics calculateDynamicStats (ptr); calculateCreatureStatModifiers (ptr); - // AI if(!MWBase::Environment::get().getWindowManager()->isGuiMode()) { + // AI CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); creatureStats.getAiSequence().execute (ptr); + + // fatigue restoration + calculateRestoration(ptr, duration); } } void Actors::updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused) { if(!paused) + { updateDrowning(ptr, duration); + updateEquippedLight(ptr, duration); + } } void Actors::adjustMagicEffects (const MWWorld::Ptr& creature) @@ -93,39 +99,29 @@ namespace MWMechanics void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration) { CreatureStats& stats = MWWorld::Class::get (ptr).getCreatureStats (ptr); + const MWWorld::Store<ESM::GameSetting>& settings = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>(); + + int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + + float capacity = MWWorld::Class::get(ptr).getCapacity(ptr); + float encumbrance = MWWorld::Class::get(ptr).getEncumbrance(ptr); + float normalizedEncumbrance = (capacity == 0 ? 1 : encumbrance/capacity); + if (normalizedEncumbrance > 1) + normalizedEncumbrance = 1; if (duration == 3600) { - bool stunted = stats.getMagicEffects ().get(MWMechanics::EffectKey(ESM::MagicEffect::StuntedMagicka)).mMagnitude > 0; + // the actor is sleeping, restore health and magicka - int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + bool stunted = stats.getMagicEffects ().get(MWMechanics::EffectKey(ESM::MagicEffect::StuntedMagicka)).mMagnitude > 0; DynamicStat<float> health = stats.getHealth(); health.setCurrent (health.getCurrent() + 0.1 * endurance); stats.setHealth (health); - const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - - float fFatigueReturnBase = store.get<ESM::GameSetting>().find("fFatigueReturnBase")->getFloat (); - float fFatigueReturnMult = store.get<ESM::GameSetting>().find("fFatigueReturnMult")->getFloat (); - float fEndFatigueMult = store.get<ESM::GameSetting>().find("fEndFatigueMult")->getFloat (); - - float capacity = MWWorld::Class::get(ptr).getCapacity(ptr); - float encumbrance = MWWorld::Class::get(ptr).getEncumbrance(ptr); - float normalizedEncumbrance = (capacity == 0 ? 1 : encumbrance/capacity); - if (normalizedEncumbrance > 1) - normalizedEncumbrance = 1; - - float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); - x *= fEndFatigueMult * endurance; - - DynamicStat<float> fatigue = stats.getFatigue(); - fatigue.setCurrent (fatigue.getCurrent() + 3600 * x); - stats.setFatigue (fatigue); - if (!stunted) { - float fRestMagicMult = store.get<ESM::GameSetting>().find("fRestMagicMult")->getFloat (); + float fRestMagicMult = settings.find("fRestMagicMult")->getFloat (); DynamicStat<float> magicka = stats.getMagicka(); magicka.setCurrent (magicka.getCurrent() @@ -133,6 +129,19 @@ namespace MWMechanics stats.setMagicka (magicka); } } + + // restore fatigue + + float fFatigueReturnBase = settings.find("fFatigueReturnBase")->getFloat (); + float fFatigueReturnMult = settings.find("fFatigueReturnMult")->getFloat (); + float fEndFatigueMult = settings.find("fEndFatigueMult")->getFloat (); + + float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); + x *= fEndFatigueMult * endurance; + + DynamicStat<float> fatigue = stats.getFatigue(); + fatigue.setCurrent (fatigue.getCurrent() + duration * x); + stats.setFatigue (fatigue); } void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr) @@ -196,6 +205,49 @@ namespace MWMechanics stats.setTimeToStartDrowning(20); } + void Actors::updateEquippedLight (const MWWorld::Ptr& ptr, float duration) + { + //If holding a light... + MWWorld::InventoryStore &inventoryStore = MWWorld::Class::get(ptr).getInventoryStore(ptr); + MWWorld::ContainerStoreIterator heldIter = + inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); + + if(heldIter.getType() == MWWorld::ContainerStore::Type_Light) + { + // Use time from the player's light + bool isPlayer = ptr.getRefData().getHandle()=="player"; + if(isPlayer) + { + float timeRemaining = heldIter->getClass().getRemainingUsageTime(*heldIter); + + // -1 is infinite light source. Other negative values are treated as 0. + if(timeRemaining != -1.0f) + { + timeRemaining -= duration; + + if(timeRemaining > 0.0f) + heldIter->getClass().setRemainingUsageTime(*heldIter, timeRemaining); + else + { + heldIter->getRefData().setCount(0); // remove it + return; + } + } + } + + // Both NPC and player lights extinguish in water. + if(MWBase::Environment::get().getWorld()->isSwimming(ptr)) + { + heldIter->getRefData().setCount(0); // remove it + + // ...But, only the player makes a sound. + if(isPlayer) + MWBase::Environment::get().getSoundManager()->playSound("torch out", + 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoEnv); + } + } + } + Actors::Actors() : mDuration (0) {} void Actors::addActor (const MWWorld::Ptr& ptr) @@ -275,9 +327,9 @@ namespace MWMechanics continue; } - // workaround: always keep player alive for now - // \todo remove workaround, once player death can be handled - if(iter->first.getRefData().getHandle()=="player") + // If it's the player and God Mode is turned on, keep it alive + if(iter->first.getRefData().getHandle()=="player" && + MWBase::Environment::get().getWorld()->getGodModeState()) { MWMechanics::DynamicStat<float> stat(stats.getHealth()); diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 69878a000e..a77e52ba30 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -44,6 +44,8 @@ namespace MWMechanics void updateDrowning (const MWWorld::Ptr& ptr, float duration); + void updateEquippedLight (const MWWorld::Ptr& ptr, float duration); + public: Actors(); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index ec2bb1b59e..c4260d907d 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -767,10 +767,25 @@ void CharacterController::update(float duration) } if(sneak || inwater || flying) + { vec.z = 0.0f; + mFallHeight = mPtr.getRefData().getPosition().pos[2]; + } if(!onground && !flying && !inwater) { + // The player is in the air (either getting up —ascending part of jump— or falling). + + if (world->isSlowFalling(mPtr)) + { + // SlowFalling spell effect is active, do not keep previous fall height + mFallHeight = mPtr.getRefData().getPosition().pos[2]; + } + else + { + mFallHeight = std::max(mFallHeight, mPtr.getRefData().getPosition().pos[2]); + } + const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>(); forcestateupdate = (mJumpState != JumpState_Falling); @@ -794,6 +809,8 @@ void CharacterController::update(float duration) } else if(vec.z > 0.0f && mJumpState == JumpState_None) { + // The player has started a jump. + float z = cls.getJump(mPtr); if(vec.x == 0 && vec.y == 0) vec = Ogre::Vector3(0.0f, 0.0f, z); @@ -803,13 +820,49 @@ void CharacterController::update(float duration) vec = Ogre::Vector3(lat.x, lat.y, 1.0f) * z * 0.707f; } - //decrease fatigue by fFatigueJumpBase + (1 - normalizedEncumbrance) * fFatigueJumpMult; + // advance acrobatics + cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 0); + + // decrease fatigue + const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>(); + const float fatigueJumpBase = gmst.find("fFatigueJumpBase")->getFloat(); + const float fatigueJumpMult = gmst.find("fFatigueJumpMult")->getFloat(); + const float normalizedEncumbrance = cls.getEncumbrance(mPtr) / cls.getCapacity(mPtr); + const int fatigueDecrease = fatigueJumpBase + (1 - normalizedEncumbrance) * fatigueJumpMult; + DynamicStat<float> fatigue = cls.getCreatureStats(mPtr).getFatigue(); + fatigue.setCurrent(fatigue.getCurrent() - fatigueDecrease); + cls.getCreatureStats(mPtr).setFatigue(fatigue); } else if(mJumpState == JumpState_Falling) { + // The player is landing. + forcestateupdate = true; mJumpState = JumpState_Landing; vec.z = 0.0f; + + float healthLost = cls.getFallDamage(mPtr, mFallHeight - mPtr.getRefData().getPosition().pos[2]); + if (healthLost > 0.0f) + { + const float fatigueTerm = cls.getCreatureStats(mPtr).getFatigueTerm(); + + // inflict fall damages + DynamicStat<float> health = cls.getCreatureStats(mPtr).getHealth(); + int realHealthLost = healthLost * (1.0f - 0.25 * fatigueTerm); + health.setCurrent(health.getCurrent() - realHealthLost); + cls.getCreatureStats(mPtr).setHealth(health); + + // report acrobatics progression + cls.skillUsageSucceeded(mPtr, ESM::Skill::Acrobatics, 1); + + const float acrobaticsSkill = cls.getNpcStats(mPtr).getSkill(ESM::Skill::Acrobatics).getModified(); + if (healthLost > (acrobaticsSkill * fatigueTerm)) + { + //TODO: actor falls over + } + } + + mFallHeight = mPtr.getRefData().getPosition().pos[2]; } else { diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index c943b95977..8670b385e3 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -3,6 +3,8 @@ #include <OgreVector3.h> +#include <components/esm/loadmgef.hpp> + #include "../mwworld/ptr.hpp" namespace MWWorld @@ -154,6 +156,9 @@ class CharacterController float mSecondsOfSwimming; float mSecondsOfRunning; + // used for acrobatics progress and fall damages + float mFallHeight; + std::string mAttackType; // slash, chop or thrust void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); diff --git a/apps/openmw/mwmechanics/magiceffects.cpp b/apps/openmw/mwmechanics/magiceffects.cpp index 1a7b348170..3ed458c3fa 100644 --- a/apps/openmw/mwmechanics/magiceffects.cpp +++ b/apps/openmw/mwmechanics/magiceffects.cpp @@ -151,8 +151,7 @@ namespace MWMechanics for (Collection::const_iterator iter (prev.begin()); iter!=prev.end(); ++iter) { Collection::const_iterator other = now.mCollection.find (iter->first); - - if (other==prev.end()) + if (other==now.end()) { result.add (iter->first, EffectParam() - iter->second); } diff --git a/apps/openmw/mwrender/.gitignore b/apps/openmw/mwrender/.gitignore new file mode 100644 index 0000000000..3367afdbbf --- /dev/null +++ b/apps/openmw/mwrender/.gitignore @@ -0,0 +1 @@ +old diff --git a/apps/openmw/mwrender/camera.cpp b/apps/openmw/mwrender/camera.cpp index 36f53c0fea..9af3987a8e 100644 --- a/apps/openmw/mwrender/camera.cpp +++ b/apps/openmw/mwrender/camera.cpp @@ -187,6 +187,12 @@ namespace MWRender rotateCamera(Ogre::Vector3(getPitch(), 0.f, getYaw()), false); } + void Camera::setSneakOffset() + { + if(mAnimation) + mAnimation->addFirstPersonOffset(Ogre::Vector3(0.f, 0.f, -9.8f)); + } + float Camera::getYaw() { if(mVanity.enabled || mPreviewMode) diff --git a/apps/openmw/mwrender/camera.hpp b/apps/openmw/mwrender/camera.hpp index dc552371e6..baf2f3685a 100644 --- a/apps/openmw/mwrender/camera.hpp +++ b/apps/openmw/mwrender/camera.hpp @@ -79,6 +79,12 @@ namespace MWRender void togglePreviewMode(bool enable); + /// \brief Lowers the camera for sneak. + /// As animation is tied to the camera, this needs + /// to be set each frame after the animation is + /// applied. + void setSneakOffset(); + bool isFirstPerson() const { return !(mVanity.enabled || mPreviewMode || !mFirstPersonView); } diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 13b5971e2a..9ffe53eabb 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -84,7 +84,8 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, MWWor mWeapon(inv.end()), mShield(inv.end()), mViewMode(viewMode), - mShowWeapons(false) + mShowWeapons(false), + mFirstPersonOffset(0.f, 0.f, 0.f) { mNpc = mPtr.get<ESM::NPC>()->mBase; @@ -392,6 +393,11 @@ void NpcAnimation::updateParts(bool forceupdate) } } +void NpcAnimation::addFirstPersonOffset(const Ogre::Vector3 &offset) +{ + mFirstPersonOffset += offset; +} + class SetObjectGroup { int mGroup; @@ -448,7 +454,12 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed) float pitch = mCamera->getPitch(); Ogre::Node *node = baseinst->getBone("Bip01 Neck"); node->pitch(Ogre::Radian(pitch*0.75f), Ogre::Node::TS_WORLD); + + // This has to be done before this function ends; + // updateSkeletonInstance, below, touches the hands. + node->translate(mFirstPersonOffset, Ogre::Node::TS_WORLD); } + mFirstPersonOffset = 0.f; // reset the X, Y, Z offset for the next frame. for(size_t i = 0;i < ESM::PRT_Count;i++) { diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 24205acafd..b1abf97af0 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -64,6 +64,8 @@ private: int mPartslots[ESM::PRT_Count]; //Each part slot is taken by clothing, armor, or is empty int mPartPriorities[ESM::PRT_Count]; + Ogre::Vector3 mFirstPersonOffset; + void updateNpcBase(); NifOgre::ObjectList insertBoundedPart(const std::string &model, int group, const std::string &bonename); @@ -89,6 +91,11 @@ public: void updateParts(bool forceupdate = false); + /// \brief Applies a translation to the arms and hands. + /// This may be called multiple times before the animation + /// is updated to add additional offsets. + void addFirstPersonOffset(const Ogre::Vector3 &offset); + /// Rebuilds the NPC, updating their root model, animation sources, and equipment. void rebuild(); }; diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index e03b2ccfcf..93425191dd 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -354,6 +354,15 @@ void RenderingManager::update (float duration, bool paused) mCamera->setCameraDistance(test.second * orig.distance(dest), false, false); } + // Sink the camera while sneaking + bool isSneaking = MWWorld::Class::get(player).getStance(player, MWWorld::Class::Sneak); + bool isInAir = !world->isOnGround(player); + bool isSwimming = world->isSwimming(player); + + if(isSneaking && !(isSwimming || isInAir)) + mCamera->setSneakOffset(); + + mOcclusionQuery->update(duration); mVideoPlayer->update (); @@ -760,6 +769,13 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec || it->second == "resolution y" || it->second == "fullscreen")) changeRes = true; + else if (it->first == "Video" && it->second == "vsync") + { + // setVSyncEnabled is bugged in 1.8 +#if OGRE_VERSION >= (1 << 16 | 9 << 8 | 0) + mRendering.getWindow()->setVSyncEnabled(Settings::Manager::getBool("vsync", "Video")); +#endif + } else if (it->second == "field of view" && it->first == "General") mRendering.setFov(Settings::Manager::getFloat("field of view", "General")); else if ((it->second == "texture filtering" && it->first == "General") diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 08b4991753..7bbb336999 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -351,5 +351,8 @@ op 0x200021b: SetWerewolfAcrobatics op 0x200021c: SetWerewolfAcrobaticsExplicit op 0x200021d: ShowVars op 0x200021e: ShowVarsExplicit +op 0x200021f: ToggleGodMode +op 0x2000220: DisableLevitation +op 0x2000221: EnableLevitation -opcodes 0x200021f-0x3ffffff unused +opcodes 0x2000222-0x3ffffff unused diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 3141f13a25..4ae1136e22 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -635,7 +635,18 @@ namespace MWScript world->enableTeleporting(Enable); } }; - + + template <bool Enable> + class OpEnableLevitation : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + MWBase::World *world = MWBase::Environment::get().getWorld(); + world->enableLevitation(Enable); + } + }; template <class R> class OpShowVars : public Interpreter::Opcode0 @@ -717,6 +728,19 @@ namespace MWScript } }; + class OpToggleGodMode : public Interpreter::Opcode0 + { + public: + virtual void execute (Interpreter::Runtime& runtime) + { + InterpreterContext& context = static_cast<InterpreterContext&> (runtime.getContext()); + + bool enabled = MWBase::Environment::get().getWorld()->toggleGodMode(); + + context.report (enabled ? "God Mode -> On" : "God Mode -> Off"); + } + }; + void installOpcodes (Interpreter::Interpreter& interpreter) { @@ -775,6 +799,9 @@ namespace MWScript interpreter.installSegment5 (Compiler::Misc::opcodeEnableTeleporting, new OpEnableTeleporting<true>); interpreter.installSegment5 (Compiler::Misc::opcodeShowVars, new OpShowVars<ImplicitRef>); interpreter.installSegment5 (Compiler::Misc::opcodeShowVarsExplicit, new OpShowVars<ExplicitRef>); + interpreter.installSegment5 (Compiler::Misc::opcodeToggleGodMode, new OpToggleGodMode); + interpreter.installSegment5 (Compiler::Misc::opcodeDisableLevitation, new OpEnableLevitation<false>); + interpreter.installSegment5 (Compiler::Misc::opcodeEnableLevitation, new OpEnableLevitation<true>); } } } diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index eca9d279b2..603515ff4a 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -1081,7 +1081,6 @@ namespace MWScript } }; - void installOpcodes (Interpreter::Interpreter& interpreter) { for (int i=0; i<Compiler::Stats::numberOfAttributes; ++i) @@ -1207,7 +1206,7 @@ namespace MWScript interpreter.installSegment5 (Compiler::Stats::opcodeUndoWerewolf, new OpSetWerewolf<ImplicitRef, false>); interpreter.installSegment5 (Compiler::Stats::opcodeUndoWerewolfExplicit, new OpSetWerewolf<ExplicitRef, false>); interpreter.installSegment5 (Compiler::Stats::opcodeSetWerewolfAcrobatics, new OpSetWerewolfAcrobatics<ImplicitRef>); - interpreter.installSegment5 (Compiler::Stats::opcodeSetWerewolfAcrobaticsExplicit, new OpSetWerewolfAcrobatics<ExplicitRef>); + interpreter.installSegment5 (Compiler::Stats::opcodeSetWerewolfAcrobaticsExplicit, new OpSetWerewolfAcrobatics<ExplicitRef>); } } } diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index c739ea831c..d3d1aff49b 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -132,6 +132,16 @@ namespace MWWorld throw std::runtime_error ("class does not support unlocking"); } + void Class::setRemainingUsageTime (const Ptr& ptr, float duration) const + { + throw std::runtime_error ("class does not support time-based uses"); + } + + float Class::getRemainingUsageTime (const Ptr& ptr) const + { + throw std::runtime_error ("class does not support time-based uses"); + } + std::string Class::getScript (const Ptr& ptr) const { return ""; @@ -167,6 +177,11 @@ namespace MWWorld throw std::runtime_error ("class does not support enchanting"); } + float Class::getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const + { + return 0; + } + MWMechanics::Movement& Class::getMovementSettings (const Ptr& ptr) const { throw std::runtime_error ("movement settings not supported by class"); diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 28e37cbf3c..2db293e688 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -156,6 +156,14 @@ namespace MWWorld virtual void unlock (const Ptr& ptr) const; ///< Unlock object (default implementation: throw an exception) + virtual void setRemainingUsageTime (const Ptr& ptr, float duration) const; + ///< Sets the remaining duration of the object, such as an equippable light + /// source. (default implementation: throw an exception) + + virtual float getRemainingUsageTime (const Ptr& ptr) const; + ///< Returns the remaining duration of the object, such as an equippable light + /// source. (default implementation: throw an exception) + virtual std::string getScript (const Ptr& ptr) const; ///< Return name of the script attached to ptr (default implementation: return an empty /// string). @@ -175,6 +183,9 @@ namespace MWWorld virtual float getJump(const MWWorld::Ptr &ptr) const; ///< Return jump velocity (not accounting for movement) + virtual float getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const; + ///< Return amount of health points lost when falling + virtual MWMechanics::Movement& getMovementSettings (const Ptr& ptr) const; ///< Return desired movement. diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 9d111a5257..c6768f5fdd 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -80,7 +80,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end() bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2) { /// \todo add current enchantment charge here when it is implemented - if ( ptr1.getCellRef().mRefID == ptr2.getCellRef().mRefID + if ( Misc::StringUtils::ciEqual(ptr1.getCellRef().mRefID, ptr2.getCellRef().mRefID) && MWWorld::Class::get(ptr1).getScript(ptr1) == "" // item with a script never stacks && MWWorld::Class::get(ptr1).getEnchantment(ptr1) == "" // item with enchantment never stacks (we could revisit this later, but for now it makes selecting items in the spell window much easier) && ptr1.getCellRef().mOwner == ptr2.getCellRef().mOwner diff --git a/apps/openmw/mwworld/contentloader.hpp b/apps/openmw/mwworld/contentloader.hpp new file mode 100644 index 0000000000..46bd7d3f96 --- /dev/null +++ b/apps/openmw/mwworld/contentloader.hpp @@ -0,0 +1,36 @@ +#ifndef CONTENTLOADER_HPP +#define CONTENTLOADER_HPP + +#include <iosfwd> +#include <iostream> +#include <boost/filesystem/path.hpp> + +#include "components/loadinglistener/loadinglistener.hpp" + +namespace MWWorld +{ + +struct ContentLoader +{ + ContentLoader(Loading::Listener& listener) + : mListener(listener) + { + } + + virtual ~ContentLoader() + { + } + + virtual void load(const boost::filesystem::path& filepath, int& index) + { + std::cout << "Loading content file " << filepath.string() << std::endl; + mListener.setLabel(filepath.string()); + } + + protected: + Loading::Listener& mListener; +}; + +} /* namespace MWWorld */ + +#endif /* CONTENTLOADER_HPP */ diff --git a/apps/openmw/mwworld/esmloader.cpp b/apps/openmw/mwworld/esmloader.cpp new file mode 100644 index 0000000000..1b8880d375 --- /dev/null +++ b/apps/openmw/mwworld/esmloader.cpp @@ -0,0 +1,31 @@ +#include "esmloader.hpp" +#include "esmstore.hpp" + +#include "components/to_utf8/to_utf8.hpp" + +namespace MWWorld +{ + +EsmLoader::EsmLoader(MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& readers, + ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener) + : ContentLoader(listener) + , mStore(store) + , mEsm(readers) + , mEncoder(encoder) +{ +} + +void EsmLoader::load(const boost::filesystem::path& filepath, int& index) +{ + ContentLoader::load(filepath.filename(), index); + + ESM::ESMReader lEsm; + lEsm.setEncoder(mEncoder); + lEsm.setIndex(index); + lEsm.setGlobalReaderList(&mEsm); + lEsm.open(filepath.string()); + mEsm[index] = lEsm; + mStore.load(mEsm[index], &mListener); +} + +} /* namespace MWWorld */ diff --git a/apps/openmw/mwworld/esmloader.hpp b/apps/openmw/mwworld/esmloader.hpp new file mode 100644 index 0000000000..d799c3f152 --- /dev/null +++ b/apps/openmw/mwworld/esmloader.hpp @@ -0,0 +1,34 @@ +#ifndef ESMLOADER_HPP +#define ESMLOADER_HPP + +#include <vector> + +#include "contentloader.hpp" +#include "components/esm/esmreader.hpp" + +namespace ToUTF8 +{ + class Utf8Encoder; +} + +namespace MWWorld +{ + +class ESMStore; + +struct EsmLoader : public ContentLoader +{ + EsmLoader(MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& readers, + ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener); + + void load(const boost::filesystem::path& filepath, int& index); + + private: + std::vector<ESM::ESMReader>& mEsm; + MWWorld::ESMStore& mStore; + ToUTF8::Utf8Encoder* mEncoder; +}; + +} /* namespace MWWorld */ + +#endif // ESMLOADER_HPP diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 7703f2d233..ab88787175 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -36,7 +36,7 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) // all files/readers used by the engine. This will greaty accelerate // refnumber mangling, as required for handling moved references. int index = ~0; - const std::vector<ESM::Header::MasterData> &masters = esm.getMasters(); + const std::vector<ESM::Header::MasterData> &masters = esm.getGameFiles(); std::vector<ESM::ESMReader> *allPlugins = esm.getGlobalReaderList(); for (size_t j = 0; j < masters.size(); j++) { ESM::Header::MasterData &mast = const_cast<ESM::Header::MasterData&>(masters[j]); @@ -100,11 +100,7 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) it->second->load(esm, id); if (n.val==ESM::REC_DIAL) { - // dirty hack, but it is better than non-const search() - // or friends - //dialogue = &mDialogs.mStatic.back(); dialogue = const_cast<ESM::Dialogue*>(mDialogs.find(id)); - assert (dialogue->mId == id); } else { dialogue = 0; } diff --git a/apps/openmw/mwworld/omwloader.cpp b/apps/openmw/mwworld/omwloader.cpp new file mode 100644 index 0000000000..8562a4fe04 --- /dev/null +++ b/apps/openmw/mwworld/omwloader.cpp @@ -0,0 +1,17 @@ +#include "omwloader.hpp" + +namespace MWWorld +{ + +OmwLoader::OmwLoader(Loading::Listener& listener) + : ContentLoader(listener) +{ +} + +void OmwLoader::load(const boost::filesystem::path& filepath, int& index) +{ + ContentLoader::load(filepath.filename(), index); +} + +} /* namespace MWWorld */ + diff --git a/apps/openmw/mwworld/omwloader.hpp b/apps/openmw/mwworld/omwloader.hpp new file mode 100644 index 0000000000..cb9faa4303 --- /dev/null +++ b/apps/openmw/mwworld/omwloader.hpp @@ -0,0 +1,21 @@ +#ifndef OMWLOADER_HPP +#define OMWLOADER_HPP + +#include "contentloader.hpp" + +namespace MWWorld +{ + +/** + * @brief Placeholder for real OpenMW content loader + */ +struct OmwLoader : public ContentLoader +{ + OmwLoader(Loading::Listener& listener); + + void load(const boost::filesystem::path& filepath, int& index); +}; + +} /* namespace MWWorld */ + +#endif /* OMWLOADER_HPP */ diff --git a/apps/openmw/mwworld/store.hpp b/apps/openmw/mwworld/store.hpp index 2ee23dbd61..90452f6614 100644 --- a/apps/openmw/mwworld/store.hpp +++ b/apps/openmw/mwworld/store.hpp @@ -656,26 +656,38 @@ namespace MWWorld return iterator(mSharedExt.end()); } - /// \todo implement appropriate index + // Return the northernmost cell in the easternmost column. const ESM::Cell *searchExtByName(const std::string &id) const { + ESM::Cell *cell = 0; std::vector<ESM::Cell *>::const_iterator it = mSharedExt.begin(); for (; it != mSharedExt.end(); ++it) { if (Misc::StringUtils::ciEqual((*it)->mName, id)) { - return *it; + if ( cell == 0 || + ( (*it)->mData.mX > cell->mData.mX ) || + ( (*it)->mData.mX == cell->mData.mX && (*it)->mData.mY > cell->mData.mY ) ) + { + cell = *it; + } } } - return 0; + return cell; } - /// \todo implement appropriate index + // Return the northernmost cell in the easternmost column. const ESM::Cell *searchExtByRegion(const std::string &id) const { + ESM::Cell *cell = 0; std::vector<ESM::Cell *>::const_iterator it = mSharedExt.begin(); for (; it != mSharedExt.end(); ++it) { if (Misc::StringUtils::ciEqual((*it)->mRegion, id)) { - return *it; + if ( cell == 0 || + ( (*it)->mData.mX > cell->mData.mX ) || + ( (*it)->mData.mX == cell->mData.mX && (*it)->mData.mY > cell->mData.mY ) ) + { + cell = *it; + } } } - return 0; + return cell; } size_t getSize() const { diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index eac0a39776..6a4a380d65 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,4 +1,11 @@ #include "worldimp.hpp" +#ifdef _WIN32 +#include <boost/tr1/tr1/unordered_map> +#elif defined HAVE_UNORDERED_MAP +#include <unordered_map> +#else +#include <tr1/unordered_map> +#endif #include <OgreSceneNode.h> @@ -31,6 +38,10 @@ #include "containerstore.hpp" #include "inventorystore.hpp" +#include "contentloader.hpp" +#include "esmloader.hpp" +#include "omwloader.hpp" + using namespace Ogre; namespace @@ -80,6 +91,38 @@ namespace namespace MWWorld { + struct GameContentLoader : public ContentLoader + { + GameContentLoader(Loading::Listener& listener) + : ContentLoader(listener) + { + } + + bool addLoader(const std::string& extension, ContentLoader* loader) + { + return mLoaders.insert(std::make_pair(extension, loader)).second; + } + + void load(const boost::filesystem::path& filepath, int& index) + { + LoadersContainer::iterator it(mLoaders.find(filepath.extension().string())); + if (it != mLoaders.end()) + { + it->second->load(filepath, index); + } + else + { + std::string msg("Cannot load file: "); + msg += filepath.string(); + throw std::runtime_error(msg.c_str()); + } + } + + private: + typedef std::tr1::unordered_map<std::string, ContentLoader*> LoadersContainer; + LoadersContainer mLoaders; + }; + Ptr World::getPtrViaHandle (const std::string& handle, Ptr::CellStore& cell) { if (MWWorld::LiveCellRef<ESM::Activator> *ref = @@ -163,14 +206,14 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::vector<std::string>& master, const std::vector<std::string>& plugins, + const std::vector<std::string>& contentFiles, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap, int mActivationDistanceOverride) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), mSky (true), mCells (mStore, mEsm), mActivationDistanceOverride (mActivationDistanceOverride), mFallback(fallbackMap), mPlayIntro(0), mTeleportEnabled(true), - mFacedDistance(FLT_MAX) + mFacedDistance(FLT_MAX), mGodMode(false) { mPhysics = new PhysicsSystem(renderer); mPhysEngine = mPhysics->getEngine(); @@ -181,52 +224,30 @@ namespace MWWorld mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); - int idx = 0; // NOTE: We might need to reserve one more for the running game / save. - mEsm.resize(master.size() + plugins.size()); + mEsm.resize(contentFiles.size()); Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); listener->loadingOn(); - for (std::vector<std::string>::size_type i = 0; i < master.size(); i++, idx++) - { - boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master[i])); - - std::cout << "Loading ESM " << masterPath.string() << "\n"; - listener->setLabel(masterPath.filename().string()); - // This parses the ESM file - ESM::ESMReader lEsm; - lEsm.setEncoder(encoder); - lEsm.setIndex(idx); - lEsm.setGlobalReaderList(&mEsm); - lEsm.open (masterPath.string()); - mEsm[idx] = lEsm; - mStore.load (mEsm[idx], listener); - } + GameContentLoader gameContentLoader(*listener); + EsmLoader esmLoader(mStore, mEsm, encoder, *listener); + OmwLoader omwLoader(*listener); - for (std::vector<std::string>::size_type i = 0; i < plugins.size(); i++, idx++) - { - boost::filesystem::path pluginPath (fileCollections.getCollection (".esp").getPath (plugins[i])); + gameContentLoader.addLoader(".esm", &esmLoader); + gameContentLoader.addLoader(".esp", &esmLoader); + gameContentLoader.addLoader(".omwgame", &omwLoader); + gameContentLoader.addLoader(".omwaddon", &omwLoader); - std::cout << "Loading ESP " << pluginPath.string() << "\n"; - listener->setLabel(pluginPath.filename().string()); + loadContentFiles(fileCollections, contentFiles, gameContentLoader); - // This parses the ESP file - ESM::ESMReader lEsm; - lEsm.setEncoder(encoder); - lEsm.setIndex(idx); - lEsm.setGlobalReaderList(&mEsm); - lEsm.open (pluginPath.string()); - mEsm[idx] = lEsm; - mStore.load (mEsm[idx], listener); - } listener->loadingOff(); // insert records that may not be present in all versions of MW if (mEsm[0].getFormat() == 0) ensureNeededRecords(); - mStore.movePlayerRecord(); mStore.setUp(); + mStore.movePlayerRecord(); mGlobalVariables = new Globals (mStore); @@ -1160,7 +1181,7 @@ namespace MWWorld bool World::toggleCollisionMode() { - return mPhysics->toggleCollisionMode();; + return mPhysics->toggleCollisionMode(); } bool World::toggleRenderMode (RenderMode mode) @@ -1582,6 +1603,19 @@ namespace MWWorld return false; } + bool + World::isSlowFalling(const MWWorld::Ptr &ptr) const + { + if(!ptr.getClass().isActor()) + return false; + + const MWMechanics::CreatureStats &stats = ptr.getClass().getCreatureStats(ptr); + if(stats.getMagicEffects().get(MWMechanics::EffectKey(ESM::MagicEffect::SlowFall)).mMagnitude > 0) + return true; + + return false; + } + bool World::isSubmerged(const MWWorld::Ptr &object) const { float *fpos = object.getRefData().getPosition().pos; @@ -1877,6 +1911,16 @@ namespace MWWorld return mTeleportEnabled; } + void World::enableLevitation(bool enable) + { + mLevitationEnabled = enable; + } + + bool World::isLevitationEnabled() const + { + return mLevitationEnabled; + } + void World::setWerewolf(const MWWorld::Ptr& actor, bool werewolf) { MWMechanics::NpcStats& npcStats = Class::get(actor).getNpcStats(actor); @@ -1948,4 +1992,31 @@ namespace MWWorld stats.getSkill(ESM::Skill::Acrobatics).setModified(gmst.find("fWerewolfAcrobatics")->getFloat(), 0); } + bool World::getGodModeState() + { + return mGodMode; + } + + bool World::toggleGodMode() + { + mGodMode = !mGodMode; + + return mGodMode; + } + + void World::loadContentFiles(const Files::Collections& fileCollections, + const std::vector<std::string>& content, ContentLoader& contentLoader) + { + std::vector<std::string>::const_iterator it(content.begin()); + std::vector<std::string>::const_iterator end(content.end()); + for (int idx = 0; it != end; ++it, ++idx) + { + boost::filesystem::path filename(*it); + const Files::MultiDirCollection& col = fileCollections.getCollection(filename.extension().string()); + if (col.doesExist(*it)) + { + contentLoader.load(col.getPath(*it), idx); + } + } + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 71391239b3..aadf7ce981 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -14,6 +14,8 @@ #include "../mwbase/world.hpp" +#include "contentloader.hpp" + namespace Ogre { class Vector3; @@ -41,6 +43,8 @@ namespace MWRender class Animation; } +struct ContentLoader; + namespace MWWorld { class WeatherManager; @@ -68,6 +72,8 @@ namespace MWWorld OEngine::Physic::PhysicEngine* mPhysEngine; + bool mGodMode; + // not implemented World (const World&); World& operator= (const World&); @@ -111,15 +117,25 @@ namespace MWWorld void ensureNeededRecords(); + /** + * @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon) + * @param fileCollections- Container which holds content file names and their paths + * @param content - Container which holds content file names + * @param contentLoader - + */ + void loadContentFiles(const Files::Collections& fileCollections, + const std::vector<std::string>& content, ContentLoader& contentLoader); + int mPlayIntro; bool mTeleportEnabled; + bool mLevitationEnabled; public: World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::vector<std::string>& master, const std::vector<std::string>& plugins, + const std::vector<std::string>& contentFiles, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, ToUTF8::Utf8Encoder* encoder, const std::map<std::string,std::string>& fallbackMap, int mActivationDistanceOverride); @@ -353,6 +369,7 @@ namespace MWWorld virtual void processChangedSettings(const Settings::CategorySettingVector& settings); virtual bool isFlying(const MWWorld::Ptr &ptr) const; + virtual bool isSlowFalling(const MWWorld::Ptr &ptr) const; ///Is the head of the creature underwater? virtual bool isSubmerged(const MWWorld::Ptr &object) const; virtual bool isSwimming(const MWWorld::Ptr &object) const; @@ -436,9 +453,19 @@ namespace MWWorld /// Returns true if teleport spell effects are allowed. virtual bool isTeleportingEnabled() const; + /// Enables or disables use of levitation spell effect. + virtual void enableLevitation(bool enable); + + /// Returns true if levitation spell effect is allowed. + virtual bool isLevitationEnabled() const; + virtual void setWerewolf(const MWWorld::Ptr& actor, bool werewolf); virtual void applyWerewolfAcrobatics(const MWWorld::Ptr& actor); + + virtual bool getGodModeState(); + + virtual bool toggleGodMode(); }; } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 04423dc6f0..acb70b04db 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -1,5 +1,5 @@ project (Components) - +set (CMAKE_BUILD_TYPE DEBUG) # source files add_component_dir (settings @@ -74,21 +74,25 @@ add_component_dir (loadinglistener loadinglistener ) +set (ESM_UI ${CMAKE_SOURCE_DIR}/files/ui/contentselector.ui + ) find_package(Qt4 COMPONENTS QtCore QtGui) if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) - add_component_qt_dir (fileorderlist - model/modelitem model/datafilesmodel model/pluginsproxymodel model/esm/esmfile - utils/profilescombobox utils/comboboxlineedit utils/lineedit utils/naturalsort + add_component_qt_dir (contentselector + model/modelitem model/esmfile + model/naturalsort model/contentmodel + view/combobox view/contentselector ) include(${QT_USE_FILE}) + QT4_WRAP_UI(ESM_UI_HDR ${ESM_UI}) QT4_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES}) endif(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY) -include_directories(${BULLET_INCLUDE_DIRS}) +include_directories(${BULLET_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) -add_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS}) +add_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS} ${ESM_UI_HDR}) target_link_libraries(components ${Boost_LIBRARIES} ${OGRE_LIBRARIES}) diff --git a/components/bsa/tests/.gitignore b/components/bsa/tests/.gitignore new file mode 100644 index 0000000000..e2f2f332df --- /dev/null +++ b/components/bsa/tests/.gitignore @@ -0,0 +1,3 @@ +*_test +bsatool +*.bsa diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index 9e0c36825a..65f6e112a3 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -258,6 +258,10 @@ namespace Compiler extensions.registerInstruction ("enableteleporting", "", opcodeEnableTeleporting); extensions.registerInstruction ("showvars", "", opcodeShowVars, opcodeShowVarsExplicit); extensions.registerInstruction ("sv", "", opcodeShowVars, opcodeShowVarsExplicit); + extensions.registerInstruction("tgm", "", opcodeToggleGodMode); + extensions.registerInstruction("togglegodmode", "", opcodeToggleGodMode); + extensions.registerInstruction ("disablelevitation", "", opcodeDisableLevitation); + extensions.registerInstruction ("enablelevitation", "", opcodeEnableLevitation); } } diff --git a/components/compiler/opcodes.hpp b/components/compiler/opcodes.hpp index c4e2c1bc6b..aca24e0d30 100644 --- a/components/compiler/opcodes.hpp +++ b/components/compiler/opcodes.hpp @@ -221,6 +221,9 @@ namespace Compiler const int opcodeEnableTeleporting = 0x2000216; const int opcodeShowVars = 0x200021d; const int opcodeShowVarsExplicit = 0x200021e; + const int opcodeToggleGodMode = 0x200021f; + const int opcodeDisableLevitation = 0x2000220; + const int opcodeEnableLevitation = 0x2000221; } namespace Sky diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp new file mode 100644 index 0000000000..5f3575eb40 --- /dev/null +++ b/components/contentselector/model/contentmodel.cpp @@ -0,0 +1,616 @@ +#include "contentmodel.hpp" +#include "esmfile.hpp" + +#include <QDir> +#include <QTextCodec> +#include <QDebug> + +#include "components/esm/esmreader.hpp" + +ContentSelectorModel::ContentModel::ContentModel(QObject *parent) : + QAbstractTableModel(parent), + mMimeType ("application/omwcontent"), + mMimeTypes (QStringList() << mMimeType), + mColumnCount (1), + mDragDropFlags (Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled), + mDropActions (Qt::CopyAction | Qt::MoveAction) +{ + setEncoding ("win1252"); + uncheckAll(); +} + +void ContentSelectorModel::ContentModel::setEncoding(const QString &encoding) +{ + if (encoding == QLatin1String("win1252")) + mCodec = QTextCodec::codecForName("windows-1252"); + + else if (encoding == QLatin1String("win1251")) + mCodec = QTextCodec::codecForName("windows-1251"); + + else if (encoding == QLatin1String("win1250")) + mCodec = QTextCodec::codecForName("windows-1250"); + + else + return; // This should never happen; +} + +int ContentSelectorModel::ContentModel::columnCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return mColumnCount; +} + +int ContentSelectorModel::ContentModel::rowCount(const QModelIndex &parent) const +{ + if(parent.isValid()) + return 0; + + return mFiles.size(); +} + +const ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(int row) const +{ + if (row >= 0 && row < mFiles.size()) + return mFiles.at(row); + + return 0; +} + +ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(int row) +{ + if (row >= 0 && row < mFiles.count()) + return mFiles.at(row); + + return 0; +} +const ContentSelectorModel::EsmFile *ContentSelectorModel::ContentModel::item(const QString &name) const +{ + EsmFile::FileProperty fp = EsmFile::FileProperty_FileName; + + if (name.contains ('/')) + fp = EsmFile::FileProperty_FilePath; + + foreach (const EsmFile *file, mFiles) + { + if (name == file->fileProperty (fp).toString()) + return file; + } + return 0; +} + +QModelIndex ContentSelectorModel::ContentModel::indexFromItem(const EsmFile *item) const +{ + //workaround: non-const pointer cast for calls from outside contentmodel/contentselector + EsmFile *non_const_file_ptr = const_cast<EsmFile *>(item); + + if (item) + return index(mFiles.indexOf(non_const_file_ptr),0); + + return QModelIndex(); +} + +Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return Qt::NoItemFlags; + + const EsmFile *file = item(index.row()); + + if (!file) + return Qt::NoItemFlags; + + //game files can always be checked + if (file->isGameFile()) + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; + + Qt::ItemFlags returnFlags; + bool allDependenciesFound = true; + bool gamefileChecked = false; + + //addon can be checked if its gamefile is and all other dependencies exist + foreach (const QString &fileName, file->gameFiles()) + { + bool depFound = false; + foreach (EsmFile *dependency, mFiles) + { + //compare filenames only. Multiple instances + //of the filename (with different paths) is not relevant here. + depFound = (dependency->fileName() == fileName); + + if (!depFound) + continue; + + if (!gamefileChecked) + { + if (isChecked (dependency->filePath())) + gamefileChecked = (dependency->isGameFile()); + } + + // force it to iterate all files in cases where the current + // dependency is a game file to ensure that a later duplicate + // game file is / is not checked. + // (i.e., break only if it's not a gamefile or the game file has been checked previously) + if (gamefileChecked || !(dependency->isGameFile())) + break; + } + + allDependenciesFound = allDependenciesFound && depFound; + } + + if (gamefileChecked) + { + if (allDependenciesFound) + returnFlags = returnFlags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | mDragDropFlags; + else + returnFlags = Qt::ItemIsSelectable; + } + + return returnFlags; +} + +QVariant ContentSelectorModel::ContentModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= mFiles.size()) + return QVariant(); + + const EsmFile *file = item(index.row()); + + if (!file) + return QVariant(); + + const int column = index.column(); + + switch (role) + { + case Qt::EditRole: + case Qt::DisplayRole: + { + if (column >=0 && column <=EsmFile::FileProperty_GameFile) + return file->fileProperty(static_cast<const EsmFile::FileProperty>(column)); + + return QVariant(); + break; + } + + case Qt::TextAlignmentRole: + { + switch (column) + { + case 0: + case 1: + return Qt::AlignLeft + Qt::AlignVCenter; + case 2: + case 3: + return Qt::AlignRight + Qt::AlignVCenter; + default: + return Qt::AlignLeft + Qt::AlignVCenter; + } + return QVariant(); + break; + } + + case Qt::ToolTipRole: + { + if (column != 0) + return QVariant(); + + return file->toolTip(); + break; + } + + case Qt::CheckStateRole: + { + if (!file->isGameFile()) + return isChecked(file->filePath()); + break; + } + + case Qt::UserRole: + { + if (file->isGameFile()) + return ContentType_GameFile; + else + if (flags(index)) + return ContentType_Addon; + + break; + } + + case Qt::UserRole + 1: + return isChecked(file->filePath()); + break; + } + return QVariant(); +} + +bool ContentSelectorModel::ContentModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if(!index.isValid()) + return false; + + EsmFile *file = item(index.row()); + QString fileName = file->filePath(); + bool success = false; + + switch(role) + { + case Qt::EditRole: + { + QStringList list = value.toStringList(); + + for (int i = 0; i < EsmFile::FileProperty_GameFile; i++) + file->setFileProperty(static_cast<EsmFile::FileProperty>(i), list.at(i)); + + for (int i = EsmFile::FileProperty_GameFile; i < list.size(); i++) + file->setFileProperty (EsmFile::FileProperty_GameFile, list.at(i)); + + emit dataChanged(index, index); + + success = true; + } + break; + + case Qt::UserRole+1: + { + success = (flags (index) & Qt::ItemIsEnabled); + + if (success) + { + success = setCheckState(fileName, value.toBool()); + emit dataChanged(index, index); + } + } + break; + + case Qt::CheckStateRole: + { + int checkValue = value.toInt(); + bool success = false; + bool setState = false; + if ((checkValue==Qt::Checked) && !isChecked(fileName)) + { + setState = true; + success = true; + } + else if ((checkValue == Qt::Checked) && isChecked (fileName)) + setState = true; + else if (checkValue == Qt::Unchecked) + setState = true; + + if (setState) + { + setCheckState(fileName, success); + emit dataChanged(index, index); + + } + else + return success; + + + foreach (EsmFile *file, mFiles) + { + if (file->gameFiles().contains(fileName)) + { + QModelIndex idx = indexFromItem(file); + emit dataChanged(idx, idx); + } + } + + success = true; + } + break; + } + + return success; +} + +bool ContentSelectorModel::ContentModel::insertRows(int position, int rows, const QModelIndex &parent) +{ + if (parent.isValid()) + return false; + + beginInsertRows(parent, position, position+rows-1); + { + for (int row = 0; row < rows; ++row) + mFiles.insert(position, new EsmFile); + + } endInsertRows(); + + return true; +} + +bool ContentSelectorModel::ContentModel::removeRows(int position, int rows, const QModelIndex &parent) +{ + if (parent.isValid()) + return false; + + beginRemoveRows(parent, position, position+rows-1); + { + for (int row = 0; row < rows; ++row) + delete mFiles.takeAt(position); + + } endRemoveRows(); + + return true; +} + +Qt::DropActions ContentSelectorModel::ContentModel::supportedDropActions() const +{ + return mDropActions; +} + +QStringList ContentSelectorModel::ContentModel::mimeTypes() const +{ + return mMimeTypes; +} + +QMimeData *ContentSelectorModel::ContentModel::mimeData(const QModelIndexList &indexes) const +{ + QByteArray encodedData; + + foreach (const QModelIndex &index, indexes) + { + if (!index.isValid()) + continue; + + encodedData.append(item(index.row())->encodedData()); + } + + QMimeData *mimeData = new QMimeData(); + mimeData->setData(mMimeType, encodedData); + + return mimeData; +} + +bool ContentSelectorModel::ContentModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) +{ + if (action == Qt::IgnoreAction) + return true; + + if (column > 0) + return false; + + if (!data->hasFormat(mMimeType)) + return false; + + int beginRow = rowCount(); + + if (row != -1) + beginRow = row; + + else if (parent.isValid()) + beginRow = parent.row(); + + QByteArray encodedData = data->data(mMimeType); + QDataStream stream(&encodedData, QIODevice::ReadOnly); + + while (!stream.atEnd()) + { + + QString value; + QStringList values; + QStringList gamefiles; + + for (int i = 0; i < EsmFile::FileProperty_GameFile; ++i) + { + stream >> value; + values << value; + } + + stream >> gamefiles; + + insertRows(beginRow, 1); + + QModelIndex idx = index(beginRow++, 0, QModelIndex()); + setData(idx, QStringList() << values << gamefiles, Qt::EditRole); + } + + return true; +} + +void ContentSelectorModel::ContentModel::addFile(EsmFile *file) +{ + beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); + mFiles.append(file); + endInsertRows(); + + QModelIndex idx = index (mFiles.size() - 2, 0, QModelIndex()); + + emit dataChanged (idx, idx); +} + +void ContentSelectorModel::ContentModel::addFiles(const QString &path) +{ + QDir dir(path); + QStringList filters; + filters << "*.esp" << "*.esm" << "*.omwgame" << "*.omwaddon"; + dir.setNameFilters(filters); + + QTextCodec *codec = QTextCodec::codecForName("UTF8"); + + // Create a decoder for non-latin characters in esx metadata + QTextDecoder *decoder = codec->makeDecoder(); + + foreach (const QString &path, dir.entryList()) + { + QFileInfo info(dir.absoluteFilePath(path)); + EsmFile *file = new EsmFile(path); + + try { + ESM::ESMReader fileReader; + ToUTF8::Utf8Encoder encoder = + ToUTF8::calculateEncoding(QString(mCodec->name()).toStdString()); + fileReader.setEncoder(&encoder); + fileReader.open(dir.absoluteFilePath(path).toStdString()); + + foreach (const ESM::Header::MasterData &item, fileReader.getGameFiles()) + file->addGameFile(QString::fromStdString(item.name)); + + file->setAuthor (decoder->toUnicode(fileReader.getAuthor().c_str())); + file->setDate (info.lastModified()); + file->setFormat (fileReader.getFormat()); + file->setFilePath (info.absoluteFilePath()); + file->setDescription(decoder->toUnicode(fileReader.getDesc().c_str())); + + + // Put the file in the table + if (item(file->filePath()) == 0) + addFile(file); + + } catch(std::runtime_error &e) { + // An error occurred while reading the .esp + qWarning() << "Error reading addon file: " << e.what(); + continue; + } + + } + + delete decoder; + + sortFiles(); +} + +void ContentSelectorModel::ContentModel::sortFiles() +{ + //first, sort the model such that all dependencies are ordered upstream (gamefile) first. + bool movedFiles = true; + int fileCount = mFiles.size(); + + //Dependency sort + //iterate until no sorting of files occurs + while (movedFiles) + { + movedFiles = false; + //iterate each file, obtaining a reference to it's gamefiles list + for (int i = 0; i < fileCount; i++) + { + QModelIndex idx1 = index (i, 0, QModelIndex()); + const QStringList &gamefiles = mFiles.at(i)->gameFiles(); + //iterate each file after the current file, verifying that none of it's + //dependencies appear. + for (int j = i + 1; j < fileCount; j++) + { + if (gamefiles.contains(mFiles.at(j)->fileName())) + { + mFiles.move(j, i); + + QModelIndex idx2 = index (j, 0, QModelIndex()); + + emit dataChanged (idx1, idx2); + + movedFiles = true; + } + } + if (movedFiles) + break; + } + } +} + +bool ContentSelectorModel::ContentModel::isChecked(const QString& name) const +{ + if (mCheckStates.contains(name)) + return (mCheckStates[name] == Qt::Checked); + + return false; +} + +bool ContentSelectorModel::ContentModel::isEnabled (QModelIndex index) const +{ + return (flags(index) & Qt::ItemIsEnabled); +} + +void ContentSelectorModel::ContentModel::setCheckStates (const QStringList &fileList, bool isChecked) +{ + foreach (const QString &file, fileList) + { + setCheckState (file, isChecked); + } +} + +void ContentSelectorModel::ContentModel::refreshModel() +{ + emit dataChanged (index(0,0), index(rowCount()-1,0)); +} + +bool ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool checkState) +{ + if (name.isEmpty()) + return false; + + const EsmFile *file = item(name); + + if (!file) + return false; + + Qt::CheckState state = Qt::Unchecked; + + if (checkState) + state = Qt::Checked; + + mCheckStates[name] = state; + emit dataChanged(indexFromItem(item(name)), indexFromItem(item(name))); + + if (file->isGameFile()) + refreshModel(); + + //if we're checking an item, ensure all "upstream" files (dependencies) are checked as well. + if (state == Qt::Checked) + { + foreach (QString upstreamName, file->gameFiles()) + { + const EsmFile *upstreamFile = item(upstreamName); + + if (!upstreamFile) + continue; + + if (!isChecked(upstreamFile->filePath())) + mCheckStates[upstreamFile->filePath()] = Qt::Checked; + + emit dataChanged(indexFromItem(upstreamFile), indexFromItem(upstreamFile)); + + } + } + //otherwise, if we're unchecking an item (or the file is a game file) ensure all downstream files are unchecked. + if (state == Qt::Unchecked) + { + foreach (const EsmFile *downstreamFile, mFiles) + { + if (downstreamFile->gameFiles().contains(name)) + { + if (mCheckStates.contains(downstreamFile->filePath())) + mCheckStates[downstreamFile->filePath()] = Qt::Unchecked; + + emit dataChanged(indexFromItem(downstreamFile), indexFromItem(downstreamFile)); + } + } + } + + return true; +} + +ContentSelectorModel::ContentFileList ContentSelectorModel::ContentModel::checkedItems() const +{ + ContentFileList list; + + // TODO: + // First search for game files and next addons, + // so we get more or less correct game files vs addons order. + foreach (EsmFile *file, mFiles) + if (isChecked(file->filePath())) + list << file; + + return list; +} + +void ContentSelectorModel::ContentModel::uncheckAll() +{ + emit layoutAboutToBeChanged(); + mCheckStates.clear(); + emit layoutChanged(); +} diff --git a/components/contentselector/model/contentmodel.hpp b/components/contentselector/model/contentmodel.hpp new file mode 100644 index 0000000000..8c8c2124bc --- /dev/null +++ b/components/contentselector/model/contentmodel.hpp @@ -0,0 +1,77 @@ +#ifndef CONTENTMODEL_HPP +#define CONTENTMODEL_HPP + +#include <QAbstractTableModel> +#include <QStringList> + +namespace ContentSelectorModel +{ + class EsmFile; + + typedef QList<EsmFile *> ContentFileList; + + enum ContentType + { + ContentType_GameFile, + ContentType_Addon + }; + + class ContentModel : public QAbstractTableModel + { + Q_OBJECT + public: + explicit ContentModel(QObject *parent = 0); + + void setEncoding(const QString &encoding); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + + QVariant data(const QModelIndex &index, int role) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()); + bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()); + + Qt::DropActions supportedDropActions() const; + QStringList mimeTypes() const; + QMimeData *mimeData(const QModelIndexList &indexes) const; + bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + + void addFiles(const QString &path); + + QModelIndex indexFromItem(const EsmFile *item) const; + const EsmFile *item(const QString &name) const; + + bool isEnabled (QModelIndex index) const; + bool isChecked(const QString &name) const; + bool setCheckState(const QString &name, bool isChecked); + void setCheckStates (const QStringList &fileList, bool isChecked); + ContentFileList checkedItems() const; + void uncheckAll(); + + void refreshModel(); + + private: + + void addFile(EsmFile *file); + const EsmFile *item(int row) const; + EsmFile *item(int row); + + void sortFiles(); + + ContentFileList mFiles; + QHash<QString, Qt::CheckState> mCheckStates; + QTextCodec *mCodec; + + public: + + QString mMimeType; + QStringList mMimeTypes; + int mColumnCount; + Qt::ItemFlags mDragDropFlags; + Qt::DropActions mDropActions; + }; +} +#endif // CONTENTMODEL_HPP diff --git a/components/contentselector/model/esmfile.cpp b/components/contentselector/model/esmfile.cpp new file mode 100644 index 0000000000..a0a09105a7 --- /dev/null +++ b/components/contentselector/model/esmfile.cpp @@ -0,0 +1,137 @@ +#include "esmfile.hpp" + +#include <QMimeData> +#include <QDataStream> + +int ContentSelectorModel::EsmFile::sPropertyCount = 7; +QString ContentSelectorModel::EsmFile::sToolTip = QString("<b>Author:</b> %1<br/> \ + <b>Version:</b> %2<br/> \ + <b>Path:</b><br/>%3<br/> \ + <br/><b>Description:</b><br/>%4<br/> \ + <br/><b>Dependencies: </b>%5<br/>"); + + +ContentSelectorModel::EsmFile::EsmFile(QString fileName, ModelItem *parent) + : ModelItem(parent), mFileName(fileName), mFormat(0) +{} + +void ContentSelectorModel::EsmFile::setFileName(const QString &fileName) +{ + mFileName = fileName; +} + +void ContentSelectorModel::EsmFile::setAuthor(const QString &author) +{ + mAuthor = author; +} + +void ContentSelectorModel::EsmFile::setDate(const QDateTime &modified) +{ + mModified = modified; +} + +void ContentSelectorModel::EsmFile::setFormat(int format) +{ + mFormat = format; +} + +void ContentSelectorModel::EsmFile::setFilePath(const QString &path) +{ + mPath = path; +} + +void ContentSelectorModel::EsmFile::setGameFiles(const QStringList &gamefiles) +{ + mGameFiles = gamefiles; +} + +void ContentSelectorModel::EsmFile::setDescription(const QString &description) +{ + mDescription = description; +} + +QByteArray ContentSelectorModel::EsmFile::encodedData() const +{ + QByteArray encodedData; + QDataStream stream(&encodedData, QIODevice::WriteOnly); + + stream << mFileName << mAuthor << QString::number(mFormat) + << mModified.toString() << mPath << mDescription + << mGameFiles; + + return encodedData; +} + +QVariant ContentSelectorModel::EsmFile::fileProperty(const FileProperty prop) const +{ + switch (prop) + { + case FileProperty_FileName: + return mFileName; + break; + + case FileProperty_Author: + return mAuthor; + break; + + case FileProperty_Format: + return mFormat; + break; + + case FileProperty_DateModified: + return mModified.toString(Qt::ISODate); + break; + + case FileProperty_FilePath: + return mPath; + break; + + case FileProperty_Description: + return mDescription; + break; + + case FileProperty_GameFile: + return mGameFiles; + break; + + default: + break; + } + return QVariant(); +} +void ContentSelectorModel::EsmFile::setFileProperty (const FileProperty prop, const QString &value) +{ + switch (prop) + { + case FileProperty_FileName: + mFileName = value; + break; + + case FileProperty_Author: + mAuthor = value; + break; + + case FileProperty_Format: + mFormat = value.toInt(); + break; + + case FileProperty_DateModified: + mModified = QDateTime::fromString(value); + break; + + case FileProperty_FilePath: + mPath = value; + break; + + case FileProperty_Description: + mDescription = value; + break; + + case FileProperty_GameFile: + mGameFiles << value; + break; + + default: + break; + } +} diff --git a/components/contentselector/model/esmfile.hpp b/components/contentselector/model/esmfile.hpp new file mode 100644 index 0000000000..ca24b52d11 --- /dev/null +++ b/components/contentselector/model/esmfile.hpp @@ -0,0 +1,86 @@ +#ifndef ESMFILE_HPP +#define ESMFILE_HPP + +#include <QDateTime> +#include <QStringList> + +#include "modelitem.hpp" + +class QMimeData; + +namespace ContentSelectorModel +{ + class EsmFile : public ModelItem + { + Q_OBJECT + Q_PROPERTY(QString filename READ fileName) + + public: + + enum FileProperty + { + FileProperty_FileName = 0, + FileProperty_Author = 1, + FileProperty_Format = 2, + FileProperty_DateModified = 3, + FileProperty_FilePath = 4, + FileProperty_Description = 5, + FileProperty_GameFile = 6 + }; + + EsmFile(QString fileName = QString(), ModelItem *parent = 0); + // EsmFile(const EsmFile &); + + ~EsmFile() + {} + + void setFileProperty (const FileProperty prop, const QString &value); + + void setFileName(const QString &fileName); + void setAuthor(const QString &author); + void setSize(const int size); + void setDate(const QDateTime &modified); + void setFormat(const int format); + void setFilePath(const QString &path); + void setGameFiles(const QStringList &gameFiles); + void setDescription(const QString &description); + + inline void addGameFile (const QString &name) {mGameFiles.append(name); } + QVariant fileProperty (const FileProperty prop) const; + + inline QString fileName() const { return mFileName; } + inline QString author() const { return mAuthor; } + inline QDateTime modified() const { return mModified; } + inline float format() const { return mFormat; } + inline QString filePath() const { return mPath; } + inline const QStringList &gameFiles() const { return mGameFiles; } + inline QString description() const { return mDescription; } + inline QString toolTip() const { return sToolTip.arg(mAuthor) + .arg(mFormat) + .arg(mPath) + .arg(mDescription) + .arg(mGameFiles.join(", ")); + } + + inline bool isGameFile() const { return (mGameFiles.size() == 0); } + QByteArray encodedData() const; + + public: + static int sPropertyCount; + static QString sToolTip; + + private: + + QString mFileName; + QString mAuthor; + QDateTime mModified; + int mFormat; + QString mPath; + QStringList mGameFiles; + QString mDescription; + QString mToolTip; + + }; +} + +#endif diff --git a/components/contentselector/model/modelitem.cpp b/components/contentselector/model/modelitem.cpp new file mode 100644 index 0000000000..e1d737c2d4 --- /dev/null +++ b/components/contentselector/model/modelitem.cpp @@ -0,0 +1,69 @@ +#include "modelitem.hpp" + +ContentSelectorModel::ModelItem::ModelItem(ModelItem *parent) + : mParentItem(parent) +{ +} +/* +ContentSelectorModel::ModelItem::ModelItem(const ModelItem *parent) + // : mParentItem(parent) +{ +} +*/ + +ContentSelectorModel::ModelItem::~ModelItem() +{ + qDeleteAll(mChildItems); +} + + +ContentSelectorModel::ModelItem *ContentSelectorModel::ModelItem::parent() const +{ + return mParentItem; +} + +bool ContentSelectorModel::ModelItem::hasFormat(const QString &mimetype) const +{ + if (mimetype == "application/omwcontent") + return true; + + return QMimeData::hasFormat(mimetype); +} +int ContentSelectorModel::ModelItem::row() const +{ + if (mParentItem) + return 1; + //return mParentItem->childRow(const_cast<ModelItem*>(this)); + //return mParentItem->mChildItems.indexOf(const_cast<ModelItem*>(this)); + + return -1; +} + + +int ContentSelectorModel::ModelItem::childCount() const +{ + return mChildItems.count(); +} + +int ContentSelectorModel::ModelItem::childRow(ModelItem *child) const +{ + Q_ASSERT(child); + + return mChildItems.indexOf(child); +} + +ContentSelectorModel::ModelItem *ContentSelectorModel::ModelItem::child(int row) +{ + return mChildItems.value(row); +} + + +void ContentSelectorModel::ModelItem::appendChild(ModelItem *item) +{ + mChildItems.append(item); +} + +void ContentSelectorModel::ModelItem::removeChild(int row) +{ + mChildItems.removeAt(row); +} diff --git a/components/contentselector/model/modelitem.hpp b/components/contentselector/model/modelitem.hpp new file mode 100644 index 0000000000..57214b09cf --- /dev/null +++ b/components/contentselector/model/modelitem.hpp @@ -0,0 +1,39 @@ +#ifndef MODELITEM_HPP +#define MODELITEM_HPP + +#include <QMimeData> +#include <QList> + +namespace ContentSelectorModel +{ + class ModelItem : public QMimeData + { + Q_OBJECT + + public: + ModelItem(ModelItem *parent = 0); + //ModelItem(const ModelItem *parent = 0); + + ~ModelItem(); + + ModelItem *parent() const; + int row() const; + + int childCount() const; + int childRow(ModelItem *child) const; + ModelItem *child(int row); + + void appendChild(ModelItem *child); + void removeChild(int row); + + bool hasFormat(const QString &mimetype) const; + + //virtual bool acceptChild(ModelItem *child); + + protected: + ModelItem *mParentItem; + QList<ModelItem*> mChildItems; + }; +} + +#endif diff --git a/components/fileorderlist/utils/naturalsort.cpp b/components/contentselector/model/naturalsort.cpp index 50d1e77de0..50d1e77de0 100644 --- a/components/fileorderlist/utils/naturalsort.cpp +++ b/components/contentselector/model/naturalsort.cpp diff --git a/components/contentselector/model/naturalsort.hpp b/components/contentselector/model/naturalsort.hpp new file mode 100644 index 0000000000..8386e4e9f0 --- /dev/null +++ b/components/contentselector/model/naturalsort.hpp @@ -0,0 +1,11 @@ +#ifndef NATURALSORT_H +#define NATURALSORT_H + +#include <QString> + + bool naturalSortLessThanCS( const QString &left, const QString &right ); + bool naturalSortLessThanCI( const QString &left, const QString &right ); + bool naturalSortGreaterThanCS( const QString &left, const QString &right ); + bool naturalSortGreaterThanCI( const QString &left, const QString &right ); + +#endif diff --git a/components/contentselector/view/combobox.cpp b/components/contentselector/view/combobox.cpp new file mode 100644 index 0000000000..1d773b62dd --- /dev/null +++ b/components/contentselector/view/combobox.cpp @@ -0,0 +1,39 @@ +#include <QRegExpValidator> +#include <QLineEdit> +#include <QString> +#include <QApplication> +#include <QKeyEvent> + +#include "combobox.hpp" + +ContentSelectorView::ComboBox::ComboBox(QWidget *parent) : + QComboBox(parent) +{ + mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore + setValidator(mValidator); + setCompleter(0); + setEnabled (true); + + setInsertPolicy(QComboBox::NoInsert); +} + +void ContentSelectorView::ComboBox::paintEvent(QPaintEvent *) +{ + QStylePainter painter(this); + painter.setPen(palette().color(QPalette::Text)); + + // draw the combobox frame, focusrect and selected etc. + QStyleOptionComboBox opt; + initStyleOption(&opt); + painter.drawComplexControl(QStyle::CC_ComboBox, opt); + + // draw the icon and text + if (!opt.editable && currentIndex() == -1) // <<< we adjust the text displayed when nothing is selected + opt.currentText = mPlaceholderText; + painter.drawControl(QStyle::CE_ComboBoxLabel, opt); +} + +void ContentSelectorView::ComboBox::setPlaceholderText(const QString &text) +{ + mPlaceholderText = text; +} diff --git a/components/contentselector/view/combobox.hpp b/components/contentselector/view/combobox.hpp new file mode 100644 index 0000000000..e3888af2c7 --- /dev/null +++ b/components/contentselector/view/combobox.hpp @@ -0,0 +1,30 @@ +#ifndef COMBOBOX_HPP +#define COMBOBOX_HPP + +#include <QComboBox> +#include <QStylePainter> + +class QString; +class QRegExpValidator; + +namespace ContentSelectorView +{ + class ComboBox : public QComboBox + { + Q_OBJECT + + public: + explicit ComboBox (QWidget *parent = 0); + + void setPlaceholderText(const QString &text); + + private: + QString mPlaceholderText; + + protected: + void paintEvent(QPaintEvent *); + QRegExpValidator *mValidator; + }; +} + +#endif // COMBOBOX_HPP diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp new file mode 100644 index 0000000000..e9599de498 --- /dev/null +++ b/components/contentselector/view/contentselector.cpp @@ -0,0 +1,203 @@ +#include "contentselector.hpp" + +#include "../model/esmfile.hpp" + +#include <QSortFilterProxyModel> + +#include <QMenu> +#include <QContextMenuEvent> + +#include <QGridLayout> +#include <QMessageBox> +#include <QModelIndex> +#include <assert.h> + +ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) : + QObject(parent) +{ + ui.setupUi (parent); + + buildContentModel(); + buildGameFileView(); + buildAddonView(); +} + +void ContentSelectorView::ContentSelector::buildContentModel() +{ + mContentModel = new ContentSelectorModel::ContentModel(); +} + +void ContentSelectorView::ContentSelector::buildGameFileView() +{ + ui.gameFileView->setVisible (true); + + mGameFileProxyModel = new QSortFilterProxyModel(this); + mGameFileProxyModel->setFilterRegExp(QString::number((int)ContentSelectorModel::ContentType_GameFile)); + mGameFileProxyModel->setFilterRole (Qt::UserRole); + mGameFileProxyModel->setSourceModel (mContentModel); + + ui.gameFileView->setPlaceholderText(QString("Select a game file...")); + ui.gameFileView->setModel(mGameFileProxyModel); + + connect (ui.gameFileView, SIGNAL (currentIndexChanged(int)), + this, SLOT (slotCurrentGameFileIndexChanged(int))); + + ui.gameFileView->setCurrentIndex(-1); + ui.gameFileView->setCurrentIndex(0); +} + +void ContentSelectorView::ContentSelector::buildAddonView() +{ + ui.addonView->setVisible (true); + + mAddonProxyModel = new QSortFilterProxyModel(this); + mAddonProxyModel->setFilterRegExp (QString::number((int)ContentSelectorModel::ContentType_Addon)); + mAddonProxyModel->setFilterRole (Qt::UserRole); + mAddonProxyModel->setDynamicSortFilter (true); + mAddonProxyModel->setSourceModel (mContentModel); + + ui.addonView->setModel(mAddonProxyModel); + + connect(ui.addonView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(slotAddonTableItemClicked(const QModelIndex &))); +} + +void ContentSelectorView::ContentSelector::setProfileContent(const QStringList &fileList) +{ + clearCheckStates(); + bool foundGamefile = false; + + foreach (const QString &filepath, fileList) + { + if (!foundGamefile) + { + const ContentSelectorModel::EsmFile *file = mContentModel->item(filepath); + + foundGamefile = (file->isGameFile()); + + if (foundGamefile) + { + setGameFile (filepath); + break; + } + } + } + +/* if (!foundGameFile) + { + //throw gamefile error here. + }*/ + + setCheckStates (fileList); +} + +void ContentSelectorView::ContentSelector::setGameFile(const QString &filename) +{ + int index = -1; + + if (!filename.isEmpty()) + { + const ContentSelectorModel::EsmFile *file = mContentModel->item (filename); + index = ui.gameFileView->findText (file->fileName()); + + //verify that the current index is also checked in the model + if (!mContentModel->setCheckState(filename, true)) + { + //throw error in case file not found? + return; + } + } + + ui.gameFileView->setCurrentIndex(index); +} + +void ContentSelectorView::ContentSelector::clearCheckStates() +{ + mContentModel->uncheckAll(); +} + +void ContentSelectorView::ContentSelector::setCheckStates(const QStringList &list) +{ + if (list.isEmpty()) + { + slotCurrentGameFileIndexChanged (ui.gameFileView->currentIndex()); + } + else + mContentModel->setCheckStates (list, true); +} + +ContentSelectorModel::ContentFileList + ContentSelectorView::ContentSelector::selectedFiles() const +{ + if (!mContentModel) + return ContentSelectorModel::ContentFileList(); + + return mContentModel->checkedItems(); +} + +void ContentSelectorView::ContentSelector::addFiles(const QString &path) +{ + mContentModel->addFiles(path); + + if (ui.gameFileView->currentIndex() != -1) + ui.gameFileView->setCurrentIndex(-1); + + mContentModel->uncheckAll(); +} + +QString ContentSelectorView::ContentSelector::currentFile() const +{ + QModelIndex currentIdx = ui.addonView->currentIndex(); + + if (!currentIdx.isValid()) + return ui.gameFileView->currentText(); + + QModelIndex idx = mContentModel->index(mAddonProxyModel->mapToSource(currentIdx).row(), 0, QModelIndex()); + return mContentModel->data(idx, Qt::DisplayRole).toString(); +} + +void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int index) +{ + static int oldIndex = -1; + + QAbstractItemModel *const model = ui.gameFileView->model(); + QSortFilterProxyModel *proxy = dynamic_cast<QSortFilterProxyModel *>(model); + + if (proxy) + proxy->setDynamicSortFilter(false); + + if (index != oldIndex) + { + if (oldIndex > -1) + model->setData(model->index(oldIndex, 0), false, Qt::UserRole + 1); + + oldIndex = index; + + model->setData(model->index(index, 0), true, Qt::UserRole + 1); + } + + if (proxy) + proxy->setDynamicSortFilter(true); + + emit signalCurrentGamefileIndexChanged (index); +} + +void ContentSelectorView::ContentSelector::slotAddonTableItemClicked(const QModelIndex &index) +{ + QModelIndex sourceIndex = mAddonProxyModel->mapToSource (index); + + if (!mContentModel->isEnabled (sourceIndex)) + return; + + Qt::CheckState checkState = Qt::Unchecked; + + if (mContentModel->data(sourceIndex, Qt::CheckStateRole).toInt() == Qt::Unchecked) + checkState = Qt::Checked; + + mContentModel->setData(sourceIndex, checkState, Qt::CheckStateRole); + + if (checkState == Qt::Checked) + emit signalAddonFileSelected (index.row()); + else + emit signalAddonFileUnselected (index.row()); + +} diff --git a/components/contentselector/view/contentselector.hpp b/components/contentselector/view/contentselector.hpp new file mode 100644 index 0000000000..a25eb20ae3 --- /dev/null +++ b/components/contentselector/view/contentselector.hpp @@ -0,0 +1,68 @@ +#ifndef CONTENTSELECTOR_HPP +#define CONTENTSELECTOR_HPP + +#include <QDialog> + +#include "ui_contentselector.h" +#include "../model/contentmodel.hpp" + +class QSortFilterProxyModel; + +namespace ContentSelectorView +{ + class ContentSelector : public QObject + { + Q_OBJECT + + QStringList mFilePaths; + + protected: + + ContentSelectorModel::ContentModel *mContentModel; + QSortFilterProxyModel *mGameFileProxyModel; + QSortFilterProxyModel *mAddonProxyModel; + + public: + + explicit ContentSelector(QWidget *parent = 0); + + QString currentFile() const; + + void addFiles(const QString &path); + void setProfileContent (const QStringList &fileList); + + void clearCheckStates(); + void setCheckStates (const QStringList &list); + + ContentSelectorModel::ContentFileList selectedFiles() const; + + void setGameFile (const QString &filename = QString("")); + + bool isGamefileSelected() const + { return ui.gameFileView->currentIndex() != -1; } + + QWidget *uiWidget() const + { return ui.contentGroupBox; } + + + private: + + Ui::ContentSelector ui; + + void buildContentModel(); + void buildGameFileView(); + void buildAddonView(); + + signals: + void signalCurrentGamefileIndexChanged (int); + void signalAddonFileSelected (int); + void signalAddonFileUnselected (int); + + private slots: + + void slotCurrentGameFileIndexChanged(int index); + void slotAddonTableItemClicked(const QModelIndex &index); + }; +} + +#endif // CONTENTSELECTOR_HPP diff --git a/components/esm/aipackage.cpp b/components/esm/aipackage.cpp index 1440dbd138..cf4951de7e 100644 --- a/components/esm/aipackage.cpp +++ b/components/esm/aipackage.cpp @@ -44,9 +44,9 @@ namespace ESM } } - void AIPackageList::save(ESMWriter &esm) + void AIPackageList::save(ESMWriter &esm) const { - typedef std::vector<AIPackage>::iterator PackageIter; + typedef std::vector<AIPackage>::const_iterator PackageIter; for (PackageIter it = mList.begin(); it != mList.end(); ++it) { switch (it->mType) { case AI_Wander: diff --git a/components/esm/aipackage.hpp b/components/esm/aipackage.hpp index 38499b2dd8..b06cb529a7 100644 --- a/components/esm/aipackage.hpp +++ b/components/esm/aipackage.hpp @@ -93,7 +93,7 @@ namespace ESM /// it needs to use retSubName() if needed. But, hey, there /// is only one field left (XSCL) and only two records uses AI void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 95cf24d331..e91059b26f 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -3,7 +3,7 @@ #include "esmwriter.hpp" -void ESM::CellRef::save(ESMWriter &esm) +void ESM::CellRef::save(ESMWriter &esm) const { esm.writeHNT("FRMR", mRefnum); esm.writeHNCString("NAME", mRefID); diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 31889914ce..47cb0b99ed 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -83,7 +83,7 @@ namespace ESM // Position and rotation of this object within the cell Position mPos; - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); }; diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index bd86f9ba03..dd7ebfe932 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -36,6 +36,7 @@ struct Position enum RecNameInts { + // format 0 / legacy REC_ACTI = 0x49544341, REC_ALCH = 0x48434c41, REC_APPA = 0x41505041, @@ -80,7 +81,10 @@ enum RecNameInts REC_SPEL = 0x4c455053, REC_SSCR = 0x52435353, REC_STAT = 0x54415453, - REC_WEAP = 0x50414557 + REC_WEAP = 0x50414557, + + // format 1 + REC_FILT = 0x544C4946 }; } diff --git a/components/esm/effectlist.cpp b/components/esm/effectlist.cpp index 88f87d6e29..bc126846b1 100644 --- a/components/esm/effectlist.cpp +++ b/components/esm/effectlist.cpp @@ -14,9 +14,9 @@ void EffectList::load(ESMReader &esm) } } -void EffectList::save(ESMWriter &esm) +void EffectList::save(ESMWriter &esm) const { - for (std::vector<ENAMstruct>::iterator it = mList.begin(); it != mList.end(); ++it) { + for (std::vector<ENAMstruct>::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNT<ENAMstruct>("ENAM", *it, 24); } } diff --git a/components/esm/effectlist.hpp b/components/esm/effectlist.hpp index 9f5b87aeda..04adcc5cd8 100644 --- a/components/esm/effectlist.hpp +++ b/components/esm/effectlist.hpp @@ -35,9 +35,9 @@ namespace ESM std::vector<ENAMstruct> mList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; - + } #endif diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index edc724cd2a..3bf194c4e3 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -34,7 +34,7 @@ public: float getFVer() const { if(mHeader.mData.version == VER_12) return 1.2; else return 1.3; } const std::string getAuthor() const { return mHeader.mData.author.toString(); } const std::string getDesc() const { return mHeader.mData.desc.toString(); } - const std::vector<Header::MasterData> &getMasters() const { return mHeader.mMaster; } + const std::vector<Header::MasterData> &getGameFiles() const { return mHeader.mMaster; } int getFormat() const; const NAME &retSubName() const { return mCtx.subName; } uint32_t getSubSize() const { return mCtx.leftSub; } diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 3ea6bd350a..f39aa2b898 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -2,185 +2,188 @@ #include <cassert> #include <fstream> -#include <iostream> - -bool count = true; +#include <stdexcept> namespace ESM { + ESMWriter::ESMWriter() : mRecordCount (0), mCounting (true) {} -int ESMWriter::getVersion() -{ - return mHeader.mData.version; -} - -void ESMWriter::setVersion(int ver) -{ - mHeader.mData.version = ver; -} + unsigned int ESMWriter::getVersion() const + { + return mHeader.mData.version; + } -void ESMWriter::setAuthor(const std::string& auth) -{ - mHeader.mData.author.assign (auth); -} + void ESMWriter::setVersion(unsigned int ver) + { + mHeader.mData.version = ver; + } -void ESMWriter::setDescription(const std::string& desc) -{ - mHeader.mData.desc.assign (desc); -} + void ESMWriter::setAuthor(const std::string& auth) + { + mHeader.mData.author.assign (auth); + } -void ESMWriter::setRecordCount (int count) -{ - mHeader.mData.records = count; -} + void ESMWriter::setDescription(const std::string& desc) + { + mHeader.mData.desc.assign (desc); + } -void ESMWriter::setFormat (int format) -{ - mHeader.mFormat = format; -} + void ESMWriter::setRecordCount (int count) + { + mHeader.mData.records = count; + } -void ESMWriter::addMaster(const std::string& name, uint64_t size) -{ - Header::MasterData d; - d.name = name; - d.size = size; - mHeader.mMaster.push_back(d); -} + void ESMWriter::setFormat (int format) + { + mHeader.mFormat = format; + } -void ESMWriter::save(const std::string& file) -{ - std::ofstream fs(file.c_str(), std::ios_base::out | std::ios_base::trunc); - save(fs); -} + void ESMWriter::clearMaster() + { + mHeader.mMaster.clear(); + } -void ESMWriter::save(std::ostream& file) -{ - m_recordCount = 0; - m_stream = &file; + void ESMWriter::addMaster(const std::string& name, uint64_t size) + { + Header::MasterData d; + d.name = name; + d.size = size; + mHeader.mMaster.push_back(d); + } - startRecord("TES3", 0); + void ESMWriter::save(const std::string& file) + { + std::ofstream fs(file.c_str(), std::ios_base::out | std::ios_base::trunc); + save(fs); + } - mHeader.save (*this); + void ESMWriter::save(std::ostream& file) + { + mRecordCount = 0; + mRecords.clear(); + mCounting = true; + mStream = &file; - endRecord("TES3"); -} + startRecord("TES3", 0); -void ESMWriter::close() -{ - m_stream->flush(); + mHeader.save (*this); - if (!m_records.empty()) - throw "Unclosed record remaining"; -} - -void ESMWriter::startRecord(const std::string& name, uint32_t flags) -{ - m_recordCount++; - - writeName(name); - RecordData rec; - rec.name = name; - rec.position = m_stream->tellp(); - rec.size = 0; - writeT<int>(0); // Size goes here - writeT<int>(0); // Unused header? - writeT(flags); - m_records.push_back(rec); - - assert(m_records.back().size == 0); -} + endRecord("TES3"); + } -void ESMWriter::startSubRecord(const std::string& name) -{ - writeName(name); - RecordData rec; - rec.name = name; - rec.position = m_stream->tellp(); - rec.size = 0; - writeT<int>(0); // Size goes here - m_records.push_back(rec); - - assert(m_records.back().size == 0); -} + void ESMWriter::close() + { + if (!mRecords.empty()) + throw std::runtime_error ("Unclosed record remaining"); + } -void ESMWriter::endRecord(const std::string& name) -{ - RecordData rec = m_records.back(); - assert(rec.name == name); - m_records.pop_back(); + void ESMWriter::startRecord(const std::string& name, uint32_t flags) + { + mRecordCount++; + + writeName(name); + RecordData rec; + rec.name = name; + rec.position = mStream->tellp(); + rec.size = 0; + writeT<int>(0); // Size goes here + writeT<int>(0); // Unused header? + writeT(flags); + mRecords.push_back(rec); + + assert(mRecords.back().size == 0); + } - m_stream->seekp(rec.position); + void ESMWriter::startSubRecord(const std::string& name) + { + writeName(name); + RecordData rec; + rec.name = name; + rec.position = mStream->tellp(); + rec.size = 0; + writeT<int>(0); // Size goes here + mRecords.push_back(rec); + + assert(mRecords.back().size == 0); + } - count = false; - write((char*)&rec.size, sizeof(int)); - count = true; + void ESMWriter::endRecord(const std::string& name) + { + RecordData rec = mRecords.back(); + assert(rec.name == name); + mRecords.pop_back(); - m_stream->seekp(0, std::ios::end); + mStream->seekp(rec.position); -} + mCounting = false; + write (reinterpret_cast<const char*> (&rec.size), sizeof(int)); + mCounting = true; -void ESMWriter::writeHNString(const std::string& name, const std::string& data) -{ - startSubRecord(name); - writeHString(data); - endRecord(name); -} + mStream->seekp(0, std::ios::end); -void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) -{ - assert(data.size() <= size); - startSubRecord(name); - writeHString(data); + } - if (data.size() < size) + void ESMWriter::writeHNString(const std::string& name, const std::string& data) { - for (size_t i = data.size(); i < size; ++i) - write("\0",1); + startSubRecord(name); + writeHString(data); + endRecord(name); } - endRecord(name); -} - -void ESMWriter::writeHString(const std::string& data) -{ - if (data.size() == 0) - write("\0", 1); - else + void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) { - // Convert to UTF8 and return - std::string ascii = m_encoder->getLegacyEnc(data); + assert(data.size() <= size); + startSubRecord(name); + writeHString(data); + + if (data.size() < size) + { + for (size_t i = data.size(); i < size; ++i) + write("\0",1); + } - write(ascii.c_str(), ascii.size()); + endRecord(name); } -} -void ESMWriter::writeHCString(const std::string& data) -{ - writeHString(data); - if (data.size() > 0 && data[data.size()-1] != '\0') - write("\0", 1); -} + void ESMWriter::writeHString(const std::string& data) + { + if (data.size() == 0) + write("\0", 1); + else + { + // Convert to UTF8 and return + std::string ascii = mEncoder->getLegacyEnc(data); + + write(ascii.c_str(), ascii.size()); + } + } -void ESMWriter::writeName(const std::string& name) -{ - assert((name.size() == 4 && name[3] != '\0')); - write(name.c_str(), name.size()); -} + void ESMWriter::writeHCString(const std::string& data) + { + writeHString(data); + if (data.size() > 0 && data[data.size()-1] != '\0') + write("\0", 1); + } -void ESMWriter::write(const char* data, size_t size) -{ - if (count && !m_records.empty()) + void ESMWriter::writeName(const std::string& name) { - for (std::list<RecordData>::iterator it = m_records.begin(); it != m_records.end(); ++it) - it->size += size; + assert((name.size() == 4 && name[3] != '\0')); + write(name.c_str(), name.size()); } - m_stream->write(data, size); -} + void ESMWriter::write(const char* data, size_t size) + { + if (mCounting && !mRecords.empty()) + { + for (std::list<RecordData>::iterator it = mRecords.begin(); it != mRecords.end(); ++it) + it->size += size; + } -void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) -{ - m_encoder = encoder; -} + mStream->write(data, size); + } + void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) + { + mEncoder = encoder; + } } diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index be3ae33abe..104f97f909 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -13,92 +13,103 @@ namespace ESM { class ESMWriter { - struct RecordData - { - std::string name; - std::streampos position; - size_t size; + struct RecordData + { + std::string name; + std::streampos position; + size_t size; + }; + + public: + + ESMWriter(); + + unsigned int getVersion() const; + void setVersion(unsigned int ver = 0x3fa66666); + void setEncoder(ToUTF8::Utf8Encoder *encoding); + void setAuthor(const std::string& author); + void setDescription(const std::string& desc); + void setRecordCount (int count); + void setFormat (int format); + + void clearMaster(); + + void addMaster(const std::string& name, uint64_t size); + + void save(const std::string& file); + ///< Start saving a file by writing the TES3 header. + + void save(std::ostream& file); + ///< Start saving a file by writing the TES3 header. + + void close(); + ///< \note Does not close the stream. + + void writeHNString(const std::string& name, const std::string& data); + void writeHNString(const std::string& name, const std::string& data, size_t size); + void writeHNCString(const std::string& name, const std::string& data) + { + startSubRecord(name); + writeHCString(data); + endRecord(name); + } + void writeHNOString(const std::string& name, const std::string& data) + { + if (!data.empty()) + writeHNString(name, data); + } + void writeHNOCString(const std::string& name, const std::string& data) + { + if (!data.empty()) + writeHNCString(name, data); + } + + template<typename T> + void writeHNT(const std::string& name, const T& data) + { + startSubRecord(name); + writeT(data); + endRecord(name); + } + + template<typename T> + void writeHNT(const std::string& name, const T& data, int size) + { + startSubRecord(name); + writeT(data, size); + endRecord(name); + } + + template<typename T> + void writeT(const T& data) + { + write((char*)&data, sizeof(T)); + } + + template<typename T> + void writeT(const T& data, size_t size) + { + write((char*)&data, size); + } + + void startRecord(const std::string& name, uint32_t flags = 0); + void startSubRecord(const std::string& name); + void endRecord(const std::string& name); + void writeHString(const std::string& data); + void writeHCString(const std::string& data); + void writeName(const std::string& data); + void write(const char* data, size_t size); + + private: + std::list<RecordData> mRecords; + std::ostream* mStream; + std::streampos mHeaderPos; + ToUTF8::Utf8Encoder* mEncoder; + int mRecordCount; + bool mCounting; + + Header mHeader; }; - -public: - int getVersion(); - void setVersion(int ver); - void setEncoder(ToUTF8::Utf8Encoder *encoding); // Write strings as UTF-8? - void setAuthor(const std::string& author); - void setDescription(const std::string& desc); - void setRecordCount (int count); - void setFormat (int format); - - void addMaster(const std::string& name, uint64_t size); - - void save(const std::string& file); - void save(std::ostream& file); - void close(); - - void writeHNString(const std::string& name, const std::string& data); - void writeHNString(const std::string& name, const std::string& data, size_t size); - void writeHNCString(const std::string& name, const std::string& data) - { - startSubRecord(name); - writeHCString(data); - endRecord(name); - } - void writeHNOString(const std::string& name, const std::string& data) - { - if (!data.empty()) - writeHNString(name, data); - } - void writeHNOCString(const std::string& name, const std::string& data) - { - if (!data.empty()) - writeHNCString(name, data); - } - - template<typename T> - void writeHNT(const std::string& name, const T& data) - { - startSubRecord(name); - writeT(data); - endRecord(name); - } - - template<typename T> - void writeHNT(const std::string& name, const T& data, int size) - { - startSubRecord(name); - writeT(data, size); - endRecord(name); - } - - template<typename T> - void writeT(const T& data) - { - write((char*)&data, sizeof(T)); - } - - template<typename T> - void writeT(const T& data, size_t size) - { - write((char*)&data, size); - } - - void startRecord(const std::string& name, uint32_t flags); - void startSubRecord(const std::string& name); - void endRecord(const std::string& name); - void writeHString(const std::string& data); - void writeHCString(const std::string& data); - void writeName(const std::string& data); - void write(const char* data, size_t size); - -private: - std::list<RecordData> m_records; - std::ostream* m_stream; - std::streampos m_headerPos; - ToUTF8::Utf8Encoder* m_encoder; - int m_recordCount; - - Header mHeader; -}; - } + #endif diff --git a/components/esm/filter.cpp b/components/esm/filter.cpp index 7d4851a5f6..a80427bbed 100644 --- a/components/esm/filter.cpp +++ b/components/esm/filter.cpp @@ -3,6 +3,9 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" + +unsigned int ESM::Filter::sRecordId = REC_FILT; void ESM::Filter::load (ESMReader& esm) { @@ -10,7 +13,7 @@ void ESM::Filter::load (ESMReader& esm) mDescription = esm.getHNString ("DESC"); } -void ESM::Filter::save (ESMWriter& esm) +void ESM::Filter::save (ESMWriter& esm) const { esm.writeHNCString ("FILT", mFilter); esm.writeHNCString ("DESC", mDescription); diff --git a/components/esm/filter.hpp b/components/esm/filter.hpp index 0fd564361e..bc3dd7bdcb 100644 --- a/components/esm/filter.hpp +++ b/components/esm/filter.hpp @@ -10,6 +10,8 @@ namespace ESM struct Filter { + static unsigned int sRecordId; + std::string mId; std::string mDescription; @@ -17,7 +19,7 @@ namespace ESM std::string mFilter; void load (ESMReader& esm); - void save (ESMWriter& esm); + void save (ESMWriter& esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadacti.cpp b/components/esm/loadacti.cpp index fd022af7e6..6ba0df0b36 100644 --- a/components/esm/loadacti.cpp +++ b/components/esm/loadacti.cpp @@ -2,16 +2,19 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Activator::sRecordId = REC_ACTI; + void Activator::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); mName = esm.getHNString("FNAM"); mScript = esm.getHNOString("SCRI"); } -void Activator::save(ESMWriter &esm) +void Activator::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadacti.hpp b/components/esm/loadacti.hpp index a62990590d..88f27de27e 100644 --- a/components/esm/loadacti.hpp +++ b/components/esm/loadacti.hpp @@ -11,10 +11,12 @@ class ESMWriter; struct Activator { + static unsigned int sRecordId; + std::string mId, mName, mScript, mModel; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadalch.cpp b/components/esm/loadalch.cpp index dbb69c066f..f6bfc6a11d 100644 --- a/components/esm/loadalch.cpp +++ b/components/esm/loadalch.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Potion::sRecordId = REC_ALCH; + void Potion::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); @@ -14,7 +17,7 @@ void Potion::load(ESMReader &esm) esm.getHNT(mData, "ALDT", 12); mEffects.load(esm); } -void Potion::save(ESMWriter &esm) +void Potion::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("TEXT", mIcon); diff --git a/components/esm/loadalch.hpp b/components/esm/loadalch.hpp index 3ede853424..141765aa82 100644 --- a/components/esm/loadalch.hpp +++ b/components/esm/loadalch.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Potion { + static unsigned int sRecordId; + struct ALDTstruct { float mWeight; @@ -29,7 +31,7 @@ struct Potion EffectList mEffects; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadappa.cpp b/components/esm/loadappa.cpp index 4b8d2b763c..29ea78acc0 100644 --- a/components/esm/loadappa.cpp +++ b/components/esm/loadappa.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Apparatus::sRecordId = REC_APPA; + void Apparatus::load(ESMReader &esm) { // we will not treat duplicated subrecords as errors here @@ -28,7 +31,7 @@ void Apparatus::load(ESMReader &esm) } } -void Apparatus::save(ESMWriter &esm) +void Apparatus::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadappa.hpp b/components/esm/loadappa.hpp index ed9d335be6..adc8e071f1 100644 --- a/components/esm/loadappa.hpp +++ b/components/esm/loadappa.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct Apparatus { + static unsigned int sRecordId; + enum AppaType { MortarPestle = 0, @@ -35,7 +37,7 @@ struct Apparatus std::string mId, mModel, mIcon, mScript, mName; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadarmo.cpp b/components/esm/loadarmo.cpp index e64c8705d7..ec8ff4f20c 100644 --- a/components/esm/loadarmo.cpp +++ b/components/esm/loadarmo.cpp @@ -2,6 +2,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -18,9 +19,9 @@ void PartReferenceList::load(ESMReader &esm) } } -void PartReferenceList::save(ESMWriter &esm) +void PartReferenceList::save(ESMWriter &esm) const { - for (std::vector<PartReference>::iterator it = mParts.begin(); it != mParts.end(); ++it) + for (std::vector<PartReference>::const_iterator it = mParts.begin(); it != mParts.end(); ++it) { esm.writeHNT("INDX", it->mPart); esm.writeHNOString("BNAM", it->mMale); @@ -28,6 +29,8 @@ void PartReferenceList::save(ESMWriter &esm) } } +unsigned int Armor::sRecordId = REC_ARMO; + void Armor::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); @@ -39,7 +42,7 @@ void Armor::load(ESMReader &esm) mEnchant = esm.getHNOString("ENAM"); } -void Armor::save(ESMWriter &esm) +void Armor::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadarmo.hpp b/components/esm/loadarmo.hpp index eaef42be83..991f4e1855 100644 --- a/components/esm/loadarmo.hpp +++ b/components/esm/loadarmo.hpp @@ -56,11 +56,13 @@ struct PartReferenceList std::vector<PartReference> mParts; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; struct Armor { + static unsigned int sRecordId; + enum Type { Helmet = 0, @@ -89,7 +91,7 @@ struct Armor std::string mId, mName, mModel, mIcon, mScript, mEnchant; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadbody.cpp b/components/esm/loadbody.cpp index e95a8a8603..4015e6c91a 100644 --- a/components/esm/loadbody.cpp +++ b/components/esm/loadbody.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int BodyPart::sRecordId = REC_BODY; + void BodyPart::load(ESMReader &esm) { @@ -12,7 +15,7 @@ void BodyPart::load(ESMReader &esm) mRace = esm.getHNString("FNAM"); esm.getHNT(mData, "BYDT", 4); } -void BodyPart::save(ESMWriter &esm) +void BodyPart::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mRace); diff --git a/components/esm/loadbody.hpp b/components/esm/loadbody.hpp index 3ad9b1b958..9623caa31e 100644 --- a/components/esm/loadbody.hpp +++ b/components/esm/loadbody.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct BodyPart { + static unsigned int sRecordId; + enum MeshPart { MP_Head = 0, @@ -57,7 +59,7 @@ struct BodyPart std::string mId, mModel, mRace; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } #endif diff --git a/components/esm/loadbook.cpp b/components/esm/loadbook.cpp index 3a70ac7869..c8b7e94789 100644 --- a/components/esm/loadbook.cpp +++ b/components/esm/loadbook.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Book::sRecordId = REC_BOOK; void Book::load(ESMReader &esm) { @@ -16,7 +18,7 @@ void Book::load(ESMReader &esm) mText = esm.getHNOString("TEXT"); mEnchant = esm.getHNOString("ENAM"); } -void Book::save(ESMWriter &esm) +void Book::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loadbook.hpp b/components/esm/loadbook.hpp index 68042e246e..f96fbd709b 100644 --- a/components/esm/loadbook.hpp +++ b/components/esm/loadbook.hpp @@ -14,6 +14,8 @@ class ESMWriter; struct Book { + static unsigned int sRecordId; + struct BKDTstruct { float mWeight; @@ -25,7 +27,7 @@ struct Book std::string mId; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadbsgn.cpp b/components/esm/loadbsgn.cpp index cb500f6748..55e1e7f65a 100644 --- a/components/esm/loadbsgn.cpp +++ b/components/esm/loadbsgn.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int BirthSign::sRecordId = REC_BSGN; void BirthSign::load(ESMReader &esm) { @@ -15,7 +17,7 @@ void BirthSign::load(ESMReader &esm) mPowers.load(esm); } -void BirthSign::save(ESMWriter &esm) +void BirthSign::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mName); esm.writeHNOCString("TNAM", mTexture); diff --git a/components/esm/loadbsgn.hpp b/components/esm/loadbsgn.hpp index 434ddf68ea..9f9435c8f1 100644 --- a/components/esm/loadbsgn.hpp +++ b/components/esm/loadbsgn.hpp @@ -13,13 +13,15 @@ class ESMWriter; struct BirthSign { + static unsigned int sRecordId; + std::string mId, mName, mDescription, mTexture; // List of powers and abilities that come with this birth sign. SpellList mPowers; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index d8d0c12912..c22c1b22b6 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -7,9 +7,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Cell::sRecordId = REC_CELL; /// Some overloaded compare operators. bool operator==(const MovedCellRef& ref, int pRefnum) @@ -89,7 +91,7 @@ void Cell::postLoad(ESMReader &esm) esm.skipRecord(); } -void Cell::save(ESMWriter &esm) +void Cell::save(ESMWriter &esm) const { esm.writeHNT("DATA", mData, 12); if (mData.mFlags & Interior) @@ -172,7 +174,7 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) // If the most significant 8 bits are used, then this reference already exists. // In this case, do not spawn a new reference, but overwrite the old one. ref.mRefnum &= 0x00ffffff; // delete old plugin ID - const std::vector<Header::MasterData> &masters = esm.getMasters(); + const std::vector<Header::MasterData> &masters = esm.getGameFiles(); global = masters[local-1].index + 1; ref.mRefnum |= global << 24; // insert global plugin ID } @@ -276,7 +278,7 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref) int local = (mref.mRefnum & 0xff000000) >> 24; size_t global = esm.getIndex() + 1; mref.mRefnum &= 0x00ffffff; // delete old plugin ID - const std::vector<Header::MasterData> &masters = esm.getMasters(); + const std::vector<Header::MasterData> &masters = esm.getGameFiles(); global = masters[local-1].index + 1; mref.mRefnum |= global << 24; // insert global plugin ID diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 51288b2919..61d586b9d8 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -16,7 +16,6 @@ namespace MWWorld namespace ESM { - class ESMReader; class ESMWriter; @@ -55,6 +54,8 @@ typedef std::list<CellRef> CellRefTracker; */ struct Cell { + static unsigned int sRecordId; + enum Flags { Interior = 0x01, // Interior cell @@ -102,7 +103,7 @@ struct Cell // This method is left in for compatibility with esmtool. Parsing moved references currently requires // passing ESMStore, bit it does not know about this parameter, so we do it this way. void load(ESMReader &esm, bool saveContext = true); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; bool isExterior() const { diff --git a/components/esm/loadclas.cpp b/components/esm/loadclas.cpp index bdc4614625..33489eec46 100644 --- a/components/esm/loadclas.cpp +++ b/components/esm/loadclas.cpp @@ -4,9 +4,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Class::sRecordId = REC_CLAS; const Class::Specialization Class::sSpecializationIds[3] = { Class::Combat, @@ -47,7 +49,7 @@ void Class::load(ESMReader &esm) mDescription = esm.getHNOString("DESC"); } -void Class::save(ESMWriter &esm) +void Class::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mName); esm.writeHNT("CLDT", mData, 60); diff --git a/components/esm/loadclas.hpp b/components/esm/loadclas.hpp index 4f85e6ee8b..3e489bb58a 100644 --- a/components/esm/loadclas.hpp +++ b/components/esm/loadclas.hpp @@ -17,6 +17,8 @@ class ESMWriter; // class struct Class { + static unsigned int sRecordId; + enum AutoCalc { Weapon = 0x00001, @@ -70,7 +72,7 @@ struct Class CLDTstruct mData; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadclot.cpp b/components/esm/loadclot.cpp index 10b00970fb..d64564d77f 100644 --- a/components/esm/loadclot.cpp +++ b/components/esm/loadclot.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Clothing::sRecordId = REC_CLOT; void Clothing::load(ESMReader &esm) { @@ -20,7 +22,7 @@ void Clothing::load(ESMReader &esm) mEnchant = esm.getHNOString("ENAM"); } -void Clothing::save(ESMWriter &esm) +void Clothing::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loadclot.hpp b/components/esm/loadclot.hpp index 816d03cb23..50896622aa 100644 --- a/components/esm/loadclot.hpp +++ b/components/esm/loadclot.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Clothing { + static unsigned int sRecordId; + enum Type { Pants = 0, @@ -45,7 +47,7 @@ struct Clothing std::string mId, mName, mModel, mIcon, mEnchant, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadcont.cpp b/components/esm/loadcont.cpp index 853c8bd500..7bdf9f05b1 100644 --- a/components/esm/loadcont.cpp +++ b/components/esm/loadcont.cpp @@ -2,6 +2,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -16,14 +17,16 @@ void InventoryList::load(ESMReader &esm) } } -void InventoryList::save(ESMWriter &esm) +void InventoryList::save(ESMWriter &esm) const { - for (std::vector<ContItem>::iterator it = mList.begin(); it != mList.end(); ++it) + for (std::vector<ContItem>::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNT("NPCO", *it, 36); } } + unsigned int Container::sRecordId = REC_CONT; + void Container::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); @@ -41,7 +44,7 @@ void Container::load(ESMReader &esm) mInventory.load(esm); } -void Container::save(ESMWriter &esm) +void Container::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loadcont.hpp b/components/esm/loadcont.hpp index b2bbab73d0..2808b67b56 100644 --- a/components/esm/loadcont.hpp +++ b/components/esm/loadcont.hpp @@ -27,11 +27,13 @@ struct InventoryList std::vector<ContItem> mList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; struct Container { + static unsigned int sRecordId; + enum Flags { Organic = 1, // Objects cannot be placed in this container @@ -46,7 +48,7 @@ struct Container InventoryList mInventory; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadcrea.cpp b/components/esm/loadcrea.cpp index 86d05b8a57..650de08011 100644 --- a/components/esm/loadcrea.cpp +++ b/components/esm/loadcrea.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Creature::sRecordId = REC_CREA; + void Creature::load(ESMReader &esm) { mPersistent = esm.getRecordFlags() & 0x0400; @@ -35,7 +38,7 @@ void Creature::load(ESMReader &esm) esm.skipRecord(); } -void Creature::save(ESMWriter &esm) +void Creature::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("CNAM", mOriginal); diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index 279e2ea3f4..99c4f52257 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -20,6 +20,8 @@ class ESMWriter; struct Creature { + static unsigned int sRecordId; + // Default is 0x48? enum Flags { @@ -86,7 +88,7 @@ struct Creature AIPackageList mAiPackage; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadcrec.hpp b/components/esm/loadcrec.hpp index 6904df15a4..280739acad 100644 --- a/components/esm/loadcrec.hpp +++ b/components/esm/loadcrec.hpp @@ -17,6 +17,8 @@ class ESMWriter; /// Changes a creature struct LoadCREC { + static unsigned int sRecordId; + std::string mId; void load(ESMReader &esm) @@ -24,7 +26,7 @@ struct LoadCREC esm.skipRecord(); } - void save(ESMWriter &esm) + void save(ESMWriter &esm) const { } }; @@ -39,7 +41,7 @@ struct LoadCNTC esm.skipRecord(); } - void save(ESMWriter &esm) + void save(ESMWriter &esm) const { } }; diff --git a/components/esm/loaddial.cpp b/components/esm/loaddial.cpp index fb50d5e9f5..fb43ee8586 100644 --- a/components/esm/loaddial.cpp +++ b/components/esm/loaddial.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Dialogue::sRecordId = REC_DIAL; void Dialogue::load(ESMReader &esm) { @@ -25,7 +27,7 @@ void Dialogue::load(ESMReader &esm) esm.fail("Unknown sub record size"); } -void Dialogue::save(ESMWriter &esm) +void Dialogue::save(ESMWriter &esm) const { if (mType != Deleted) esm.writeHNT("DATA", mType); @@ -36,4 +38,9 @@ void Dialogue::save(ESMWriter &esm) } } + void Dialogue::blank() + { + mInfo.clear(); + } + } diff --git a/components/esm/loaddial.hpp b/components/esm/loaddial.hpp index 61f3f763de..63d78833e1 100644 --- a/components/esm/loaddial.hpp +++ b/components/esm/loaddial.hpp @@ -19,6 +19,8 @@ class ESMWriter; struct Dialogue { + static unsigned int sRecordId; + enum Type { Topic = 0, @@ -34,7 +36,10 @@ struct Dialogue std::vector<DialInfo> mInfo; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; + + void blank(); + ///< Set record to default state (does not touch the ID and does not change the type). }; } #endif diff --git a/components/esm/loaddoor.cpp b/components/esm/loaddoor.cpp index a4c7b7d58b..c56b063379 100644 --- a/components/esm/loaddoor.cpp +++ b/components/esm/loaddoor.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Door::sRecordId = REC_DOOR; void Door::load(ESMReader &esm) { @@ -15,7 +17,7 @@ void Door::load(ESMReader &esm) mCloseSound = esm.getHNOString("ANAM"); } -void Door::save(ESMWriter &esm) +void Door::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loaddoor.hpp b/components/esm/loaddoor.hpp index 77ffc64899..ee2b7f7ac5 100644 --- a/components/esm/loaddoor.hpp +++ b/components/esm/loaddoor.hpp @@ -11,10 +11,12 @@ class ESMWriter; struct Door { + static unsigned int sRecordId; + std::string mId, mName, mModel, mScript, mOpenSound, mCloseSound; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadench.cpp b/components/esm/loadench.cpp index c4e278368e..a1e885f23b 100644 --- a/components/esm/loadench.cpp +++ b/components/esm/loadench.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Enchantment::sRecordId = REC_ENCH; void Enchantment::load(ESMReader &esm) { @@ -12,7 +14,7 @@ void Enchantment::load(ESMReader &esm) mEffects.load(esm); } -void Enchantment::save(ESMWriter &esm) +void Enchantment::save(ESMWriter &esm) const { esm.writeHNT("ENDT", mData, 16); mEffects.save(esm); diff --git a/components/esm/loadench.hpp b/components/esm/loadench.hpp index 999f93ad97..f6ba8c6ab3 100644 --- a/components/esm/loadench.hpp +++ b/components/esm/loadench.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Enchantment { + static unsigned int sRecordId; + enum Type { CastOnce = 0, @@ -39,7 +41,7 @@ struct Enchantment EffectList mEffects; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } #endif diff --git a/components/esm/loadfact.cpp b/components/esm/loadfact.cpp index e2712d462d..61fa902639 100644 --- a/components/esm/loadfact.cpp +++ b/components/esm/loadfact.cpp @@ -4,9 +4,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Faction::sRecordId = REC_FACT; + int& Faction::FADTstruct::getSkill (int index, bool ignored) { if (index<0 || index>=6) @@ -47,7 +50,7 @@ void Faction::load(ESMReader &esm) mReactions.push_back(r); } } -void Faction::save(ESMWriter &esm) +void Faction::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mName); @@ -61,7 +64,7 @@ void Faction::save(ESMWriter &esm) esm.writeHNT("FADT", mData, 240); - for (std::vector<Reaction>::iterator it = mReactions.begin(); it != mReactions.end(); ++it) + for (std::vector<Reaction>::const_iterator it = mReactions.begin(); it != mReactions.end(); ++it) { esm.writeHNString("ANAM", it->mFaction); esm.writeHNT("INTV", it->mReaction); diff --git a/components/esm/loadfact.hpp b/components/esm/loadfact.hpp index 891b996473..9c257e068e 100644 --- a/components/esm/loadfact.hpp +++ b/components/esm/loadfact.hpp @@ -29,6 +29,8 @@ struct RankData struct Faction { + static unsigned int sRecordId; + std::string mId, mName; struct FADTstruct @@ -63,7 +65,7 @@ struct Faction std::string mRanks[10]; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadglob.cpp b/components/esm/loadglob.cpp index ccb519acd4..a78ed1a1be 100644 --- a/components/esm/loadglob.cpp +++ b/components/esm/loadglob.cpp @@ -1,13 +1,17 @@ #include "loadglob.hpp" +#include "defs.hpp" + namespace ESM { + unsigned int Global::sRecordId = REC_GLOB; + void Global::load (ESMReader &esm) { mValue.read (esm, ESM::Variant::Format_Global); } - void Global::save (ESMWriter &esm) + void Global::save (ESMWriter &esm) const { mValue.write (esm, ESM::Variant::Format_Global); } diff --git a/components/esm/loadglob.hpp b/components/esm/loadglob.hpp index 72e16c0ce5..51b2e2dc98 100644 --- a/components/esm/loadglob.hpp +++ b/components/esm/loadglob.hpp @@ -17,11 +17,13 @@ class ESMWriter; struct Global { + static unsigned int sRecordId; + std::string mId; Variant mValue; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadgmst.cpp b/components/esm/loadgmst.cpp index fe1cc1b047..21d66339a9 100644 --- a/components/esm/loadgmst.cpp +++ b/components/esm/loadgmst.cpp @@ -1,13 +1,17 @@ #include "loadgmst.hpp" +#include "defs.hpp" + namespace ESM { + unsigned int GameSetting::sRecordId = REC_GMST; + void GameSetting::load (ESMReader &esm) { mValue.read (esm, ESM::Variant::Format_Gmst); } - void GameSetting::save (ESMWriter &esm) + void GameSetting::save (ESMWriter &esm) const { mValue.write (esm, ESM::Variant::Format_Gmst); } diff --git a/components/esm/loadgmst.hpp b/components/esm/loadgmst.hpp index a6e0c2ecbe..6b66ac832d 100644 --- a/components/esm/loadgmst.hpp +++ b/components/esm/loadgmst.hpp @@ -18,13 +18,15 @@ class ESMWriter; struct GameSetting { + static unsigned int sRecordId; + std::string mId; Variant mValue; void load(ESMReader &esm); - /// \todo remove the get* functions (redundant, since mValue as equivalent functions now). + /// \todo remove the get* functions (redundant, since mValue has equivalent functions now). int getInt() const; ///< Throws an exception if GMST is not of type int or float. @@ -35,7 +37,7 @@ struct GameSetting std::string getString() const; ///< Throwns an exception if GMST is not of type string. - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadinfo.cpp b/components/esm/loadinfo.cpp index 90f8fcf35b..4f248cc65f 100644 --- a/components/esm/loadinfo.cpp +++ b/components/esm/loadinfo.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int DialInfo::sRecordId = REC_INFO; void DialInfo::load(ESMReader &esm) { @@ -120,7 +122,7 @@ void DialInfo::load(ESMReader &esm) esm.skipRecord(); } -void DialInfo::save(ESMWriter &esm) +void DialInfo::save(ESMWriter &esm) const { esm.writeHNCString("INAM", mId); esm.writeHNCString("PNAM", mPrev); @@ -135,7 +137,7 @@ void DialInfo::save(ESMWriter &esm) esm.writeHNOCString("SNAM", mSound); esm.writeHNOString("NAME", mResponse); - for (std::vector<SelectStruct>::iterator it = mSelects.begin(); it != mSelects.end(); ++it) + for (std::vector<SelectStruct>::const_iterator it = mSelects.begin(); it != mSelects.end(); ++it) { esm.writeHNString("SCVR", it->mSelectRule); it->mValue.write (esm, Variant::Format_Info); diff --git a/components/esm/loadinfo.hpp b/components/esm/loadinfo.hpp index 2361ed9eb5..2589ea7b87 100644 --- a/components/esm/loadinfo.hpp +++ b/components/esm/loadinfo.hpp @@ -20,6 +20,8 @@ class ESMWriter; struct DialInfo { + static unsigned int sRecordId; + enum Gender { Male = 0, @@ -99,7 +101,7 @@ struct DialInfo }; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } diff --git a/components/esm/loadingr.cpp b/components/esm/loadingr.cpp index 7e31a4116d..0e02433621 100644 --- a/components/esm/loadingr.cpp +++ b/components/esm/loadingr.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Ingredient::sRecordId = REC_INGR; void Ingredient::load(ESMReader &esm) { @@ -37,7 +39,7 @@ void Ingredient::load(ESMReader &esm) } } -void Ingredient::save(ESMWriter &esm) +void Ingredient::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadingr.hpp b/components/esm/loadingr.hpp index 5e286535f4..85f2d5e7d2 100644 --- a/components/esm/loadingr.hpp +++ b/components/esm/loadingr.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct Ingredient { + static unsigned int sRecordId; + struct IRDTstruct { float mWeight; @@ -28,7 +30,7 @@ struct Ingredient std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index 60c475040f..ede200d79d 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Land::sRecordId = REC_LAND; void Land::LandData::save(ESMWriter &esm) { @@ -16,14 +18,14 @@ void Land::LandData::save(ESMWriter &esm) offsets.mHeightOffset = mHeights[0] / HEIGHT_SCALE; offsets.mUnk1 = mUnk1; offsets.mUnk2 = mUnk2; - + float prevY = mHeights[0], prevX; int number = 0; // avoid multiplication for (int i = 0; i < LAND_SIZE; ++i) { float diff = (mHeights[number] - prevY) / HEIGHT_SCALE; offsets.mHeightData[number] = (diff >= 0) ? (int8_t) (diff + 0.5) : (int8_t) (diff - 0.5); - + prevX = prevY = mHeights[number]; ++number; @@ -132,7 +134,7 @@ void Land::load(ESMReader &esm) mLandData = NULL; } -void Land::save(ESMWriter &esm) +void Land::save(ESMWriter &esm) const { esm.startSubRecord("INTV"); esm.writeT(mX); @@ -140,18 +142,6 @@ void Land::save(ESMWriter &esm) esm.endRecord("INTV"); esm.writeHNT("DATA", mFlags); - - // TODO: Land! - bool wasLoaded = mDataLoaded; - if (mDataTypes) { - // Try to load all available data before saving - loadData(mDataTypes); - } - if (mDataLoaded) - mLandData->save(esm); - - if (!wasLoaded) - unloadData(); // Don't need to keep the data loaded if it wasn't already } /// \todo remove memory allocation when only defaults needed diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 9c1fd1f5c6..5649f99801 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Land { + static unsigned int sRecordId; + Land(); ~Land(); @@ -94,7 +96,7 @@ struct Land LandData *mLandData; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; /** * Actually loads data diff --git a/components/esm/loadlevlist.cpp b/components/esm/loadlevlist.cpp index b54a912760..6385b9a718 100644 --- a/components/esm/loadlevlist.cpp +++ b/components/esm/loadlevlist.cpp @@ -2,6 +2,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -33,13 +34,13 @@ void LeveledListBase::load(ESMReader &esm) esm.getHNT(li.mLevel, "INTV"); } } -void LeveledListBase::save(ESMWriter &esm) +void LeveledListBase::save(ESMWriter &esm) const { esm.writeHNT("DATA", mFlags); esm.writeHNT("NNAM", mChanceNone); esm.writeHNT<int>("INDX", mList.size()); - for (std::vector<LevelItem>::iterator it = mList.begin(); it != mList.end(); ++it) + for (std::vector<LevelItem>::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNCString(mRecName, it->mId); esm.writeHNT("INTV", it->mLevel); @@ -52,4 +53,8 @@ void LeveledListBase::save(ESMWriter &esm) mChanceNone = 0; mList.clear(); } + + unsigned int CreatureLevList::sRecordId = REC_LEVC; + + unsigned int ItemLevList::sRecordId = REC_LEVI; } diff --git a/components/esm/loadlevlist.hpp b/components/esm/loadlevlist.hpp index 7339cac56f..9dcc6177a1 100644 --- a/components/esm/loadlevlist.hpp +++ b/components/esm/loadlevlist.hpp @@ -51,7 +51,7 @@ struct LeveledListBase std::vector<LevelItem> mList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). @@ -59,6 +59,8 @@ struct LeveledListBase struct CreatureLevList: LeveledListBase { + static unsigned int sRecordId; + CreatureLevList() { mRecName = "CNAM"; @@ -67,6 +69,8 @@ struct CreatureLevList: LeveledListBase struct ItemLevList: LeveledListBase { + static unsigned int sRecordId; + ItemLevList() { mRecName = "INAM"; diff --git a/components/esm/loadligh.cpp b/components/esm/loadligh.cpp index 89a2b8c65b..c02bb46b69 100644 --- a/components/esm/loadligh.cpp +++ b/components/esm/loadligh.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Light::sRecordId = REC_LIGH; void Light::load(ESMReader &esm) { @@ -16,7 +18,7 @@ void Light::load(ESMReader &esm) mScript = esm.getHNOString("SCRI"); mSound = esm.getHNOString("SNAM"); } -void Light::save(ESMWriter &esm) +void Light::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loadligh.hpp b/components/esm/loadligh.hpp index 3f0b76d6e2..74eb37197e 100644 --- a/components/esm/loadligh.hpp +++ b/components/esm/loadligh.hpp @@ -16,6 +16,8 @@ class ESMWriter; struct Light { + static unsigned int sRecordId; + enum Flags { Dynamic = 0x001, @@ -44,7 +46,7 @@ struct Light std::string mSound, mScript, mModel, mIcon, mName, mId; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadlock.cpp b/components/esm/loadlock.cpp index 03eac52bd5..9ffce78a7d 100644 --- a/components/esm/loadlock.cpp +++ b/components/esm/loadlock.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Lockpick::sRecordId = REC_LOCK; void Lockpick::load(ESMReader &esm) { @@ -17,7 +19,7 @@ void Lockpick::load(ESMReader &esm) mIcon = esm.getHNOString("ITEX"); } -void Lockpick::save(ESMWriter &esm) +void Lockpick::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadlock.hpp b/components/esm/loadlock.hpp index 953066cb2f..c44e2b0063 100644 --- a/components/esm/loadlock.hpp +++ b/components/esm/loadlock.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct Lockpick { + static unsigned int sRecordId; + struct Data { float mWeight; @@ -24,7 +26,7 @@ struct Lockpick std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadltex.cpp b/components/esm/loadltex.cpp index e523e9fa7f..bd28c84883 100644 --- a/components/esm/loadltex.cpp +++ b/components/esm/loadltex.cpp @@ -2,16 +2,18 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int LandTexture::sRecordId = REC_LTEX; void LandTexture::load(ESMReader &esm) { esm.getHNT(mIndex, "INTV"); mTexture = esm.getHNString("DATA"); } -void LandTexture::save(ESMWriter &esm) +void LandTexture::save(ESMWriter &esm) const { esm.writeHNT("INTV", mIndex); esm.writeHNCString("DATA", mTexture); diff --git a/components/esm/loadltex.hpp b/components/esm/loadltex.hpp index 6e6d987d49..5e84428b29 100644 --- a/components/esm/loadltex.hpp +++ b/components/esm/loadltex.hpp @@ -27,11 +27,13 @@ class ESMWriter; struct LandTexture { + static unsigned int sRecordId; + std::string mId, mTexture; int mIndex; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } #endif diff --git a/components/esm/loadmgef.cpp b/components/esm/loadmgef.cpp index 060645b5f4..1a90f5b09c 100644 --- a/components/esm/loadmgef.cpp +++ b/components/esm/loadmgef.cpp @@ -6,6 +6,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace { @@ -34,6 +35,7 @@ namespace namespace ESM { + unsigned int MagicEffect::sRecordId = REC_MGEF; void MagicEffect::load(ESMReader &esm) { @@ -58,15 +60,11 @@ void MagicEffect::load(ESMReader &esm) mDescription = esm.getHNOString("DESC"); } -void MagicEffect::save(ESMWriter &esm) +void MagicEffect::save(ESMWriter &esm) const { esm.writeHNT("INDX", mIndex); - mData.mFlags &= 0xe00; esm.writeHNT("MEDT", mData, 36); - if (mIndex>=0 && mIndex<NumberOfHardcodedFlags) { - mData.mFlags |= HardcodedFlags[mIndex]; - } esm.writeHNOCString("ITEX", mIcon); esm.writeHNOCString("PTEX", mParticle); @@ -74,12 +72,12 @@ void MagicEffect::save(ESMWriter &esm) esm.writeHNOCString("CSND", mCastSound); esm.writeHNOCString("HSND", mHitSound); esm.writeHNOCString("ASND", mAreaSound); - + esm.writeHNOCString("CVFX", mCasting); esm.writeHNOCString("BVFX", mBolt); esm.writeHNOCString("HVFX", mHit); esm.writeHNOCString("AVFX", mArea); - + esm.writeHNOString("DESC", mDescription); } @@ -274,5 +272,23 @@ short MagicEffect::effectStringToId(const std::string &effect) return name->first; } +MagicEffect::MagnitudeDisplayType MagicEffect::getMagnitudeDisplayType() const { + if ( mData.mFlags & NoMagnitude ) + return MDT_None; + if ( mIndex == 84 ) + return MDT_TimesInt; + if ( mIndex == 59 || + ( mIndex >= 64 && mIndex <= 66) ) + return MDT_Feet; + if ( mIndex == 118 || mIndex == 119 ) + return MDT_Level; + if ( ( mIndex >= 28 && mIndex <= 36 ) + || ( mIndex >= 90 && mIndex <= 99 ) + || mIndex == 40 || mIndex == 47 + || mIndex == 57 || mIndex == 68 ) + return MDT_Percentage; + + return MDT_Points; +} } diff --git a/components/esm/loadmgef.hpp b/components/esm/loadmgef.hpp index b74efb4662..9d7397a341 100644 --- a/components/esm/loadmgef.hpp +++ b/components/esm/loadmgef.hpp @@ -12,6 +12,8 @@ class ESMWriter; struct MagicEffect { + static unsigned int sRecordId; + enum Flags { TargetSkill = 0x1, // Affects a specific skill, which is specified elsewhere in the effect structure. @@ -32,6 +34,15 @@ struct MagicEffect Negative = 0x0800 // A harmful effect. Will determine whether // eg. NPCs regard this spell as an attack. (same as 0x10?) }; + enum MagnitudeDisplayType + { + MDT_None, + MDT_Feet, + MDT_Level, + MDT_Percentage, + MDT_Points, + MDT_TimesInt + }; struct MEDTstruct { @@ -47,6 +58,7 @@ struct MagicEffect static const std::string &effectIdToString(short effectID); static short effectStringToId(const std::string &effect); + MagnitudeDisplayType getMagnitudeDisplayType() const; MEDTstruct mData; @@ -69,7 +81,7 @@ struct MagicEffect int mIndex; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; enum Effects diff --git a/components/esm/loadmisc.cpp b/components/esm/loadmisc.cpp index 6006334ea4..2ca09e8aec 100644 --- a/components/esm/loadmisc.cpp +++ b/components/esm/loadmisc.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Miscellaneous::sRecordId = REC_MISC; void Miscellaneous::load(ESMReader &esm) { @@ -14,7 +16,7 @@ void Miscellaneous::load(ESMReader &esm) mScript = esm.getHNOString("SCRI"); mIcon = esm.getHNOString("ITEX"); } -void Miscellaneous::save(ESMWriter &esm) +void Miscellaneous::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loadmisc.hpp b/components/esm/loadmisc.hpp index 25a9c5865a..576bd18c0f 100644 --- a/components/esm/loadmisc.hpp +++ b/components/esm/loadmisc.hpp @@ -16,6 +16,8 @@ class ESMWriter; struct Miscellaneous { + static unsigned int sRecordId; + struct MCDTstruct { float mWeight; @@ -29,7 +31,7 @@ struct Miscellaneous std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadnpc.cpp b/components/esm/loadnpc.cpp index 7e17a93dc6..9fff2d885d 100644 --- a/components/esm/loadnpc.cpp +++ b/components/esm/loadnpc.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int NPC::sRecordId = REC_NPC_; void NPC::load(ESMReader &esm) { @@ -63,7 +65,7 @@ void NPC::load(ESMReader &esm) mAiPackage.load(esm); esm.skipRecord(); } -void NPC::save(ESMWriter &esm) +void NPC::save(ESMWriter &esm) const { esm.writeHNOCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); @@ -87,7 +89,7 @@ void NPC::save(ESMWriter &esm) esm.writeHNT("AIDT", mAiData, sizeof(mAiData)); } - typedef std::vector<Dest>::iterator DestIter; + typedef std::vector<Dest>::const_iterator DestIter; for (DestIter it = mTransport.begin(); it != mTransport.end(); ++it) { esm.writeHNT("DODT", it->mPos, sizeof(it->mPos)); esm.writeHNOCString("DNAM", it->mCellName); diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index 009bc5ef3b..d9e691669c 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -20,6 +20,8 @@ class ESMWriter; struct NPC { + static unsigned int sRecordId; + // Services enum Services { @@ -117,7 +119,7 @@ struct NPC std::string mHair, mHead; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; bool isMale() const; diff --git a/components/esm/loadnpcc.hpp b/components/esm/loadnpcc.hpp index 79d92397f8..c87c2545f7 100644 --- a/components/esm/loadnpcc.hpp +++ b/components/esm/loadnpcc.hpp @@ -78,13 +78,15 @@ class ESMWriter; struct LoadNPCC { + static unsigned int sRecordId; + std::string mId; void load(ESMReader &esm) { esm.skipRecord(); } - void save(ESMWriter &esm) + void save(ESMWriter &esm) const { } }; diff --git a/components/esm/loadpgrd.cpp b/components/esm/loadpgrd.cpp index 882addcb9d..3b5330e9fd 100644 --- a/components/esm/loadpgrd.cpp +++ b/components/esm/loadpgrd.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Pathgrid::sRecordId = REC_PGRD; void Pathgrid::load(ESMReader &esm) { @@ -70,25 +72,25 @@ void Pathgrid::load(ESMReader &esm) } } } -void Pathgrid::save(ESMWriter &esm) +void Pathgrid::save(ESMWriter &esm) const { esm.writeHNT("DATA", mData, 12); esm.writeHNCString("NAME", mCell); - + if (!mPoints.empty()) { esm.startSubRecord("PGRP"); - for (PointList::iterator it = mPoints.begin(); it != mPoints.end(); ++it) + for (PointList::const_iterator it = mPoints.begin(); it != mPoints.end(); ++it) { esm.writeT(*it); } esm.endRecord("PGRP"); } - + if (!mEdges.empty()) { esm.startSubRecord("PGRC"); - for (std::vector<Edge>::iterator it = mEdges.begin(); it != mEdges.end(); ++it) + for (std::vector<Edge>::const_iterator it = mEdges.begin(); it != mEdges.end(); ++it) { esm.writeT(it->mV1); } diff --git a/components/esm/loadpgrd.hpp b/components/esm/loadpgrd.hpp index c3f50fc4da..9ee49552db 100644 --- a/components/esm/loadpgrd.hpp +++ b/components/esm/loadpgrd.hpp @@ -15,6 +15,8 @@ class ESMWriter; */ struct Pathgrid { + static unsigned int sRecordId; + struct DATAstruct { int mX, mY; // Grid location, matches cell for exterior cells @@ -46,7 +48,7 @@ struct Pathgrid EdgeList mEdges; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } #endif diff --git a/components/esm/loadprob.cpp b/components/esm/loadprob.cpp index 729f8404e5..caa3d7e0e7 100644 --- a/components/esm/loadprob.cpp +++ b/components/esm/loadprob.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Probe::sRecordId = REC_PROB; void Probe::load(ESMReader &esm) { @@ -17,7 +19,7 @@ void Probe::load(ESMReader &esm) mIcon = esm.getHNOString("ITEX"); } -void Probe::save(ESMWriter &esm) +void Probe::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadprob.hpp b/components/esm/loadprob.hpp index 55b896bcda..b89b2ddeb6 100644 --- a/components/esm/loadprob.hpp +++ b/components/esm/loadprob.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct Probe { + static unsigned int sRecordId; + struct Data { float mWeight; @@ -24,7 +26,7 @@ struct Probe std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadrace.cpp b/components/esm/loadrace.cpp index 955424e2b9..e50e43a744 100644 --- a/components/esm/loadrace.cpp +++ b/components/esm/loadrace.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Race::sRecordId = REC_RACE; + int Race::MaleFemale::getValue (bool male) const { return male ? mMale : mFemale; @@ -22,7 +25,7 @@ void Race::load(ESMReader &esm) mPowers.load(esm); mDescription = esm.getHNOString("DESC"); } -void Race::save(ESMWriter &esm) +void Race::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mName); esm.writeHNT("RADT", mData, 140); diff --git a/components/esm/loadrace.hpp b/components/esm/loadrace.hpp index 6ecec8ebb9..7d5736d9b7 100644 --- a/components/esm/loadrace.hpp +++ b/components/esm/loadrace.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Race { + static unsigned int sRecordId; + struct SkillBonus { int mSkill; // SkillEnum @@ -65,7 +67,7 @@ struct Race SpellList mPowers; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadregn.cpp b/components/esm/loadregn.cpp index 41c7f507ae..fa4271e26a 100644 --- a/components/esm/loadregn.cpp +++ b/components/esm/loadregn.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Region::sRecordId = REC_REGN; void Region::load(ESMReader &esm) { @@ -28,7 +30,7 @@ void Region::load(ESMReader &esm) mSoundList.push_back(sr); } } -void Region::save(ESMWriter &esm) +void Region::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mName); @@ -40,7 +42,7 @@ void Region::save(ESMWriter &esm) esm.writeHNOCString("BNAM", mSleepList); esm.writeHNT("CNAM", mMapColor); - for (std::vector<SoundRef>::iterator it = mSoundList.begin(); it != mSoundList.end(); ++it) + for (std::vector<SoundRef>::const_iterator it = mSoundList.begin(); it != mSoundList.end(); ++it) { esm.writeHNT<SoundRef>("SNAM", *it); } diff --git a/components/esm/loadregn.hpp b/components/esm/loadregn.hpp index f2a3d9a108..1992c951b4 100644 --- a/components/esm/loadregn.hpp +++ b/components/esm/loadregn.hpp @@ -18,6 +18,8 @@ class ESMWriter; struct Region { + static unsigned int sRecordId; + #pragma pack(push) #pragma pack(1) struct WEATstruct @@ -47,7 +49,7 @@ struct Region std::vector<SoundRef> mSoundList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadrepa.cpp b/components/esm/loadrepa.cpp index ced6daa2e9..a7132828d4 100644 --- a/components/esm/loadrepa.cpp +++ b/components/esm/loadrepa.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Repair::sRecordId = REC_REPA; void Repair::load(ESMReader &esm) { @@ -17,7 +19,7 @@ void Repair::load(ESMReader &esm) mIcon = esm.getHNOString("ITEX"); } -void Repair::save(ESMWriter &esm) +void Repair::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNCString("FNAM", mName); diff --git a/components/esm/loadrepa.hpp b/components/esm/loadrepa.hpp index 83812bad9c..5b404b0e4c 100644 --- a/components/esm/loadrepa.hpp +++ b/components/esm/loadrepa.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct Repair { + static unsigned int sRecordId; + struct Data { float mWeight; @@ -24,7 +26,7 @@ struct Repair std::string mId, mName, mModel, mIcon, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadscpt.cpp b/components/esm/loadscpt.cpp index 2c1b018d97..30460c17a6 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -2,6 +2,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -12,6 +13,8 @@ struct SCHD Script::SCHDstruct mData; }; + unsigned int Script::sRecordId = REC_SCPT; + void Script::load(ESMReader &esm) { SCHD data; @@ -50,11 +53,11 @@ void Script::load(ESMReader &esm) // Script text mScriptText = esm.getHNOString("SCTX"); } -void Script::save(ESMWriter &esm) +void Script::save(ESMWriter &esm) const { std::string varNameString; if (!mVarNames.empty()) - for (std::vector<std::string>::iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) + for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) varNameString.append(*it); SCHD data; @@ -68,7 +71,7 @@ void Script::save(ESMWriter &esm) if (!mVarNames.empty()) { esm.startSubRecord("SCVR"); - for (std::vector<std::string>::iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) + for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) { esm.writeHCString(*it); } diff --git a/components/esm/loadscpt.hpp b/components/esm/loadscpt.hpp index be7e839002..d5200d4c12 100644 --- a/components/esm/loadscpt.hpp +++ b/components/esm/loadscpt.hpp @@ -19,6 +19,8 @@ class ESMWriter; class Script { public: + static unsigned int sRecordId; + struct SCHDstruct { /* Script name. @@ -56,7 +58,7 @@ public: std::string mScriptText; // Uncompiled script void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadskil.cpp b/components/esm/loadskil.cpp index 676a835c3b..b6724e9381 100644 --- a/components/esm/loadskil.cpp +++ b/components/esm/loadskil.cpp @@ -6,6 +6,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -126,6 +127,8 @@ namespace ESM HandToHand }}; + unsigned int Skill::sRecordId = REC_SKIL; + void Skill::load(ESMReader &esm) { esm.getHNT(mIndex, "INDX"); @@ -137,7 +140,7 @@ void Skill::load(ESMReader &esm) mId = indexToId (mIndex); } -void Skill::save(ESMWriter &esm) +void Skill::save(ESMWriter &esm) const { esm.writeHNT("INDX", mIndex); esm.writeHNT("SKDT", mData, 24); diff --git a/components/esm/loadskil.hpp b/components/esm/loadskil.hpp index 384f874545..1b9db5bcff 100644 --- a/components/esm/loadskil.hpp +++ b/components/esm/loadskil.hpp @@ -19,6 +19,8 @@ class ESMWriter; struct Skill { + static unsigned int sRecordId; + std::string mId; struct SKDTstruct @@ -75,7 +77,7 @@ struct Skill static const boost::array<SkillEnum, Length> sSkillIds; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadsndg.cpp b/components/esm/loadsndg.cpp index 42d524226d..1a8ca63354 100644 --- a/components/esm/loadsndg.cpp +++ b/components/esm/loadsndg.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int SoundGenerator::sRecordId = REC_SNDG; void SoundGenerator::load(ESMReader &esm) { @@ -13,7 +15,7 @@ void SoundGenerator::load(ESMReader &esm) mCreature = esm.getHNOString("CNAM"); mSound = esm.getHNOString("SNAM"); } -void SoundGenerator::save(ESMWriter &esm) +void SoundGenerator::save(ESMWriter &esm) const { esm.writeHNT("DATA", mType, 4); esm.writeHNOCString("CNAM", mCreature); diff --git a/components/esm/loadsndg.hpp b/components/esm/loadsndg.hpp index a6226c1545..5509661c18 100644 --- a/components/esm/loadsndg.hpp +++ b/components/esm/loadsndg.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct SoundGenerator { + static unsigned int sRecordId; + enum Type { LeftFoot = 0, @@ -33,7 +35,7 @@ struct SoundGenerator std::string mId, mCreature, mSound; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } #endif diff --git a/components/esm/loadsoun.cpp b/components/esm/loadsoun.cpp index 07af2b5e91..49c9eb54e2 100644 --- a/components/esm/loadsoun.cpp +++ b/components/esm/loadsoun.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Sound::sRecordId = REC_SOUN; void Sound::load(ESMReader &esm) { @@ -17,7 +19,7 @@ void Sound::load(ESMReader &esm) << endl; */ } -void Sound::save(ESMWriter &esm) +void Sound::save(ESMWriter &esm) const { esm.writeHNCString("FNAM", mSound); esm.writeHNT("DATA", mData, 3); diff --git a/components/esm/loadsoun.hpp b/components/esm/loadsoun.hpp index f8e38ac092..04a0984fda 100644 --- a/components/esm/loadsoun.hpp +++ b/components/esm/loadsoun.hpp @@ -16,11 +16,13 @@ struct SOUNstruct struct Sound { + static unsigned int sRecordId; + SOUNstruct mData; std::string mId, mSound; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadspel.cpp b/components/esm/loadspel.cpp index 8149fe4cef..2c98d796d3 100644 --- a/components/esm/loadspel.cpp +++ b/components/esm/loadspel.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Spell::sRecordId = REC_SPEL; void Spell::load(ESMReader &esm) { @@ -13,7 +15,7 @@ void Spell::load(ESMReader &esm) mEffects.load(esm); } -void Spell::save(ESMWriter &esm) +void Spell::save(ESMWriter &esm) const { esm.writeHNOCString("FNAM", mName); esm.writeHNT("SPDT", mData, 12); diff --git a/components/esm/loadspel.hpp b/components/esm/loadspel.hpp index 3a620962d1..cbf5366c4b 100644 --- a/components/esm/loadspel.hpp +++ b/components/esm/loadspel.hpp @@ -13,6 +13,8 @@ class ESMWriter; struct Spell { + static unsigned int sRecordId; + enum SpellType { ST_Spell = 0, // Normal spell, must be cast and costs mana @@ -42,7 +44,7 @@ struct Spell EffectList mEffects; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadsscr.cpp b/components/esm/loadsscr.cpp index ae50de517c..69b04bb237 100644 --- a/components/esm/loadsscr.cpp +++ b/components/esm/loadsscr.cpp @@ -2,16 +2,18 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int StartScript::sRecordId = REC_SSCR; void StartScript::load(ESMReader &esm) { mData = esm.getHNString("DATA"); mScript = esm.getHNString("NAME"); } -void StartScript::save(ESMWriter &esm) +void StartScript::save(ESMWriter &esm) const { esm.writeHNString("DATA", mData); esm.writeHNString("NAME", mScript); diff --git a/components/esm/loadsscr.hpp b/components/esm/loadsscr.hpp index 713fe96b52..d09ad883eb 100644 --- a/components/esm/loadsscr.hpp +++ b/components/esm/loadsscr.hpp @@ -19,12 +19,14 @@ class ESMWriter; struct StartScript { + static unsigned int sRecordId; + std::string mData; std::string mId, mScript; // Load a record and add it to the list void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } diff --git a/components/esm/loadstat.cpp b/components/esm/loadstat.cpp index c9346dafca..a71f22dc23 100644 --- a/components/esm/loadstat.cpp +++ b/components/esm/loadstat.cpp @@ -2,15 +2,17 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Static::sRecordId = REC_STAT; void Static::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); } -void Static::save(ESMWriter &esm) +void Static::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); } diff --git a/components/esm/loadstat.hpp b/components/esm/loadstat.hpp index 1adb7d05be..d912d10583 100644 --- a/components/esm/loadstat.hpp +++ b/components/esm/loadstat.hpp @@ -22,10 +22,12 @@ class ESMWriter; struct Static { + static unsigned int sRecordId; + std::string mId, mModel; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/loadtes3.cpp b/components/esm/loadtes3.cpp index 74d578ba7d..87a8d1d57e 100644 --- a/components/esm/loadtes3.cpp +++ b/components/esm/loadtes3.cpp @@ -4,6 +4,7 @@ #include "esmcommon.hpp" #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" void ESM::Header::blank() { @@ -13,6 +14,7 @@ void ESM::Header::blank() mData.desc.assign (""); mData.records = 0; mFormat = CurrentFormat; + mMaster.clear(); } void ESM::Header::load (ESMReader &esm) diff --git a/components/esm/loadtes3.hpp b/components/esm/loadtes3.hpp index b73a4c31e4..5614d295f6 100644 --- a/components/esm/loadtes3.hpp +++ b/components/esm/loadtes3.hpp @@ -24,7 +24,7 @@ namespace ESM versions are 1.2 and 1.3. These correspond to: 1.2 = 0x3f99999a and 1.3 = 0x3fa66666 */ - int version; + unsigned int version; int type; // 0=esp, 1=esm, 32=ess (unused) NAME32 author; // Author's name NAME256 desc; // File description diff --git a/components/esm/loadweap.cpp b/components/esm/loadweap.cpp index 2537123969..1d0b149df1 100644 --- a/components/esm/loadweap.cpp +++ b/components/esm/loadweap.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Weapon::sRecordId = REC_WEAP; void Weapon::load(ESMReader &esm) { @@ -15,7 +17,7 @@ void Weapon::load(ESMReader &esm) mIcon = esm.getHNOString("ITEX"); mEnchant = esm.getHNOString("ENAM"); } -void Weapon::save(ESMWriter &esm) +void Weapon::save(ESMWriter &esm) const { esm.writeHNCString("MODL", mModel); esm.writeHNOCString("FNAM", mName); diff --git a/components/esm/loadweap.hpp b/components/esm/loadweap.hpp index b62179ccb1..fde716b916 100644 --- a/components/esm/loadweap.hpp +++ b/components/esm/loadweap.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct Weapon { + static unsigned int sRecordId; + enum Type { ShortBladeOneHand = 0, @@ -59,7 +61,7 @@ struct Weapon std::string mId, mName, mModel, mIcon, mEnchant, mScript; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). diff --git a/components/esm/spelllist.cpp b/components/esm/spelllist.cpp index dd886cf7ff..24d3c3d0a5 100644 --- a/components/esm/spelllist.cpp +++ b/components/esm/spelllist.cpp @@ -12,9 +12,9 @@ void SpellList::load(ESMReader &esm) } } -void SpellList::save(ESMWriter &esm) +void SpellList::save(ESMWriter &esm) const { - for (std::vector<std::string>::iterator it = mList.begin(); it != mList.end(); ++it) { + for (std::vector<std::string>::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNString("NPCS", *it, 32); } } diff --git a/components/esm/spelllist.hpp b/components/esm/spelllist.hpp index 52999270a0..934bdda7ad 100644 --- a/components/esm/spelllist.hpp +++ b/components/esm/spelllist.hpp @@ -17,7 +17,7 @@ namespace ESM std::vector<std::string> mList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } diff --git a/components/esm/variant.hpp b/components/esm/variant.hpp index 8c5f3b3d47..2bba60a152 100644 --- a/components/esm/variant.hpp +++ b/components/esm/variant.hpp @@ -11,7 +11,7 @@ namespace ESM enum VarType { - VT_Unknown, + VT_Unknown = 0, VT_None, VT_Short, // stored as a float, kinda VT_Int, diff --git a/components/esm/variantimp.cpp b/components/esm/variantimp.cpp index 160402aa4b..1bacdc0770 100644 --- a/components/esm/variantimp.cpp +++ b/components/esm/variantimp.cpp @@ -193,7 +193,7 @@ void ESM::VariantIntegerData::write (ESMWriter& esm, Variant::Format format, Var } else if (format==Variant::Format_Gmst || format==Variant::Format_Info) { - if (type==VT_Int) + if (type!=VT_Int) { std::ostringstream stream; stream diff --git a/components/fileorderlist/model/datafilesmodel.cpp b/components/fileorderlist/model/datafilesmodel.cpp deleted file mode 100644 index 02a6766b02..0000000000 --- a/components/fileorderlist/model/datafilesmodel.cpp +++ /dev/null @@ -1,445 +0,0 @@ -#include <QTextDecoder> -#include <QTextCodec> -#include <QFileInfo> -#include <QDir> - -#include <stdexcept> - -#include <components/esm/esmreader.hpp> - -#include "esm/esmfile.hpp" - -#include "datafilesmodel.hpp" - -#include <QDebug> - -DataFilesModel::DataFilesModel(QObject *parent) : - QAbstractTableModel(parent) -{ - mEncoding = QString("win1252"); -} - -DataFilesModel::~DataFilesModel() -{ -} - -void DataFilesModel::setEncoding(const QString &encoding) -{ - mEncoding = encoding; -} - -void DataFilesModel::setCheckState(const QModelIndex &index, Qt::CheckState state) -{ - setData(index, state, Qt::CheckStateRole); -} - -Qt::CheckState DataFilesModel::checkState(const QModelIndex &index) -{ - EsmFile *file = item(index.row()); - return mCheckStates[file->fileName()]; -} - -int DataFilesModel::columnCount(const QModelIndex &parent) const -{ - return parent.isValid() ? 0 : 9; -} - -int DataFilesModel::rowCount(const QModelIndex &parent) const -{ - return parent.isValid() ? 0 : mFiles.count(); -} - - -bool DataFilesModel::moveRow(int oldrow, int row, const QModelIndex &parent) -{ - if (oldrow < 0 || row < 0 || oldrow == row) - return false; - - emit layoutAboutToBeChanged(); - //emit beginMoveRows(parent, oldrow, oldrow, parent, row); - mFiles.swap(oldrow, row); - //emit endInsertRows(); - emit layoutChanged(); - - return true; -} - -QVariant DataFilesModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - EsmFile *file = item(index.row()); - - if (!file) - return QVariant(); - - const int column = index.column(); - - switch (role) { - case Qt::DisplayRole: { - - switch (column) { - case 0: - return file->fileName(); - case 1: - return file->author(); - case 2: - return QString("%1 kB").arg(int((file->size() + 1023) / 1024)); - case 3: - //return file->modified().toString(Qt::TextDate); - return file->modified().toString(Qt::ISODate); - case 4: - return file->accessed().toString(Qt::TextDate); - case 5: - return file->version(); - case 6: - return file->path(); - case 7: - return file->masters().join(", "); - case 8: - return file->description(); - } - } - - case Qt::TextAlignmentRole: { - switch (column) { - case 0: - case 1: - return Qt::AlignLeft + Qt::AlignVCenter; - case 2: - case 3: - case 4: - case 5: - return Qt::AlignRight + Qt::AlignVCenter; - default: - return Qt::AlignLeft + Qt::AlignVCenter; - } - } - - case Qt::CheckStateRole: { - if (column != 0) - return QVariant(); - return mCheckStates[file->fileName()]; - } - case Qt::ToolTipRole: - { - if (column != 0) - return QVariant(); - - if (file->version() == 0.0f) - return QVariant(); // Data not set - - QString tooltip = - QString("<b>Author:</b> %1<br/> \ - <b>Version:</b> %2<br/> \ - <br/><b>Description:</b><br/>%3<br/> \ - <br/><b>Dependencies: </b>%4<br/>") - .arg(file->author()) - .arg(QString::number(file->version())) - .arg(file->description()) - .arg(file->masters().join(", ")); - - - return tooltip; - - } - default: - return QVariant(); - } - -} - -Qt::ItemFlags DataFilesModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return Qt::NoItemFlags; - - EsmFile *file = item(index.row()); - - if (!file) - return Qt::NoItemFlags; - - if (canBeChecked(file)) { - if (index.column() == 0) { - return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; - } else { - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; - } - } else { - if (index.column() == 0) { - return Qt::ItemIsUserCheckable | Qt::ItemIsSelectable; - } else { - return Qt::NoItemFlags | Qt::ItemIsSelectable; - } - } - -} - -QVariant DataFilesModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (role != Qt::DisplayRole) - return QVariant(); - - if (orientation == Qt::Horizontal) { - switch (section) { - case 0: return tr("Name"); - case 1: return tr("Author"); - case 2: return tr("Size"); - case 3: return tr("Modified"); - case 4: return tr("Accessed"); - case 5: return tr("Version"); - case 6: return tr("Path"); - case 7: return tr("Masters"); - case 8: return tr("Description"); - } - } else { - // Show row numbers - return ++section; - } - - return QVariant(); -} - -bool DataFilesModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - if (!index.isValid()) - return false; - - if (role == Qt::CheckStateRole) { - QString name = item(index.row())->fileName(); - mCheckStates[name] = static_cast<Qt::CheckState>(value.toInt()); - - // Force a redraw of the view since unchecking one item can affect another - QModelIndex firstIndex = indexFromItem(mFiles.first()); - QModelIndex lastIndex = indexFromItem(mFiles.last()); - - emit dataChanged(firstIndex, lastIndex); - emit checkedItemsChanged(checkedItems()); - return true; - } - - return false; -} - -bool lessThanEsmFile(const EsmFile *e1, const EsmFile *e2) -{ - //Masters first then alphabetically - if (e1->fileName().endsWith(".esm") && !e2->fileName().endsWith(".esm")) - return true; - if (!e1->fileName().endsWith(".esm") && e2->fileName().endsWith(".esm")) - return false; - - return e1->fileName().toLower() < e2->fileName().toLower(); -} - -bool lessThanDate(const EsmFile *e1, const EsmFile *e2) -{ - if (e1->modified().toString(Qt::ISODate) < e2->modified().toString(Qt::ISODate)) { - return true; - } else { - return false; - } -} - -void DataFilesModel::sort(int column, Qt::SortOrder order) -{ - emit layoutAboutToBeChanged(); - - if (column == 3) { - qSort(mFiles.begin(), mFiles.end(), lessThanDate); - } else { - qSort(mFiles.begin(), mFiles.end(), lessThanEsmFile); - } - - emit layoutChanged(); -} - -void DataFilesModel::addFile(EsmFile *file) -{ - emit beginInsertRows(QModelIndex(), mFiles.count(), mFiles.count()); - mFiles.append(file); - emit endInsertRows(); -} - -void DataFilesModel::addFiles(const QString &path) -{ - QDir dir(path); - QStringList filters; - filters << "*.esp" << "*.esm"; - dir.setNameFilters(filters); - - // Create a decoder for non-latin characters in esx metadata - QTextCodec *codec; - - if (mEncoding == QLatin1String("win1252")) { - codec = QTextCodec::codecForName("windows-1252"); - } else if (mEncoding == QLatin1String("win1251")) { - codec = QTextCodec::codecForName("windows-1251"); - } else if (mEncoding == QLatin1String("win1250")) { - codec = QTextCodec::codecForName("windows-1250"); - } else { - return; // This should never happen; - } - - QTextDecoder *decoder = codec->makeDecoder(); - - foreach (const QString &path, dir.entryList()) { - QFileInfo info(dir.absoluteFilePath(path)); - EsmFile *file = new EsmFile(path); - - try { - ESM::ESMReader fileReader; - ToUTF8::Utf8Encoder encoder(ToUTF8::calculateEncoding(mEncoding.toStdString())); - fileReader.setEncoder(&encoder); - fileReader.open(dir.absoluteFilePath(path).toStdString()); - - std::vector<ESM::Header::MasterData> mlist = fileReader.getMasters(); - - QStringList masters; - - for (unsigned int i = 0; i < mlist.size(); ++i) { - QString master = QString::fromStdString(mlist[i].name); - masters.append(master); - } - - file->setAuthor(decoder->toUnicode(fileReader.getAuthor().c_str())); - file->setSize(info.size()); - file->setDates(info.lastModified(), info.lastRead()); - file->setVersion(fileReader.getFVer()); - file->setPath(info.absoluteFilePath()); - file->setMasters(masters); - file->setDescription(decoder->toUnicode(fileReader.getDesc().c_str())); - - - // Put the file in the table - if (findItem(path) == 0) - addFile(file); - } catch(std::runtime_error &e) { - // An error occurred while reading the .esp - qWarning() << "Error reading esp: " << e.what(); - continue; - } - - } - - delete decoder; -} - -QModelIndex DataFilesModel::indexFromItem(EsmFile *item) const -{ - if (item) - return createIndex(mFiles.indexOf(item), 0); - - return QModelIndex(); -} - -EsmFile* DataFilesModel::findItem(const QString &name) -{ - QList<EsmFile *>::ConstIterator it; - QList<EsmFile *>::ConstIterator itEnd = mFiles.constEnd(); - - int i = 0; - for (it = mFiles.constBegin(); it != itEnd; ++it) { - EsmFile *file = item(i); - ++i; - - if (name == file->fileName()) - return file; - } - - // Not found - return 0; -} - -EsmFile* DataFilesModel::item(int row) const -{ - if (row >= 0 && row < mFiles.count()) - return mFiles.at(row); - else - return 0; -} - -QStringList DataFilesModel::checkedItems() -{ - QStringList list; - - QList<EsmFile *>::ConstIterator it; - QList<EsmFile *>::ConstIterator itEnd = mFiles.constEnd(); - - int i = 0; - for (it = mFiles.constBegin(); it != itEnd; ++it) { - EsmFile *file = item(i); - ++i; - - QString name = file->fileName(); - - // Only add the items that are in the checked list and available - if (mCheckStates[name] == Qt::Checked && canBeChecked(file)) - list << name; - } - - return list; -} - -QStringList DataFilesModel::checkedItemsPaths() -{ - QStringList list; - - QList<EsmFile *>::ConstIterator it; - QList<EsmFile *>::ConstIterator itEnd = mFiles.constEnd(); - - int i = 0; - for (it = mFiles.constBegin(); it != itEnd; ++it) { - EsmFile *file = item(i); - ++i; - - if (mCheckStates[file->fileName()] == Qt::Checked && canBeChecked(file)) - list << file->path(); - } - - return list; -} - -void DataFilesModel::uncheckAll() -{ - emit layoutAboutToBeChanged(); - mCheckStates.clear(); - emit layoutChanged(); -} - -QStringList DataFilesModel::uncheckedItems() -{ - QStringList list; - QStringList checked = checkedItems(); - - QList<EsmFile *>::ConstIterator it; - QList<EsmFile *>::ConstIterator itEnd = mFiles.constEnd(); - - int i = 0; - for (it = mFiles.constBegin(); it != itEnd; ++it) { - EsmFile *file = item(i); - ++i; - - // Add the items that are not in the checked list - if (!checked.contains(file->fileName())) - list << file->fileName(); - } - - return list; -} - -bool DataFilesModel::canBeChecked(EsmFile *file) const -{ - //element can be checked if all its dependencies are - bool canBeChecked = true; - foreach (const QString &master, file->masters()) - { - if (!mCheckStates.contains(master) || mCheckStates[master] != Qt::Checked) - { - canBeChecked = false; - break; - } - } - return canBeChecked; -} diff --git a/components/fileorderlist/model/datafilesmodel.hpp b/components/fileorderlist/model/datafilesmodel.hpp deleted file mode 100644 index 0a07a536f8..0000000000 --- a/components/fileorderlist/model/datafilesmodel.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef DATAFILESMODEL_HPP -#define DATAFILESMODEL_HPP - -#include <QAbstractTableModel> -#include <QStringList> -#include <QString> -#include <QHash> - - -class EsmFile; - -class DataFilesModel : public QAbstractTableModel -{ - Q_OBJECT - -public: - explicit DataFilesModel(QObject *parent = 0); - virtual ~DataFilesModel(); - virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; - - bool moveRow(int oldrow, int row, const QModelIndex &parent = QModelIndex()); - - virtual Qt::ItemFlags flags(const QModelIndex &index) const; - - virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - - virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); - - inline QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const - { return QAbstractTableModel::index(row, column, parent); } - - void setEncoding(const QString &encoding); - - void addFiles(const QString &path); - - void uncheckAll(); - - QStringList checkedItems(); - QStringList uncheckedItems(); - QStringList checkedItemsPaths(); - - Qt::CheckState checkState(const QModelIndex &index); - void setCheckState(const QModelIndex &index, Qt::CheckState state); - - QModelIndex indexFromItem(EsmFile *item) const; - EsmFile* findItem(const QString &name); - EsmFile* item(int row) const; - -signals: - void checkedItemsChanged(const QStringList &items); - -private: - bool canBeChecked(EsmFile *file) const; - void addFile(EsmFile *file); - - QList<EsmFile *> mFiles; - QHash<QString, Qt::CheckState> mCheckStates; - - QString mEncoding; - -}; - -#endif // DATAFILESMODEL_HPP diff --git a/components/fileorderlist/model/esm/esmfile.cpp b/components/fileorderlist/model/esm/esmfile.cpp deleted file mode 100644 index 93d83091e7..0000000000 --- a/components/fileorderlist/model/esm/esmfile.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "esmfile.hpp" - -EsmFile::EsmFile(QString fileName, ModelItem *parent) - : ModelItem(parent) -{ - mFileName = fileName; - mSize = 0; - mVersion = 0.0f; -} - -void EsmFile::setFileName(const QString &fileName) -{ - mFileName = fileName; -} - -void EsmFile::setAuthor(const QString &author) -{ - mAuthor = author; -} - -void EsmFile::setSize(const int size) -{ - mSize = size; -} - -void EsmFile::setDates(const QDateTime &modified, const QDateTime &accessed) -{ - mModified = modified; - mAccessed = accessed; -} - -void EsmFile::setVersion(float version) -{ - mVersion = version; -} - -void EsmFile::setPath(const QString &path) -{ - mPath = path; -} - -void EsmFile::setMasters(const QStringList &masters) -{ - mMasters = masters; -} - -void EsmFile::setDescription(const QString &description) -{ - mDescription = description; -} diff --git a/components/fileorderlist/model/esm/esmfile.hpp b/components/fileorderlist/model/esm/esmfile.hpp deleted file mode 100644 index 52b3fbd007..0000000000 --- a/components/fileorderlist/model/esm/esmfile.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef ESMFILE_HPP -#define ESMFILE_HPP - -#include <QDateTime> -#include <QStringList> - -#include "../modelitem.hpp" - -class EsmFile : public ModelItem -{ - Q_OBJECT - Q_PROPERTY(QString filename READ fileName) - -public: - EsmFile(QString fileName = QString(), ModelItem *parent = 0); - - ~EsmFile() - {} - - void setFileName(const QString &fileName); - void setAuthor(const QString &author); - void setSize(const int size); - void setDates(const QDateTime &modified, const QDateTime &accessed); - void setVersion(const float version); - void setPath(const QString &path); - void setMasters(const QStringList &masters); - void setDescription(const QString &description); - - inline QString fileName() const { return mFileName; } - inline QString author() const { return mAuthor; } - inline int size() const { return mSize; } - inline QDateTime modified() const { return mModified; } - inline QDateTime accessed() const { return mAccessed; } - inline float version() const { return mVersion; } - inline QString path() const { return mPath; } - inline QStringList masters() const { return mMasters; } - inline QString description() const { return mDescription; } - - -private: - QString mFileName; - QString mAuthor; - int mSize; - QDateTime mModified; - QDateTime mAccessed; - float mVersion; - QString mPath; - QStringList mMasters; - QString mDescription; - -}; - - -#endif diff --git a/components/fileorderlist/model/modelitem.cpp b/components/fileorderlist/model/modelitem.cpp deleted file mode 100644 index 0ff7e45cb9..0000000000 --- a/components/fileorderlist/model/modelitem.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "modelitem.hpp" - -ModelItem::ModelItem(ModelItem *parent) - : mParentItem(parent) - , QObject(parent) -{ -} - -ModelItem::~ModelItem() -{ - qDeleteAll(mChildItems); -} - - -ModelItem *ModelItem::parent() -{ - return mParentItem; -} - -int ModelItem::row() const -{ - if (mParentItem) - return 1; - //return mParentItem->childRow(const_cast<ModelItem*>(this)); - //return mParentItem->mChildItems.indexOf(const_cast<ModelItem*>(this)); - - return -1; -} - - -int ModelItem::childCount() const -{ - return mChildItems.count(); -} - -int ModelItem::childRow(ModelItem *child) const -{ - Q_ASSERT(child); - - return mChildItems.indexOf(child); -} - -ModelItem *ModelItem::child(int row) -{ - return mChildItems.value(row); -} - - -void ModelItem::appendChild(ModelItem *item) -{ - mChildItems.append(item); -} - -void ModelItem::removeChild(int row) -{ - mChildItems.removeAt(row); -} diff --git a/components/fileorderlist/model/modelitem.hpp b/components/fileorderlist/model/modelitem.hpp deleted file mode 100644 index f4cb4322ff..0000000000 --- a/components/fileorderlist/model/modelitem.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef MODELITEM_HPP -#define MODELITEM_HPP - -#include <QObject> -#include <QList> - -class ModelItem : public QObject -{ - Q_OBJECT - -public: - ModelItem(ModelItem *parent = 0); - ~ModelItem(); - - ModelItem *parent(); - int row() const; - - int childCount() const; - int childRow(ModelItem *child) const; - ModelItem *child(int row); - - void appendChild(ModelItem *child); - void removeChild(int row); - - //virtual bool acceptChild(ModelItem *child); - -protected: - ModelItem *mParentItem; - QList<ModelItem*> mChildItems; -}; - -#endif diff --git a/components/fileorderlist/model/pluginsproxymodel.cpp b/components/fileorderlist/model/pluginsproxymodel.cpp deleted file mode 100644 index 6be152b555..0000000000 --- a/components/fileorderlist/model/pluginsproxymodel.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "pluginsproxymodel.hpp" - -PluginsProxyModel::PluginsProxyModel(QObject *parent) : - QSortFilterProxyModel(parent) -{ -} - -PluginsProxyModel::~PluginsProxyModel() -{ -} - -QVariant PluginsProxyModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if (orientation != Qt::Vertical || role != Qt::DisplayRole) - return QSortFilterProxyModel::headerData(section, orientation, role); - return section + 1; -} diff --git a/components/fileorderlist/model/pluginsproxymodel.hpp b/components/fileorderlist/model/pluginsproxymodel.hpp deleted file mode 100644 index 8fde732361..0000000000 --- a/components/fileorderlist/model/pluginsproxymodel.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef PLUGINSPROXYMODEL_HPP -#define PLUGINSPROXYMODEL_HPP - -#include <QSortFilterProxyModel> - -class QVariant; - -class PluginsProxyModel : public QSortFilterProxyModel -{ - Q_OBJECT -public: - explicit PluginsProxyModel(QObject *parent = 0); - ~PluginsProxyModel(); - - QVariant headerData(int section, Qt::Orientation orientation, int role) const; -}; - -#endif // PLUGINSPROXYMODEL_HPP diff --git a/components/fileorderlist/utils/comboboxlineedit.cpp b/components/fileorderlist/utils/comboboxlineedit.cpp deleted file mode 100644 index 4d62e1399a..0000000000 --- a/components/fileorderlist/utils/comboboxlineedit.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include <QToolButton> -#include <QStyle> - -#include "comboboxlineedit.hpp" - -ComboBoxLineEdit::ComboBoxLineEdit(QWidget *parent) - : QLineEdit(parent) -{ - mClearButton = new QToolButton(this); - QPixmap pixmap(":images/clear.png"); - mClearButton->setIcon(QIcon(pixmap)); - mClearButton->setIconSize(pixmap.size()); - mClearButton->setCursor(Qt::ArrowCursor); - mClearButton->setStyleSheet("QToolButton { border: none; padding: 0px; }"); - mClearButton->hide(); - connect(mClearButton, SIGNAL(clicked()), this, SLOT(clear())); - connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateClearButton(const QString&))); - int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); - - setObjectName(QString("ComboBoxLineEdit")); - setStyleSheet(QString("ComboBoxLineEdit { background-color: transparent; padding-right: %1px; } ").arg(mClearButton->sizeHint().width() + frameWidth + 1)); -} - -void ComboBoxLineEdit::resizeEvent(QResizeEvent *) -{ - QSize sz = mClearButton->sizeHint(); - int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); - mClearButton->move(rect().right() - frameWidth - sz.width(), - (rect().bottom() + 1 - sz.height())/2); -} - -void ComboBoxLineEdit::updateClearButton(const QString& text) -{ - mClearButton->setVisible(!text.isEmpty()); -} diff --git a/components/fileorderlist/utils/comboboxlineedit.hpp b/components/fileorderlist/utils/comboboxlineedit.hpp deleted file mode 100644 index ba10731ae3..0000000000 --- a/components/fileorderlist/utils/comboboxlineedit.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/**************************************************************************** -** -** Copyright (c) 2007 Trolltech ASA <info@trolltech.com> -** -** Use, modification and distribution is allowed without limitation, -** warranty, liability or support of any kind. -** -****************************************************************************/ - -#ifndef LINEEDIT_H -#define LINEEDIT_H - -#include <QLineEdit> - -class QToolButton; - -class ComboBoxLineEdit : public QLineEdit -{ - Q_OBJECT - -public: - ComboBoxLineEdit(QWidget *parent = 0); - -protected: - void resizeEvent(QResizeEvent *); - -private slots: - void updateClearButton(const QString &text); - -private: - QToolButton *mClearButton; -}; - -#endif // LIENEDIT_H - diff --git a/components/fileorderlist/utils/naturalsort.hpp b/components/fileorderlist/utils/naturalsort.hpp deleted file mode 100644 index 59271547a5..0000000000 --- a/components/fileorderlist/utils/naturalsort.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef NATURALSORT_H -#define NATURALSORT_H - -#include <QString> - -bool naturalSortLessThanCS( const QString &left, const QString &right ); -bool naturalSortLessThanCI( const QString &left, const QString &right ); -bool naturalSortGreaterThanCS( const QString &left, const QString &right ); -bool naturalSortGreaterThanCI( const QString &left, const QString &right ); - -#endif diff --git a/components/fileorderlist/utils/profilescombobox.hpp b/components/fileorderlist/utils/profilescombobox.hpp deleted file mode 100644 index 08ead9a7ab..0000000000 --- a/components/fileorderlist/utils/profilescombobox.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef PROFILESCOMBOBOX_HPP -#define PROFILESCOMBOBOX_HPP - -#include <QComboBox> - -class QString; -class QRegExpValidator; - -class ProfilesComboBox : public QComboBox -{ - Q_OBJECT -public: - explicit ProfilesComboBox(QWidget *parent = 0); - void setEditEnabled(bool editable); - -signals: - void profileChanged(const QString &previous, const QString ¤t); - void profileRenamed(const QString &oldName, const QString &newName); - -private slots: - void slotEditingFinished(); - void slotIndexChanged(int index); - void slotTextChanged(const QString &text); - -private: - QString mOldProfile; - QRegExpValidator *mValidator; -}; - -#endif // PROFILESCOMBOBOX_HPP diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 56e55a98d1..75c877dc5d 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -56,7 +56,7 @@ void ConfigurationManager::readConfiguration(boost::program_options::variables_m } -void ConfigurationManager::processPaths(Files::PathContainer& dataDirs) +void ConfigurationManager::processPaths(Files::PathContainer& dataDirs, bool create) { std::string path; for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) @@ -94,6 +94,18 @@ void ConfigurationManager::processPaths(Files::PathContainer& dataDirs) if (!boost::filesystem::is_directory(*it)) { + if (create) + { + try + { + boost::filesystem::create_directories (*it); + } + catch (...) {} + + if (boost::filesystem::is_directory(*it)) + continue; + } + (*it).clear(); } } diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 765f1cebf8..4df8716647 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -30,7 +30,9 @@ struct ConfigurationManager void readConfiguration(boost::program_options::variables_map& variables, boost::program_options::options_description& description); - void processPaths(Files::PathContainer& dataDirs); + + void processPaths(Files::PathContainer& dataDirs, bool create = false); + ///< \param create Try creating the directory, if it does not exist. /**< Fixed paths */ const boost::filesystem::path& getGlobalPath() const; diff --git a/components/misc/tests/.gitignore b/components/misc/tests/.gitignore new file mode 100644 index 0000000000..8144904045 --- /dev/null +++ b/components/misc/tests/.gitignore @@ -0,0 +1 @@ +*_test diff --git a/components/nif/.gitignore b/components/nif/.gitignore new file mode 100644 index 0000000000..731498d9a1 --- /dev/null +++ b/components/nif/.gitignore @@ -0,0 +1 @@ +old_d diff --git a/components/nif/niffile.cpp b/components/nif/niffile.cpp index cb7c2feb07..402eadefb4 100644 --- a/components/nif/niffile.cpp +++ b/components/nif/niffile.cpp @@ -425,7 +425,7 @@ void Node::getProperties(const Nif::NiTexturingProperty *&texprop, Ogre::Matrix4 Node::getLocalTransform() const { - Ogre::Matrix4 mat4(Ogre::Matrix4::IDENTITY); + Ogre::Matrix4 mat4 = Ogre::Matrix4(Ogre::Matrix4::IDENTITY); mat4.makeTransform(trafo.pos, Ogre::Vector3(trafo.scale), Ogre::Quaternion(trafo.rotation)); return mat4; } diff --git a/components/nif/tests/.gitignore b/components/nif/tests/.gitignore new file mode 100644 index 0000000000..b01c11f272 --- /dev/null +++ b/components/nif/tests/.gitignore @@ -0,0 +1,5 @@ +niftool +*_test +*.nif +*.kf +output.txt diff --git a/components/nifogre/tests/.gitignore b/components/nifogre/tests/.gitignore new file mode 100644 index 0000000000..1a55699834 --- /dev/null +++ b/components/nifogre/tests/.gitignore @@ -0,0 +1,5 @@ +*.png +meshlist.txt +ogre.cfg +*_test +chris* diff --git a/components/to_utf8/.gitignore b/components/to_utf8/.gitignore new file mode 100644 index 0000000000..4e0357749e --- /dev/null +++ b/components/to_utf8/.gitignore @@ -0,0 +1 @@ +gen_iconv diff --git a/components/to_utf8/tests/.gitignore b/components/to_utf8/tests/.gitignore new file mode 100644 index 0000000000..8144904045 --- /dev/null +++ b/components/to_utf8/tests/.gitignore @@ -0,0 +1 @@ +*_test diff --git a/credits.txt b/credits.txt index 9a84c5327d..bd0c6ca743 100644 --- a/credits.txt +++ b/credits.txt @@ -12,6 +12,7 @@ Marc Zinnschlag (Zini) - Lead Programmer/Project Manager Adam Hogan (aurix) Aleksandar Jovanov +Alex Haddad (rainChu) Alex McKibben (WeirdSexy) Alexander Nadeau (wareya) Alexander Olofsson (Ace) @@ -47,6 +48,7 @@ Marc Bouvier (CramitDeFrog) Marcin Hulist (Gohan) Mark Siewert (mark76) Mateusz KoÅ‚aczek (PL_kolek) +Michael Hogan (Xethik) Michael Mc Donnell Michael Papageorgiou (werdanith) MichaÅ‚ BieÅ„ (Glorf) @@ -61,6 +63,7 @@ Roman Proskuryakov (humbug) Sandy Carter (bwrsandman) Sebastian Wick (swick) Sergey Shambir +sir_herrbatka Sylvain Thesnieres (Garvek) Tom Mason (wheybags) Torben Leif Carrington (TorbenC) diff --git a/extern/oics/CMakeLists.txt b/extern/oics/CMakeLists.txt index 7c14387a4b..5c1edbf62b 100644 --- a/extern/oics/CMakeLists.txt +++ b/extern/oics/CMakeLists.txt @@ -9,12 +9,23 @@ set(OICS_SOURCE_FILES ICSInputControlSystem_keyboard.cpp ICSInputControlSystem_mouse.cpp ICSInputControlSystem_joystick.cpp +) + +set(TINYXML_SOURCE_FILES tinyxml.cpp tinyxmlparser.cpp tinyxmlerror.cpp - tinystr.cpp + tinystr.cpp ) -add_library(${OICS_LIBRARY} STATIC ${OICS_SOURCE_FILES}) +if(USE_SYSTEM_TINYXML) + add_library(${OICS_LIBRARY} STATIC ${OICS_SOURCE_FILES}) + target_link_libraries(${OICS_LIBRARY} ${TINYXML_LIBRARIES}) +else() + add_library(${OICS_LIBRARY} STATIC + ${OICS_SOURCE_FILES} + ${TINYXML_SOURCE_FILES}) +endif() +# Does this do anything? link_directories(${CMAKE_CURRENT_BINARY_DIR}) diff --git a/extern/sdl4ogre/sdlinputwrapper.cpp b/extern/sdl4ogre/sdlinputwrapper.cpp index 931d6aca30..df74bba3b6 100644 --- a/extern/sdl4ogre/sdlinputwrapper.cpp +++ b/extern/sdl4ogre/sdlinputwrapper.cpp @@ -30,9 +30,6 @@ namespace SFO InputWrapper::~InputWrapper() { - if(mSDLWindow != NULL) - SDL_DestroyWindow(mSDLWindow); - mSDLWindow = NULL; } void InputWrapper::capture(bool windowEventsOnly) diff --git a/files/mac/opencs.icns b/files/mac/opencs.icns Binary files differnew file mode 100644 index 0000000000..98812f871d --- /dev/null +++ b/files/mac/opencs.icns diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 1ec1e08cb5..21153a4740 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -9,7 +9,6 @@ set(MYGUI_FILES core.skin core.xml EBGaramond-Regular.ttf - Obliviontt.zip openmw_alchemy_window.layout openmw_book.layout openmw_box.skin.xml diff --git a/files/mygui/Obliviontt.zip b/files/mygui/Obliviontt.zip Binary files differdeleted file mode 100644 index af4f809fd8..0000000000 --- a/files/mygui/Obliviontt.zip +++ /dev/null diff --git a/files/mygui/openmw_console.layout b/files/mygui/openmw_console.layout index bfda40c68b..7d24a283e1 100644 --- a/files/mygui/openmw_console.layout +++ b/files/mygui/openmw_console.layout @@ -9,6 +9,7 @@ <!-- Log window --> <Widget type="EditBox" skin="MW_ConsoleLog" position="5 5 380 328" align="Stretch" name="list_History"> <Property key="MultiLine" value="1"/> + <Property key="ReadOnly" value="true"/> </Widget> <!-- Command line --> diff --git a/files/mygui/openmw_hud.layout b/files/mygui/openmw_hud.layout index e39777dd0f..72d337e45d 100644 --- a/files/mygui/openmw_hud.layout +++ b/files/mygui/openmw_hud.layout @@ -45,6 +45,7 @@ <Widget type="ProgressBar" skin="MW_Progress_Loading" position="12 36 196 8" align="Center Top" name="Drowning"> <Property key="NeedMouse" value="false"/> </Widget> + <Widget type="Widget" skin="MW_Progress_Drowning" position="14 38 192 4" align="Center Top" name="Flash"/> </Widget> <!-- Equipped weapon/selected spell name display for a few seconds after it changes --> diff --git a/files/mygui/openmw_progress.skin.xml b/files/mygui/openmw_progress.skin.xml index 35114ffebb..f5418e3f88 100644 --- a/files/mygui/openmw_progress.skin.xml +++ b/files/mygui/openmw_progress.skin.xml @@ -17,6 +17,11 @@ <State name="normal" offset="0 28 2 14"/> </BasisSkin> </Skin> + <Skin name="MW_BigTrack_Progress_Red_Small" size="2 6" texture="smallbars.png" > + <BasisSkin type="MainSkin" offset="0 0 2 6" align="Stretch"> + <State name="normal" offset="0 0 2 8"/> + </BasisSkin> + </Skin> <Skin name="MW_BigTrack_Progress_Blue_Small" size="2 6" texture="smallbars.png" > <BasisSkin type="MainSkin" offset="0 0 2 6" align="Stretch"> <State name="normal" offset="0 26 2 6"/> @@ -65,6 +70,10 @@ <Child type="Widget" skin="BlackBG" offset="2 2 60 2" align="Stretch" name="Client"/> </Skin> + <Skin name="MW_Progress_Drowning" size="64 6"> + <Child type="Widget" skin="MW_BigTrack_Progress_Red_Small" offset="0 0 64 6" align="Stretch"/> + </Skin> + <Skin name="MW_ProgressScroll_Loading" size="64 6"> <Property key="TrackWidth" value="1"/> <Property key="TrackRangeMargins" value="0 0"/> diff --git a/files/opencs/GMST.png b/files/opencs/GMST.png Binary files differnew file mode 100644 index 0000000000..f246202882 --- /dev/null +++ b/files/opencs/GMST.png diff --git a/files/opencs/Info.png b/files/opencs/Info.png Binary files differnew file mode 100644 index 0000000000..d7bdad6cb1 --- /dev/null +++ b/files/opencs/Info.png diff --git a/files/opencs/LandTexture.png b/files/opencs/LandTexture.png Binary files differnew file mode 100644 index 0000000000..84f7290981 --- /dev/null +++ b/files/opencs/LandTexture.png diff --git a/files/opencs/PathGrid.png b/files/opencs/PathGrid.png Binary files differnew file mode 100644 index 0000000000..23b6b84d74 --- /dev/null +++ b/files/opencs/PathGrid.png diff --git a/files/opencs/activator.png b/files/opencs/activator.png Binary files differindex 0446af22cf..32cc6f8a3c 100755..100644 --- a/files/opencs/activator.png +++ b/files/opencs/activator.png diff --git a/files/opencs/added.png b/files/opencs/added.png Binary files differindex aff7e25d46..ddd9c2108e 100644 --- a/files/opencs/added.png +++ b/files/opencs/added.png diff --git a/files/opencs/apparatus.png b/files/opencs/apparatus.png Binary files differindex 3cef537e14..3cef537e14 100755..100644 --- a/files/opencs/apparatus.png +++ b/files/opencs/apparatus.png diff --git a/files/opencs/armor.png b/files/opencs/armor.png Binary files differindex fc534c7d1c..fc534c7d1c 100755..100644 --- a/files/opencs/armor.png +++ b/files/opencs/armor.png diff --git a/files/opencs/attribute.png b/files/opencs/attribute.png Binary files differnew file mode 100644 index 0000000000..4aa5dc02e5 --- /dev/null +++ b/files/opencs/attribute.png diff --git a/files/opencs/birthsign.png b/files/opencs/birthsign.png Binary files differnew file mode 100644 index 0000000000..8192d2ebf8 --- /dev/null +++ b/files/opencs/birthsign.png diff --git a/files/opencs/body-part.png b/files/opencs/body-part.png Binary files differnew file mode 100644 index 0000000000..823e437126 --- /dev/null +++ b/files/opencs/body-part.png diff --git a/files/opencs/book.png b/files/opencs/book.png Binary files differindex 3afa9e8aae..9d7669bd7e 100755..100644 --- a/files/opencs/book.png +++ b/files/opencs/book.png diff --git a/files/opencs/cell.png b/files/opencs/cell.png Binary files differnew file mode 100644 index 0000000000..c4f00c1f07 --- /dev/null +++ b/files/opencs/cell.png diff --git a/files/opencs/class.png b/files/opencs/class.png Binary files differnew file mode 100644 index 0000000000..316380363a --- /dev/null +++ b/files/opencs/class.png diff --git a/files/opencs/clothing.png b/files/opencs/clothing.png Binary files differindex 88c9b6ab8b..88c9b6ab8b 100755..100644 --- a/files/opencs/clothing.png +++ b/files/opencs/clothing.png diff --git a/files/opencs/container.png b/files/opencs/container.png Binary files differindex 2a6ed01eb9..2a6ed01eb9 100755..100644 --- a/files/opencs/container.png +++ b/files/opencs/container.png diff --git a/files/opencs/creature.png b/files/opencs/creature.png Binary files differindex 99cf9c87ca..99cf9c87ca 100755..100644 --- a/files/opencs/creature.png +++ b/files/opencs/creature.png diff --git a/files/opencs/defaultfilters b/files/opencs/defaultfilters Binary files differnew file mode 100644 index 0000000000..0ac3c8db4b --- /dev/null +++ b/files/opencs/defaultfilters diff --git a/files/opencs/dialogoue-info.png b/files/opencs/dialogoue-info.png Binary files differnew file mode 100644 index 0000000000..f6743d43c9 --- /dev/null +++ b/files/opencs/dialogoue-info.png diff --git a/files/opencs/dialogoue-journal.png b/files/opencs/dialogoue-journal.png Binary files differnew file mode 100644 index 0000000000..b6a95c5384 --- /dev/null +++ b/files/opencs/dialogoue-journal.png diff --git a/files/opencs/dialogoue-regular.png b/files/opencs/dialogoue-regular.png Binary files differnew file mode 100644 index 0000000000..f9b8d252d3 --- /dev/null +++ b/files/opencs/dialogoue-regular.png diff --git a/files/opencs/dialogue-greeting.png b/files/opencs/dialogue-greeting.png Binary files differnew file mode 100644 index 0000000000..a35e1fe6d1 --- /dev/null +++ b/files/opencs/dialogue-greeting.png diff --git a/files/opencs/dialogue-persuasion.png b/files/opencs/dialogue-persuasion.png Binary files differnew file mode 100644 index 0000000000..5bc5d61136 --- /dev/null +++ b/files/opencs/dialogue-persuasion.png diff --git a/files/opencs/dialogue-speech.png b/files/opencs/dialogue-speech.png Binary files differnew file mode 100644 index 0000000000..11eb9f1ca8 --- /dev/null +++ b/files/opencs/dialogue-speech.png diff --git a/files/opencs/door.png b/files/opencs/door.png Binary files differindex aa48858efa..aa48858efa 100755..100644 --- a/files/opencs/door.png +++ b/files/opencs/door.png diff --git a/files/opencs/enchantment.png b/files/opencs/enchantment.png Binary files differnew file mode 100644 index 0000000000..c90fb27ce3 --- /dev/null +++ b/files/opencs/enchantment.png diff --git a/files/opencs/faction.png b/files/opencs/faction.png Binary files differnew file mode 100644 index 0000000000..8ac1f5200c --- /dev/null +++ b/files/opencs/faction.png diff --git a/files/opencs/filter.png b/files/opencs/filter.png Binary files differnew file mode 100644 index 0000000000..94a57ecd97 --- /dev/null +++ b/files/opencs/filter.png diff --git a/files/opencs/globvar.png b/files/opencs/globvar.png Binary files differnew file mode 100644 index 0000000000..646145f0f4 --- /dev/null +++ b/files/opencs/globvar.png diff --git a/files/opencs/ingredient.png b/files/opencs/ingredient.png Binary files differindex 6b36d008d2..564a930479 100755..100644 --- a/files/opencs/ingredient.png +++ b/files/opencs/ingredient.png diff --git a/files/opencs/land.png b/files/opencs/land.png Binary files differnew file mode 100644 index 0000000000..20dd321dda --- /dev/null +++ b/files/opencs/land.png diff --git a/files/opencs/landpaint.png b/files/opencs/landpaint.png Binary files differnew file mode 100644 index 0000000000..711c0d8f5f --- /dev/null +++ b/files/opencs/landpaint.png diff --git a/files/opencs/leveled-creature.png b/files/opencs/leveled-creature.png Binary files differindex ad4a7c6f83..ad4a7c6f83 100755..100644 --- a/files/opencs/leveled-creature.png +++ b/files/opencs/leveled-creature.png diff --git a/files/opencs/light.png b/files/opencs/light.png Binary files differindex c606fcd987..2765ef1d33 100755..100644 --- a/files/opencs/light.png +++ b/files/opencs/light.png diff --git a/files/opencs/lockpick.png b/files/opencs/lockpick.png Binary files differindex d9bd27f5e9..d9bd27f5e9 100755..100644 --- a/files/opencs/lockpick.png +++ b/files/opencs/lockpick.png diff --git a/files/opencs/magic-effect.png b/files/opencs/magic-effect.png Binary files differnew file mode 100644 index 0000000000..e672ffccb3 --- /dev/null +++ b/files/opencs/magic-effect.png diff --git a/files/opencs/magicrabbit.png b/files/opencs/magicrabbit.png Binary files differnew file mode 100644 index 0000000000..d1d7c8270b --- /dev/null +++ b/files/opencs/magicrabbit.png diff --git a/files/opencs/map.png b/files/opencs/map.png Binary files differnew file mode 100644 index 0000000000..3653797cca --- /dev/null +++ b/files/opencs/map.png diff --git a/files/opencs/miscellaneous.png b/files/opencs/miscellaneous.png Binary files differindex 744bcd9dbc..744bcd9dbc 100755..100644 --- a/files/opencs/miscellaneous.png +++ b/files/opencs/miscellaneous.png diff --git a/files/opencs/modified.png b/files/opencs/modified.png Binary files differindex d15ad827c3..39bd182ac2 100644 --- a/files/opencs/modified.png +++ b/files/opencs/modified.png diff --git a/files/opencs/npc.png b/files/opencs/npc.png Binary files differindex 7a07f26dfe..7a07f26dfe 100755..100644 --- a/files/opencs/npc.png +++ b/files/opencs/npc.png diff --git a/files/opencs/potion.png b/files/opencs/potion.png Binary files differindex 678f61fbf3..678f61fbf3 100755..100644 --- a/files/opencs/potion.png +++ b/files/opencs/potion.png diff --git a/files/opencs/probe.png b/files/opencs/probe.png Binary files differindex 01536186de..01536186de 100755..100644 --- a/files/opencs/probe.png +++ b/files/opencs/probe.png diff --git a/files/opencs/race.png b/files/opencs/race.png Binary files differnew file mode 100644 index 0000000000..94a2de696a --- /dev/null +++ b/files/opencs/race.png diff --git a/files/opencs/random-item.png b/files/opencs/random-item.png Binary files differnew file mode 100644 index 0000000000..7b8e68e605 --- /dev/null +++ b/files/opencs/random-item.png diff --git a/files/opencs/random.png b/files/opencs/random.png Binary files differnew file mode 100644 index 0000000000..2667630f5c --- /dev/null +++ b/files/opencs/random.png diff --git a/files/opencs/raster/GMST.png b/files/opencs/raster/GMST.png Binary files differnew file mode 100644 index 0000000000..f246202882 --- /dev/null +++ b/files/opencs/raster/GMST.png diff --git a/files/opencs/raster/activator.png b/files/opencs/raster/activator.png Binary files differindex 0446af22cf..32cc6f8a3c 100644 --- a/files/opencs/raster/activator.png +++ b/files/opencs/raster/activator.png diff --git a/files/opencs/raster/attribute.png b/files/opencs/raster/attribute.png Binary files differnew file mode 100644 index 0000000000..4aa5dc02e5 --- /dev/null +++ b/files/opencs/raster/attribute.png diff --git a/files/opencs/raster/body-part.png b/files/opencs/raster/body-part.png Binary files differindex 4aa5dc02e5..823e437126 100644 --- a/files/opencs/raster/body-part.png +++ b/files/opencs/raster/body-part.png diff --git a/files/opencs/raster/book.png b/files/opencs/raster/book.png Binary files differindex 3afa9e8aae..9d7669bd7e 100644 --- a/files/opencs/raster/book.png +++ b/files/opencs/raster/book.png diff --git a/files/opencs/raster/filter.png b/files/opencs/raster/filter.png Binary files differnew file mode 100644 index 0000000000..94a57ecd97 --- /dev/null +++ b/files/opencs/raster/filter.png diff --git a/files/opencs/raster/ingredient.png b/files/opencs/raster/ingredient.png Binary files differindex 6b36d008d2..564a930479 100644 --- a/files/opencs/raster/ingredient.png +++ b/files/opencs/raster/ingredient.png diff --git a/files/opencs/raster/light.png b/files/opencs/raster/light.png Binary files differindex c606fcd987..2765ef1d33 100644 --- a/files/opencs/raster/light.png +++ b/files/opencs/raster/light.png diff --git a/files/opencs/raster/random.png b/files/opencs/raster/random.png Binary files differnew file mode 100644 index 0000000000..2667630f5c --- /dev/null +++ b/files/opencs/raster/random.png diff --git a/files/opencs/raster/soundgen.png b/files/opencs/raster/soundgen.png Binary files differindex 70ae43a1d7..222fc4c7fe 100644 --- a/files/opencs/raster/soundgen.png +++ b/files/opencs/raster/soundgen.png diff --git a/files/opencs/raster/startup/big/configure.png b/files/opencs/raster/startup/big/configure.png Binary files differnew file mode 100644 index 0000000000..f0be888a15 --- /dev/null +++ b/files/opencs/raster/startup/big/configure.png diff --git a/files/opencs/raster/startup/big/create-addon.png b/files/opencs/raster/startup/big/create-addon.png Binary files differnew file mode 100644 index 0000000000..fa059264ef --- /dev/null +++ b/files/opencs/raster/startup/big/create-addon.png diff --git a/files/opencs/raster/startup/big/edit-content.png b/files/opencs/raster/startup/big/edit-content.png Binary files differnew file mode 100644 index 0000000000..dbb2602e5a --- /dev/null +++ b/files/opencs/raster/startup/big/edit-content.png diff --git a/files/opencs/raster/startup/big/new-game.png b/files/opencs/raster/startup/big/new-game.png Binary files differnew file mode 100644 index 0000000000..5cec444178 --- /dev/null +++ b/files/opencs/raster/startup/big/new-game.png diff --git a/files/opencs/raster/startup/small/configure.png b/files/opencs/raster/startup/small/configure.png Binary files differnew file mode 100644 index 0000000000..e91b7f7731 --- /dev/null +++ b/files/opencs/raster/startup/small/configure.png diff --git a/files/opencs/raster/startup/small/create-addon.png b/files/opencs/raster/startup/small/create-addon.png Binary files differnew file mode 100644 index 0000000000..64fd138be5 --- /dev/null +++ b/files/opencs/raster/startup/small/create-addon.png diff --git a/files/opencs/raster/startup/small/edit-content.png b/files/opencs/raster/startup/small/edit-content.png Binary files differnew file mode 100644 index 0000000000..6297f1169c --- /dev/null +++ b/files/opencs/raster/startup/small/edit-content.png diff --git a/files/opencs/raster/startup/small/new-game.png b/files/opencs/raster/startup/small/new-game.png Binary files differnew file mode 100644 index 0000000000..0d7d14c558 --- /dev/null +++ b/files/opencs/raster/startup/small/new-game.png diff --git a/files/opencs/raster/static.png b/files/opencs/raster/static.png Binary files differindex b53be12d9a..aedf2d30ee 100644 --- a/files/opencs/raster/static.png +++ b/files/opencs/raster/static.png diff --git a/files/opencs/removed.png b/files/opencs/removed.png Binary files differindex 2ca9e094be..2354bc7430 100644 --- a/files/opencs/removed.png +++ b/files/opencs/removed.png diff --git a/files/opencs/repair.png b/files/opencs/repair.png Binary files differindex 6cf1c0aacd..6cf1c0aacd 100755..100644 --- a/files/opencs/repair.png +++ b/files/opencs/repair.png diff --git a/files/opencs/resources.qrc b/files/opencs/resources.qrc index 321413763e..eadcf96977 100644 --- a/files/opencs/resources.qrc +++ b/files/opencs/resources.qrc @@ -1,29 +1,64 @@ <RCC> <qresource prefix="/"> <file>opencs.png</file> - <file>added.png</file> - <file>modified.png</file> - <file>removed.png</file> - <file>base.png</file> <file>activator.png</file> - <file>apparatus.png</file> - <file>armor.png</file> - <file>book.png</file> - <file>clothing.png</file> - <file>container.png</file> - <file>creature.png</file> - <file>door.png</file> - <file>ingredient.png</file> - <file>leveled-creature.png</file> - <file>leveled-item.png</file> - <file>light.png</file> - <file>lockpick.png</file> - <file>miscellaneous.png</file> - <file>npc.png</file> - <file>potion.png</file> - <file>probe.png</file> - <file>repair.png</file> - <file>static.png</file> - <file>weapon.png</file> + <file>added.png</file> + <file>apparatus.png</file> + <file>armor.png</file> + <file>attribute.png</file> + <file>base.png</file> + <file>birthsign.png</file> + <file>body-part.png</file> + <file>book.png</file> + <file>cell.png</file> + <file>class.png</file> + <file>clothing.png</file> + <file>container.png</file> + <file>creature.png</file> + <file>dialogoue-info.png</file> + <file>dialogoue-journal.png</file> + <file>dialogoue-regular.png</file> + <file>dialogue-greeting.png</file> + <file>dialogue-persuasion.png</file> + <file>dialogue-speech.png</file> + <file>door.png</file> + <file>enchantment.png</file> + <file>faction.png</file> + <file>filter.png</file> + <file>globvar.png</file> + <file>GMST.png</file> + <file>Info.png</file> + <file>ingredient.png</file> + <file>landpaint.png</file> + <file>land.png</file> + <file>LandTexture.png</file> + <file>leveled-creature.png</file> + <file>light.png</file> + <file>lockpick.png</file> + <file>magic-effect.png</file> + <file>magicrabbit.png</file> + <file>map.png</file> + <file>miscellaneous.png</file> + <file>modified.png</file> + <file>npc.png</file> + <file>PathGrid.png</file> + <file>potion.png</file> + <file>probe.png</file> + <file>race.png</file> + <file>random-item.png</file> + <file>random.png</file> + <file>removed.png</file> + <file>repair.png</file> + <file>script.png</file> + <file>skill.png</file> + <file>soundgen.png</file> + <file>sound.png</file> + <file>spell.png</file> + <file>static.png</file> + <file>weapon.png</file> + <file alias="startup/create-addon">raster/startup/big/create-addon.png</file> + <file alias="startup/create-game">raster/startup/big/new-game.png</file> + <file alias="startup/edit-content">raster/startup/big/edit-content.png</file> + <file alias="startup/configure">raster/startup/small/configure.png</file> </qresource> </RCC> diff --git a/files/opencs/scalable/referenceable-record/.directory b/files/opencs/scalable/referenceable-record/.directory index c633c38a9a..98e1b7f92a 100644 --- a/files/opencs/scalable/referenceable-record/.directory +++ b/files/opencs/scalable/referenceable-record/.directory @@ -1,7 +1,7 @@ [Dolphin] GroupedSorting=true SortFoldersFirst=false -Timestamp=2013,8,11,16,50,43 +Timestamp=2013,8,25,18,35,16 Version=3 ViewMode=2 VisibleRoles=Compact_text,Compact_size diff --git a/files/opencs/scalable/referenceable-record/book2.svgz b/files/opencs/scalable/referenceable-record/book2.svgz Binary files differnew file mode 100644 index 0000000000..4535a2fce4 --- /dev/null +++ b/files/opencs/scalable/referenceable-record/book2.svgz diff --git a/files/opencs/scalable/referenceable-record/miscellaneous.svg b/files/opencs/scalable/referenceable-record/miscellaneous.svg deleted file mode 100644 index 96522048cb..0000000000 --- a/files/opencs/scalable/referenceable-record/miscellaneous.svg +++ /dev/null @@ -1,965 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="48" - height="48" - id="svg2" - version="1.1" - inkscape:version="0.48.4 r9939" - sodipodi:docname="miscelanius.svg"> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="8.0000002" - inkscape:cx="3.9119117" - inkscape:cy="-1.8033034" - inkscape:document-units="px" - inkscape:current-layer="g4862" - showgrid="false" - showguides="true" - inkscape:guide-bbox="true" - inkscape:window-width="1280" - inkscape:window-height="994" - inkscape:window-x="0" - inkscape:window-y="30" - inkscape:window-maximized="1" - inkscape:snap-page="true" - inkscape:snap-bbox="true" - inkscape:bbox-paths="true" - inkscape:bbox-nodes="true" - inkscape:snap-bbox-edge-midpoints="true" - inkscape:snap-bbox-midpoints="true" - inkscape:object-paths="true" - inkscape:snap-intersection-paths="false" - inkscape:object-nodes="true" - inkscape:snap-global="true" - inkscape:snap-smooth-nodes="true" - inkscape:snap-grids="false" /> - <defs - id="defs4"> - <linearGradient - id="linearGradient3902" - inkscape:collect="always"> - <stop - id="stop3904" - offset="0" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - id="stop3906" - offset="1" - style="stop-color:#000000;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient3886" - inkscape:collect="always"> - <stop - id="stop3888" - offset="0" - style="stop-color:#fff226;stop-opacity:1;" /> - <stop - id="stop3890" - offset="1" - style="stop-color:#fff226;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient9248"> - <stop - id="stop9250" - offset="0" - style="stop-color:#ffffff;stop-opacity:1;" /> - <stop - id="stop9252" - offset="1" - style="stop-color:#b4b4b4;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient8521"> - <stop - id="stop8523" - offset="0" - style="stop-color:#00105d;stop-opacity:1;" /> - <stop - id="stop8525" - offset="1" - style="stop-color:#00105d;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient7487"> - <stop - style="stop-color:#000e50;stop-opacity:1;" - offset="0" - id="stop7489" /> - <stop - style="stop-color:#000000;stop-opacity:1;" - offset="1" - id="stop7491" /> - </linearGradient> - <linearGradient - id="linearGradient6034"> - <stop - style="stop-color:#ffffff;stop-opacity:1;" - offset="0" - id="stop6036" /> - <stop - style="stop-color:#b3bbad;stop-opacity:1;" - offset="1" - id="stop6038" /> - </linearGradient> - <linearGradient - id="linearGradient6815"> - <stop - id="stop6817" - offset="0" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - style="stop-color:#3c3c3c;stop-opacity:0.58823532;" - offset="0.40229002" - id="stop6825" /> - <stop - id="stop6819" - offset="1" - style="stop-color:#3c3c3c;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient4022"> - <stop - id="stop4024" - offset="0" - style="stop-color:#204a87;stop-opacity:1;" /> - <stop - id="stop4026" - offset="1" - style="stop-color:#729fcf;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient3723"> - <stop - id="stop3725" - offset="0" - style="stop-color:#ffffff;stop-opacity:0.78431374;" /> - <stop - id="stop3727" - offset="1" - style="stop-color:#ffffff;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient3715"> - <stop - id="stop3717" - offset="0" - style="stop-color:#99bbd4;stop-opacity:1;" /> - <stop - id="stop3719" - offset="1" - style="stop-color:#5d93ba;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient3707"> - <stop - id="stop3709" - offset="0" - style="stop-color:#a6c4d9;stop-opacity:0.78431374;" /> - <stop - id="stop3711" - offset="1" - style="stop-color:#75a3c3;stop-opacity:1;" /> - </linearGradient> - <inkscape:perspective - id="perspective10" - inkscape:persp3d-origin="372.04724 : 350.78739 : 1" - inkscape:vp_z="744.09448 : 526.18109 : 1" - inkscape:vp_y="0 : 1000 : 0" - inkscape:vp_x="0 : 526.18109 : 1" - sodipodi:type="inkscape:persp3d" /> - <inkscape:perspective - sodipodi:type="inkscape:persp3d" - inkscape:vp_x="0 : 0.5 : 1" - inkscape:vp_y="0 : 1000 : 0" - inkscape:vp_z="1 : 0.5 : 1" - inkscape:persp3d-origin="0.5 : 0.33333333 : 1" - id="perspective2920" /> - <linearGradient - gradientTransform="translate(0.2341189,5.74082)" - gradientUnits="userSpaceOnUse" - y2="1029.8864" - x2="63.765881" - y1="949.3266" - x1="63.765881" - id="linearGradient3713" - xlink:href="#linearGradient3707" - inkscape:collect="always" /> - <linearGradient - gradientTransform="translate(0.2341196,6.0908086)" - gradientUnits="userSpaceOnUse" - y2="983.41931" - x2="63.765881" - y1="936.95227" - x1="63.765881" - id="linearGradient3721" - xlink:href="#linearGradient3715" - inkscape:collect="always" /> - <linearGradient - gradientTransform="translate(0.2341189,5.74082)" - gradientUnits="userSpaceOnUse" - y2="1030.7611" - x2="37.375645" - y1="948.8266" - x1="37.375645" - id="linearGradient3729" - xlink:href="#linearGradient3723" - inkscape:collect="always" /> - <linearGradient - y2="983.41931" - x2="63.765881" - y1="936.95227" - x1="63.765881" - gradientTransform="translate(0.2341196,6.0908086)" - gradientUnits="userSpaceOnUse" - id="linearGradient3735" - xlink:href="#linearGradient3715" - inkscape:collect="always" /> - <linearGradient - y2="1029.8864" - x2="63.765881" - y1="949.3266" - x1="63.765881" - gradientTransform="translate(0.2341189,5.74082)" - gradientUnits="userSpaceOnUse" - id="linearGradient3737" - xlink:href="#linearGradient3707" - inkscape:collect="always" /> - <linearGradient - y2="1030.7611" - x2="37.375645" - y1="948.8266" - x1="37.375645" - gradientTransform="translate(0.2341189,5.74082)" - gradientUnits="userSpaceOnUse" - id="linearGradient3739" - xlink:href="#linearGradient3723" - inkscape:collect="always" /> - <linearGradient - y2="1029.8864" - x2="63.765881" - y1="949.3266" - x1="63.765881" - gradientTransform="matrix(1.0066488,0,0,1.0066488,0.21965498,-1.1089658)" - gradientUnits="userSpaceOnUse" - id="linearGradient3742" - xlink:href="#linearGradient3707" - inkscape:collect="always" /> - <linearGradient - y2="1030.7611" - x2="37.375645" - y1="948.8266" - x1="37.375645" - gradientTransform="matrix(1.0066488,0,0,1.0066488,0.21965498,-1.1089658)" - gradientUnits="userSpaceOnUse" - id="linearGradient3744" - xlink:href="#linearGradient3723" - inkscape:collect="always" /> - <linearGradient - y2="983.41931" - x2="63.765881" - y1="936.95227" - x1="63.765881" - gradientTransform="matrix(1.0066488,0,0,1.0066488,0.21965567,-0.75665045)" - gradientUnits="userSpaceOnUse" - id="linearGradient3747" - xlink:href="#linearGradient3715" - inkscape:collect="always" /> - <linearGradient - y2="983.41931" - x2="63.765881" - y1="936.95227" - x1="63.765881" - gradientTransform="matrix(1.0066488,0,0,1.0066488,0.21965567,-0.75665045)" - gradientUnits="userSpaceOnUse" - id="linearGradient3753" - xlink:href="#linearGradient3715" - inkscape:collect="always" /> - <linearGradient - y2="1029.8864" - x2="63.765881" - y1="949.3266" - x1="63.765881" - gradientTransform="matrix(1.0066488,0,0,1.0066488,0.21965498,-1.1089658)" - gradientUnits="userSpaceOnUse" - id="linearGradient3755" - xlink:href="#linearGradient3707" - inkscape:collect="always" /> - <linearGradient - y2="1030.7611" - x2="37.375645" - y1="948.8266" - x1="37.375645" - gradientTransform="matrix(1.0066488,0,0,1.0066488,0.21965498,-1.1089658)" - gradientUnits="userSpaceOnUse" - id="linearGradient3757" - xlink:href="#linearGradient3723" - inkscape:collect="always" /> - <linearGradient - gradientTransform="matrix(0.91716429,0,0,0.91716429,2.2512556,85.18512)" - gradientUnits="userSpaceOnUse" - y2="1008.9376" - x2="47.902649" - y1="1048.3364" - x1="14.991861" - id="linearGradient4028" - xlink:href="#linearGradient4022" - inkscape:collect="always" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3818" - id="linearGradient5433" - x1="-14.939182" - y1="166.73387" - x2="-15.495684" - y2="164.65698" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.71906912,0,0,0.71906912,35.096859,924.87424)" /> - <linearGradient - id="linearGradient3818"> - <stop - style="stop-color:#1e1e1e;stop-opacity:1;" - offset="0" - id="stop3820" /> - <stop - style="stop-color:#000000;stop-opacity:0;" - offset="1" - id="stop3822" /> - </linearGradient> - <linearGradient - id="linearGradient3715-2"> - <stop - style="stop-color:#ffffff;stop-opacity:1;" - offset="0" - id="stop3717-4" /> - <stop - style="stop-color:#b3bbad;stop-opacity:1;" - offset="1" - id="stop3719-9" /> - </linearGradient> - <linearGradient - id="linearGradient5299"> - <stop - style="stop-color:#000e50;stop-opacity:1;" - offset="0" - id="stop5301" /> - <stop - style="stop-color:#000000;stop-opacity:1;" - offset="1" - id="stop5303" /> - </linearGradient> - <radialGradient - r="29.000444" - fy="119.59179" - fx="-172.875" - cy="119.59179" - cx="-172.875" - gradientTransform="matrix(1.0541003,0.65674043,-0.42405323,0.68062603,240.54684,45.0811)" - gradientUnits="userSpaceOnUse" - id="radialGradient6812" - xlink:href="#linearGradient3715-2" - inkscape:collect="always" /> - <linearGradient - y2="965.56183" - x2="63.765881" - y1="941.44623" - x1="63.765881" - gradientTransform="matrix(0.37749331,0,0,0.37749331,-0.0711918,657.55701)" - gradientUnits="userSpaceOnUse" - id="linearGradient2843" - xlink:href="#linearGradient3715-6" - inkscape:collect="always" /> - <linearGradient - id="linearGradient3715-6"> - <stop - id="stop3717-6" - offset="0" - style="stop-color:#99bbd4;stop-opacity:1;" /> - <stop - id="stop3719-4" - offset="1" - style="stop-color:#5d93ba;stop-opacity:1;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3715-6" - id="linearGradient3184" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(-0.37749331,0,0,0.37749331,4.1838142,536.26868)" - x1="63.765881" - y1="941.44623" - x2="63.765881" - y2="965.56183" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3715-6" - id="linearGradient3321" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(-0.28231332,0,0,0.28231332,64.982195,664.15172)" - x1="63.765881" - y1="941.44623" - x2="63.765881" - y2="965.56183" /> - <linearGradient - id="linearGradient6815-6"> - <stop - id="stop6817-4" - offset="0" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - style="stop-color:#3c3c3c;stop-opacity:0.58823532;" - offset="0.40229002" - id="stop6825-9" /> - <stop - id="stop6819-5" - offset="1" - style="stop-color:#3c3c3c;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient3715-2-4"> - <stop - style="stop-color:#ffffff;stop-opacity:1;" - offset="0" - id="stop3717-4-8" /> - <stop - style="stop-color:#b3bbad;stop-opacity:1;" - offset="1" - id="stop3719-9-7" /> - </linearGradient> - <radialGradient - r="28.421875" - fy="116.19179" - fx="-182.4375" - cy="116.19179" - cx="-182.4375" - gradientTransform="matrix(0.30667246,1.5835715,-1.396495,0.27044345,218.94267,1272.3725)" - gradientUnits="userSpaceOnUse" - id="radialGradient5641-1" - xlink:href="#linearGradient5299-7" - inkscape:collect="always" /> - <linearGradient - id="linearGradient5299-7"> - <stop - style="stop-color:#000e50;stop-opacity:1;" - offset="0" - id="stop5301-2" /> - <stop - style="stop-color:#000000;stop-opacity:1;" - offset="1" - id="stop5303-7" /> - </linearGradient> - <linearGradient - y2="350.56357" - x2="518.24652" - y1="536.11566" - x1="553.62225" - gradientTransform="translate(-26.263966,56.568543)" - gradientUnits="userSpaceOnUse" - id="linearGradient4078-954" - xlink:href="#linearGradient3955-87-471" - inkscape:collect="always" /> - <linearGradient - inkscape:collect="always" - id="linearGradient3955-87-471"> - <stop - style="stop-color:#436123;stop-opacity:1" - offset="0" - id="stop4122" /> - <stop - id="stop4124" - offset="0.04243463" - style="stop-color:#74984d;stop-opacity:1;" /> - <stop - style="stop-color:#74984d;stop-opacity:1" - offset="1" - id="stop4126" /> - </linearGradient> - <pattern - patternUnits="userSpaceOnUse" - width="2" - height="1" - patternTransform="matrix(0,4.4721359,-4.4721359,0,-50.004131,-3.0322266e-6)" - id="Strips1_1" - inkscape:stockid="Stripes 1:1"> - <rect - style="fill:black;stroke:none" - x="0" - y="-0.5" - width="1" - height="2" - id="rect3917" /> - </pattern> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient6034" - id="linearGradient6042" - gradientUnits="userSpaceOnUse" - x1="-2256.6802" - y1="1067.036" - x2="37.487514" - y2="2532.4438" - gradientTransform="translate(-1.6900304,-354.84909)" /> - <linearGradient - y2="2444.7776" - x2="-2151.6707" - y1="2903.8035" - x1="-2148.2864" - gradientUnits="userSpaceOnUse" - id="linearGradient8509" - xlink:href="#linearGradient7487" - inkscape:collect="always" - gradientTransform="translate(-1.6900304,-354.84909)" /> - <linearGradient - gradientUnits="userSpaceOnUse" - y2="3353.4497" - x2="-1962.6486" - y1="2540.8635" - x1="-1962.6486" - id="linearGradient8527" - xlink:href="#linearGradient7487" - inkscape:collect="always" /> - <linearGradient - spreadMethod="reflect" - gradientUnits="userSpaceOnUse" - y2="2914.2673" - x2="-115.04873" - y1="2899.3862" - x1="-115.04873" - id="linearGradient9254" - xlink:href="#linearGradient9248" - inkscape:collect="always" - gradientTransform="translate(-1.6900304,-354.84909)" /> - <radialGradient - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1,0,0,0.60717495,0,6.6355278)" - r="3.1054714" - fy="16.891813" - fx="29.111721" - cy="16.891813" - cx="29.111721" - id="radialGradient3892" - xlink:href="#linearGradient3886" - inkscape:collect="always" /> - <radialGradient - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(2.1494315,0.36121083,-0.30519899,1.8161266,2.9871553,-34.404392)" - r="15.46875" - fy="32.971859" - fx="13.599908" - cy="32.971859" - cx="13.599908" - id="radialGradient3908" - xlink:href="#linearGradient3902" - inkscape:collect="always" /> - <inkscape:path-effect - fuse_tolerance="0" - vertical_pattern="false" - prop_units="false" - tang_offset="0" - normal_offset="0" - spacing="0" - scale_y_rel="false" - prop_scale="1" - copytype="single_stretched" - pattern="m 1273.479,-26681.071 0,10 10,-5 z" - is_visible="true" - id="path-effect4754" - effect="skeletal" /> - <inkscape:path-effect - effect="skeletal" - id="path-effect4760" - is_visible="true" - pattern="m 1315.479,-26621.071 0,10 10,-5 z" - copytype="single_stretched" - prop_scale="1" - scale_y_rel="false" - spacing="0" - normal_offset="0" - tang_offset="0" - prop_units="false" - vertical_pattern="false" - fuse_tolerance="0" /> - <inkscape:path-effect - effect="skeletal" - id="path-effect2991" - is_visible="true" - pattern="m 595.66194,-26736.611 0,10 10,-5 z" - copytype="single_stretched" - prop_scale="1" - scale_y_rel="false" - spacing="0" - normal_offset="0" - tang_offset="0" - prop_units="false" - vertical_pattern="false" - fuse_tolerance="0" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient4904" - id="radialGradient4910" - cx="270.45764" - cy="499.72882" - fx="270.45764" - fy="499.72882" - r="96.228813" - gradientTransform="matrix(1,0,0,0.44359312,0,278.05255)" - gradientUnits="userSpaceOnUse" /> - <linearGradient - id="linearGradient4904"> - <stop - style="stop-color:#321b00;stop-opacity:1;" - offset="0" - id="stop4906" /> - <stop - style="stop-color:#653700;stop-opacity:1;" - offset="1" - id="stop4908" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4787" - id="linearGradient4793" - x1="270.45764" - y1="458.54239" - x2="270.45764" - y2="540.91528" - gradientUnits="userSpaceOnUse" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4787"> - <stop - style="stop-color:#000000;stop-opacity:1;" - offset="0" - id="stop4789" /> - <stop - style="stop-color:#000000;stop-opacity:0;" - offset="1" - id="stop4791" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient4805" - id="radialGradient4813" - cx="279.13174" - cy="537.41302" - fx="279.13174" - fy="537.41302" - r="94.728813" - gradientTransform="matrix(1.0793238,-0.1626202,0.09156796,0.60774413,-71.351608,234.89611)" - gradientUnits="userSpaceOnUse" /> - <linearGradient - id="linearGradient4805"> - <stop - style="stop-color:#cf0000;stop-opacity:1;" - offset="0" - id="stop4807" /> - <stop - style="stop-color:#800000;stop-opacity:1;" - offset="1" - id="stop4809" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4850" - id="linearGradient4856" - x1="9" - y1="314.39062" - x2="136.21875" - y2="314.39062" - gradientUnits="userSpaceOnUse" /> - <linearGradient - id="linearGradient4850"> - <stop - style="stop-color:#232620;stop-opacity:1;" - offset="0" - id="stop4852" /> - <stop - id="stop4858" - offset="0.5" - style="stop-color:#ffffff;stop-opacity:0;" /> - <stop - style="stop-color:#232620;stop-opacity:1;" - offset="1" - id="stop4854" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4795" - id="linearGradient4801" - x1="179.76239" - y1="205.93243" - x2="211.7963" - y2="205.93243" - gradientUnits="userSpaceOnUse" /> - <linearGradient - id="linearGradient4795"> - <stop - style="stop-color:#800000;stop-opacity:1;" - offset="0" - id="stop4797" /> - <stop - id="stop4803" - offset="0.5" - style="stop-color:#d07200;stop-opacity:1;" /> - <stop - style="stop-color:#800000;stop-opacity:1;" - offset="1" - id="stop4799" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient4805" - id="radialGradient4872" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0793238,-0.1626202,0.09156796,0.60774413,-71.351608,234.89611)" - cx="279.13174" - cy="537.41302" - fx="279.13174" - fy="537.41302" - r="94.728813" /> - <linearGradient - id="linearGradient4965"> - <stop - style="stop-color:#cf0000;stop-opacity:1;" - offset="0" - id="stop4967" /> - <stop - style="stop-color:#800000;stop-opacity:1;" - offset="1" - id="stop4969" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4850" - id="linearGradient4874" - gradientUnits="userSpaceOnUse" - x1="9" - y1="314.39062" - x2="136.21875" - y2="314.39062" /> - <linearGradient - id="linearGradient4972"> - <stop - style="stop-color:#232620;stop-opacity:1;" - offset="0" - id="stop4974" /> - <stop - id="stop4976" - offset="0.5" - style="stop-color:#ffffff;stop-opacity:0;" /> - <stop - style="stop-color:#232620;stop-opacity:1;" - offset="1" - id="stop4978" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient4882" - id="radialGradient4888" - cx="195.79498" - cy="55.193817" - fx="195.79498" - fy="55.193817" - r="30.780195" - gradientTransform="matrix(0.68778076,0,0,0.83721193,61.130961,8.984896)" - gradientUnits="userSpaceOnUse" /> - <linearGradient - id="linearGradient4882"> - <stop - style="stop-color:#824700;stop-opacity:1;" - offset="0" - id="stop4884" /> - <stop - style="stop-color:#d07200;stop-opacity:1;" - offset="1" - id="stop4886" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4892" - id="linearGradient4902" - x1="165.0148" - y1="55.193817" - x2="226.5752" - y2="55.193817" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.68778076,0,0,0.68778076,61.130961,17.232572)" /> - <linearGradient - id="linearGradient4892"> - <stop - style="stop-color:#000000;stop-opacity:1;" - offset="0" - id="stop4894" /> - <stop - id="stop4900" - offset="0.5" - style="stop-color:#595d56;stop-opacity:0;" /> - <stop - style="stop-color:#000000;stop-opacity:1;" - offset="1" - id="stop4896" /> - </linearGradient> - <linearGradient - y2="55.193817" - x2="226.5752" - y1="55.193817" - x1="165.0148" - gradientTransform="matrix(0.68778076,0,0,0.68778076,61.130961,17.232572)" - gradientUnits="userSpaceOnUse" - id="linearGradient5008" - xlink:href="#linearGradient4892" - inkscape:collect="always" /> - </defs> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - transform="translate(0,-1004.3622)" - id="layer1" - inkscape:groupmode="layer" - inkscape:label="Livello 1"> - <g - id="g3891" - transform="matrix(0.02092262,0,0,0.02092262,47.215663,982.03701)"> - <path - style="fill:#fa0000;fill-opacity:1;stroke:none" - d="m -684.61499,2674.2647 0,170.9884 -318.88181,0 520.49211,515.9507 520.492204,-515.9507 -318.881804,0 0,-170.9884 -403.2207,0 z" - id="path5146" - inkscape:connector-curvature="0" /> - <g - transform="matrix(5.0625238,0,0,5.0625238,-2302.2429,918.07499)" - id="g4912"> - <g - transform="matrix(-1,0,0,1,391.56935,0.01189)" - id="g4860"> - <g - id="g4862"> - <path - sodipodi:type="arc" - style="fill:url(#radialGradient4872);fill-opacity:1;stroke:none" - id="path4864" - sodipodi:cx="270.45764" - sodipodi:cy="499.72882" - sodipodi:rx="94.728813" - sodipodi:ry="41.18644" - d="m 365.18645,499.72882 c 0,22.74664 -42.41153,41.18644 -94.72881,41.18644 -52.31728,0 -94.72881,-18.4398 -94.72881,-41.18644 0,-22.74664 42.41153,-41.18644 94.72881,-41.18644 52.31728,0 94.72881,18.4398 94.72881,41.18644 z" - transform="matrix(-0.67149758,0,0,0.67149758,254.22789,-33.552002)" /> - <path - style="fill:none;stroke:#d07200;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" - d="M 130.40221,313.59159 76.506851,145.58186 16.670251,315.18688" - id="path4866" - inkscape:connector-curvature="0" /> - <path - style="fill:none;stroke:#d07200;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" - d="m 76.506851,145.58186 -3.8906,128.77624" - id="path4868" - inkscape:connector-curvature="0" /> - <path - inkscape:connector-curvature="0" - id="path4870" - d="M 9.375,299.125 C 9.1474242,300.07637 9,301.02245 9,302 c 0,15.27432 28.494074,27.65625 63.625,27.65625 35.13093,0 63.59375,-12.38193 63.59375,-27.65625 0,-0.97755 -0.11617,-1.92363 -0.34375,-2.875 -3.35909,13.89991 -30.39038,24.71875 -63.25,24.71875 -32.85962,0 -59.890909,-10.81884 -63.25,-24.71875 z" - style="opacity:0.5;fill:url(#linearGradient4874);fill-opacity:1;stroke:none" /> - </g> - </g> - <path - transform="translate(-74.678308,-139.57606)" - sodipodi:type="arc" - style="fill:url(#radialGradient4910);fill-opacity:1;stroke:#000023;stroke-width:3;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" - id="path3344" - sodipodi:cx="270.45764" - sodipodi:cy="499.72882" - sodipodi:rx="94.728813" - sodipodi:ry="41.18644" - d="m 365.18645,499.72882 c 0,22.74664 -42.41153,41.18644 -94.72881,41.18644 -52.31728,0 -94.72881,-18.4398 -94.72881,-41.18644 0,-22.74664 42.41153,-41.18644 94.72881,-41.18644 52.31728,0 94.72881,18.4398 94.72881,41.18644 z" /> - <path - inkscape:connector-curvature="0" - id="rect3350" - d="m 76.5,126.5 0,19.09375 c 0,-4.64658 6.52034,-8.4375 14.5625,-8.4375 8.04215,0 14.5625,3.79092 14.5625,8.4375 l 180.3125,0 c 0,-4.64658 6.52035,-8.4375 14.5625,-8.4375 8.04215,0 14.5625,3.79092 14.5625,8.4375 l 0,-19.09375 -238.5625,0 z" - style="fill:#d07200;fill-opacity:1;stroke:none" /> - <path - d="m 365.18645,499.72882 c 0,22.74664 -42.41153,41.18644 -94.72881,41.18644 -52.31728,0 -94.72881,-18.4398 -94.72881,-41.18644 0,-22.74664 42.41153,-41.18644 94.72881,-41.18644 52.31728,0 94.72881,18.4398 94.72881,41.18644 z" - sodipodi:ry="41.18644" - sodipodi:rx="94.728813" - sodipodi:cy="499.72882" - sodipodi:cx="270.45764" - id="path4785" - style="opacity:0.5;fill:url(#linearGradient4793);fill-opacity:1;stroke:none" - sodipodi:type="arc" - transform="translate(-74.678303,-139.57606)" /> - <g - id="g3358"> - <g - id="g4832"> - <path - transform="matrix(-0.67149758,0,0,0.67149758,254.22789,-33.552002)" - d="m 365.18645,499.72882 c 0,22.74664 -42.41153,41.18644 -94.72881,41.18644 -52.31728,0 -94.72881,-18.4398 -94.72881,-41.18644 0,-22.74664 42.41153,-41.18644 94.72881,-41.18644 52.31728,0 94.72881,18.4398 94.72881,41.18644 z" - sodipodi:ry="41.18644" - sodipodi:rx="94.728813" - sodipodi:cy="499.72882" - sodipodi:cx="270.45764" - id="path3388" - style="fill:url(#radialGradient4813);fill-opacity:1;stroke:none" - sodipodi:type="arc" /> - <path - inkscape:connector-curvature="0" - id="path3390" - d="M 130.40221,313.59159 76.506851,145.58186 16.670251,315.18688" - style="fill:none;stroke:#d07200;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> - <path - inkscape:connector-curvature="0" - id="path3392" - d="m 76.506851,145.58186 -3.8906,128.77624" - style="fill:none;stroke:#d07200;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> - <path - inkscape:connector-curvature="0" - style="opacity:0.5;fill:url(#linearGradient4856);fill-opacity:1;stroke:none" - d="M 9.375,299.125 C 9.1474242,300.07637 9,301.02245 9,302 c 0,15.27432 28.494074,27.65625 63.625,27.65625 35.13093,0 63.59375,-12.38193 63.59375,-27.65625 0,-0.97755 -0.11617,-1.92363 -0.34375,-2.875 -3.35909,13.89991 -30.39038,24.71875 -63.25,24.71875 -32.85962,0 -59.890909,-10.81884 -63.25,-24.71875 z" - id="path4821" /> - </g> - </g> - <rect - y="51.71209" - x="179.76239" - height="308.44067" - width="32.033897" - id="rect3340" - style="fill:url(#linearGradient4801);fill-opacity:1;stroke:none" /> - <path - inkscape:connector-curvature="0" - id="path4876" - d="m 195.7894,29.424271 c -8.89509,0 -16.09832,7.214351 -16.09832,16.109438 0,0.172769 0.005,0.339601 0.0111,0.511058 -3.16547,3.699588 -5.07725,8.503386 -5.07725,13.754126 0,11.690687 9.47378,21.16447 21.16447,21.16447 11.69069,0 21.17558,-9.473783 21.17558,-21.16447 0,-5.25074 -1.91178,-10.054538 -5.07725,-13.754126 0.005,-0.171457 0.0111,-0.338289 0.0111,-0.511058 0,-8.895087 -7.21435,-16.109438 -16.10943,-16.109438 z" - style="fill:url(#radialGradient4888);fill-opacity:1;stroke:none" /> - <path - style="opacity:0.35;fill:url(#linearGradient5008);fill-opacity:1;stroke:none" - d="m 195.7894,29.424271 c -8.89509,0 -16.09832,7.214351 -16.09832,16.109438 0,0.172769 0.005,0.339601 0.0111,0.511058 -3.16547,3.699588 -5.07725,8.503386 -5.07725,13.754126 0,11.690687 9.47378,21.16447 21.16447,21.16447 11.69069,0 21.17558,-9.473783 21.17558,-21.16447 0,-5.25074 -1.91178,-10.054538 -5.07725,-13.754126 0.005,-0.171457 0.0111,-0.338289 0.0111,-0.511058 0,-8.895087 -7.21435,-16.109438 -16.10943,-16.109438 z" - id="path4890" - inkscape:connector-curvature="0" /> - </g> - </g> - </g> -</svg> diff --git a/files/opencs/scalable/startup/configure.svgz b/files/opencs/scalable/startup/configure.svgz Binary files differnew file mode 100644 index 0000000000..1275ec53fa --- /dev/null +++ b/files/opencs/scalable/startup/configure.svgz diff --git a/files/opencs/scalable/startup/create-addon.svgz b/files/opencs/scalable/startup/create-addon.svgz Binary files differnew file mode 100644 index 0000000000..75425667bd --- /dev/null +++ b/files/opencs/scalable/startup/create-addon.svgz diff --git a/files/opencs/scalable/startup/edit-content.svgz b/files/opencs/scalable/startup/edit-content.svgz Binary files differnew file mode 100644 index 0000000000..049f1e8132 --- /dev/null +++ b/files/opencs/scalable/startup/edit-content.svgz diff --git a/files/opencs/scalable/startup/new-game.svgz b/files/opencs/scalable/startup/new-game.svgz Binary files differnew file mode 100644 index 0000000000..e815719111 --- /dev/null +++ b/files/opencs/scalable/startup/new-game.svgz diff --git a/files/opencs/script.png b/files/opencs/script.png Binary files differnew file mode 100644 index 0000000000..297da40210 --- /dev/null +++ b/files/opencs/script.png diff --git a/files/opencs/skill.png b/files/opencs/skill.png Binary files differnew file mode 100644 index 0000000000..418f4f35c2 --- /dev/null +++ b/files/opencs/skill.png diff --git a/files/opencs/sound.png b/files/opencs/sound.png Binary files differnew file mode 100644 index 0000000000..b072acf767 --- /dev/null +++ b/files/opencs/sound.png diff --git a/files/opencs/soundgen.png b/files/opencs/soundgen.png Binary files differnew file mode 100644 index 0000000000..222fc4c7fe --- /dev/null +++ b/files/opencs/soundgen.png diff --git a/files/opencs/spell.png b/files/opencs/spell.png Binary files differnew file mode 100644 index 0000000000..69c8971805 --- /dev/null +++ b/files/opencs/spell.png diff --git a/files/opencs/static.png b/files/opencs/static.png Binary files differindex b53be12d9a..aedf2d30ee 100755..100644 --- a/files/opencs/static.png +++ b/files/opencs/static.png diff --git a/files/opencs/weapon.png b/files/opencs/weapon.png Binary files differindex 3d4b534661..3d4b534661 100755..100644 --- a/files/opencs/weapon.png +++ b/files/opencs/weapon.png diff --git a/files/openmw.cfg.local b/files/openmw.cfg.local index dd116e1080..d6ca2d5549 100644 --- a/files/openmw.cfg.local +++ b/files/openmw.cfg.local @@ -1,2 +1,5 @@ +data="?global?data" +data="?mw?Data Files" data=./data +data-local="?user?data" resources=./resources diff --git a/files/ui/contentselector.ui b/files/ui/contentselector.ui new file mode 100644 index 0000000000..b9b5ba5a03 --- /dev/null +++ b/files/ui/contentselector.ui @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ContentSelector</class> + <widget class="QWidget" name="ContentSelector"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>518</width> + <height>436</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="contextMenuPolicy"> + <enum>Qt::DefaultContextMenu</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QGroupBox" name="contentGroupBox"> + <property name="title"> + <string>Content</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="bottomMargin"> + <number>3</number> + </property> + <item> + <widget class="ContentSelectorView::ComboBox" name="gameFileView"> + <property name="editable"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QTableView" name="addonView"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="contextMenuPolicy"> + <enum>Qt::DefaultContextMenu</enum> + </property> + <property name="acceptDrops"> + <bool>true</bool> + </property> + <property name="editTriggers"> + <set>QAbstractItemView::NoEditTriggers</set> + </property> + <property name="dragEnabled"> + <bool>true</bool> + </property> + <property name="dragDropOverwriteMode"> + <bool>false</bool> + </property> + <property name="dragDropMode"> + <enum>QAbstractItemView::DragDrop</enum> + </property> + <property name="defaultDropAction"> + <enum>Qt::MoveAction</enum> + </property> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="selectionMode"> + <enum>QAbstractItemView::SingleSelection</enum> + </property> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + <property name="textElideMode"> + <enum>Qt::ElideLeft</enum> + </property> + <property name="showGrid"> + <bool>false</bool> + </property> + <attribute name="horizontalHeaderVisible"> + <bool>false</bool> + </attribute> + <attribute name="horizontalHeaderStretchLastSection"> + <bool>true</bool> + </attribute> + <attribute name="verticalHeaderVisible"> + <bool>false</bool> + </attribute> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>ContentSelectorView::ComboBox</class> + <extends>QComboBox</extends> + <header>components/contentselector/view/combobox.hpp</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 041a9576d0..eb5ebc61d6 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -7,108 +7,97 @@ <x>0</x> <y>0</y> <width>518</width> - <height>304</height> + <height>108</height> </rect> </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="contextMenuPolicy"> + <enum>Qt::DefaultContextMenu</enum> + </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> - <layout class="QHBoxLayout" name="filterLayout"> - <item> - <spacer name="horizontalSpacer"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> - </property> - </spacer> - </item> - <item> - <widget class="QLabel" name="filterLabel"> - <property name="text"> - <string>Filter:</string> - </property> - </widget> - </item> - <item> - <widget class="LineEdit" name="filterLineEdit"/> - </item> - </layout> + <widget class="QWidget" name="contentSelectorWidget" native="true"/> </item> <item> - <widget class="QSplitter" name="splitter"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> + <widget class="QGroupBox" name="profileGroupBox"> + <property name="focusPolicy"> + <enum>Qt::NoFocus</enum> </property> - <property name="orientation"> - <enum>Qt::Horizontal</enum> + <property name="title"> + <string>Profile</string> </property> - <property name="childrenCollapsible"> + <property name="flat"> <bool>false</bool> </property> - <widget class="QTableView" name="mastersTable"/> - <widget class="QTableView" name="pluginsTable"/> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <property name="spacing"> + <number>6</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>6</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>6</number> + </property> + <item> + <widget class="ProfilesComboBox" name="profilesComboBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Select a profiile</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="newProfileButton"> + <property name="toolTip"> + <string>New Profile</string> + </property> + <property name="text"> + <string>&New Profile</string> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="deleteProfileButton"> + <property name="toolTip"> + <string>Delete Profile</string> + </property> + <property name="text"> + <string>Delete Profile</string> + </property> + <property name="shortcut"> + <string>Ctrl+D</string> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> </widget> </item> - <item> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <widget class="QLabel" name="profileLabel"> - <property name="text"> - <string>Current Profile:</string> - </property> - </widget> - </item> - <item> - <widget class="ProfilesComboBox" name="profilesComboBox"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="sizePolicy"> - <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="newProfileButton"> - <property name="toolTip"> - <string>New Profile</string> - </property> - <property name="text"> - <string>&New Profile</string> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - <item> - <widget class="QToolButton" name="deleteProfileButton"> - <property name="toolTip"> - <string>Delete Profile</string> - </property> - <property name="text"> - <string>Delete Profile</string> - </property> - <property name="shortcut"> - <string>Ctrl+D</string> - </property> - <property name="autoRaise"> - <bool>true</bool> - </property> - </widget> - </item> - </layout> - </item> </layout> <action name="newProfileAction"> <property name="icon"> @@ -127,6 +116,9 @@ </property> </action> <action name="deleteProfileAction"> + <property name="enabled"> + <bool>false</bool> + </property> <property name="icon"> <iconset theme="edit-delete"> <normaloff/> @@ -140,6 +132,9 @@ </property> </action> <action name="checkAction"> + <property name="checkable"> + <bool>true</bool> + </property> <property name="text"> <string>Check Selection</string> </property> @@ -152,14 +147,9 @@ </widget> <customwidgets> <customwidget> - <class>LineEdit</class> - <extends>QLineEdit</extends> - <header location="global">components/fileorderlist/utils/lineedit.hpp</header> - </customwidget> - <customwidget> <class>ProfilesComboBox</class> <extends>QComboBox</extends> - <header location="global">components/fileorderlist/utils/profilescombobox.hpp</header> + <header>apps/launcher/utils/profilescombobox.hpp</header> </customwidget> </customwidgets> <resources/> diff --git a/files/ui/filedialog.ui b/files/ui/filedialog.ui new file mode 100644 index 0000000000..b3af166dab --- /dev/null +++ b/files/ui/filedialog.ui @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>FileDialog</class> + <widget class="QWidget" name="FileDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>518</width> + <height>109</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Minimum" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="contextMenuPolicy"> + <enum>Qt::DefaultContextMenu</enum> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QWidget" name="contentSelectorWidget" native="true"/> + </item> + <item> + <widget class="QGroupBox" name="projectGroupBox"> + <property name="focusPolicy"> + <enum>Qt::NoFocus</enum> + </property> + <property name="title"> + <string>Project Name</string> + </property> + <property name="flat"> + <bool>false</bool> + </property> + <layout class="QVBoxLayout" name="projectGroupBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="leftMargin"> + <number>3</number> + </property> + <property name="topMargin"> + <number>6</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>6</number> + </property> + <item> + <widget class="QWidget" name="projectWidget" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QDialogButtonBox" name="projectButtonBox"> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/libs/openengine/.gitignore b/libs/openengine/.gitignore new file mode 100644 index 0000000000..23a5e931b8 --- /dev/null +++ b/libs/openengine/.gitignore @@ -0,0 +1,3 @@ +*~ +*.o +*_test diff --git a/libs/openengine/ogre/.gitignore b/libs/openengine/ogre/.gitignore new file mode 100644 index 0000000000..3367afdbbf --- /dev/null +++ b/libs/openengine/ogre/.gitignore @@ -0,0 +1 @@ +old diff --git a/libs/openengine/ogre/lights.cpp b/libs/openengine/ogre/lights.cpp index 52aca6a705..348057b846 100644 --- a/libs/openengine/ogre/lights.cpp +++ b/libs/openengine/ogre/lights.cpp @@ -1,20 +1,11 @@ #include "lights.hpp" #include <OgreLight.h> -#include <OgreMath.h> + namespace OEngine { namespace Render { - -LightFunction::LightFunction(LightType type) - : ControllerFunction<Ogre::Real>(true) - , mType(type) - , mPhase(Ogre::Math::RangeRandom(-500.0f, +500.0f)) - , mDirection(1.0f) -{ -} - Ogre::Real LightFunction::pulseAmplitude(Ogre::Real time) { return std::sin(time); @@ -97,13 +88,6 @@ Ogre::Real LightFunction::calculate(Ogre::Real value) return brightness; } - -LightValue::LightValue(Ogre::Light *light, const Ogre::ColourValue &color) - : mTarget(light) - , mColor(color) -{ -} - Ogre::Real LightValue::getValue() const { return 0.0f; diff --git a/libs/openengine/ogre/lights.hpp b/libs/openengine/ogre/lights.hpp index c63f164255..61d09a0e65 100644 --- a/libs/openengine/ogre/lights.hpp +++ b/libs/openengine/ogre/lights.hpp @@ -3,6 +3,7 @@ #include <OgreController.h> #include <OgreColourValue.h> +#include <OgreMath.h> /* * Controller classes to handle pulsing and flicker lights @@ -30,7 +31,14 @@ namespace Render { static Ogre::Real flickerFrequency(Ogre::Real phase); public: - LightFunction(LightType type); + // MSVC needs the constructor for a class inheriting a template to be defined in header + LightFunction(LightType type) + : ControllerFunction<Ogre::Real>(true) + , mType(type) + , mPhase(Ogre::Math::RangeRandom(-500.0f, +500.0f)) + , mDirection(1.0f) + { + } virtual Ogre::Real calculate(Ogre::Real value); }; @@ -40,7 +48,12 @@ namespace Render { Ogre::ColourValue mColor; public: - LightValue(Ogre::Light *light, const Ogre::ColourValue &color); + // MSVC needs the constructor for a class inheriting a template to be defined in header + LightValue(Ogre::Light *light, const Ogre::ColourValue &color) + : mTarget(light) + , mColor(color) + { + } virtual Ogre::Real getValue() const; virtual void setValue(Ogre::Real value); diff --git a/readme.txt b/readme.txt index 7865f8dba2..afcfadea3d 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ OpenMW: A reimplementation of The Elder Scrolls III: Morrowind OpenMW is an attempt at recreating the engine for the popular role-playing game Morrowind by Bethesda Softworks. You need to own and install the original game for OpenMW to work. -Version: 0.26.0 +Version: 0.27.0 License: GPL (see GPL3.txt for more information) Website: http://www.openmw.org @@ -82,6 +82,32 @@ Allowed options: CHANGELOG +0.27.0 + +Bug #597: Assertion `dialogue->mId == id' failed in esmstore.cpp +Bug #794: incorrect display of decimal numbers +Bug #840: First-person sneaking camera height +Bug #887: Ambient sounds playing while paused +Bug #902: Problems with Polish character encoding +Bug #907: Entering third person using the mousewheel is possible even if it's impossible using the key +Bug #910: Some CDs not working correctly with Unshield installer +Bug #917: Quick character creation plugin does not work +Bug #918: Fatigue does not refill +Bug #919: The PC falls dead in Beshara - OpenMW nightly Win64 (708CDE2) +Feature #57: Acrobatics Skill +Feature #462: Editor: Start Dialogue +Feature #546: Modify ESX selector to handle new content file scheme +Feature #588: Editor: Adjust name/path of edited content files +Feature #644: Editor: Save +Feature #710: Editor: Configure script compiler context +Feature #790: God Mode +Feature #881: Editor: Allow only one instance of OpenCS +Feature #889: Editor: Record filtering +Feature #895: Extinguish torches +Feature #898: Breath meter enhancements +Feature #901: Editor: Default record filter +Feature #913: Merge --master and --plugin switches + 0.26.0 Bug #274: Inconsistencies in the terrain |