diff --git a/CMakeLists.txt b/CMakeLists.txt index 625239aa0..b85fabf52 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}") @@ -79,7 +79,6 @@ set(OENGINE_OGRE ${LIBDIR}/openengine/ogre/renderer.cpp ${LIBDIR}/openengine/ogre/fader.cpp ${LIBDIR}/openengine/ogre/lights.cpp - ${LIBDIR}/openengine/ogre/particles.cpp ${LIBDIR}/openengine/ogre/selectionbuffer.cpp ${LIBDIR}/openengine/ogre/imagerotate.cpp ) @@ -319,6 +318,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 @@ -380,7 +382,6 @@ IF(NOT WIN32 AND NOT APPLE) # Install licenses INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" ) - INSTALL(FILES "Daedric Font License.txt" DESTINATION "${LICDIR}" ) INSTALL(FILES "OFL.txt" DESTINATION "${LICDIR}" ) INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" ) ENDIF (DPKG_PROGRAM) @@ -428,7 +429,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") @@ -455,13 +456,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") @@ -471,7 +481,13 @@ if(WIN32) SET(CPACK_PACKAGE_VERSION_MAJOR ${OPENMW_VERSION_MAJOR}) SET(CPACK_PACKAGE_VERSION_MINOR ${OPENMW_VERSION_MINOR}) SET(CPACK_PACKAGE_VERSION_PATCH ${OPENMW_VERSION_RELEASE}) - SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW;omwlauncher;OpenMW Launcher") + SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW") + IF(BUILD_LAUNCHER) + SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};omwlauncher;OpenMW Launcher") + ENDIF(BUILD_LAUNCHER) + IF(BUILD_OPENCS) + SET(CPACK_PACKAGE_EXECUTABLES "${CPACK_PACKAGE_EXECUTABLES};opencs;OpenMW Construction Set") + ENDIF(BUILD_OPENCS) SET(CPACK_NSIS_CREATE_ICONS_EXTRA "CreateShortCut '\$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Readme.lnk' '\$INSTDIR\\\\readme.txt'") SET(CPACK_NSIS_DELETE_ICONS_EXTRA " !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP diff --git a/Daedric Font License.txt b/Daedric Font License.txt deleted file mode 100644 index a1553d0b0..000000000 --- a/Daedric Font License.txt +++ /dev/null @@ -1,10 +0,0 @@ -Dongle's Oblivion Daedric font set -http://www.uesp.net/wiki/Lore:Daedric_Alphabet#Daedric_Font - ---------------------------------------------------- - -This was done entirely as a personal project. Bethesda Softworks graciously granted me the permission for it. I am not connected with them in any way. -You may freely use these fonts to create anything you'd like. You may re-distribute the fonts freely, over the Internet, or by any other means. Always keep the .zip file intact, and this read me included. -Please do not modify and redistribute the fonts without my permission. -You may NOT sell any of these fonts under any circumstances. This includes putting them on compilation font CDs for sale, putting them in a "members only" pay-area of a website, or any other means of financial gain connected in ANY way with the redistribution of any of these fonts. -You have my permission to create and sell any artwork made with these fonts, however you may need to contact Bethesda Softworks before doing so. diff --git a/README_Mac.md b/README_Mac.md deleted file mode 100644 index dc3918368..000000000 --- a/README_Mac.md +++ /dev/null @@ -1,160 +0,0 @@ -#Getting OpenMW Working on OS X - -## Initial setup -First of all, clone OpenMW repo. - - $ git clone github.com/zinnschlag/openmw - -Or use your github url if you forked. - -About dependencies: I prefer not to install them globally (i. e. in /usr/local/), so I'm installing them in directory in my home directory. If OpenMW sources is in $HOME/path/openmw, I'm using $HOME/path/libs/root as prefix for boost and other libs. - -It's useful to create env var for lib install prefix: - - $ export OMW_LIB_PREFIX=$HOME/path/libs/root` - -Most of libs can be installed from [Homebrew][homebrew]. Only mpg123 needs to be installed from source (due to lack of universal compilation support). I think that some of libs can be installed from MacPorts or Fink too. - -As OpenMW currently only supports i386 architecture on OS X, denendencies also should support it. Set some env vars in current terminal: - - $ export CFLAGS="-arch i386" - $ export CXXFLAGS="-arch i386" - $ export LDFLAGS="-arch i386" - -If you close your terminal, you should set env vars again before pcoceeding to next steps! - -## Boost -Download [boost][boost] and install it with the following command: - - $ cd /path/to/boost/source - $ ./bootstrap.sh --prefix=$OMW_LIB_PREFIX - $ ./bjam --build-dir=build --layout=versioned \ - --toolset=darwin architecture=x86 address-model=32 \ - --link-shared,static --prefix=$OMW_LIB_PREFIX install - - -Alternatively you can install boost with homebrew: - - $ brew install boost --universal - -I think MacPorts also should support universal build for boost. - -## Ogre -Download [Ogre][] SDK (tested with 1.7.3), unpack it somewhere and move -`lib/Release/Ogre.framework` into `/Library/Frameworks`. - -## OIS -Download patched [OIS][] and use the XCode project provided. Be sure to set your build architecture to - `i386`. Once it built, locate built OIS.framework with Xcode and move it to `/Library/Frameworks`. - -## mpg123 -Download [MPG 123][mpg123] and build it: - - $ cd /path/to/mpg123/source - $ ./configure --prefix=$OMW_LIB_PREFIX --disable-debug \ - --disable-dependency-tracking \ - --with-optimization=4 \ - --with-audio=dummy \ - --with-default-audio=dummy \ - --with-cpu=sse_alone \ - $ make install - -## libsndfile -Download [libsndfile][] and build it: - - $ cd /path/to/libsndfile/source - $ ./configure --prefix=$OMW_LIB_PREFIX \ - --disable-dependency-tracking - $ make install - -or install with homebrew: - - $ brew install libsndfile --universal - -## Bullet -Download [Bullet][] and build it: - - $ cd /path/to/bullet/source - $ mkdir build - $ cd build - $ cmake -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=$OMW_LIB_PREFIX \ - -DBUILD_EXTRAS=OFF \ - -DBUILD_DEMOS=OFF \ - -DCMAKE_OSX_ARCHITECTURES=i386 \ - -DCMAKE_INSTALL_NAME_DIR=$OMW_LIB_RPEFIX/lib \ - -G"Unix Makefiles" ../ - $ make install - -or install with homebrew: - - $ brew install bullet --HEAD --universal - -I prefer head because 2.79 has some issue which causes OpenMW to lag. Also you can edit formula and install 2.77, which is stable and haven't mentioned issue. - -## Qt -Install [Qt][qt]. Qt SDK distributed by Nokia is not an option because it's 64 bit only, and OpenMW currently doesn't build for 64 bit on OS X. I'm installing it from Homebrew: - - $ brew install qt --universal - -## Run CMake -Generate the Makefile for OpenMW as follows and build OpenMW: - - $ mkdir /path/to/openmw/build/dir - $ cd /path/to/open/build/dir - $ cmake \ - -D CMAKE_OSX_ARCHITECTURES=i386 \ - -D OGRE_SDK=/path/to/ogre/sdk \ - -D BOOST_INCLUDEDIR=$OMW_LIB_PREFIX/include/boost-1_45 \ - -D BOOST_LIBRARYDIR=$OMW_LIB_PREFIX/lib \ - -D SNDFILE_INCLUDE_DIR=$OMW_LIB_PREFIX/include \ - -D SNDFILE_LIBRARY=$OMW_LIB_PREFIX/lib/libsndfile.a \ - -D MPG123_LIBRARY=$OMW_LIB_PREFIX/lib/libmpg123.a \ - -D MPG123_INCLUDE_DIR=$OMW_LIB_PREFIX/include \ - -D BULLET_DYNAMICS_LIBRARY=$OMW_LIB_PREFIX/lib/libBulletDynamics.a \ - -D BULLET_COLLISION_LIBRARY=$OMW_LIB_PREFIX/lib/libBulletCollision.a \ - -D BULLET_MATH_LIBRARY=$OMW_LIB_PREFIX/lib/libLinearMath.a \ - -D BULLET_SOFTBODY_LIBRARY=$OMW_LIB_PREFIX/lib/libBulletSoftBody.a \ - -D BULLET_INCLUDE_DIR=$OMW_LIB_PREFIX/include/bullet/ \ - -G "Unix Makefiles" /path/to/openmw/source/dir - $ make - -You can use `-G"Xcode"` if you prefer Xcode, or -G"Eclipse CDT4 - Unix Makefiles" -if you prefer Eclipse. You also can specify `-D CMAKE_BUILD_TYPE=Debug` for debug -build. As for CMake 2.8.7 and Xcode 4.3, Xcode generator is broken. Sadly Eclipse CDT also cannot import generated project at least on my machine. - -If all libs installed via homebrew (excluding mpg123), then command would be even simplier: - - $ cmake \ - -D CMAKE_OSX_ARCHITECTURES="i386" \ - -D OGRE_SDK=/path/to/ogre/sdk \ - -D MPG123_LIBRARY=$OMW_LIB_PREFIX/lib/libmpg123.a \ - -D MPG123_INCLUDE_DIR=$OMW_LIB_PREFIX/include \ - -G "Unix Makefiles" /path/to/openmw/source/dir - $ make - -Note for users with recent Xcode versions: you must explicitly specify what set of compilers do you use! If not, gcc will be used for C and Clang for C++. Just add this two -D's to command: `-D CMAKE_C_COMPILER=/usr/bin/clang` and `-D CMAKE_CXX_COMPILER=/usr/bin/clang` - -Note for Xcode 4.3 users: you should specify full path to used SDK, because current CMake (2.8.7) couldn't find SDKs inside Xcode app bundle: - - -D CMAKE_OSX_SYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk" - -# Run -From your build directory run: - - $ OpenMW.app/Contents/MacOS/openmw -or: - - $ open OpenMW.app -Enjoy! - -[homebrew]: https://github.com/mxcl/homebrew -[boost]: http://www.boost.org -[Ogre]: http://www.ogre3d.org -[Bullet]: http://bulletphysics.org -[OIS]: https://github.com/corristo/ois-fork -[mpg123]: http://www.mpg123.de -[libsndfile]: http://www.mega-nerd.com/libsndfile -[official website]: http://openmw.com -[Will Thimbleby's Ogre Framework]: http://www.thimbleby.net/ogre/ -[qt]: http://qt.nokia.com/ \ No newline at end of file diff --git a/apps/esmtool/esmtool.cpp b/apps/esmtool/esmtool.cpp index a60e9f0e2..6ccf9c3f3 100644 --- a/apps/esmtool/esmtool.cpp +++ b/apps/esmtool/esmtool.cpp @@ -305,14 +305,14 @@ int load(Arguments& info) info.data.author = esm.getAuthor(); info.data.description = esm.getDesc(); - info.data.masters = esm.getMasters(); + info.data.masters = esm.getGameFiles(); if (!quiet) { std::cout << "Author: " << esm.getAuthor() << std::endl << "Description: " << esm.getDesc() << std::endl << "File format version: " << esm.getFVer() << std::endl; - std::vector m = esm.getMasters(); + std::vector m = esm.getGameFiles(); if (!m.empty()) { std::cout << "Masters:" << std::endl; diff --git a/apps/launcher/CMakeLists.txt b/apps/launcher/CMakeLists.txt index 92cabffff..18c555a24 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 ) @@ -32,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) @@ -48,8 +51,11 @@ set(LAUNCHER_HEADER_MOC playpage.hpp textslotmsgbox.hpp - utils/checkablemessagebox.hpp utils/textinputdialog.hpp + utils/checkablemessagebox.hpp + utils/profilescombobox.hpp + utils/lineedit.hpp + ) if(NOT WIN32) @@ -62,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 add3dea40..734277b97 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -4,548 +4,307 @@ #include #include #include +#include #include -#include -#include -#include +#include -#include -#include -#include +#include + +#include "utils/textinputdialog.hpp" +#include "utils/profilescombobox.hpp" #include "settings/gamesettings.hpp" #include "settings/launchersettings.hpp" -#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 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 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 %0?").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 %0?").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 a0b029330..37603a210 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 -#include -#include "ui_datafilespage.h" + +#include +#include 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 97a24d54b..5df2d364c 100644 --- a/apps/launcher/graphicspage.cpp +++ b/apps/launcher/graphicspage.cpp @@ -10,14 +10,11 @@ #endif #include -#include - #include #include -#include -#include +#include #include "settings/graphicssettings.hpp" @@ -33,11 +30,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,15 +50,11 @@ 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; - logMgr->createLog((mCfgMgr.getLogPath().string() + "/launcherOgre.log"), true, false, false); - try { - mOgre = new Ogre::Root("", "", "./launcherOgre.log"); + mOgre = mOgreInit.init(mCfgMgr.getLogPath().string() + "/launcherOgre.log"); } catch(Ogre::Exception &ex) { @@ -78,40 +72,6 @@ bool GraphicsPage::setupOgre() return false; } - - std::string pluginDir; - const char* pluginEnv = getenv("OPENMW_OGRE_PLUGIN_DIR"); - if (pluginEnv) - pluginDir = pluginEnv; - else - { -#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 - pluginDir = ".\\"; -#endif -#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE - pluginDir = OGRE_PLUGIN_DIR; -#endif -#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX - pluginDir = OGRE_PLUGIN_DIR_REL; -#endif - } - - QDir dir(QString::fromStdString(pluginDir)); - pluginDir = dir.absolutePath().toStdString(); - - Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mOgre); - Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mOgre); - Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mOgre); - -#ifdef ENABLE_PLUGIN_GL - mGLPlugin = new Ogre::GLPlugin(); - mOgre->installPlugin(mGLPlugin); -#endif -#ifdef ENABLE_PLUGIN_Direct3D9 - mD3D9Plugin = new Ogre::D3D9Plugin(); - mOgre->installPlugin(mD3D9Plugin); -#endif - // Get the available renderers and put them in the combobox const Ogre::RenderSystemList &renderers = mOgre->getAvailableRenderers(); @@ -156,7 +116,7 @@ bool GraphicsPage::setupOgre() return true; } -bool GraphicsPage::setupSDL() +bool Launcher::GraphicsPage::setupSDL() { int displays = SDL_GetNumVideoDisplays(); @@ -179,7 +139,7 @@ bool GraphicsPage::setupSDL() return true; } -bool GraphicsPage::loadSettings() +bool Launcher::GraphicsPage::loadSettings() { if (!setupSDL()) return false; @@ -218,7 +178,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 +205,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 +238,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 +285,7 @@ QStringList GraphicsPage::getAvailableResolutions(int screen) return result; } -QRect GraphicsPage::getMaximumResolution() +QRect Launcher::GraphicsPage::getMaximumResolution() { QRect max; int screens = QApplication::desktop()->screenCount(); @@ -340,7 +300,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 +309,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 +317,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 +331,7 @@ void GraphicsPage::slotFullScreenChanged(int state) } } -void GraphicsPage::slotStandardToggled(bool checked) +void Launcher::GraphicsPage::slotStandardToggled(bool checked) { if (checked) { resolutionComboBox->setEnabled(true); diff --git a/apps/launcher/graphicspage.hpp b/apps/launcher/graphicspage.hpp index d233ea12e..da4cb9fb3 100644 --- a/apps/launcher/graphicspage.hpp +++ b/apps/launcher/graphicspage.hpp @@ -5,62 +5,53 @@ #include #include -//#include -//#include -// Static plugin headers -#ifdef ENABLE_PLUGIN_GL -# include "OgreGLPlugin.h" -#endif -#ifdef ENABLE_PLUGIN_Direct3D9 -# include "OgreD3D9Plugin.h" -#endif +#include + #include "ui_graphicspage.h" -class GraphicsSettings; namespace Files { struct ConfigurationManager; } -class GraphicsPage : public QWidget, private Ui::GraphicsPage +namespace Launcher { - Q_OBJECT + class GraphicsSettings; -public: - GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0); + class GraphicsPage : public QWidget, private Ui::GraphicsPage + { + Q_OBJECT - void saveSettings(); - bool loadSettings(); + public: + GraphicsPage(Files::ConfigurationManager &cfg, GraphicsSettings &graphicsSettings, QWidget *parent = 0); -public slots: - void rendererChanged(const QString &renderer); - void screenChanged(int screen); + void saveSettings(); + bool loadSettings(); -private slots: - void slotFullScreenChanged(int state); - void slotStandardToggled(bool checked); + public slots: + void rendererChanged(const QString &renderer); + void screenChanged(int screen); -private: - Ogre::Root *mOgre; - Ogre::RenderSystem *mSelectedRenderSystem; - Ogre::RenderSystem *mOpenGLRenderSystem; - Ogre::RenderSystem *mDirect3DRenderSystem; - #ifdef ENABLE_PLUGIN_GL - Ogre::GLPlugin* mGLPlugin; - #endif - #ifdef ENABLE_PLUGIN_Direct3D9 - Ogre::D3D9Plugin* mD3D9Plugin; - #endif + private slots: + void slotFullScreenChanged(int state); + void slotStandardToggled(bool checked); - Files::ConfigurationManager &mCfgMgr; - GraphicsSettings &mGraphicsSettings; + private: + OgreInit::OgreInit mOgreInit; + Ogre::Root *mOgre; + Ogre::RenderSystem *mSelectedRenderSystem; + Ogre::RenderSystem *mOpenGLRenderSystem; + Ogre::RenderSystem *mDirect3DRenderSystem; - QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer); - QStringList getAvailableResolutions(int screen); - QRect getMaximumResolution(); + Files::ConfigurationManager &mCfgMgr; + GraphicsSettings &mGraphicsSettings; - bool setupOgre(); - bool setupSDL(); -}; + QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer); + QStringList getAvailableResolutions(int screen); + QRect getMaximumResolution(); + bool setupOgre(); + bool setupSDL(); + }; +} #endif diff --git a/apps/launcher/main.cpp b/apps/launcher/main.cpp index f67f5edcf..eb8ad8507 100644 --- a/apps/launcher/main.cpp +++ b/apps/launcher/main.cpp @@ -10,8 +10,6 @@ #include #include "maindialog.hpp" -// SDL workaround -#include "graphicspage.hpp" int main(int argc, char *argv[]) { @@ -49,7 +47,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 +59,3 @@ int main(int argc, char *argv[]) SDL_Quit(); return returnValue; } - diff --git a/apps/launcher/maindialog.cpp b/apps/launcher/maindialog.cpp index 032f70916..4012a1fbd 100644 --- a/apps/launcher/maindialog.cpp +++ b/apps/launcher/maindialog.cpp @@ -1,5 +1,6 @@ #include "maindialog.hpp" +#include #include #include #include @@ -23,8 +24,8 @@ #include "graphicspage.hpp" #include "datafilespage.hpp" -MainDialog::MainDialog() - : mGameSettings(mCfgMgr) +Launcher::MainDialog::MainDialog(QWidget *parent) + : mGameSettings(mCfgMgr), QMainWindow (parent) { // Install the stylesheet font QFile file; @@ -69,7 +70,7 @@ MainDialog::MainDialog() createIcons(); } -void MainDialog::createIcons() +void Launcher::MainDialog::createIcons() { if (!QIcon::hasThemeIcon("document-new")) QIcon::setThemeName("tango"); @@ -101,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(pagesWidget->widget(previousIndex)); + DataFilesPage *currentPage = dynamic_cast(pagesWidget->widget(currentIndex)); + + //special call to update/save data files page list view when it's displayed/hidden. + if (previousPage) + { + if (previousPage->objectName() == "DataFilesPage") + previousPage->saveSettings(); + } + else if (currentPage) + { + if (currentPage->objectName() == "DataFilesPage") + currentPage->loadSettings(); + } } -bool MainDialog::setupLauncherSettings() +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("
Would you like to install expansions now ? (make sure you have the disc)
\ If you want to install both Bloodmoon and Tribunal, you have to install Tribunal first.
")); - + QAbstractButton* tribunalButton = NULL; if(!cd.TribunalDone()) tribunalButton = expansionsBox.addButton(QObject::tr("&Tribunal"), QMessageBox::ActionRole); @@ -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("
You do not have any master files selected.

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

\ + OpenMW will not start without a game file selected.
")); msgBox.exec(); return; } @@ -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 824dff6e8..5b8e4908e 100644 --- a/apps/launcher/maindialog.hpp +++ b/apps/launcher/maindialog.hpp @@ -11,57 +11,59 @@ #include "ui_mainwindow.h" -class QListWidget; class QListWidgetItem; -class QStackedWidget; -class QStringList; -class QStringListModel; -class QString; -class PlayPage; -class GraphicsPage; -class DataFilesPage; - -class MainDialog : public QMainWindow, private Ui::MainWindow +namespace Launcher { - Q_OBJECT + class PlayPage; + class GraphicsPage; + class DataFilesPage; + class UnshieldThread; + +#ifndef WIN32 + bool expansions(Launcher::UnshieldThread& cd); +#endif -public: - MainDialog(); - bool setup(); - bool showFirstRunDialog(); + class MainDialog : public QMainWindow, private Ui::MainWindow + { + Q_OBJECT -public slots: - void changePage(QListWidgetItem *current, QListWidgetItem *previous); - void play(); + public: + explicit MainDialog(QWidget *parent = 0); + bool setup(); + bool showFirstRunDialog(); -private: - void createIcons(); - void createPages(); + public slots: + void changePage(QListWidgetItem *current, QListWidgetItem *previous); + void play(); - bool setupLauncherSettings(); - bool setupGameSettings(); - bool setupGraphicsSettings(); + private: + void createIcons(); + void createPages(); - void loadSettings(); - void saveSettings(); - bool writeSettings(); + bool setupLauncherSettings(); + bool setupGameSettings(); + bool setupGraphicsSettings(); - inline bool startProgram(const QString &name, bool detached = false) { return startProgram(name, QStringList(), detached); } - bool startProgram(const QString &name, const QStringList &arguments, bool detached = false); + void loadSettings(); + void saveSettings(); + bool writeSettings(); - void closeEvent(QCloseEvent *event); + inline bool startProgram(const QString &name, bool detached = false) { return startProgram(name, QStringList(), detached); } + bool startProgram(const QString &name, const QStringList &arguments, bool detached = false); - PlayPage *mPlayPage; - GraphicsPage *mGraphicsPage; - DataFilesPage *mDataFilesPage; + void closeEvent(QCloseEvent *event); - Files::ConfigurationManager mCfgMgr; + PlayPage *mPlayPage; + GraphicsPage *mGraphicsPage; + DataFilesPage *mDataFilesPage; - GameSettings mGameSettings; - GraphicsSettings mGraphicsSettings; - LauncherSettings mLauncherSettings; + Files::ConfigurationManager mCfgMgr; -}; + GameSettings mGameSettings; + GraphicsSettings mGraphicsSettings; + LauncherSettings mLauncherSettings; + }; +} #endif diff --git a/apps/launcher/playpage.cpp b/apps/launcher/playpage.cpp index 46900c595..6cfb9686f 100644 --- a/apps/launcher/playpage.cpp +++ b/apps/launcher/playpage.cpp @@ -6,8 +6,9 @@ #include #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 4306396bd..1dc5bb0fe 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 205879bc3..41113c35a 100644 --- a/apps/launcher/settings/gamesettings.cpp +++ b/apps/launcher/settings/gamesettings.cpp @@ -9,6 +9,7 @@ #include #include + /** * Workaround for problems with whitespaces in paths in older versions of Boost library */ @@ -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 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 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 55b2107e2..60236200a 100644 --- a/apps/launcher/settings/gamesettings.hpp +++ b/apps/launcher/settings/gamesettings.hpp @@ -8,55 +8,61 @@ #include -namespace Files { typedef std::vector PathContainer; - struct ConfigurationManager;} - -class GameSettings +namespace Files { -public: - GameSettings(Files::ConfigurationManager &cfg); - ~GameSettings(); + typedef std::vector 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 mSettings; + private: + Files::ConfigurationManager &mCfgMgr; - QStringList mDataDirs; - QString mDataLocal; -}; + void validatePaths(); + QMap mSettings; + QStringList mDataDirs; + QString mDataLocal; + }; +} #endif // GAMESETTINGS_HPP diff --git a/apps/launcher/settings/graphicssettings.cpp b/apps/launcher/settings/graphicssettings.cpp index 0c5580091..9dad3dee6 100644 --- a/apps/launcher/settings/graphicssettings.cpp +++ b/apps/launcher/settings/graphicssettings.cpp @@ -5,15 +5,15 @@ #include #include -GraphicsSettings::GraphicsSettings() +Launcher::GraphicsSettings::GraphicsSettings() { } -GraphicsSettings::~GraphicsSettings() +Launcher::GraphicsSettings::~GraphicsSettings() { } -bool GraphicsSettings::writeFile(QTextStream &stream) +bool Launcher::GraphicsSettings::writeFile(QTextStream &stream) { QString sectionPrefix; QRegExp sectionRe("([^/]+)/(.+)$"); diff --git a/apps/launcher/settings/graphicssettings.hpp b/apps/launcher/settings/graphicssettings.hpp index 3e8617849..6f7c13547 100644 --- a/apps/launcher/settings/graphicssettings.hpp +++ b/apps/launcher/settings/graphicssettings.hpp @@ -3,14 +3,16 @@ #include "settingsbase.hpp" -class GraphicsSettings : public SettingsBase > +namespace Launcher { -public: - GraphicsSettings(); - ~GraphicsSettings(); + class GraphicsSettings : public SettingsBase > + { + public: + GraphicsSettings(); + ~GraphicsSettings(); - bool writeFile(QTextStream &stream); - -}; + bool writeFile(QTextStream &stream); + }; +} #endif // GRAPHICSSETTINGS_HPP diff --git a/apps/launcher/settings/launchersettings.cpp b/apps/launcher/settings/launchersettings.cpp index 5d298e814..705453555 100644 --- a/apps/launcher/settings/launchersettings.cpp +++ b/apps/launcher/settings/launchersettings.cpp @@ -5,15 +5,17 @@ #include #include -LauncherSettings::LauncherSettings() +#include + +Launcher::LauncherSettings::LauncherSettings() { } -LauncherSettings::~LauncherSettings() +Launcher::LauncherSettings::~LauncherSettings() { } -QStringList LauncherSettings::values(const QString &key, Qt::MatchFlags flags) +QStringList Launcher::LauncherSettings::values(const QString &key, Qt::MatchFlags flags) { QMap settings = SettingsBase::getSettings(); @@ -34,7 +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 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 60c6f86bc..8acc389a9 100644 --- a/apps/launcher/settings/launchersettings.hpp +++ b/apps/launcher/settings/launchersettings.hpp @@ -3,17 +3,19 @@ #include "settingsbase.hpp" -class LauncherSettings : public SettingsBase > +namespace Launcher { -public: - LauncherSettings(); - ~LauncherSettings(); + class LauncherSettings : public SettingsBase > + { + public: + LauncherSettings(); + ~LauncherSettings(); - QStringList subKeys(const QString &key); - QStringList values(const QString &key, Qt::MatchFlags flags = Qt::MatchExactly); + QStringList subKeys(const QString &key); + QStringList values(const QString &key, Qt::MatchFlags flags = Qt::MatchExactly); - bool writeFile(QTextStream &stream); - -}; + bool writeFile(QTextStream &stream); + }; +} #endif // LAUNCHERSETTINGS_HPP diff --git a/apps/launcher/settings/settingsbase.hpp b/apps/launcher/settings/settingsbase.hpp index ed8ada56c..3a1cf8e30 100644 --- a/apps/launcher/settings/settingsbase.hpp +++ b/apps/launcher/settings/settingsbase.hpp @@ -7,103 +7,105 @@ #include #include -template -class SettingsBase +namespace Launcher { + template + class SettingsBase + { -public: - SettingsBase() { mMultiValue = false; } - ~SettingsBase() {} + public: + SettingsBase() { mMultiValue = false; } + ~SettingsBase() {} - inline QString value(const QString &key, const QString &defaultValue = QString()) - { - return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key); - } + inline QString value(const QString &key, const QString &defaultValue = QString()) + { + return mSettings.value(key).isEmpty() ? defaultValue : mSettings.value(key); + } - inline void setValue(const QString &key, const QString &value) - { - QStringList values = mSettings.values(key); - if (!values.contains(value)) - mSettings.insert(key, value); - } + inline void setValue(const QString &key, const QString &value) + { + QStringList values = mSettings.values(key); + if (!values.contains(value)) + mSettings.insert(key, value); + } - inline void setMultiValue(const QString &key, const QString &value) - { - QStringList values = mSettings.values(key); - if (!values.contains(value)) - mSettings.insertMulti(key, value); - } + inline void setMultiValue(const QString &key, const QString &value) + { + QStringList values = mSettings.values(key); + if (!values.contains(value)) + mSettings.insertMulti(key, value); + } - inline void setMultiValueEnabled(bool enable) - { - mMultiValue = enable; - } + inline void setMultiValueEnabled(bool enable) + { + mMultiValue = enable; + } - inline void remove(const QString &key) - { - mSettings.remove(key); - } + inline void remove(const QString &key) + { + mSettings.remove(key); + } - Map getSettings() {return mSettings;} + Map getSettings() {return mSettings;} - bool readFile(QTextStream &stream) - { - mCache.clear(); + bool readFile(QTextStream &stream) + { + mCache.clear(); - QString sectionPrefix; + QString sectionPrefix; - QRegExp sectionRe("^\\[([^]]+)\\]"); - QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); + QRegExp sectionRe("^\\[([^]]+)\\]"); + QRegExp keyRe("^([^=]+)\\s*=\\s*(.+)$"); - while (!stream.atEnd()) { - QString line = stream.readLine(); + while (!stream.atEnd()) { + QString line = stream.readLine(); - if (line.isEmpty() || line.startsWith("#")) - continue; + if (line.isEmpty() || line.startsWith("#")) + continue; - if (sectionRe.exactMatch(line)) { - sectionPrefix = sectionRe.cap(1); - sectionPrefix.append("/"); - continue; - } + if (sectionRe.exactMatch(line)) { + sectionPrefix = sectionRe.cap(1); + sectionPrefix.append("/"); + continue; + } - if (keyRe.indexIn(line) != -1) { + if (keyRe.indexIn(line) != -1) { - QString key = keyRe.cap(1).trimmed(); - QString value = keyRe.cap(2).trimmed(); + QString key = keyRe.cap(1).trimmed(); + QString value = keyRe.cap(2).trimmed(); - if (!sectionPrefix.isEmpty()) - key.prepend(sectionPrefix); + if (!sectionPrefix.isEmpty()) + key.prepend(sectionPrefix); - mSettings.remove(key); + mSettings.remove(key); - QStringList values = mCache.values(key); + QStringList values = mCache.values(key); - if (!values.contains(value)) { - if (mMultiValue) { - mCache.insertMulti(key, value); - } else { - mCache.insert(key, value); + if (!values.contains(value)) { + if (mMultiValue) { + mCache.insertMulti(key, value); + } else { + mCache.insert(key, value); + } } } } - } - if (mSettings.isEmpty()) { - mSettings = mCache; // This is the first time we read a file + if (mSettings.isEmpty()) { + mSettings = mCache; // This is the first time we read a file + return true; + } + + // Merge the changed keys with those which didn't + mSettings.unite(mCache); return true; } - // Merge the changed keys with those which didn't - mSettings.unite(mCache); - return true; - } - -private: - Map mSettings; - Map mCache; - - bool mMultiValue; -}; + private: + Map mSettings; + Map mCache; + bool mMultiValue; + }; +} #endif // SETTINGSBASE_HPP diff --git a/apps/launcher/textslotmsgbox.cpp b/apps/launcher/textslotmsgbox.cpp index 0607d1cc6..62d9cf576 100644 --- a/apps/launcher/textslotmsgbox.cpp +++ b/apps/launcher/textslotmsgbox.cpp @@ -1,6 +1,6 @@ #include "textslotmsgbox.hpp" -void TextSlotMsgBox::setTextSlot(const QString& string) +void Launcher::TextSlotMsgBox::setTextSlot(const QString& string) { setText(string); } diff --git a/apps/launcher/textslotmsgbox.hpp b/apps/launcher/textslotmsgbox.hpp index a29e2c354..a0fefaa25 100644 --- a/apps/launcher/textslotmsgbox.hpp +++ b/apps/launcher/textslotmsgbox.hpp @@ -3,11 +3,13 @@ #include -class TextSlotMsgBox : public QMessageBox +namespace Launcher { -Q_OBJECT - public slots: - void setTextSlot(const QString& string); -}; - + class TextSlotMsgBox : public QMessageBox + { + Q_OBJECT + public slots: + void setTextSlot(const QString& string); + }; +} #endif diff --git a/apps/launcher/unshieldthread.cpp b/apps/launcher/unshieldthread.cpp index 69b241365..d0dbeb1bd 100644 --- a/apps/launcher/unshieldthread.cpp +++ b/apps/launcher/unshieldthread.cpp @@ -292,30 +292,30 @@ namespace } -bool UnshieldThread::SetMorrowindPath(const std::string& path) +bool Launcher::UnshieldThread::SetMorrowindPath(const std::string& path) { mMorrowindPath = path; return true; } -bool UnshieldThread::SetTribunalPath(const std::string& path) +bool Launcher::UnshieldThread::SetTribunalPath(const std::string& path) { mTribunalPath = path; return true; } -bool UnshieldThread::SetBloodmoonPath(const std::string& path) +bool Launcher::UnshieldThread::SetBloodmoonPath(const std::string& path) { mBloodmoonPath = path; return true; } -void UnshieldThread::SetOutputPath(const std::string& path) +void Launcher::UnshieldThread::SetOutputPath(const std::string& path) { mOutputPath = path; } -bool UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, const char* prefix, int index) +bool Launcher::UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, const char* prefix, int index) { bool success; bfs::path dirname; @@ -349,7 +349,7 @@ bool UnshieldThread::extract_file(Unshield* unshield, bfs::path output_dir, cons return success; } -void UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_dir, bool extract_ini) +void Launcher::UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_dir, bool extract_ini) { Unshield * unshield; unshield = unshield_open(cab.c_str()); @@ -369,7 +369,7 @@ void UnshieldThread::extract_cab(const bfs::path& cab, const bfs::path& output_d } -bool UnshieldThread::extract() +bool Launcher::UnshieldThread::extract() { bfs::path outputDataFilesDir = mOutputPath; outputDataFilesDir /= "Data Files"; @@ -475,7 +475,7 @@ bool UnshieldThread::extract() return true; } -void UnshieldThread::Done() +void Launcher::UnshieldThread::Done() { // Get rid of unnecessary files bfs::remove_all(mOutputPath / "extract-temp"); @@ -491,28 +491,28 @@ void UnshieldThread::Done() bfs::last_write_time(findFile(mOutputPath, "bloodmoon.esm"), getTime("3 June 2003")); } -std::string UnshieldThread::GetMWEsmPath() +std::string Launcher::UnshieldThread::GetMWEsmPath() { return findFile(mOutputPath / "Data Files", "morrowind.esm").string(); } -bool UnshieldThread::TribunalDone() +bool Launcher::UnshieldThread::TribunalDone() { return mTribunalDone; } -bool UnshieldThread::BloodmoonDone() +bool Launcher::UnshieldThread::BloodmoonDone() { return mBloodmoonDone; } -void UnshieldThread::run() +void Launcher::UnshieldThread::run() { extract(); emit close(); } -UnshieldThread::UnshieldThread() +Launcher::UnshieldThread::UnshieldThread() { unshield_set_log_level(0); mMorrowindDone = false; diff --git a/apps/launcher/unshieldthread.hpp b/apps/launcher/unshieldthread.hpp index 655cb5b53..de6a32b44 100644 --- a/apps/launcher/unshieldthread.hpp +++ b/apps/launcher/unshieldthread.hpp @@ -7,50 +7,52 @@ #include -class UnshieldThread : public QThread +namespace Launcher { - Q_OBJECT + class UnshieldThread : public QThread + { + Q_OBJECT - public: - bool SetMorrowindPath(const std::string& path); - bool SetTribunalPath(const std::string& path); - bool SetBloodmoonPath(const std::string& path); + public: + bool SetMorrowindPath(const std::string& path); + bool SetTribunalPath(const std::string& path); + bool SetBloodmoonPath(const std::string& path); - void SetOutputPath(const std::string& path); - - bool extract(); + void SetOutputPath(const std::string& path); - bool TribunalDone(); - bool BloodmoonDone(); + bool extract(); - void Done(); + bool TribunalDone(); + bool BloodmoonDone(); - std::string GetMWEsmPath(); + void Done(); - UnshieldThread(); + std::string GetMWEsmPath(); - private: + UnshieldThread(); - void extract_cab(const boost::filesystem::path& cab, const boost::filesystem::path& output_dir, bool extract_ini = false); - bool extract_file(Unshield* unshield, boost::filesystem::path output_dir, const char* prefix, int index); - - boost::filesystem::path mMorrowindPath; - boost::filesystem::path mTribunalPath; - boost::filesystem::path mBloodmoonPath; + private: - bool mMorrowindDone; - bool mTribunalDone; - bool mBloodmoonDone; + void extract_cab(const boost::filesystem::path& cab, const boost::filesystem::path& output_dir, bool extract_ini = false); + bool extract_file(Unshield* unshield, boost::filesystem::path output_dir, const char* prefix, int index); - boost::filesystem::path mOutputPath; + boost::filesystem::path mMorrowindPath; + boost::filesystem::path mTribunalPath; + boost::filesystem::path mBloodmoonPath; + bool mMorrowindDone; + bool mTribunalDone; + bool mBloodmoonDone; - protected: - virtual void run(); + boost::filesystem::path mOutputPath; - signals: - void signalGUI(QString); - void close(); -}; + protected: + virtual void run(); + + signals: + void signalGUI(QString); + void close(); + }; +} #endif diff --git a/apps/launcher/utils/checkablemessagebox.cpp b/apps/launcher/utils/checkablemessagebox.cpp index 41207a8de..2f775af57 100644 --- a/apps/launcher/utils/checkablemessagebox.cpp +++ b/apps/launcher/utils/checkablemessagebox.cpp @@ -54,72 +54,61 @@ Emulates the QMessageBox API with static conveniences. The message label can open external URLs. */ - -class CheckableMessageBoxPrivate -{ -public: - CheckableMessageBoxPrivate(QDialog *q) +Launcher::CheckableMessageBoxPrivate::CheckableMessageBoxPrivate(QDialog *q) : clickedButton(0) - { - QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); - - pixmapLabel = new QLabel(q); - sizePolicy.setHorizontalStretch(0); - sizePolicy.setVerticalStretch(0); - sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth()); - pixmapLabel->setSizePolicy(sizePolicy); - pixmapLabel->setVisible(false); - - QSpacerItem *pixmapSpacer = - new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); - - messageLabel = new QLabel(q); - messageLabel->setMinimumSize(QSize(300, 0)); - messageLabel->setWordWrap(true); - messageLabel->setOpenExternalLinks(true); - messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse); - - QSpacerItem *checkBoxRightSpacer = - new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); - QSpacerItem *buttonSpacer = - new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum); - - checkBox = new QCheckBox(q); - checkBox->setText(CheckableMessageBox::tr("Do not ask again")); - - buttonBox = new QDialogButtonBox(q); - buttonBox->setOrientation(Qt::Horizontal); - buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); - - QVBoxLayout *verticalLayout = new QVBoxLayout(); - verticalLayout->addWidget(pixmapLabel); - verticalLayout->addItem(pixmapSpacer); - - QHBoxLayout *horizontalLayout_2 = new QHBoxLayout(); - horizontalLayout_2->addLayout(verticalLayout); - horizontalLayout_2->addWidget(messageLabel); - - QHBoxLayout *horizontalLayout = new QHBoxLayout(); - horizontalLayout->addWidget(checkBox); - horizontalLayout->addItem(checkBoxRightSpacer); - - QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q); - verticalLayout_2->addLayout(horizontalLayout_2); - verticalLayout_2->addLayout(horizontalLayout); - verticalLayout_2->addItem(buttonSpacer); - verticalLayout_2->addWidget(buttonBox); - } - - QLabel *pixmapLabel; - QLabel *messageLabel; - QCheckBox *checkBox; - QDialogButtonBox *buttonBox; - QAbstractButton *clickedButton; -}; +{ + QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + + pixmapLabel = new QLabel(q); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth()); + pixmapLabel->setSizePolicy(sizePolicy); + pixmapLabel->setVisible(false); + + QSpacerItem *pixmapSpacer = + new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); + + messageLabel = new QLabel(q); + messageLabel->setMinimumSize(QSize(300, 0)); + messageLabel->setWordWrap(true); + messageLabel->setOpenExternalLinks(true); + messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse); + + QSpacerItem *checkBoxRightSpacer = + new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); + QSpacerItem *buttonSpacer = + new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum); + + checkBox = new QCheckBox(q); + checkBox->setText(Launcher::CheckableMessageBox::tr("Do not ask again")); + + buttonBox = new QDialogButtonBox(q); + buttonBox->setOrientation(Qt::Horizontal); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + + QVBoxLayout *verticalLayout = new QVBoxLayout(); + verticalLayout->addWidget(pixmapLabel); + verticalLayout->addItem(pixmapSpacer); + + QHBoxLayout *horizontalLayout_2 = new QHBoxLayout(); + horizontalLayout_2->addLayout(verticalLayout); + horizontalLayout_2->addWidget(messageLabel); + + QHBoxLayout *horizontalLayout = new QHBoxLayout(); + horizontalLayout->addWidget(checkBox); + horizontalLayout->addItem(checkBoxRightSpacer); + + QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q); + verticalLayout_2->addLayout(horizontalLayout_2); + verticalLayout_2->addLayout(horizontalLayout); + verticalLayout_2->addItem(buttonSpacer); + verticalLayout_2->addWidget(buttonBox); +} -CheckableMessageBox::CheckableMessageBox(QWidget *parent) : +Launcher::CheckableMessageBox::CheckableMessageBox(QWidget *parent) : QDialog(parent), - d(new CheckableMessageBoxPrivate(this)) + d(new Launcher::CheckableMessageBoxPrivate(this)) { setModal(true); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); @@ -129,102 +118,102 @@ CheckableMessageBox::CheckableMessageBox(QWidget *parent) : SLOT(slotClicked(QAbstractButton*))); } -CheckableMessageBox::~CheckableMessageBox() +Launcher::CheckableMessageBox::~CheckableMessageBox() { delete d; } -void CheckableMessageBox::slotClicked(QAbstractButton *b) +void Launcher::CheckableMessageBox::slotClicked(QAbstractButton *b) { d->clickedButton = b; } -QAbstractButton *CheckableMessageBox::clickedButton() const +QAbstractButton *Launcher::CheckableMessageBox::clickedButton() const { return d->clickedButton; } -QDialogButtonBox::StandardButton CheckableMessageBox::clickedStandardButton() const +QDialogButtonBox::StandardButton Launcher::CheckableMessageBox::clickedStandardButton() const { if (d->clickedButton) return d->buttonBox->standardButton(d->clickedButton); return QDialogButtonBox::NoButton; } -QString CheckableMessageBox::text() const +QString Launcher::CheckableMessageBox::text() const { return d->messageLabel->text(); } -void CheckableMessageBox::setText(const QString &t) +void Launcher::CheckableMessageBox::setText(const QString &t) { d->messageLabel->setText(t); } -QPixmap CheckableMessageBox::iconPixmap() const +QPixmap Launcher::CheckableMessageBox::iconPixmap() const { if (const QPixmap *p = d->pixmapLabel->pixmap()) return QPixmap(*p); return QPixmap(); } -void CheckableMessageBox::setIconPixmap(const QPixmap &p) +void Launcher::CheckableMessageBox::setIconPixmap(const QPixmap &p) { d->pixmapLabel->setPixmap(p); d->pixmapLabel->setVisible(!p.isNull()); } -bool CheckableMessageBox::isChecked() const +bool Launcher::CheckableMessageBox::isChecked() const { return d->checkBox->isChecked(); } -void CheckableMessageBox::setChecked(bool s) +void Launcher::CheckableMessageBox::setChecked(bool s) { d->checkBox->setChecked(s); } -QString CheckableMessageBox::checkBoxText() const +QString Launcher::CheckableMessageBox::checkBoxText() const { return d->checkBox->text(); } -void CheckableMessageBox::setCheckBoxText(const QString &t) +void Launcher::CheckableMessageBox::setCheckBoxText(const QString &t) { d->checkBox->setText(t); } -bool CheckableMessageBox::isCheckBoxVisible() const +bool Launcher::CheckableMessageBox::isCheckBoxVisible() const { return d->checkBox->isVisible(); } -void CheckableMessageBox::setCheckBoxVisible(bool v) +void Launcher::CheckableMessageBox::setCheckBoxVisible(bool v) { d->checkBox->setVisible(v); } -QDialogButtonBox::StandardButtons CheckableMessageBox::standardButtons() const +QDialogButtonBox::StandardButtons Launcher::CheckableMessageBox::standardButtons() const { return d->buttonBox->standardButtons(); } -void CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s) +void Launcher::CheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s) { d->buttonBox->setStandardButtons(s); } -QPushButton *CheckableMessageBox::button(QDialogButtonBox::StandardButton b) const +QPushButton *Launcher::CheckableMessageBox::button(QDialogButtonBox::StandardButton b) const { return d->buttonBox->button(b); } -QPushButton *CheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role) +QPushButton *Launcher::CheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role) { return d->buttonBox->addButton(text, role); } -QDialogButtonBox::StandardButton CheckableMessageBox::defaultButton() const +QDialogButtonBox::StandardButton Launcher::CheckableMessageBox::defaultButton() const { foreach (QAbstractButton *b, d->buttonBox->buttons()) if (QPushButton *pb = qobject_cast(b)) @@ -233,7 +222,7 @@ QDialogButtonBox::StandardButton CheckableMessageBox::defaultButton() const return QDialogButtonBox::NoButton; } -void CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s) +void Launcher::CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s) { if (QPushButton *b = d->buttonBox->button(s)) { b->setDefault(true); @@ -242,7 +231,7 @@ void CheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s) } QDialogButtonBox::StandardButton -CheckableMessageBox::question(QWidget *parent, +Launcher::CheckableMessageBox::question(QWidget *parent, const QString &title, const QString &question, const QString &checkBoxText, @@ -263,7 +252,7 @@ CheckableMessageBox::question(QWidget *parent, return mb.clickedStandardButton(); } -QMessageBox::StandardButton CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db) +QMessageBox::StandardButton Launcher::CheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db) { return static_cast(int(db)); } diff --git a/apps/launcher/utils/checkablemessagebox.hpp b/apps/launcher/utils/checkablemessagebox.hpp index 93fd43fe1..09a501b9c 100644 --- a/apps/launcher/utils/checkablemessagebox.hpp +++ b/apps/launcher/utils/checkablemessagebox.hpp @@ -34,67 +34,83 @@ #include #include -class CheckableMessageBoxPrivate; +class QCheckBox; -class CheckableMessageBox : public QDialog +namespace Launcher { - Q_OBJECT - Q_PROPERTY(QString text READ text WRITE setText) - Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap) - Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked) - Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText) - Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons) - Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton) - -public: - explicit CheckableMessageBox(QWidget *parent); - virtual ~CheckableMessageBox(); - - static QDialogButtonBox::StandardButton - question(QWidget *parent, - const QString &title, - const QString &question, - const QString &checkBoxText, - bool *checkBoxSetting, - QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No, - QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No); - - QString text() const; - void setText(const QString &); - - bool isChecked() const; - void setChecked(bool s); - - QString checkBoxText() const; - void setCheckBoxText(const QString &); - - bool isCheckBoxVisible() const; - void setCheckBoxVisible(bool); - - QDialogButtonBox::StandardButtons standardButtons() const; - void setStandardButtons(QDialogButtonBox::StandardButtons s); - QPushButton *button(QDialogButtonBox::StandardButton b) const; - QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role); - - QDialogButtonBox::StandardButton defaultButton() const; - void setDefaultButton(QDialogButtonBox::StandardButton s); - - // See static QMessageBox::standardPixmap() - QPixmap iconPixmap() const; - void setIconPixmap (const QPixmap &p); - - // Query the result - QAbstractButton *clickedButton() const; - QDialogButtonBox::StandardButton clickedStandardButton() const; - - // Conversion convenience - static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton); - -private slots: - void slotClicked(QAbstractButton *b); - -private: - CheckableMessageBoxPrivate *d; -}; - + class CheckableMessageBoxPrivate + { + public: + + QLabel *pixmapLabel; + QLabel *messageLabel; + QCheckBox *checkBox; + QDialogButtonBox *buttonBox; + QAbstractButton *clickedButton; + + public: + CheckableMessageBoxPrivate(QDialog *q); + }; + + class CheckableMessageBox : public QDialog + { + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap) + Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked) + Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText) + Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons) + Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton) + + public: + explicit CheckableMessageBox(QWidget *parent); + virtual ~CheckableMessageBox(); + + static QDialogButtonBox::StandardButton + question(QWidget *parent, + const QString &title, + const QString &question, + const QString &checkBoxText, + bool *checkBoxSetting, + QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No, + QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No); + + QString text() const; + void setText(const QString &); + + bool isChecked() const; + void setChecked(bool s); + + QString checkBoxText() const; + void setCheckBoxText(const QString &); + + bool isCheckBoxVisible() const; + void setCheckBoxVisible(bool); + + QDialogButtonBox::StandardButtons standardButtons() const; + void setStandardButtons(QDialogButtonBox::StandardButtons s); + QPushButton *button(QDialogButtonBox::StandardButton b) const; + QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role); + + QDialogButtonBox::StandardButton defaultButton() const; + void setDefaultButton(QDialogButtonBox::StandardButton s); + + // See static QMessageBox::standardPixmap() + QPixmap iconPixmap() const; + void setIconPixmap (const QPixmap &p); + + // Query the result + QAbstractButton *clickedButton() const; + QDialogButtonBox::StandardButton clickedStandardButton() const; + + // Conversion convenience + static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton); + + private slots: + void slotClicked(QAbstractButton *b); + + private: + CheckableMessageBoxPrivate *d; + }; +} #endif // CHECKABLEMESSAGEBOX_HPP diff --git a/components/fileorderlist/utils/lineedit.cpp b/apps/launcher/utils/lineedit.cpp similarity index 66% rename from components/fileorderlist/utils/lineedit.cpp rename to apps/launcher/utils/lineedit.cpp index b0f339589..348707580 100644 --- a/components/fileorderlist/utils/lineedit.cpp +++ b/apps/launcher/utils/lineedit.cpp @@ -1,10 +1,12 @@ -#include -#include - #include "lineedit.hpp" LineEdit::LineEdit(QWidget *parent) : QLineEdit(parent) +{ + setupClearButton(); +} + +void LineEdit::setupClearButton() { mClearButton = new QToolButton(this); QPixmap pixmap(":images/clear.png"); @@ -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 similarity index 83% rename from components/fileorderlist/utils/lineedit.hpp rename to apps/launcher/utils/lineedit.hpp index 14bd7b1b4..2dd7da32b 100644 --- a/components/fileorderlist/utils/lineedit.hpp +++ b/apps/launcher/utils/lineedit.hpp @@ -11,6 +11,9 @@ #define LINEEDIT_H #include +#include +#include +#include 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 similarity index 70% rename from components/fileorderlist/utils/profilescombobox.cpp rename to apps/launcher/utils/profilescombobox.cpp index c3ff953ae..c14330724 100644 --- a/components/fileorderlist/utils/profilescombobox.cpp +++ b/apps/launcher/utils/profilescombobox.cpp @@ -5,18 +5,12 @@ #include #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 000000000..7b83c41b2 --- /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 + +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 a4b36b95e..76cbe32d0 100644 --- a/apps/launcher/utils/textinputdialog.cpp +++ b/apps/launcher/utils/textinputdialog.cpp @@ -7,19 +7,18 @@ #include #include -#include - -TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) : +Launcher::TextInputDialog::TextInputDialog(const QString& title, const QString &text, QWidget *parent) : QDialog(parent) { setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); 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 cbb453ac8..bb01778be 100644 --- a/apps/launcher/utils/textinputdialog.hpp +++ b/apps/launcher/utils/textinputdialog.hpp @@ -2,27 +2,39 @@ #define TEXTINPUTDIALOG_HPP #include -//#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 8732b3eab..b8b7e4c9d 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -813,8 +813,7 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con } void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) const { - std::vector esmFiles; - std::vector espFiles; + std::vector contentFiles; std::string baseGameFile("Game Files:GameFile"); std::string gameFile(""); @@ -832,29 +831,19 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) co std::string filetype(entry->substr(entry->length()-3)); Misc::StringUtils::toLower(filetype); - if(filetype.compare("esm") == 0) { - esmFiles.push_back(*entry); - } - else if(filetype.compare("esp") == 0) { - espFiles.push_back(*entry); + if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) { + contentFiles.push_back(*entry); } } gameFile = ""; } - cfg.erase("master"); - cfg.insert( std::make_pair > ("master", std::vector() ) ); - - for(std::vector::const_iterator it=esmFiles.begin(); it!=esmFiles.end(); ++it) { - cfg["master"].push_back(*it); - } - - cfg.erase("plugin"); - cfg.insert( std::make_pair > ("plugin", std::vector() ) ); + cfg.erase("content"); + cfg.insert( std::make_pair("content", std::vector() ) ); - for(std::vector::const_iterator it=espFiles.begin(); it!=espFiles.end(); ++it) { - cfg["plugin"].push_back(*it); + for(std::vector::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) { + cfg["content"].push_back(*it); } } diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index f918cfebf..895970696 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 @@ -33,18 +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 newgame filewidget - adjusterwidget + viewmanager view operations operation subview startup filedialog newgame + filewidget adjusterwidget ) @@ -63,10 +63,14 @@ opencs_units (view/world scenetoolmode ) +opencs_units (view/render + scenewidget + ) + opencs_units_noqt (view/world dialoguesubview subviews enumdelegate vartypedelegate recordstatusdelegate idtypedelegate datadisplaydelegate - scripthighlighter idvalidator + scripthighlighter idvalidator dialoguecreator ) @@ -124,11 +128,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}) @@ -158,3 +164,8 @@ target_link_libraries(opencs ${QT_LIBRARIES} components ) + +if(DPKG_PROGRAM) + INSTALL(TARGETS opencs RUNTIME DESTINATION games COMPONENT opencs) +endif() + diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index 9a6832ec0..1c1e37c2d 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -6,17 +6,21 @@ #include #include +#include +#include + #include "model/doc/document.hpp" #include "model/world/data.hpp" - -CS::Editor::Editor() : mViewManager (mDocumentManager) +CS::Editor::Editor() + : mDocumentManager (mCfgMgr), mViewManager (mDocumentManager) { mIpcServerName = "org.openmw.OpenCS"; setupDataFiles(); mNewGame.setLocalData (mLocal); + mFileDialog.setLocalData (mLocal); connect (&mViewManager, SIGNAL (newGameRequest ()), this, SLOT (createGame ())); connect (&mViewManager, SIGNAL (newAddonRequest ()), this, SLOT (createAddon ())); @@ -28,23 +32,27 @@ CS::Editor::Editor() : mViewManager (mDocumentManager) connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ())); connect (&mStartup, SIGNAL (editConfig()), this, SLOT (showSettings ())); - connect (&mFileDialog, SIGNAL(openFiles()), this, SLOT(openFiles())); - connect (&mFileDialog, SIGNAL(createNewFile()), this, SLOT(createNewFile())); + connect (&mFileDialog, SIGNAL(signalOpenFiles (const boost::filesystem::path&)), + this, SLOT(openFiles (const boost::filesystem::path&))); + + connect (&mFileDialog, SIGNAL(signalCreateNewFile (const boost::filesystem::path&)), + this, SLOT(createNewFile (const boost::filesystem::path&))); connect (&mNewGame, SIGNAL (createRequest (const boost::filesystem::path&)), - this, SLOT (createNewGame (const boost::filesystem::path&))); + 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 \nAllowed options"); desc.add_options() ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()) ("data-local", boost::program_options::value()->default_value("")) ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) - ("encoding", boost::program_options::value()->default_value("win1252")); + ("encoding", boost::program_options::value()->default_value("win1252")) + ("resources", boost::program_options::value()->default_value("resources")); boost::program_options::notify(variables); @@ -79,13 +87,16 @@ void CS::Editor::setupDataFiles() } // Set the charset for reading the esm/esp files - QString encoding = QString::fromStdString(variables["encoding"].as()); - mFileDialog.setEncoding(encoding); + // QString encoding = QString::fromStdString(variables["encoding"].as()); + //mFileDialog.setEncoding(encoding); dataDirs.insert (dataDirs.end(), dataLocal.begin(), dataLocal.end()); + mDocumentManager.setResourceDir (variables["resources"].as()); + for (Files::PathContainer::const_iterator iter = dataDirs.begin(); iter != dataDirs.end(); ++iter) { + QString path = QString::fromStdString(iter->string()); mFileDialog.addFiles(path); } @@ -109,48 +120,39 @@ void CS::Editor::createGame() void CS::Editor::createAddon() { mStartup.hide(); - - mFileDialog.newFile(); + mFileDialog.showDialog (CSVDoc::ContentAction_New); } void CS::Editor::loadDocument() { mStartup.hide(); - - mFileDialog.openFile(); + mFileDialog.showDialog (CSVDoc::ContentAction_Edit); } -void CS::Editor::openFiles() +void CS::Editor::openFiles (const boost::filesystem::path &savePath) { std::vector files; - QStringList paths = mFileDialog.checkedItemsPaths(); - foreach (const QString &path, paths) { + foreach (const QString &path, mFileDialog.selectedFilePaths()) files.push_back(path.toStdString()); - } - - /// \todo Get the save path from the file dialogue - CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), false); + 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 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()); - - /// \todo Get the save path from the file dialogue. + files.push_back(mFileDialog.filename().toStdString()); - CSMDoc::Document *document = mDocumentManager.addDocument (files, *files.rbegin(), true); + CSMDoc::Document *document = mDocumentManager.addDocument (files, savePath, true); mViewManager.addView (document); mFileDialog.hide(); @@ -212,6 +214,20 @@ int CS::Editor::run() if (mLocal.empty()) return 1; + // TODO: setting + Ogre::Root::getSingleton().setRenderSystem(Ogre::Root::getSingleton().getRenderSystemByName("OpenGL Rendering Subsystem")); + + Ogre::Root::getSingleton().initialise(false); + + // Create a hidden background window to keep resources + Ogre::NameValuePairList params; + params.insert(std::make_pair("title", "")); + params.insert(std::make_pair("FSAA", "0")); + params.insert(std::make_pair("vsync", "false")); + params.insert(std::make_pair("hidden", "true")); + Ogre::RenderWindow* hiddenWindow = Ogre::Root::getSingleton().createRenderWindow("InactiveHidden", 1, 1, false, ¶ms); + hiddenWindow->setActive(false); + mStartup.show(); QApplication::setQuitOnLastWindowClosed (true); diff --git a/apps/opencs/editor.hpp b/apps/opencs/editor.hpp index c83b2a685..930aa9d64 100644 --- a/apps/opencs/editor.hpp +++ b/apps/opencs/editor.hpp @@ -26,15 +26,15 @@ namespace CS { Q_OBJECT + Files::ConfigurationManager mCfgMgr; CSMSettings::UserSettings mUserSettings; CSMDoc::DocumentManager mDocumentManager; CSVDoc::ViewManager mViewManager; CSVDoc::StartupDialogue mStartup; CSVDoc::NewGameDialogue mNewGame; CSVSettings::UserSettingsDialog mSettings; - FileDialog mFileDialog; + CSVDoc::FileDialog mFileDialog; - Files::ConfigurationManager mCfgMgr; boost::filesystem::path mLocal; void setupDataFiles(); @@ -59,8 +59,8 @@ namespace CS void createAddon(); void loadDocument(); - void openFiles(); - void createNewFile(); + void openFiles (const boost::filesystem::path &path); + void createNewFile (const boost::filesystem::path& path); void createNewGame (const boost::filesystem::path& file); void showStartup(); diff --git a/apps/opencs/main.cpp b/apps/opencs/main.cpp index ef7123c20..449dd229e 100644 --- a/apps/opencs/main.cpp +++ b/apps/opencs/main.cpp @@ -7,6 +7,8 @@ #include #include +#include + class Application : public QApplication { private: @@ -33,6 +35,12 @@ class Application : public QApplication int main(int argc, char *argv[]) { Q_INIT_RESOURCE (resources); + + // TODO: Ogre startup shouldn't be here, but it currently has to: + // SceneWidget destructor will delete the created render window, which would be called _after_ Root has shut down :( + OgreInit::OgreInit ogreInit; + ogreInit.init("./opencsOgre.log"); // TODO log path? + Application mApplication (argc, argv); mApplication.setWindowIcon (QIcon (":./opencs.png")); @@ -42,7 +50,7 @@ int main(int argc, char *argv[]) if(!editor.makeIPCServer()) { editor.connectToIPCServer(); - return 0; + // return 0; } return editor.run(); diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index d7138f671..590a19439 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -1,8 +1,15 @@ #include "document.hpp" + #include +#include + +#ifndef Q_MOC_RUN +#include +#endif + void CSMDoc::Document::load (const std::vector::const_iterator& begin, - const std::vector::const_iterator& end, bool lastAsModified) + const std::vector::const_iterator& end, bool lastAsModified) { assert (begin!=end); @@ -12,10 +19,10 @@ void CSMDoc::Document::load (const std::vector::const_i --end2; for (std::vector::const_iterator iter (begin); iter!=end2; ++iter) - getData().loadFile (*iter, true); + getData().loadFile (*iter, true, false); if (lastAsModified) - getData().loadFile (*end2, false); + getData().loadFile (*end2, false, false); } void CSMDoc::Document::addGmsts() @@ -2058,9 +2065,9 @@ void CSMDoc::Document::addOptionalGlobals() { static const char *sGlobals[] = { - "dayspassed", - "pcwerewolf", - "pcyear", + "DaysPassed", + "PCWerewolf", + "PCYear", 0 }; @@ -2137,11 +2144,86 @@ void CSMDoc::Document::createBase() getData().getSkills().add (record); } + + static const char *sVoice[] = + { + "Intruder", + "Attack", + "Hello", + "Thief", + "Alarm", + "Idle", + "Flee", + "Hit", + 0 + }; + + for (int i=0; sVoice[i]; ++i) + { + ESM::Dialogue record; + record.mId = sVoice[i]; + record.mType = ESM::Dialogue::Voice; + record.blank(); + + getData().getTopics().add (record); + } + + static const char *sGreetings[] = + { + "Greeting 0", + "Greeting 1", + "Greeting 2", + "Greeting 3", + "Greeting 4", + "Greeting 5", + "Greeting 6", + "Greeting 7", + "Greeting 8", + "Greeting 9", + 0 + }; + + for (int i=0; sGreetings[i]; ++i) + { + ESM::Dialogue record; + record.mId = sGreetings[i]; + record.mType = ESM::Dialogue::Greeting; + record.blank(); + + getData().getTopics().add (record); + } + + static const char *sPersuasion[] = + { + "Intimidate Success", + "Intimidate Fail", + "Service Refusal", + "Admire Success", + "Taunt Success", + "Bribe Success", + "Info Refusal", + "Admire Fail", + "Taunt Fail", + "Bribe Fail", + 0 + }; + + for (int i=0; sPersuasion[i]; ++i) + { + ESM::Dialogue record; + record.mId = sPersuasion[i]; + record.mType = ESM::Dialogue::Persuasion; + record.blank(); + + getData().getTopics().add (record); + } } -CSMDoc::Document::Document (const std::vector& files, - const boost::filesystem::path& savePath, bool new_) -: mSavePath (savePath), 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"); @@ -2158,6 +2240,34 @@ CSMDoc::Document::Document (const std::vector& files, load (files.begin(), end, !new_); } + if (new_) + { + mData.setDescription (""); + mData.setAuthor (""); + } +/// \todo un-outcomment the else, once loading an existing content file works properly again. + else + { + if (boost::filesystem::exists (mProjectPath)) + { + getData().loadFile (mProjectPath, false, true); + } + else + { + boost::filesystem::path locCustomFiltersPath (configuration.getUserPath()); + locCustomFiltersPath /= "defaultfilters"; + if (boost::filesystem::exists(locCustomFiltersPath)) + { + boost::filesystem::copy_file (locCustomFiltersPath, mProjectPath); + } else { + boost::filesystem::path filters(mResDir); + filters /= "defaultfilters"; + boost::filesystem::copy_file(filters, mProjectPath); + } + getData().loadFile (mProjectPath, false, true); + } + } + addOptionalGmsts(); addOptionalGlobals(); @@ -2166,9 +2276,10 @@ CSMDoc::Document::Document (const std::vector& files, connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); connect (&mTools, SIGNAL (done (int)), this, SLOT (operationDone (int))); - // dummy implementation -> remove when proper save is implemented. - mSaveCount = 0; - connect (&mSaveTimer, SIGNAL(timeout()), this, SLOT (saving())); + connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int))); + connect (&mSaving, SIGNAL (done (int)), this, SLOT (operationDone (int))); + connect (&mSaving, SIGNAL (reportMessage (const QString&, int)), + this, SLOT (reportMessage (const QString&, int))); } CSMDoc::Document::~Document() @@ -2187,7 +2298,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,12 +2312,20 @@ const boost::filesystem::path& CSMDoc::Document::getSavePath() const return mSavePath; } +const std::vector& 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() @@ -2218,44 +2337,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 3532721ea..437b0c513 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 @@ -32,16 +38,17 @@ namespace CSMDoc private: boost::filesystem::path mSavePath; + std::vector 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,8 +71,7 @@ namespace CSMDoc public: - Document (const std::vector& files, - const boost::filesystem::path& savePath, bool new_); + Document (const Files::ConfigurationManager& configuration, const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, const boost::filesystem::path& resDir, bool new_); ~Document(); @@ -75,6 +81,10 @@ namespace CSMDoc const boost::filesystem::path& getSavePath() const; + const std::vector& getContentFiles() const; + ///< \attention The last element in this collection is the file that is being edited, + /// but with its original path instead of the save path. + void save(); CSMWorld::UniversalId verify(); @@ -98,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: @@ -110,3 +119,4 @@ namespace CSMDoc } #endif + diff --git a/apps/opencs/model/doc/documentmanager.cpp b/apps/opencs/model/doc/documentmanager.cpp index b079109ea..024c46bea 100644 --- a/apps/opencs/model/doc/documentmanager.cpp +++ b/apps/opencs/model/doc/documentmanager.cpp @@ -4,9 +4,22 @@ #include #include +#include + +#ifndef Q_MOC_RUN +#include +#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() { @@ -17,7 +30,7 @@ CSMDoc::DocumentManager::~DocumentManager() CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector& files, const boost::filesystem::path& savePath, bool new_) { - Document *document = new Document (files, savePath, 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 dfded8d5c..b80a18642 100644 --- a/apps/opencs/model/doc/documentmanager.hpp +++ b/apps/opencs/model/doc/documentmanager.hpp @@ -6,6 +6,11 @@ #include +namespace Files +{ + class ConfigurationManager; +} + namespace CSMDoc { class Document; @@ -13,18 +18,18 @@ namespace CSMDoc class DocumentManager { std::vector 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& files, - const boost::filesystem::path& savePath, bool new_); + Document *addDocument (const std::vector< boost::filesystem::path >& files, const boost::filesystem::path& savePath, bool new_); ///< The ownership of the returned document is not transferred to the caller. /// /// \param new_ Do not load the last content file in \a files and instead create in an @@ -32,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 similarity index 50% rename from apps/opencs/model/tools/operation.cpp rename to apps/opencs/model/doc/operation.cpp index 71761cdae..d29cc2631 100644 --- a/apps/opencs/model/tools/operation.cpp +++ b/apps/opencs/model/doc/operation.cpp @@ -6,16 +6,16 @@ #include -#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 >::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 >::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 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 similarity index 63% rename from apps/opencs/model/tools/operation.hpp rename to apps/opencs/model/doc/operation.hpp index 4731c58fa..316eda78f 100644 --- a/apps/opencs/model/tools/operation.hpp +++ b/apps/opencs/model/doc/operation.hpp @@ -1,11 +1,11 @@ -#ifndef CSM_TOOLS_OPERATION_H -#define CSM_TOOLS_OPERATION_H +#ifndef CSM_DOC_OPERATION_H +#define CSM_DOC_OPERATION_H #include #include -namespace CSMTools +namespace CSMDoc { class Stage; @@ -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 000000000..b756a9dc1 --- /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 > + (mDocument.getData().getGlobals(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getGmsts(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getSkills(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getClasses(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getFactions(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getRaces(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getSounds(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getScripts(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getRegions(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getBirthsigns(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getSpells(), mState)); + + /// \todo deal with info records for topcis and journals + appendStage (new WriteCollectionStage > + (mDocument.getData().getTopics(), mState)); + + appendStage (new WriteCollectionStage > + (mDocument.getData().getJournals(), mState)); + + appendStage (new WriteRefIdCollectionStage (mDocument, mState)); + + + 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 000000000..cd1bbef98 --- /dev/null +++ b/apps/opencs/model/doc/saving.hpp @@ -0,0 +1,27 @@ +#ifndef CSM_DOC_SAVING_H +#define CSM_DOC_SAVING_H + +#include + +#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 000000000..d68c72317 --- /dev/null +++ b/apps/opencs/model/doc/savingstages.cpp @@ -0,0 +1,161 @@ + +#include "savingstages.hpp" + +#include + +#include + +#include + +#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& 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& 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 dependencies = mDocument.getContentFiles(); + std::vector::const_iterator end (--dependencies.end()); + + for (std::vector::const_iterator iter (dependencies.begin()); + iter!=end; ++iter) + { + std::string name = iter->filename().string(); + uint64_t size = boost::filesystem::file_size (*iter); + + mState.getWriter().addMaster (name, size); + } + } + + mState.getWriter().save (mState.getStream()); +} + + +CSMDoc::WriteRefIdCollectionStage::WriteRefIdCollectionStage (Document& document, SavingState& state) +: mDocument (document), mState (state) +{} + +int CSMDoc::WriteRefIdCollectionStage::setup() +{ + return mDocument.getData().getReferenceables().getSize(); +} + +void CSMDoc::WriteRefIdCollectionStage::perform (int stage, std::vector& messages) +{ + mDocument.getData().getReferenceables().save (stage, mState.getWriter()); +} + + +CSMDoc::WriteFilterStage::WriteFilterStage (Document& document, SavingState& state, + CSMFilter::Filter::Scope scope) +: WriteCollectionStage > (document.getData().getFilters(), + state), + mDocument (document), mScope (scope) +{} + +void CSMDoc::WriteFilterStage::perform (int stage, std::vector& messages) +{ + const CSMWorld::Record& record = + mDocument.getData().getFilters().getRecord (stage); + + if (record.get().mScope==mScope) + WriteCollectionStage >::perform (stage, messages); +} + + +CSMDoc::CloseSaveStage::CloseSaveStage (SavingState& state) +: mState (state) +{} + +int CSMDoc::CloseSaveStage::setup() +{ + return 1; +} + +void CSMDoc::CloseSaveStage::perform (int stage, std::vector& messages) +{ + mState.getStream().close(); + + if (!mState.getStream()) + throw std::runtime_error ("saving failed"); +} + + +CSMDoc::FinalSavingStage::FinalSavingStage (Document& document, SavingState& state) +: mDocument (document), mState (state) +{} + +int CSMDoc::FinalSavingStage::setup() +{ + return 1; +} + +void CSMDoc::FinalSavingStage::perform (int stage, std::vector& 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 000000000..ff94116fd --- /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& 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& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + + + template + 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& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + + template + WriteCollectionStage::WriteCollectionStage (const CollectionT& collection, + SavingState& state) + : mCollection (collection), mState (state) + {} + + template + int WriteCollectionStage::setup() + { + return mCollection.getSize(); + } + + template + void WriteCollectionStage::perform (int stage, std::vector& messages) + { + CSMWorld::RecordBase::State state = mCollection.getRecord (stage).mState; + + if (state==CSMWorld::RecordBase::State_Modified || + state==CSMWorld::RecordBase::State_ModifiedOnly) + { + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic (change ESMWriter interface?) + type += reinterpret_cast (&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& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + + + class WriteFilterStage : public WriteCollectionStage > + { + Document& mDocument; + CSMFilter::Filter::Scope mScope; + + public: + + WriteFilterStage (Document& document, SavingState& state, CSMFilter::Filter::Scope scope); + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + + + class CloseSaveStage : public Stage + { + SavingState& mState; + + public: + + CloseSaveStage (SavingState& state); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + + class FinalSavingStage : public Stage + { + Document& mDocument; + SavingState& mState; + + public: + + FinalSavingStage (Document& document, SavingState& state); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, std::vector& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; +} + +#endif diff --git a/apps/opencs/model/doc/savingstate.cpp b/apps/opencs/model/doc/savingstate.cpp new file mode 100644 index 000000000..4a1abb888 --- /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 000000000..8cf7883e5 --- /dev/null +++ b/apps/opencs/model/doc/savingstate.hpp @@ -0,0 +1,50 @@ +#ifndef CSM_DOC_SAVINGSTATE_H +#define CSM_DOC_SAVINGSTATE_H + +#include + +#include + +#include + +namespace CSMDoc +{ + class Operation; + class Document; + + class SavingState + { + Operation& mOperation; + boost::filesystem::path mPath; + boost::filesystem::path mTmpPath; + ToUTF8::Utf8Encoder mEncoder; + std::ofstream mStream; + ESM::ESMWriter mWriter; + 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 000000000..99b765770 --- /dev/null +++ b/apps/opencs/model/doc/stage.cpp @@ -0,0 +1,4 @@ + +#include "stage.hpp" + +CSMDoc::Stage::~Stage() {} \ No newline at end of file diff --git a/apps/opencs/model/tools/stage.hpp b/apps/opencs/model/doc/stage.hpp similarity index 65% rename from apps/opencs/model/tools/stage.hpp rename to apps/opencs/model/doc/stage.hpp index 3020936f3..1f96c60b4 100644 --- a/apps/opencs/model/tools/stage.hpp +++ b/apps/opencs/model/doc/stage.hpp @@ -1,10 +1,10 @@ -#ifndef CSM_TOOLS_STAGE_H -#define CSM_TOOLS_STAGE_H +#ifndef CSM_DOC_STAGE_H +#define CSM_DOC_STAGE_H #include #include -namespace CSMTools +namespace CSMDoc { class Stage { @@ -16,7 +16,7 @@ namespace CSMTools ///< \return number of steps virtual void perform (int stage, std::vector& messages) = 0; - ///< Messages resulting from this tage will be appended to \a messages. + ///< Messages resulting from this stage will be appended to \a messages. }; } diff --git a/apps/opencs/model/tools/birthsigncheck.hpp b/apps/opencs/model/tools/birthsigncheck.hpp index 42b5a6b24..bdd65b44a 100644 --- a/apps/opencs/model/tools/birthsigncheck.hpp +++ b/apps/opencs/model/tools/birthsigncheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that birthsign records are internally consistent - class BirthsignCheckStage : public Stage + class BirthsignCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mBirthsigns; diff --git a/apps/opencs/model/tools/classcheck.hpp b/apps/opencs/model/tools/classcheck.hpp index a29d7c8b7..3604b451c 100644 --- a/apps/opencs/model/tools/classcheck.hpp +++ b/apps/opencs/model/tools/classcheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that class records are internally consistent - class ClassCheckStage : public Stage + class ClassCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mClasses; diff --git a/apps/opencs/model/tools/factioncheck.hpp b/apps/opencs/model/tools/factioncheck.hpp index 868650572..7cd80347d 100644 --- a/apps/opencs/model/tools/factioncheck.hpp +++ b/apps/opencs/model/tools/factioncheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that faction records are internally consistent - class FactionCheckStage : public Stage + class FactionCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mFactions; diff --git a/apps/opencs/model/tools/mandatoryid.hpp b/apps/opencs/model/tools/mandatoryid.hpp index 342e2d754..5fddf08d3 100644 --- a/apps/opencs/model/tools/mandatoryid.hpp +++ b/apps/opencs/model/tools/mandatoryid.hpp @@ -6,7 +6,7 @@ #include "../world/universalid.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMWorld { @@ -16,7 +16,7 @@ namespace CSMWorld namespace CSMTools { /// \brief Verify stage: make sure that records with specific IDs exist. - class MandatoryIdStage : public Stage + class MandatoryIdStage : public CSMDoc::Stage { const CSMWorld::CollectionBase& mIdCollection; CSMWorld::UniversalId mCollectionId; diff --git a/apps/opencs/model/tools/racecheck.hpp b/apps/opencs/model/tools/racecheck.hpp index 155f79902..ff9948bf6 100644 --- a/apps/opencs/model/tools/racecheck.hpp +++ b/apps/opencs/model/tools/racecheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that race records are internally consistent - class RaceCheckStage : public Stage + class RaceCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mRaces; bool mPlayable; diff --git a/apps/opencs/model/tools/regioncheck.hpp b/apps/opencs/model/tools/regioncheck.hpp index b42135651..c8c437cbd 100644 --- a/apps/opencs/model/tools/regioncheck.hpp +++ b/apps/opencs/model/tools/regioncheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that region records are internally consistent - class RegionCheckStage : public Stage + class RegionCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mRegions; diff --git a/apps/opencs/model/tools/skillcheck.hpp b/apps/opencs/model/tools/skillcheck.hpp index 30a3f01ca..662bdadee 100644 --- a/apps/opencs/model/tools/skillcheck.hpp +++ b/apps/opencs/model/tools/skillcheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that skill records are internally consistent - class SkillCheckStage : public Stage + class SkillCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mSkills; diff --git a/apps/opencs/model/tools/soundcheck.hpp b/apps/opencs/model/tools/soundcheck.hpp index a309763a1..00b45cd93 100644 --- a/apps/opencs/model/tools/soundcheck.hpp +++ b/apps/opencs/model/tools/soundcheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that sound records are internally consistent - class SoundCheckStage : public Stage + class SoundCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mSounds; diff --git a/apps/opencs/model/tools/spellcheck.hpp b/apps/opencs/model/tools/spellcheck.hpp index 056639219..880ddafcd 100644 --- a/apps/opencs/model/tools/spellcheck.hpp +++ b/apps/opencs/model/tools/spellcheck.hpp @@ -5,12 +5,12 @@ #include "../world/idcollection.hpp" -#include "stage.hpp" +#include "../doc/stage.hpp" namespace CSMTools { /// \brief VerifyStage: make sure that spell records are internally consistent - class SpellCheckStage : public Stage + class SpellCheckStage : public CSMDoc::Stage { const CSMWorld::IdCollection& mSpells; diff --git a/apps/opencs/model/tools/stage.cpp b/apps/opencs/model/tools/stage.cpp deleted file mode 100644 index 6f4567e57..000000000 --- a/apps/opencs/model/tools/stage.cpp +++ /dev/null @@ -1,4 +0,0 @@ - -#include "stage.hpp" - -CSMTools::Stage::~Stage() {} \ No newline at end of file diff --git a/apps/opencs/model/tools/tools.cpp b/apps/opencs/model/tools/tools.cpp index 803861203..cd4653280 100644 --- a/apps/opencs/model/tools/tools.cpp +++ b/apps/opencs/model/tools/tools.cpp @@ -3,9 +3,8 @@ #include -#include "verifier.hpp" - #include "../doc/state.hpp" +#include "../doc/operation.hpp" #include "../world/data.hpp" #include "../world/universalid.hpp" @@ -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 (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::iterator iter = mActiveReports.find (type); diff --git a/apps/opencs/model/tools/tools.hpp b/apps/opencs/model/tools/tools.hpp index 652345c6d..0079fab34 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 mReports; int mNextReportNumber; std::map 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 9c00d4ea7..000000000 --- a/apps/opencs/model/tools/verifier.cpp +++ /dev/null @@ -1,7 +0,0 @@ - -#include "verifier.hpp" - -#include "../doc/state.hpp" - -CSMTools::Verifier::Verifier() : Operation (CSMDoc::State_Verifying) -{} diff --git a/apps/opencs/model/tools/verifier.hpp b/apps/opencs/model/tools/verifier.hpp deleted file mode 100644 index 054f87169..000000000 --- a/apps/opencs/model/tools/verifier.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef CSM_TOOLS_VERIFIER_H -#define CSM_TOOLS_VERIFIER_H - -#include "operation.hpp" - -namespace CSMTools -{ - class Verifier : public Operation - { - public: - - Verifier(); - - }; -} - -#endif diff --git a/apps/opencs/model/world/collectionbase.cpp b/apps/opencs/model/world/collectionbase.cpp index 932ea27b5..241f198cb 100644 --- a/apps/opencs/model/world/collectionbase.cpp +++ b/apps/opencs/model/world/collectionbase.cpp @@ -1,6 +1,31 @@ #include "collectionbase.hpp" +#include + +#include "columnbase.hpp" + CSMWorld::CollectionBase::CollectionBase() {} CSMWorld::CollectionBase::~CollectionBase() {} + +int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const +{ + int columns = getColumns(); + + for (int i=0; i #include "universalid.hpp" +#include "columns.hpp" class QVariant; @@ -83,6 +84,13 @@ namespace CSMWorld ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list + + int searchColumnIndex (Columns::ColumnId id) const; + ///< Return index of column with the given \a id. If no such column exists, -1 is returned. + + int findColumnIndex (Columns::ColumnId id) const; + ///< Return index of column with the given \a id. If no such column exists, an exception is + /// thrown. }; } diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index c1b423c94..9b8d7dafb 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 a13ac9a8a..8575480fd 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -1217,6 +1217,37 @@ namespace CSMWorld } }; + template + struct ScopeColumn : public Column + { + ScopeColumn() + : Column (Columns::ColumnId_Scope, ColumnBase::Display_Integer, 0) + {} + + virtual QVariant get (const Record& record) const + { + return static_cast (record.get().mScope); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + record2.mScope = static_cast (data.toInt()); + record.setModified (record2); + } + + virtual bool isEditable() const + { + return true; + } + + virtual bool isUserEditable() const + { + return false; + } + }; + + template struct PosColumn : public Column { @@ -1284,6 +1315,39 @@ namespace CSMWorld return true; } }; + + template + struct DialogueTypeColumn : public Column + { + DialogueTypeColumn (bool hidden = false) + : Column (Columns::ColumnId_DialogueType, ColumnBase::Display_DialogueType, + hidden ? 0 : ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue) + {} + + virtual QVariant get (const Record& record) const + { + return static_cast (record.get().mType); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mType = data.toInt(); + + record.setModified (record2); + } + + virtual bool isEditable() const + { + return 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 ca37840ad..a842e7b8d 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -159,6 +159,8 @@ namespace CSMWorld { 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" }, @@ -269,6 +271,11 @@ namespace "unknown", "none", "short", "integer", "long", "float", "string", 0 }; + static const char *sDialogueTypeEnums[] = + { + "Topic", "Voice", "Greeting", "Persuasion", 0 + }; + const char **getEnumNames (CSMWorld::Columns::ColumnId column) { switch (column) @@ -283,6 +290,7 @@ namespace case CSMWorld::Columns::ColumnId_WeaponType: return sWeaponTypes; case CSMWorld::Columns::ColumnId_Modification: return sModificationEnums; case CSMWorld::Columns::ColumnId_ValueType: return sVarTypeEnums; + case CSMWorld::Columns::ColumnId_DialogueType: return sDialogueTypeEnums; default: return 0; } diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 9b26cac4c..55d085a96 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -152,6 +152,8 @@ namespace CSMWorld 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. diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 1e290d45f..10e56765f 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -44,6 +44,17 @@ void CSMWorld::Data::appendIds (std::vector& ids, const CollectionB ids.insert (ids.end(), ids2.begin(), ids2.end()); } +int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collection) +{ + int number = 0; + + for (int i=0; i); @@ -141,6 +152,14 @@ CSMWorld::Data::Data() : mRefs (mCells) mSpells.addColumn (new FlagColumn (Columns::ColumnId_StarterSpell, 0x2)); mSpells.addColumn (new FlagColumn (Columns::ColumnId_AlwaysSucceeds, 0x4)); + mTopics.addColumn (new StringIdColumn); + mTopics.addColumn (new RecordStateColumn); + mTopics.addColumn (new DialogueTypeColumn); + + mJournals.addColumn (new StringIdColumn); + mJournals.addColumn (new RecordStateColumn); + mJournals.addColumn (new DialogueTypeColumn (true)); + mCells.addColumn (new StringIdColumn); mCells.addColumn (new RecordStateColumn); mCells.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Cell)); @@ -184,6 +203,7 @@ CSMWorld::Data::Data() : mRefs (mCells) mFilters.addColumn (new RecordStateColumn); mFilters.addColumn (new FilterColumn); mFilters.addColumn (new DescriptionColumn); + mFilters.addColumn (new ScopeColumn); addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global); addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst); @@ -196,6 +216,8 @@ CSMWorld::Data::Data() : mRefs (mCells) addModel (new IdTable (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region); addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign); addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell); + addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic); + addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal); addModel (new IdTable (&mCells), UniversalId::Type_Cells, UniversalId::Type_Cell); addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, UniversalId::Type_Referenceable); @@ -319,6 +341,28 @@ CSMWorld::IdCollection& CSMWorld::Data::getSpells() return mSpells; } + +const CSMWorld::IdCollection& CSMWorld::Data::getTopics() const +{ + return mTopics; +} + +CSMWorld::IdCollection& CSMWorld::Data::getTopics() +{ + return mTopics; +} + +const CSMWorld::IdCollection& CSMWorld::Data::getJournals() const +{ + return mJournals; +} + +CSMWorld::IdCollection& CSMWorld::Data::getJournals() +{ + return mJournals; +} + + const CSMWorld::IdCollection& CSMWorld::Data::getCells() const { return mCells; @@ -387,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; @@ -397,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()) @@ -447,6 +494,54 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base) case ESM::REC_STAT: mReferenceables.load (reader, base, UniversalId::Type_Static); break; case ESM::REC_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 (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 @@ -469,10 +564,50 @@ 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 CSMWorld::Data::getIds (bool listDeleted) const { std::vector ids; @@ -487,6 +622,8 @@ std::vector CSMWorld::Data::getIds (bool listDeleted) const appendIds (ids, mRegions, listDeleted); appendIds (ids, mBirthsigns, listDeleted); appendIds (ids, mSpells, listDeleted); + appendIds (ids, mTopics, listDeleted); + appendIds (ids, mJournals, listDeleted); appendIds (ids, mCells, listDeleted); appendIds (ids, mReferenceables, listDeleted); diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index e900bb10f..1a9eae2d2 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "../filter/filter.hpp" @@ -48,12 +49,16 @@ namespace CSMWorld IdCollection mRegions; IdCollection mBirthsigns; IdCollection mSpells; + IdCollection mTopics; + IdCollection mJournals; IdCollection mCells; RefIdCollection mReferenceables; RefCollection mRefs; IdCollection mFilters; std::vector mModels; std::map mModelIndex; + std::string mAuthor; + std::string mDescription; // not implemented Data (const Data&); @@ -66,6 +71,8 @@ namespace CSMWorld bool listDeleted); ///< Append all IDs from collection to \a ids. + static int count (RecordBase::State state, const CollectionBase& collection); + public: Data(); @@ -116,6 +123,14 @@ namespace CSMWorld IdCollection& getSpells(); + const IdCollection& getTopics() const; + + IdCollection& getTopics(); + + const IdCollection& getJournals() const; + + IdCollection& getJournals(); + const IdCollection& getCells() const; IdCollection& getCells(); @@ -141,8 +156,10 @@ namespace CSMWorld void merge(); ///< Merge modified into base. - void loadFile (const boost::filesystem::path& path, bool base); + void loadFile (const boost::filesystem::path& path, bool base, bool project); ///< Merging content of a file into base or modified. + /// + /// \param project load project file instead of content file bool hasId (const std::string& id) const; @@ -151,6 +168,17 @@ namespace CSMWorld /// /// \param listDeleted include deleted record in the list + int count (RecordBase::State state) const; + ///< Return number of top-level records with the given \a state. + + void setDescription (const std::string& description); + + std::string getDescription() const; + + void setAuthor (const std::string& author); + + std::string getAuthor() const; + signals: void idListChanged(); diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index 04e65eea7..a7b37be5b 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 > class IdCollection : public Collection { 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 - void IdCollection::load (ESM::ESMReader& reader, bool base, - UniversalId::Type type) + void IdCollection::load (ESM::ESMReader& reader, bool base) { std::string id = reader.getHNOString ("NAME"); @@ -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 record2; - record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; - (base ? record2.mBase : record2.mModified) = record; + template + void IdCollection::load (const ESXRecordT& record, bool base) + { + int index = this->searchId (IdAccessorT().getId (record)); - this->appendRecord (record2); - } + if (index==-1) + { + // new record + Record record2; + record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; + (base ? record2.mBase : record2.mModified) = record; + + this->appendRecord (record2); + } + else + { + // old record + Record record2 = Collection::getRecord (index); + + if (base) + record2.mBase = record; else - { - // old record - Record record2 = Collection::getRecord (index); + record2.setModified (record); - if (base) - record2.mBase = record; - else - record2.setModified (record); + this->setRecord (index, record2); + } + } - this->setRecord (index, record2); - } + template + bool IdCollection::tryDelete (const std::string& id) + { + int index = this->searchId (id); + + if (index==-1) + return false; + + Record record = Collection::getRecord (index); + + if (record.isDeleted()) + return false; + + if (record.mState==RecordBase::State_ModifiedOnly) + { + Collection::removeRows (index, 1); } + else + { + record.mState = RecordBase::State_Deleted; + this->setRecord (index, record); + } + + return true; } } diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index baaf75289..b7b1a9db0 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -161,21 +161,10 @@ const CSMWorld::RecordBase& CSMWorld::IdTable::getRecord (const std::string& id) int CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const { - int columns = mIdCollection->getColumns(); - - for (int i=0; igetColumn (i).mColumnId==id) - return i; - - return -1; + return mIdCollection->searchColumnIndex (id); } int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const { - int index = searchColumnIndex (id); - - if (index==-1) - throw std::logic_error ("invalid column index"); - - return index; + return mIdCollection->findColumnIndex (id); } \ No newline at end of file diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index cda2711cc..3a6f70d31 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -539,3 +539,8 @@ std::vector CSMWorld::RefIdCollection::getIds (bool listDeleted) co { return mData.getIds (listDeleted); } + +void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const +{ + mData.save (index, writer); +} \ No newline at end of file diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index 22f83150d..a479735db 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -9,6 +9,11 @@ #include "collectionbase.hpp" #include "refiddata.hpp" +namespace ESM +{ + class ESMWriter; +} + namespace CSMWorld { class RefIdAdapter; @@ -94,6 +99,8 @@ namespace CSMWorld ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list + + void save (int index, ESM::ESMWriter& writer) const; }; } diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 9457937f1..8f59b0fe7 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -218,3 +218,16 @@ std::vector CSMWorld::RefIdData::getIds (bool listDeleted) const return ids; } + +void CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const +{ + LocalIndex localIndex = globalToLocalIndex (index); + + std::map::const_iterator iter = + mRecordContainers.find (localIndex.second); + + if (iter==mRecordContainers.end()) + throw std::logic_error ("invalid local index type"); + + iter->second->save (localIndex.first, writer); +} \ No newline at end of file diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index e221fbc7c..9595ab23b 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include "record.hpp" #include "universalid.hpp" @@ -51,6 +52,8 @@ namespace CSMWorld virtual void erase (int index, int count) = 0; virtual std::string getId (int index) const = 0; + + virtual void save (int index, ESM::ESMWriter& writer) const = 0; }; template @@ -71,6 +74,8 @@ namespace CSMWorld virtual void erase (int index, int count); virtual std::string getId (int index) const; + + virtual void save (int index, ESM::ESMWriter& writer) const; }; template @@ -123,6 +128,31 @@ namespace CSMWorld return mContainer.at (index).get().mId; } + template + void RefIdDataContainer::save (int index, ESM::ESMWriter& writer) const + { + CSMWorld::RecordBase::State state = mContainer.at (index).mState; + + if (state==CSMWorld::RecordBase::State_Modified || + state==CSMWorld::RecordBase::State_ModifiedOnly) + { + std::string type; + for (int i=0; i<4; ++i) + /// \todo make endianess agnostic (change ESMWriter interface?) + type += reinterpret_cast (&mContainer.at (index).mModified.sRecordId)[i]; + + writer.startRecord (type); + writer.writeHNCString ("NAME", getId (index)); + mContainer.at (index).mModified.save (writer); + writer.endRecord (type); + } + else if (state==CSMWorld::RecordBase::State_Deleted) + { + /// \todo write record with delete flag + } + } + + class RefIdData { public: @@ -187,6 +217,8 @@ namespace CSMWorld ///< Return a sorted collection of all IDs /// /// \param listDeleted include deleted record in the list + + void save (int index, ESM::ESMWriter& writer) const; }; } diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index c9edd0c16..6201a3cda 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -29,6 +29,8 @@ namespace { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Topics, "Topics", 0 }, + { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Journals, "Journals", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, "Referenceables", 0 }, @@ -54,6 +56,8 @@ namespace { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", ":./land.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", ":./birthsign.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":./spell.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", 0 }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" }, diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 246640733..ffd99e572 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -87,6 +87,10 @@ namespace CSMWorld Type_RegionMap, Type_Filter, Type_Filters, + Type_Topics, + Type_Topic, + Type_Journals, + Type_Journal, Type_Scene }; diff --git a/apps/opencs/view/doc/adjusterwidget.cpp b/apps/opencs/view/doc/adjusterwidget.cpp index 9e01f685c..09e58690f 100644 --- a/apps/opencs/view/doc/adjusterwidget.cpp +++ b/apps/opencs/view/doc/adjusterwidget.cpp @@ -11,7 +11,7 @@ #include CSVDoc::AdjusterWidget::AdjusterWidget (QWidget *parent) -: QWidget (parent), mValid (false) + : QWidget (parent), mValid (false), mAction (ContentAction_Undefined) { QHBoxLayout *layout = new QHBoxLayout (this); @@ -30,6 +30,11 @@ CSVDoc::AdjusterWidget::AdjusterWidget (QWidget *parent) setLayout (layout); } +void CSVDoc::AdjusterWidget::setAction (ContentAction action) +{ + mAction = action; +} + void CSVDoc::AdjusterWidget::setLocalData (const boost::filesystem::path& localData) { mLocalData = localData; @@ -43,41 +48,60 @@ boost::filesystem::path CSVDoc::AdjusterWidget::getPath() const 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; - if (name.isEmpty()) + mValid = (!name.isEmpty()); + + if (!mValid) { - mValid = false; message = "No name."; } else { boost::filesystem::path path (name.toUtf8().data()); - path.replace_extension (addon ? ".omwaddon" : ".omwgame"); + bool isLegacyPath = (path.extension() == ".esm" || + path.extension() == ".esp"); - if (path.parent_path().string()==mLocalData.string()) + bool isFilePathChanged = (path.parent_path().string() != mLocalData.string()); + + if (isLegacyPath) + path.replace_extension (addon ? ".omwaddon" : ".omwgame"); + + //if the file came from data-local and is not a legacy file to be converted, + //don't worry about doing a file check. + if (!isFilePathChanged && !isLegacyPath) { // path already points to the local data directory message = QString::fromUtf8 (("Will be saved as: " + path.string()).c_str()); mResultPath = path; - mValid = true; } + //in all other cases, ensure the path points to data-local and do an existing file check else { // path points somewhere else or is a leaf name. - path = mLocalData / path.filename(); + if (isFilePathChanged) + path = mLocalData / path.filename(); message = QString::fromUtf8 (("Will be saved as: " + path.string()).c_str()); mResultPath = path; - mValid = true; if (boost::filesystem::exists (path)) { /// \todo add an user setting to make this an error. - message += "

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

A file with the same name already exists. If you continue, it will be overwritten."; } } } @@ -88,4 +112,4 @@ void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon) pixmap (QSize (16, 16))); emit stateChanged (mValid); -} \ No newline at end of file +} diff --git a/apps/opencs/view/doc/adjusterwidget.hpp b/apps/opencs/view/doc/adjusterwidget.hpp index f578dc4ae..91e308236 100644 --- a/apps/opencs/view/doc/adjusterwidget.hpp +++ b/apps/opencs/view/doc/adjusterwidget.hpp @@ -9,21 +9,36 @@ 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. @@ -38,4 +53,4 @@ namespace CSVDoc }; } -#endif \ No newline at end of file +#endif diff --git a/apps/opencs/view/doc/filedialog.cpp b/apps/opencs/view/doc/filedialog.cpp index f956317a7..ab56415a1 100644 --- a/apps/opencs/view/doc/filedialog.cpp +++ b/apps/opencs/view/doc/filedialog.cpp @@ -9,264 +9,166 @@ #include #include #include +#include -#include -#include -#include +#include "components/contentselector/model/esmfile.hpp" +#include "components/contentselector/view/contentselector.hpp" -#include +#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 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 b21618d5d..d9fd56943 100644 --- a/apps/opencs/view/doc/filedialog.hpp +++ b/apps/opencs/view/doc/filedialog.hpp @@ -4,63 +4,71 @@ #include #include -#include "ui_datafilespage.h" +#include +#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 index c8f33e92d..9cd2fad42 100644 --- a/apps/opencs/view/doc/filewidget.cpp +++ b/apps/opencs/view/doc/filewidget.cpp @@ -50,4 +50,9 @@ QString CSVDoc::FileWidget::getName() const void CSVDoc::FileWidget::textChanged (const QString& text) { emit nameChanged (getName(), mAddon); -} \ No newline at end of file +} + +void CSVDoc::FileWidget::extensionLabelIsVisible(bool visible) +{ + mType->setVisible(visible); +} diff --git a/apps/opencs/view/doc/filewidget.hpp b/apps/opencs/view/doc/filewidget.hpp index c51c29632..ff09d71a3 100644 --- a/apps/opencs/view/doc/filewidget.hpp +++ b/apps/opencs/view/doc/filewidget.hpp @@ -27,6 +27,8 @@ namespace CSVDoc QString getName() const; + void extensionLabelIsVisible(bool visible); + private slots: void textChanged (const QString& text); diff --git a/apps/opencs/view/doc/newgame.hpp b/apps/opencs/view/doc/newgame.hpp index aa97682ff..9ad7ea169 100644 --- a/apps/opencs/view/doc/newgame.hpp +++ b/apps/opencs/view/doc/newgame.hpp @@ -6,7 +6,10 @@ #include #include +#ifndef CS_QT_BOOST_FILESYSTEM_PATH_DECLARED +#define CS_QT_BOOST_FILESYSTEM_PATH_DECLARED Q_DECLARE_METATYPE (boost::filesystem::path) +#endif class QPushButton; diff --git a/apps/opencs/view/doc/startup.cpp b/apps/opencs/view/doc/startup.cpp index 4cc64f2df..5d59492c6 100644 --- a/apps/opencs/view/doc/startup.cpp +++ b/apps/opencs/view/doc/startup.cpp @@ -104,6 +104,17 @@ CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2) layout->addWidget (createButtons()); layout->addWidget (createTools()); + /// \todo remove this label once loading and saving are fully implemented + QLabel *warning = new QLabel ("WARNING:

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

Please note:
If you lose data and come to the OpenMW forum to complain,
we will mock you.
"); + + QFont font; + font.setPointSize (12); + font.setBold (true); + + warning->setFont (font); + + layout->addWidget (warning, 1); + setLayout (layout); QRect scr = QApplication::desktop()->screenGeometry(); diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index b29250d20..5713449f2 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -163,6 +163,14 @@ void CSVDoc::View::setupMechanicsMenu() QAction *spells = new QAction (tr ("Spells"), this); connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView())); mechanics->addAction (spells); + + QAction *topics = new QAction (tr ("Topics"), this); + connect (topics, SIGNAL (triggered()), this, SLOT (addTopicsSubView())); + mechanics->addAction (topics); + + QAction *journals = new QAction (tr ("Journals"), this); + connect (journals, SIGNAL (triggered()), this, SLOT (addJournalsSubView())); + mechanics->addAction (journals); } void CSVDoc::View::setupAssetsMenu() @@ -412,6 +420,16 @@ void CSVDoc::View::addSceneSubView() addSubView (CSMWorld::UniversalId::Type_Scene); } +void CSVDoc::View::addTopicsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Topics); +} + +void CSVDoc::View::addJournalsSubView() +{ + addSubView (CSMWorld::UniversalId::Type_Journals); +} + void CSVDoc::View::abortOperation (int type) { mDocument->abortOperation (type); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index 6f3c38daa..2a31d9d80 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -166,6 +166,10 @@ namespace CSVDoc void addSceneSubView(); + void addTopicsSubView(); + + void addJournalsSubView(); + void toggleShowStatusBar (bool show); }; } diff --git a/apps/opencs/view/doc/viewmanager.cpp b/apps/opencs/view/doc/viewmanager.cpp index 83cd93e5d..a4849795b 100644 --- a/apps/opencs/view/doc/viewmanager.cpp +++ b/apps/opencs/view/doc/viewmanager.cpp @@ -74,7 +74,8 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager) { CSMWorld::ColumnBase::Display_ArmorType, CSMWorld::Columns::ColumnId_ArmorType, false }, { CSMWorld::ColumnBase::Display_ClothingType, CSMWorld::Columns::ColumnId_ClothingType, false }, { CSMWorld::ColumnBase::Display_CreatureType, CSMWorld::Columns::ColumnId_CreatureType, false }, - { CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false } + { CSMWorld::ColumnBase::Display_WeaponType, CSMWorld::Columns::ColumnId_WeaponType, false }, + { CSMWorld::ColumnBase::Display_DialogueType, CSMWorld::Columns::ColumnId_DialogueType, false } }; for (std::size_t i=0; icurrentIndex()) @@ -28,6 +33,15 @@ std::string CSVFilter::FilterCreator::getId() const return getNamespace() + GenericCreator::getId(); } +void CSVFilter::FilterCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const +{ + int index = + dynamic_cast (*getData().getTableModel (getCollectionId())). + findColumnIndex (CSMWorld::Columns::ColumnId_Scope); + + command.addValue (index, mScope->currentIndex()); +} + CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id) : GenericCreator (data, undoStack, id) @@ -39,7 +53,7 @@ CSVFilter::FilterCreator::FilterCreator (CSMWorld::Data& data, QUndoStack& undoS mScope->addItem ("Project"); mScope->addItem ("Session"); - /// \ŧodo re-enable for OpenMW 1.1 + /// \todo re-enable for OpenMW 1.1 // mScope->addItem ("Content"); connect (mScope, SIGNAL (currentIndexChanged (int)), this, SLOT (setScope (int))); diff --git a/apps/opencs/view/filter/filtercreator.hpp b/apps/opencs/view/filter/filtercreator.hpp index 82d38d22c..437d01c8d 100644 --- a/apps/opencs/view/filter/filtercreator.hpp +++ b/apps/opencs/view/filter/filtercreator.hpp @@ -25,6 +25,8 @@ namespace CSVFilter virtual std::string getId() const; + virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const; + public: FilterCreator (CSMWorld::Data& data, QUndoStack& undoStack, diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp new file mode 100644 index 000000000..c8b37e9bb --- /dev/null +++ b/apps/opencs/view/render/scenewidget.cpp @@ -0,0 +1,128 @@ +#include "scenewidget.hpp" + +#include +#include + +#include +#include +#include + +namespace CSVRender +{ + + SceneWidget::SceneWidget(QWidget *parent) + : QWidget(parent) + , mWindow(NULL) + , mCamera(NULL) + , mSceneMgr(NULL) + { + setAttribute(Qt::WA_PaintOnScreen); + setAttribute(Qt::WA_NoSystemBackground); + + mSceneMgr = Ogre::Root::getSingleton().createSceneManager(Ogre::ST_GENERIC); + + // Throw in a random color just to make sure multiple scenes work + Ogre::Real r = Ogre::Math::RangeRandom(0, 1); + Ogre::Real g = Ogre::Math::RangeRandom(0, 1); + Ogre::Real b = Ogre::Math::RangeRandom(0, 1); + mSceneMgr->setAmbientLight(Ogre::ColourValue(r,g,b,1)); + + Ogre::Light* l = mSceneMgr->createLight(); + l->setType (Ogre::Light::LT_DIRECTIONAL); + l->setDirection (Ogre::Vector3(-0.4, -0.7, 0.3)); + l->setDiffuseColour (Ogre::ColourValue(0.7,0.7,0.7)); + + mCamera = mSceneMgr->createCamera("foo"); + + Ogre::Entity* ent = mSceneMgr->createEntity("cube", Ogre::SceneManager::PT_CUBE); + ent->setMaterialName("BaseWhite"); + + mSceneMgr->getRootSceneNode()->attachObject(ent); + + mCamera->setPosition(300,300,300); + mCamera->lookAt(0,0,0); + mCamera->setNearClipDistance(0.1); + mCamera->setFarClipDistance(3000); + } + + void SceneWidget::updateOgreWindow() + { + if (mWindow) + { + Ogre::Root::getSingleton().destroyRenderTarget(mWindow); + mWindow = NULL; + } + + std::stringstream windowHandle; + windowHandle << this->winId(); + + std::stringstream windowTitle; + static int count=0; + windowTitle << ++count; + + Ogre::NameValuePairList params; + + params.insert(std::make_pair("externalWindowHandle", windowHandle.str())); + params.insert(std::make_pair("title", windowTitle.str())); + params.insert(std::make_pair("FSAA", "0")); // TODO setting + params.insert(std::make_pair("vsync", "false")); // TODO setting + + mWindow = Ogre::Root::getSingleton().createRenderWindow(windowTitle.str(), this->width(), this->height(), false, ¶ms); + mWindow->addViewport(mCamera)->setBackgroundColour(Ogre::ColourValue(0.3,0.3,0.3,1)); + + Ogre::Real aspectRatio = Ogre::Real(width()) / Ogre::Real(height()); + mCamera->setAspectRatio(aspectRatio); + } + + SceneWidget::~SceneWidget() + { + Ogre::Root::getSingleton().destroyRenderTarget(mWindow); + } + + void SceneWidget::paintEvent(QPaintEvent* e) + { + if (!mWindow) + updateOgreWindow(); + + mWindow->update(); + e->accept(); + } + + + QPaintEngine* SceneWidget::paintEngine() const + { + // We don't want another paint engine to get in the way. + // So we return nothing. + return NULL; + } + + void SceneWidget::resizeEvent(QResizeEvent *e) + { + if (!mWindow) + return; + + const QSize &newSize = e->size(); + + // TODO: Fix Ogre to handle this more consistently (fixed in 1.9) +#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX + mWindow->resize(newSize.width(), newSize.height()); +#else + mWindow->windowMovedOrResized(); +#endif + + Ogre::Real aspectRatio = Ogre::Real(newSize.width()) / Ogre::Real(newSize.height()); + mCamera->setAspectRatio(aspectRatio); + } + + bool SceneWidget::event(QEvent *e) + { + if (e->type() == QEvent::WinIdChange) + { + // I haven't actually seen this happen yet. + if (mWindow) + updateOgreWindow(); + } + return QWidget::event(e); + } + +} diff --git a/apps/opencs/view/render/scenewidget.hpp b/apps/opencs/view/render/scenewidget.hpp new file mode 100644 index 000000000..355a6331e --- /dev/null +++ b/apps/opencs/view/render/scenewidget.hpp @@ -0,0 +1,40 @@ +#ifndef OPENCS_VIEW_SCENEWIDGET_H +#define OPENCS_VIEW_SCENEWIDGET_H + +#include + +namespace Ogre +{ + class Camera; + class SceneManager; + class RenderWindow; +} + +namespace CSVRender +{ + + class SceneWidget : public QWidget + { + Q_OBJECT + + public: + SceneWidget(QWidget *parent); + virtual ~SceneWidget(void); + + QPaintEngine* paintEngine() const; + + private: + void paintEvent(QPaintEvent* e); + void resizeEvent(QResizeEvent* e); + bool event(QEvent* e); + + void updateOgreWindow(); + + Ogre::Camera* mCamera; + Ogre::SceneManager* mSceneMgr; + Ogre::RenderWindow* mWindow; + }; + +} + +#endif diff --git a/apps/opencs/view/world/dialoguecreator.cpp b/apps/opencs/view/world/dialoguecreator.cpp new file mode 100644 index 000000000..c16214283 --- /dev/null +++ b/apps/opencs/view/world/dialoguecreator.cpp @@ -0,0 +1,35 @@ + +#include "dialoguecreator.hpp" + +#include + +#include "../../model/world/data.hpp" +#include "../../model/world/commands.hpp" +#include "../../model/world/columns.hpp" +#include "../../model/world/idtable.hpp" + +void CSVWorld::DialogueCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const +{ + int index = + dynamic_cast (*getData().getTableModel (getCollectionId())). + findColumnIndex (CSMWorld::Columns::ColumnId_DialogueType); + + command.addValue (index, mType); +} + +CSVWorld::DialogueCreator::DialogueCreator (CSMWorld::Data& data, QUndoStack& undoStack, + const CSMWorld::UniversalId& id, int type) +: GenericCreator (data, undoStack, id), mType (type) +{} + +CSVWorld::Creator *CSVWorld::TopicCreatorFactory::makeCreator (CSMWorld::Data& data, + QUndoStack& undoStack, const CSMWorld::UniversalId& id) const +{ + return new DialogueCreator (data, undoStack, id, ESM::Dialogue::Topic); +} + +CSVWorld::Creator *CSVWorld::JournalCreatorFactory::makeCreator (CSMWorld::Data& data, + QUndoStack& undoStack, const CSMWorld::UniversalId& id) const +{ + return new DialogueCreator (data, undoStack, id, ESM::Dialogue::Journal); +} \ No newline at end of file diff --git a/apps/opencs/view/world/dialoguecreator.hpp b/apps/opencs/view/world/dialoguecreator.hpp new file mode 100644 index 000000000..26f866909 --- /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/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index e3618c549..83b30be13 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -9,6 +9,8 @@ #include "../filter/filterbox.hpp" +#include "../render/scenewidget.hpp" + #include "tablebottombox.hpp" #include "creator.hpp" #include "scenetoolbar.hpp" @@ -41,15 +43,10 @@ toolbar->addTool (new SceneToolMode (toolbar)); toolbar->addTool (new SceneToolMode (toolbar)); layout2->addWidget (toolbar, 0); - /// \todo replace with rendering widget - QPalette palette2 (palette()); - palette2.setColor (QPalette::Background, Qt::white); - QLabel *placeholder = new QLabel ("Here goes the 3D scene", this); - placeholder->setAutoFillBackground (true); - placeholder->setPalette (palette2); - placeholder->setAlignment (Qt::AlignHCenter); - layout2->addWidget (placeholder, 1); + CSVRender::SceneWidget* sceneWidget = new CSVRender::SceneWidget(this); + + layout2->addWidget (sceneWidget, 1); layout->insertLayout (0, layout2, 1); @@ -79,4 +76,4 @@ void CSVWorld::SceneSubView::updateEditorSetting(const QString &settingName, con void CSVWorld::SceneSubView::setStatusBar (bool show) { mBottom->setStatusBar (show); -} \ No newline at end of file +} diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 0e3465b38..3d98cf73c 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -14,6 +14,7 @@ #include "referenceablecreator.hpp" #include "referencecreator.hpp" #include "scenesubview.hpp" +#include "dialoguecreator.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { @@ -53,6 +54,12 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_References, new CSVDoc::SubViewFactoryWithCreator >); + manager.add (CSMWorld::UniversalId::Type_Topics, + new CSVDoc::SubViewFactoryWithCreator); + + manager.add (CSMWorld::UniversalId::Type_Journal, + new CSVDoc::SubViewFactoryWithCreator); + // Subviews for editing/viewing individual records manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 6167c084a..a58eb873f 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -87,19 +87,33 @@ std::vector CSVWorld::Table::listDeletableSelectedIds() const { QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); + // check record state CSMWorld::RecordBase::State state = static_cast ( mModel->data (mModel->index (index.row(), 1)).toInt()); - if (state!=CSMWorld::RecordBase::State_Deleted) - { - 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/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index e2dbf40de..42fd11a71 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -58,6 +58,7 @@ add_openmw_dir (mwworld cells localscripts customdata weather inventorystore ptr actionopen actionread actionequip timestamp actionalchemy cellstore actionapply actioneat esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor + contentloader esmloader omwloader ) add_openmw_dir (mwclass diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index 7e344c4db..3c4714d55 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -261,34 +261,14 @@ void OMW::Engine::setCell (const std::string& cellName) mCellName = cellName; } -// Set master file (esm) -// - If the given name does not have an extension, ".esm" is added automatically - -void OMW::Engine::addMaster (const std::string& master) +void OMW::Engine::addContentFile(const std::string& file) { - mMaster.push_back(master); - std::string &str = mMaster.back(); - - // Append .esm if not already there - std::string::size_type sep = str.find_last_of ("."); - if (sep == std::string::npos) - { - str += ".esm"; - } -} + if (file.find_last_of(".") == std::string::npos) + { + throw std::runtime_error("Missing extension in content file!"); + } -// Add plugin file (esp) -void OMW::Engine::addPlugin (const std::string& plugin) -{ - mPlugins.push_back(plugin); - std::string &str = mPlugins.back(); - - // Append .esp if not already there - std::string::size_type sep = str.find_last_of ("."); - if (sep == std::string::npos) - { - str += ".esp"; - } + mContentFiles.push_back(file); } void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity) @@ -357,8 +337,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mOgre->configure( mCfgMgr.getLogPath().string(), renderSystem, - Settings::Manager::getString("opengl rtt mode", "Video"), - false); + Settings::Manager::getString("opengl rtt mode", "Video")); // This has to be added BEFORE MyGUI is initialized, as it needs // to find core.xml here. @@ -370,7 +349,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"); @@ -401,7 +379,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) mEnvironment.setWindowManager (window); // 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(); @@ -416,8 +394,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) //Load translation data mTranslationDataStorage.setEncoder(mEncoder); - for (size_t i = 0; i < mMaster.size(); i++) - mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster[i]); + for (size_t i = 0; i < mContentFiles.size(); i++) + mTranslationDataStorage.loadTranslationData(mFileCollections, mContentFiles[i]); Compiler::registerExtensions (mExtensions); @@ -482,7 +460,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings) void OMW::Engine::go() { assert (!mCellName.empty()); - assert (!mMaster.empty()); + assert (!mContentFiles.empty()); assert (!mOgre); Settings::Manager settings; diff --git a/apps/openmw/engine.hpp b/apps/openmw/engine.hpp index 665b0094c..553d29068 100644 --- a/apps/openmw/engine.hpp +++ b/apps/openmw/engine.hpp @@ -68,8 +68,7 @@ namespace OMW boost::filesystem::path mResDir; OEngine::Render::OgreRenderer *mOgre; std::string mCellName; - std::vector mMaster; - std::vector mPlugins; + std::vector mContentFiles; int mFpsLevel; bool mVerboseScripts; bool mNewGame; @@ -135,13 +134,11 @@ namespace OMW /// Set start cell name (only interiors for now) void setCell(const std::string& cellName); - /// Set master file (esm) - /// - If the given name does not have an extension, ".esm" is added automatically - void addMaster(const std::string& master); - - /// Same as "addMaster", but for plugin files (esp) - /// - If the given name does not have an extension, ".esp" is added automatically - void addPlugin(const std::string& plugin); + /** + * @brief addContentFile - Adds content file (ie. esm/esp, or omwgame/omwaddon) to the content files container. + * @param file - filename (extension is required) + */ + void addContentFile(const std::string& file); /// Enable fps counter void showFPS(int level); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 27afd734a..33f740b31 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -110,11 +110,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("start", bpo::value()->default_value("Beshara"), "set initial cell") - ("master", bpo::value()->default_value(StringsVector(), "") - ->multitoken(), "master file(s)") - - ("plugin", bpo::value()->default_value(StringsVector(), "") - ->multitoken(), "plugin file(s)") + ("content", bpo::value()->default_value(StringsVector(), "") + ->multitoken(), "content file(s): esm/esp, or omwgame/omwaddon") ("anim-verbose", bpo::value()->implicit_value(true) ->default_value(false), "output animation indices files") @@ -152,8 +149,6 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat ("activate-dist", bpo::value ()->default_value (-1), "activation distance override"); - ; - bpo::parsed_options valid_opts = bpo::command_line_parser(argc, argv) .options(desc).allow_unregistered().run(); @@ -211,29 +206,18 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setResourceDir(variables["resources"].as()); - // master and plugin - StringsVector master = variables["master"].as(); - if (master.empty()) + StringsVector content = variables["content"].as(); + if (content.empty()) { - std::cout << "No master file given. Aborting...\n"; - return false; + std::cout << "No content file given (esm/esp, nor omwgame/omwaddon). Aborting..." << std::endl; + return false; } - StringsVector plugin = variables["plugin"].as(); - // Removed check for 255 files, which would be the hard-coded limit in Morrowind. - // I'll keep the following variable in, maybe we can use it for something different. - // Say, a feedback like "loading file x/cnt". - // Commenting this out for now to silence compiler warning. - //int cnt = master.size() + plugin.size(); - - // Prepare loading master/plugin files (i.e. send filenames to engine) - for (std::vector::size_type i = 0; i < master.size(); i++) - { - engine.addMaster(master[i]); - } - for (std::vector::size_type i = 0; i < plugin.size(); i++) + StringsVector::const_iterator it(content.begin()); + StringsVector::const_iterator end(content.end()); + for (; it != end; ++it) { - engine.addPlugin(plugin[i]); + engine.addContentFile(*it); } // startup-settings diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 8ae563e12..ddf841744 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -309,14 +309,19 @@ namespace MWBase virtual void update (float duration, bool paused) = 0; - virtual bool placeObject(const MWWorld::Ptr& object, float cursorX, float cursorY) = 0; - ///< place an object into the gameworld at the specified cursor position + virtual bool placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) = 0; + ///< copy and place an object into the gameworld at the specified cursor position /// @param object /// @param cursor X (relative 0-1) /// @param cursor Y (relative 0-1) + /// @param number of objects to place /// @return true if the object was placed, or false if it was rejected because the position is too far away - virtual void dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object) = 0; + virtual void dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount) = 0; + ///< copy and place an object into the gameworld at the given actor's position + /// @param actor giving the dropped object position + /// @param object + /// @param number of objects to place virtual bool canPlaceObject (float cursorX, float cursorY) = 0; ///< @return true if it is possible to place on object at specified cursor location @@ -408,6 +413,8 @@ namespace MWBase virtual bool toggleGodMode() = 0; virtual void castSpell (const MWWorld::Ptr& actor) = 0; + + virtual void updateAnimParts(const MWWorld::Ptr& ptr) = 0; }; } diff --git a/apps/openmw/mwclass/misc.cpp b/apps/openmw/mwclass/misc.cpp index 6247191a9..67f79c40b 100644 --- a/apps/openmw/mwclass/misc.cpp +++ b/apps/openmw/mwclass/misc.cpp @@ -219,7 +219,6 @@ namespace MWClass MWWorld::LiveCellRef *ref = newRef.getPtr().get(); newPtr = MWWorld::Ptr(&cell.mMiscItems.insert(*ref), &cell); - newPtr.getRefData ().setCount(1); newPtr.getCellRef().mGoldValue = goldAmount; } else { MWWorld::LiveCellRef *ref = diff --git a/apps/openmw/mwclass/potion.cpp b/apps/openmw/mwclass/potion.cpp index 08683a668..2f9e63d13 100644 --- a/apps/openmw/mwclass/potion.cpp +++ b/apps/openmw/mwclass/potion.cpp @@ -11,6 +11,7 @@ #include "../mwworld/actiontake.hpp" #include "../mwworld/actionapply.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwworld/containerstore.hpp" #include "../mwworld/physicssystem.hpp" #include "../mwworld/player.hpp" #include "../mwworld/nullaction.hpp" @@ -164,10 +165,11 @@ namespace MWClass MWWorld::LiveCellRef *ref = ptr.get(); - ptr.getRefData().setCount (ptr.getRefData().getCount()-1); - MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + // remove used potion (assume it is present in inventory) + ptr.getContainerStore()->remove(ptr, 1, actor); + boost::shared_ptr action ( new MWWorld::ActionApply (actor, ref->mBase->mId)); diff --git a/apps/openmw/mwgui/charactercreation.cpp b/apps/openmw/mwgui/charactercreation.cpp index 816f42e3d..b829f219d 100644 --- a/apps/openmw/mwgui/charactercreation.cpp +++ b/apps/openmw/mwgui/charactercreation.cpp @@ -219,9 +219,14 @@ namespace MWGui mReviewDialog->setClass(mPlayerClass); mReviewDialog->setBirthSign(mPlayerBirthSignId); - mReviewDialog->setHealth(mPlayerHealth); - mReviewDialog->setMagicka(mPlayerMagicka); - mReviewDialog->setFatigue(mPlayerFatigue); + { + MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWMechanics::CreatureStats stats = MWWorld::Class::get(player).getCreatureStats(player); + + mReviewDialog->setHealth ( stats.getHealth() ); + mReviewDialog->setMagicka( stats.getMagicka() ); + mReviewDialog->setFatigue( stats.getFatigue() ); + } { std::map > attributes = MWBase::Environment::get().getWindowManager()->getPlayerAttributeValues(); @@ -258,21 +263,6 @@ namespace MWGui mRaceDialog->doRenderUpdate(); } - void CharacterCreation::setPlayerHealth (const MWMechanics::DynamicStat& value) - { - mPlayerHealth = value; - } - - void CharacterCreation::setPlayerMagicka (const MWMechanics::DynamicStat& value) - { - mPlayerMagicka = value; - } - - void CharacterCreation::setPlayerFatigue (const MWMechanics::DynamicStat& value) - { - mPlayerFatigue = value; - } - void CharacterCreation::onReviewDialogDone(WindowBase* parWindow) { MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); @@ -285,7 +275,9 @@ namespace MWGui { MWBase::Environment::get().getWindowManager()->removeDialog(mReviewDialog); mReviewDialog = 0; + mCreationStage = CSE_ReviewBack; + MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); } @@ -332,24 +324,22 @@ namespace MWGui mPickClassDialog = 0; } + updatePlayerHealth(); + //TODO This bit gets repeated a few times; wrap it in a function + MWBase::Environment::get().getWindowManager()->popGuiMode(); if (mCreationStage == CSE_ReviewNext) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else if (mCreationStage >= CSE_ClassChosen) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); } else { mCreationStage = CSE_ClassChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); } - - updatePlayerHealth(); } void CharacterCreation::onPickClassDialogBack() @@ -403,20 +393,18 @@ namespace MWGui mNameDialog = 0; } + MWBase::Environment::get().getWindowManager()->popGuiMode(); if (mCreationStage == CSE_ReviewNext) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else if (mCreationStage >= CSE_NameChosen) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Race); } else { mCreationStage = CSE_NameChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); } } @@ -462,23 +450,21 @@ namespace MWGui mRaceDialog = 0; } + updatePlayerHealth(); + + MWBase::Environment::get().getWindowManager()->popGuiMode(); if (mCreationStage == CSE_ReviewNext) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else if (mCreationStage >= CSE_RaceChosen) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Class); } else { mCreationStage = CSE_RaceChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); } - - updatePlayerHealth(); } void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) @@ -492,18 +478,17 @@ namespace MWGui mBirthSignDialog = 0; } + updatePlayerHealth(); + + MWBase::Environment::get().getWindowManager()->popGuiMode(); if (mCreationStage >= CSE_BirthSignChosen) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else { mCreationStage = CSE_BirthSignChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); } - - updatePlayerHealth(); } void CharacterCreation::onBirthSignDialogBack() @@ -552,23 +537,21 @@ namespace MWGui mCreateClassDialog = 0; } + updatePlayerHealth(); + + MWBase::Environment::get().getWindowManager()->popGuiMode(); if (mCreationStage == CSE_ReviewNext) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else if (mCreationStage >= CSE_ClassChosen) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); } else { mCreationStage = CSE_ClassChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); } - - updatePlayerHealth(); } void CharacterCreation::onCreateClassDialogBack() @@ -722,23 +705,21 @@ namespace MWGui mPlayerClass = *klass; MWBase::Environment::get().getWindowManager()->setPlayerClass(mPlayerClass); + updatePlayerHealth(); + + MWBase::Environment::get().getWindowManager()->popGuiMode(); if (mCreationStage == CSE_ReviewNext) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Review); } else if (mCreationStage >= CSE_ClassChosen) { - MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->pushGuiMode(GM_Birth); } else { mCreationStage = CSE_ClassChosen; - MWBase::Environment::get().getWindowManager()->popGuiMode(); } - - updatePlayerHealth(); } CharacterCreation::~CharacterCreation() diff --git a/apps/openmw/mwgui/charactercreation.hpp b/apps/openmw/mwgui/charactercreation.hpp index bd8826677..b80aaae41 100644 --- a/apps/openmw/mwgui/charactercreation.hpp +++ b/apps/openmw/mwgui/charactercreation.hpp @@ -31,12 +31,6 @@ namespace MWGui //Show a dialog void spawnDialog(const char id); - void setPlayerHealth (const MWMechanics::DynamicStat& value); - - void setPlayerMagicka (const MWMechanics::DynamicStat& value); - - void setPlayerFatigue (const MWMechanics::DynamicStat& value); - void setValue (const std::string& id, const MWMechanics::Stat& value); void setValue (const std::string& id, const MWMechanics::DynamicStat& value); void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::Stat& value); @@ -60,9 +54,6 @@ namespace MWGui std::string mPlayerRaceId; std::string mPlayerBirthSignId; ESM::Class mPlayerClass; - MWMechanics::DynamicStat mPlayerHealth; - MWMechanics::DynamicStat mPlayerMagicka; - MWMechanics::DynamicStat mPlayerFatigue; //Class generation vars unsigned mGenerateClassStep; // Keeps track of current step in Generate Class dialog @@ -104,6 +95,7 @@ namespace MWGui CSE_RaceChosen, CSE_ClassChosen, CSE_BirthSignChosen, + CSE_ReviewBack, CSE_ReviewNext }; diff --git a/apps/openmw/mwgui/container.cpp b/apps/openmw/mwgui/container.cpp index bc869e5fe..5d864752f 100644 --- a/apps/openmw/mwgui/container.cpp +++ b/apps/openmw/mwgui/container.cpp @@ -100,6 +100,9 @@ namespace MWGui finish(); targetView->update(); + + // We need to update the view since an other item could be auto-equipped. + mSourceView->update(); } void DragAndDrop::finish() diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp index eff8fbcc1..6b0fbd890 100644 --- a/apps/openmw/mwgui/containeritemmodel.cpp +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -94,9 +94,7 @@ void ContainerItemModel::removeItem (const ItemStack& item, size_t count) { if (stacks(*it, item.mBase)) { - int refCount = it->getRefData().getCount(); - it->getRefData().setCount(std::max(0, refCount - toRemove)); - toRemove -= refCount; + toRemove -= store.remove(*it, toRemove, *source); if (toRemove <= 0) return; } diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index e7b9f9c01..f0843834d 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -243,21 +243,16 @@ namespace MWGui float mouseX = cursorPosition.left / float(viewSize.width); float mouseY = cursorPosition.top / float(viewSize.height); - int origCount = object.getRefData().getCount(); - object.getRefData().setCount(mDragAndDrop->mDraggedCount); - if (world->canPlaceObject(mouseX, mouseY)) - world->placeObject(object, mouseX, mouseY); + world->placeObject(object, mouseX, mouseY, mDragAndDrop->mDraggedCount); else - world->dropObjectOnGround(world->getPlayer().getPlayer(), object); + world->dropObjectOnGround(world->getPlayer().getPlayer(), object, mDragAndDrop->mDraggedCount); MWBase::Environment::get().getWindowManager()->changePointer("arrow"); std::string sound = MWWorld::Class::get(object).getDownSoundId(object); MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); - object.getRefData().setCount(origCount); - // remove object from the container it was coming from mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount); mDragAndDrop->finish(); diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index 62a5a75f0..712e1b6c6 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -52,18 +52,12 @@ void InventoryItemModel::copyItem (const ItemStack& item, size_t count) void InventoryItemModel::removeItem (const ItemStack& item, size_t count) { MWWorld::ContainerStore& store = MWWorld::Class::get(mActor).getContainerStore(mActor); + int removed = store.remove(item.mBase, count, mActor); - for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it) - { - if (*it == item.mBase) - { - if (it->getRefData().getCount() < static_cast(count)) - throw std::runtime_error("Not enough items in the stack to remove"); - it->getRefData().setCount(it->getRefData().getCount() - count); - return; - } - } - throw std::runtime_error("Item to remove not found in container store"); + if (removed == 0) + throw std::runtime_error("Item to remove not found in container store"); + else if (removed < count) + throw std::runtime_error("Not enough items in the stack to remove"); } void InventoryItemModel::update() diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index 4f616b312..0ae633aa0 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -145,10 +145,38 @@ namespace MWGui const ItemStack& item = mTradeModel->getItem(index); - unequipItem(item.mBase); - MWWorld::Ptr object = item.mBase; int count = item.mCount; + + if (item.mType == ItemStack::Type_Equipped) + { + MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); + MWWorld::Ptr newStack = *invStore.unequipItem(item.mBase, mPtr); + + // The unequipped item was re-stacked. We have to update the index + // since the item pointed does not exist anymore. + if (item.mBase != newStack) + { + // newIndex will store the index of the ItemStack the item was stacked on + int newIndex = -1; + for (size_t i=0; i < mTradeModel->getItemCount(); ++i) + { + if (mTradeModel->getItem(i).mBase == newStack) + { + newIndex = i; + break; + } + } + + if (newIndex == -1) + throw std::runtime_error("Can't find restacked item"); + + index = newIndex; + object = mTradeModel->getItem(index).mBase; + } + + } + bool shift = MyGUI::InputManager::getInstance().isShiftPressed(); if (MyGUI::InputManager::getInstance().isControlPressed()) count = 1; @@ -375,27 +403,6 @@ namespace MWGui return MWWorld::Ptr(); } - void InventoryWindow::unequipItem(const MWWorld::Ptr& item) - { - MWWorld::InventoryStore& invStore = MWWorld::Class::get(mPtr).getInventoryStore(mPtr); - - for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) - { - MWWorld::ContainerStoreIterator it = invStore.getSlot(slot); - if (it != invStore.end() && *it == item) - { - invStore.equip(slot, invStore.end()); - std::string script = MWWorld::Class::get(*it).getScript(*it); - - // Unset OnPCEquip Variable on item's script, if it has a script with that variable declared - if(script != "") - (*it).getRefData().getLocals().setVarByInt(script, "onpcequip", 0); - - return; - } - } - } - void InventoryWindow::updateEncumbranceBar() { MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); diff --git a/apps/openmw/mwgui/inventorywindow.hpp b/apps/openmw/mwgui/inventorywindow.hpp index 35140437d..78d00fd1e 100644 --- a/apps/openmw/mwgui/inventorywindow.hpp +++ b/apps/openmw/mwgui/inventorywindow.hpp @@ -103,7 +103,6 @@ namespace MWGui void onAvatarClicked(MyGUI::Widget* _sender); void onPinToggled(); - void unequipItem(const MWWorld::Ptr& item); void updateEncumbranceBar(); void notifyContentChanged(); }; diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index 94141b1a0..c17923608 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -202,30 +202,17 @@ namespace MWGui void TradeWindow::addOrRemoveGold(int amount) { - bool goldFound = false; - MWWorld::Ptr gold; MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::ContainerStore& playerStore = MWWorld::Class::get(player).getContainerStore(player); - for (MWWorld::ContainerStoreIterator it = playerStore.begin(); - it != playerStore.end(); ++it) + if (amount > 0) { - if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, "gold_001")) - { - goldFound = true; - gold = *it; - } - } - if (goldFound) - { - gold.getRefData().setCount(gold.getRefData().getCount() + amount); + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), "Gold_001", amount); + playerStore.add(ref.getPtr(), player); } else { - assert(amount > 0); - MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), "Gold_001"); - ref.getPtr().getRefData().setCount(amount); - playerStore.add(ref.getPtr(), player); + playerStore.remove("gold_001", - amount, player); } } diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 4b4d2dfd1..76ae65eb9 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -581,17 +581,14 @@ namespace MWGui if (id == "HBar") { mPlayerHealth = value; - mCharGen->setPlayerHealth (value); } else if (id == "MBar") { mPlayerMagicka = value; - mCharGen->setPlayerMagicka (value); } else if (id == "FBar") { mPlayerFatigue = value; - mCharGen->setPlayerFatigue (value); } } @@ -599,7 +596,7 @@ namespace MWGui MWMechanics::DynamicStat WindowManager::getValue(const std::string& id) { if(id == "HBar") - return layerHealth; + return mPlayerHealth; else if (id == "MBar") return mPlayerMagicka; else if (id == "FBar") diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 32f3b4bf9..4ed7f71b4 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -246,7 +246,7 @@ namespace MWMechanics heldIter->getClass().setRemainingUsageTime(*heldIter, timeRemaining); else { - heldIter->getRefData().setCount(0); // remove it + inventoryStore.remove(*heldIter, 1, ptr); // remove it return; } } @@ -255,7 +255,7 @@ namespace MWMechanics // Both NPC and player lights extinguish in water. if(MWBase::Environment::get().getWorld()->isSwimming(ptr)) { - heldIter->getRefData().setCount(0); // remove it + inventoryStore.remove(*heldIter, 1, ptr); // remove it // ...But, only the player makes a sound. if(isPlayer) diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index c92b2c040..82580ce0e 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -30,13 +30,13 @@ std::set MWMechanics::Alchemy::listEffects() const { std::map effects; - + for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter) { if (!iter->isEmpty()) { const MWWorld::LiveCellRef *ingredient = iter->get(); - + for (int i=0; i<4; ++i) if (ingredient->mBase->mData.mEffectID[i]!=-1) { @@ -48,13 +48,13 @@ std::set MWMechanics::Alchemy::listEffects() const } } } - + std::set effects2; - + for (std::map::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter) if (iter->second>1) effects2.insert (iter->first); - + return effects2; } @@ -67,7 +67,7 @@ void MWMechanics::Alchemy::applyTools (int flags, float& value) const int tool = negative ? ESM::Apparatus::Retort : ESM::Apparatus::Albemic; int setup = 0; - + if (!mTools[tool].isEmpty() && !mTools[ESM::Apparatus::Calcinator].isEmpty()) setup = 1; else if (!mTools[tool].isEmpty()) @@ -82,23 +82,23 @@ void MWMechanics::Alchemy::applyTools (int flags, float& value) const mTools[ESM::Apparatus::Calcinator].get()->mBase->mData.mQuality : 0; float quality = 1; - + switch (setup) { case 1: - + quality = negative ? 2 * toolQuality + 3 * calcinatorQuality : (magnitude && duration ? 2 * toolQuality + calcinatorQuality : 2/3.0 * (toolQuality + calcinatorQuality) + 0.5); break; - + case 2: - + quality = negative ? 1+toolQuality : (magnitude && duration ? toolQuality : toolQuality + 0.5); break; - + case 3: - + quality = magnitude && duration ? calcinatorQuality : calcinatorQuality + 0.5; break; } @@ -110,8 +110,8 @@ void MWMechanics::Alchemy::applyTools (int flags, float& value) const else { if (quality==0) - throw std::runtime_error ("invalid derived alchemy apparatus quality"); - + throw std::runtime_error ("invalid derived alchemy apparatus quality"); + value /= quality; } } @@ -141,21 +141,21 @@ void MWMechanics::Alchemy::updateEffects() for (std::set::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter) { const ESM::MagicEffect *magicEffect = - MWBase::Environment::get().getWorld()->getStore().get().find (iter->mId); - + MWBase::Environment::get().getWorld()->getStore().get().find (iter->mId); + if (magicEffect->mData.mBaseCost<=0) { std::ostringstream os; os << "invalid base cost for magic effect " << iter->mId; throw std::runtime_error (os.str()); } - + float fPotionT1MagMul = MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionT1MagMult")->getFloat(); if (fPotionT1MagMul<=0) throw std::runtime_error ("invalid gmst: fPotionT1MagMul"); - + float fPotionT1DurMult = MWBase::Environment::get().getWorld()->getStore().get().find ("fPotionT1DurMult")->getFloat(); @@ -172,25 +172,25 @@ void MWMechanics::Alchemy::updateEffects() if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) applyTools (magicEffect->mData.mFlags, duration); - - duration = static_cast (duration+0.5); + + duration = static_cast (duration+0.5); magnitude = static_cast (magnitude+0.5); if (magnitude>0 && duration>0) { ESM::ENAMstruct effect; effect.mEffectID = iter->mId; - + effect.mSkill = effect.mAttribute = iter->mArg; // somewhat hack-ish, but should work - + effect.mRange = 0; - effect.mArea = 0; - + effect.mArea = 0; + effect.mDuration = duration; effect.mMagnMin = effect.mMagnMax = magnitude; mEffects.push_back (effect); - } + } } } @@ -204,14 +204,14 @@ const ESM::Potion *MWMechanics::Alchemy::getRecord() const { if (iter->mEffects.mList.size() != mEffects.size()) continue; - - bool mismatch = false; + + bool mismatch = false; for (int i=0; i (iter->mEffects.mList.size()); ++i) { const ESM::ENAMstruct& first = iter->mEffects.mList[i]; const ESM::ENAMstruct& second = mEffects[i]; - + if (first.mEffectID!=second.mEffectID || first.mArea!=second.mArea || first.mRange!=second.mRange || @@ -225,29 +225,30 @@ const ESM::Potion *MWMechanics::Alchemy::getRecord() const break; } } - + if (!mismatch) return &(*iter); } - + return 0; } void MWMechanics::Alchemy::removeIngredients() { bool needsUpdate = false; - + for (TIngredientsContainer::iterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter) if (!iter->isEmpty()) { - iter->getRefData().setCount (iter->getRefData().getCount()-1); + iter->getContainerStore()->remove(*iter, 1, mAlchemist); + if (iter->getRefData().getCount()<1) { needsUpdate = true; *iter = MWWorld::Ptr(); } } - + if (needsUpdate) updateEffects(); } @@ -255,37 +256,37 @@ void MWMechanics::Alchemy::removeIngredients() void MWMechanics::Alchemy::addPotion (const std::string& name) { const ESM::Potion *record = getRecord(); - + if (!record) { ESM::Potion newRecord; - + newRecord.mData.mWeight = 0; - + for (TIngredientsIterator iter (beginIngredients()); iter!=endIngredients(); ++iter) if (!iter->isEmpty()) newRecord.mData.mWeight += iter->get()->mBase->mData.mWeight; - + newRecord.mData.mWeight /= countIngredients(); - + newRecord.mData.mValue = mValue; newRecord.mData.mAutoCalc = 0; - + newRecord.mName = name; int index = static_cast (std::rand()/(static_cast (RAND_MAX)+1)*6); assert (index>=0 && index<6); - + static const char *name[] = { "standard", "bargain", "cheap", "fresh", "exclusive", "quality" }; - + newRecord.mModel = "m\\misc_potion_" + std::string (name[index]) + "_01.nif"; newRecord.mIcon = "m\\tx_potion_" + std::string (name[index]) + "_01.dds"; - + newRecord.mEffects.mList = mEffects; - + record = MWBase::Environment::get().getWorld()->createRecord (newRecord); } - + MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), record->mId); MWWorld::Class::get (mAlchemist).getContainerStore (mAlchemist).add (ref.getPtr(), mAlchemist); } @@ -299,7 +300,7 @@ float MWMechanics::Alchemy::getChance() const { const CreatureStats& creatureStats = MWWorld::Class::get (mAlchemist).getCreatureStats (mAlchemist); const NpcStats& npcStats = MWWorld::Class::get (mAlchemist).getNpcStats (mAlchemist); - + return (npcStats.getSkill (ESM::Skill::Alchemy).getModified() + 0.1 * creatureStats.getAttribute (1).getModified() @@ -320,34 +321,34 @@ int MWMechanics::Alchemy::countIngredients() const void MWMechanics::Alchemy::setAlchemist (const MWWorld::Ptr& npc) { mAlchemist = npc; - + mIngredients.resize (4); std::fill (mIngredients.begin(), mIngredients.end(), MWWorld::Ptr()); - + mTools.resize (4); - + std::fill (mTools.begin(), mTools.end(), MWWorld::Ptr()); - + mEffects.clear(); - + MWWorld::ContainerStore& store = MWWorld::Class::get (npc).getContainerStore (npc); - + for (MWWorld::ContainerStoreIterator iter (store.begin (MWWorld::ContainerStore::Type_Apparatus)); iter!=store.end(); ++iter) - { + { MWWorld::LiveCellRef* ref = iter->get(); - + int type = ref->mBase->mData.mType; - + if (type<0 || type>=static_cast (mTools.size())) throw std::runtime_error ("invalid apparatus type"); - + if (!mTools[type].isEmpty()) if (ref->mBase->mData.mQuality<=mTools[type].get()->mBase->mData.mQuality) continue; - - mTools[type] = *iter; + + mTools[type] = *iter; } } @@ -389,19 +390,19 @@ int MWMechanics::Alchemy::addIngredient (const MWWorld::Ptr& ingredient) { slot = i; break; - } - + } + if (slot==-1) return -1; - + for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter) if (!iter->isEmpty() && ingredient.get()==iter->get()) return -1; - + mIngredients[slot] = ingredient; - + updateEffects(); - + return slot; } @@ -428,7 +429,7 @@ std::string MWMechanics::Alchemy::getPotionName() const { if (const ESM::Potion *potion = getRecord()) return potion->mName; - + return ""; } @@ -436,13 +437,13 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& na { if (mTools[ESM::Apparatus::MortarPestle].isEmpty()) return Result_NoMortarAndPestle; - + if (countIngredients()<2) return Result_LessThanTwoIngredients; if (name.empty() && getPotionName().empty()) return Result_NoName; - + if (beginEffects()==endEffects()) return Result_NoEffects; @@ -455,7 +456,7 @@ MWMechanics::Alchemy::Result MWMechanics::Alchemy::create (const std::string& na addPotion (name); removeIngredients(); - + increaseSkill(); return Result_Success; diff --git a/apps/openmw/mwmechanics/enchanting.cpp b/apps/openmw/mwmechanics/enchanting.cpp index 4e26b5027..5d148d110 100644 --- a/apps/openmw/mwmechanics/enchanting.cpp +++ b/apps/openmw/mwmechanics/enchanting.cpp @@ -14,7 +14,6 @@ namespace MWMechanics Enchanting::Enchanting() : mCastStyle(ESM::Enchantment::CastOnce) , mSelfEnchanting(false) - , mOldItemCount(0) {} void Enchanting::setOldItem(MWWorld::Ptr oldItem) @@ -24,7 +23,6 @@ namespace MWMechanics { mObjectType = mOldItemPtr.getTypeName(); mOldItemId = mOldItemPtr.getCellRef().mRefID; - mOldItemCount = mOldItemPtr.getRefData().getCount(); } else { @@ -55,17 +53,18 @@ namespace MWMechanics bool Enchanting::create() { - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); ESM::Enchantment enchantment; enchantment.mData.mCharge = getGemCharge(); - mSoulGemPtr.getRefData().setCount (mSoulGemPtr.getRefData().getCount()-1); + store.remove(mSoulGemPtr, 1, player); //Exception for Azura Star, new one will be added after enchanting if(boost::iequals(mSoulGemPtr.get()->mBase->mId, "Misc_SoulGem_Azura")) { MWWorld::ManualRef azura (MWBase::Environment::get().getWorld()->getStore(), "Misc_SoulGem_Azura"); - MWWorld::Class::get (player).getContainerStore (player).add (azura.getPtr(), player); + store.add(azura.getPtr(), player); } if(mSelfEnchanting) @@ -84,16 +83,18 @@ namespace MWMechanics enchantment.mData.mCost = getEnchantPoints(); enchantment.mEffects = mEffectList; - const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment); - - MWWorld::Class::get(mOldItemPtr).applyEnchantment(mOldItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName); + // Create a new item + MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), mOldItemId, 1); + const MWWorld::Ptr& newItemPtr = ref.getPtr(); - mOldItemPtr.getRefData().setCount(1); + // Apply the enchantment + const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment); + MWWorld::Class::get(newItemPtr).applyEnchantment(newItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName); - MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), mOldItemId); - ref.getPtr().getRefData().setCount (mOldItemCount-1); + // Add the new item to player inventory and remove the old one + store.add(newItemPtr, player); + store.remove(mOldItemPtr, 1, player); - MWWorld::Class::get (player).getContainerStore (player).add (ref.getPtr(), player); if(!mSelfEnchanting) payForEnchantment(); @@ -299,20 +300,9 @@ namespace MWMechanics void Enchanting::payForEnchantment() const { - MWWorld::Ptr gold; - - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); + const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); - for (MWWorld::ContainerStoreIterator it = store.begin(); - it != store.end(); ++it) - { - if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, "gold_001")) - { - gold = *it; - } - } - - gold.getRefData().setCount(gold.getRefData().getCount() - getEnchantPrice()); + store.remove("gold_001", getEnchantPrice(), player); } } diff --git a/apps/openmw/mwmechanics/enchanting.hpp b/apps/openmw/mwmechanics/enchanting.hpp index a25fd43ab..988ce41fc 100644 --- a/apps/openmw/mwmechanics/enchanting.hpp +++ b/apps/openmw/mwmechanics/enchanting.hpp @@ -22,7 +22,6 @@ namespace MWMechanics std::string mNewItemName; std::string mObjectType; std::string mOldItemId; - int mOldItemCount; public: Enchanting(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 8c13db790..54b02fb52 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -159,7 +159,7 @@ namespace MWMechanics // auto-equip again. we need this for when the race is changed to a beast race MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore(ptr); for (int i=0; igetPlayer().getPlayer(); + MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); + + store.remove(mTool, 1, player); std::string message = MWBase::Environment::get().getWorld()->getStore().get() .find("sNotifyMessage51")->getString(); @@ -93,8 +96,6 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) MWBase::Environment::get().getWindowManager()->messageBox((boost::format(message) % MWWorld::Class::get(mTool).getName(mTool)).str()); // try to find a new tool of the same ID - MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer(); - MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player); for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) { diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index d19da6e2a..c373e83f5 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -2,6 +2,7 @@ #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" +#include "../mwworld/containerstore.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" @@ -61,7 +62,7 @@ namespace MWMechanics lockpick.getCellRef().mCharge = lockpick.get()->mBase->mData.mUses; --lockpick.getCellRef().mCharge; if (!lockpick.getCellRef().mCharge) - lockpick.getRefData().setCount(0); + lockpick.getContainerStore()->remove(lockpick, 1, mActor); } void Security::probeTrap(const MWWorld::Ptr &trap, const MWWorld::Ptr &probe, @@ -103,7 +104,7 @@ namespace MWMechanics probe.getCellRef().mCharge = probe.get()->mBase->mData.mUses; --probe.getCellRef().mCharge; if (!probe.getCellRef().mCharge) - probe.getRefData().setCount(0); + probe.getContainerStore()->remove(probe, 1, mActor); } } diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 93425191d..817a2d236 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -327,6 +327,19 @@ void RenderingManager::rebuildPtr(const MWWorld::Ptr &ptr) } } +void RenderingManager::updateAnimParts(const MWWorld::Ptr& ptr) +{ + NpcAnimation *anim = NULL; + + if(ptr.getRefData().getHandle() == "player") + anim = mPlayerAnimation; + else if(MWWorld::Class::get(ptr).isActor()) + anim = dynamic_cast(mActors.getAnimation(ptr)); + + if(anim) + anim->updateParts(); +} + void RenderingManager::update (float duration, bool paused) { MWBase::World *world = MWBase::Environment::get().getWorld(); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 2d0813912..8913e0a78 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -141,6 +141,9 @@ public: /// and equipment. void rebuildPtr(const MWWorld::Ptr &ptr); + /// Update actor model parts. + void updateAnimParts(const MWWorld::Ptr &ptr); + void update (float duration, bool paused); void setAmbientColour(const Ogre::ColourValue& colour); diff --git a/apps/openmw/mwscript/containerextensions.cpp b/apps/openmw/mwscript/containerextensions.cpp index 2f3ef2d79..d3ed50838 100644 --- a/apps/openmw/mwscript/containerextensions.cpp +++ b/apps/openmw/mwscript/containerextensions.cpp @@ -53,10 +53,8 @@ namespace MWScript if (count == 0) return; - MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), item); + MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), item, count); - ref.getPtr().getRefData().setCount (count); - // Configure item's script variables std::string script = MWWorld::Class::get(ref.getPtr()).getScript(ref.getPtr()); if (script != "") @@ -136,41 +134,18 @@ namespace MWScript return; MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); - + std::string itemName = ""; - // originalCount holds the total number of items to remove, count holds the remaining number of items to remove - Interpreter::Type_Integer originalCount = count; + int numRemoved = store.remove(item, count, ptr); - for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end() && count; - ++iter) - { - if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item)) - { - itemName = MWWorld::Class::get(*iter).getName(*iter); - - if (iter->getRefData().getCount()<=count) - { - count -= iter->getRefData().getCount(); - iter->getRefData().setCount (0); - } - else - { - iter->getRefData().setCount (iter->getRefData().getCount()-count); - count = 0; - } - } - } - // Spawn a messagebox (only for items removed from player's inventory) - if (ptr == MWBase::Environment::get().getWorld ()->getPlayer ().getPlayer()) + if ((numRemoved > 0) + && (ptr == MWBase::Environment::get().getWorld()->getPlayer().getPlayer())) { // The two GMST entries below expand to strings informing the player of what, and how many of it has been removed from their inventory std::string msgBox; - int numRemoved = (originalCount - count); - if (numRemoved == 0) - return; - + if(numRemoved > 1) { msgBox = MyGUI::LanguageManager::getInstance().replaceTags("#{sNotifyMessage63}"); diff --git a/apps/openmw/mwscript/miscextensions.cpp b/apps/openmw/mwscript/miscextensions.cpp index 4ae1136e2..35f7a4044 100644 --- a/apps/openmw/mwscript/miscextensions.cpp +++ b/apps/openmw/mwscript/miscextensions.cpp @@ -348,9 +348,7 @@ namespace MWScript const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); store.get().find(creature); // This line throws an exception if it can't find the creature - MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), gem); - - ref.getPtr().getRefData().setCount (1); + MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), gem, 1); ref.getPtr().getCellRef().mSoul = creature; @@ -374,18 +372,7 @@ namespace MWScript MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); - - for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) - { - if (::Misc::StringUtils::ciEqual(iter->getCellRef().mSoul, soul)) - { - if (iter->getRefData().getCount() <= 1) - iter->getRefData().setCount (0); - else - iter->getRefData().setCount (iter->getRefData().getCount() - 1); - break; - } - } + store.remove(soul, 1, ptr); } }; @@ -415,24 +402,18 @@ namespace MWScript MWWorld::ContainerStore& store = MWWorld::Class::get (ptr).getContainerStore (ptr); + int toRemove = amount; for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) { if (::Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item)) { - if(iter->getRefData().getCount() <= amount) - { - MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter); - iter->getRefData().setCount(0); - } - else - { - int original = iter->getRefData().getCount(); - iter->getRefData().setCount(amount); - MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter); - iter->getRefData().setCount(original - amount); - } + int removed = store.remove(*iter, toRemove, ptr); + MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, removed); - break; + toRemove -= removed; + + if (toRemove <= 0) + break; } } } @@ -458,20 +439,8 @@ namespace MWScript { if (::Misc::StringUtils::ciEqual(iter->getCellRef().mSoul, soul)) { - - if(iter->getRefData().getCount() <= 1) - { - MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter); - iter->getRefData().setCount(0); - } - else - { - int original = iter->getRefData().getCount(); - iter->getRefData().setCount(1); - MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter); - iter->getRefData().setCount(original - 1); - } - + MWBase::Environment::get().getWorld()->dropObjectOnGround(ptr, *iter, 1); + store.remove(*iter, 1, ptr); break; } } @@ -541,12 +510,7 @@ namespace MWScript runtime.pop(); if (parameter == 1) - { - if (ptr.isInCell()) - MWBase::Environment::get().getWorld()->deleteObject (ptr); - else - ptr.getRefData().setCount(0); - } + MWBase::Environment::get().getWorld()->deleteObject(ptr); } }; diff --git a/apps/openmw/mwscript/transformationextensions.cpp b/apps/openmw/mwscript/transformationextensions.cpp index 6246daee2..4b60f47ce 100644 --- a/apps/openmw/mwscript/transformationextensions.cpp +++ b/apps/openmw/mwscript/transformationextensions.cpp @@ -450,62 +450,16 @@ namespace MWScript } }; - template - class OpPlaceAtPc : public Interpreter::Opcode0 - { - public: - - virtual void execute (Interpreter::Runtime& runtime) - { - std::string itemID = runtime.getStringLiteral (runtime[0].mInteger); - runtime.pop(); - - Interpreter::Type_Integer count = runtime[0].mInteger; - runtime.pop(); - Interpreter::Type_Float distance = runtime[0].mFloat; - runtime.pop(); - Interpreter::Type_Integer direction = runtime[0].mInteger; - runtime.pop(); - - if (count<0) - throw std::runtime_error ("count must be non-negative"); - - // no-op - if (count == 0) - return; - - ESM::Position ipos = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getRefData().getPosition(); - Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]); - Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); - if(direction == 0) pos = pos + distance*rot.yAxis(); - else if(direction == 1) pos = pos - distance*rot.yAxis(); - else if(direction == 2) pos = pos - distance*rot.xAxis(); - else if(direction == 3) pos = pos + distance*rot.xAxis(); - else throw std::runtime_error ("direction must be 0,1,2 or 3"); - - ipos.pos[0] = pos.x; - ipos.pos[1] = pos.y; - ipos.pos[2] = pos.z; - ipos.rot[0] = 0; - ipos.rot[1] = 0; - ipos.rot[2] = 0; - - MWWorld::CellStore* store = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); - MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); - ref.getPtr().getCellRef().mPos = ipos; - ref.getPtr().getRefData().setCount(count); - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos); - } - }; - - template - class OpPlaceAtMe : public Interpreter::Opcode0 + template + class OpPlaceAt : public Interpreter::Opcode0 { public: virtual void execute (Interpreter::Runtime& runtime) { - MWWorld::Ptr me = R()(runtime); + MWWorld::Ptr actor = pc + ? MWBase::Environment::get().getWorld()->getPlayer().getPlayer() + : R()(runtime); std::string itemID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); @@ -524,7 +478,7 @@ namespace MWScript if (count == 0) return; - ESM::Position ipos = me.getRefData().getPosition(); + ESM::Position ipos = actor.getRefData().getPosition(); Ogre::Vector3 pos(ipos.pos[0],ipos.pos[1],ipos.pos[2]); Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z); if(direction == 0) pos = pos + distance*rot.yAxis(); @@ -540,12 +494,12 @@ namespace MWScript ipos.rot[1] = 0; ipos.rot[2] = 0; - MWWorld::CellStore* store = me.getCell(); - MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(),itemID); + // create item + MWWorld::CellStore* store = actor.getCell(); + MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), itemID, count); ref.getPtr().getCellRef().mPos = ipos; - ref.getPtr().getRefData().setCount(count); - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos); + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),*store,ipos); } }; @@ -730,9 +684,9 @@ namespace MWScript interpreter.installSegment5(Compiler::Transformation::opcodePositionCellExplicit,new OpPositionCell); interpreter.installSegment5(Compiler::Transformation::opcodePlaceItemCell,new OpPlaceItemCell); interpreter.installSegment5(Compiler::Transformation::opcodePlaceItem,new OpPlaceItem); - interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtPc,new OpPlaceAtPc); - interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMe,new OpPlaceAtMe); - interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMeExplicit,new OpPlaceAtMe); + interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtPc,new OpPlaceAt); + interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMe,new OpPlaceAt); + interpreter.installSegment5(Compiler::Transformation::opcodePlaceAtMeExplicit,new OpPlaceAt); interpreter.installSegment5(Compiler::Transformation::opcodeModScale,new OpModScale); interpreter.installSegment5(Compiler::Transformation::opcodeModScaleExplicit,new OpModScale); interpreter.installSegment5(Compiler::Transformation::opcodeRotate,new OpRotate); diff --git a/apps/openmw/mwworld/actioneat.cpp b/apps/openmw/mwworld/actioneat.cpp index 63efff738..470eeda2b 100644 --- a/apps/openmw/mwworld/actioneat.cpp +++ b/apps/openmw/mwworld/actioneat.cpp @@ -11,6 +11,8 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwworld/containerstore.hpp" + #include "esmstore.hpp" #include "class.hpp" @@ -18,8 +20,8 @@ namespace MWWorld { void ActionEat::executeImp (const Ptr& actor) { - // remove used item - getTarget().getRefData().setCount (getTarget().getRefData().getCount()-1); + // remove used item (assume the item is present in inventory) + getTarget().getContainerStore()->remove(getTarget(), 1, actor); // check for success const MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get (actor).getCreatureStats (actor); diff --git a/apps/openmw/mwworld/actionequip.cpp b/apps/openmw/mwworld/actionequip.cpp index 0f1a85dda..38033340c 100644 --- a/apps/openmw/mwworld/actionequip.cpp +++ b/apps/openmw/mwworld/actionequip.cpp @@ -32,10 +32,10 @@ namespace MWWorld case 0: return; case 2: - invStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, invStore.end()); + invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedLeft, actor); break; case 3: - invStore.equip(MWWorld::InventoryStore::Slot_CarriedRight, invStore.end()); + invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, actor); break; } @@ -64,7 +64,7 @@ namespace MWWorld // if all slots are occupied, replace the last slot if (slot == --slots.first.end()) { - invStore.equip(*slot, it); + invStore.equip(*slot, it, actor); equipped = true; break; } @@ -72,7 +72,7 @@ namespace MWWorld if (invStore.getSlot(*slot) == invStore.end()) { // slot is not occupied - invStore.equip(*slot, it); + invStore.equip(*slot, it, actor); equipped = true; break; } diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index c6768f5fd..c041beffa 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -77,22 +77,31 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::end() return ContainerStoreIterator (this); } -bool MWWorld::ContainerStore::stacks(const Ptr& ptr1, const Ptr& ptr2) +bool MWWorld::ContainerStore::stacks(const Ptr& stack, const Ptr& item) { - /// \todo add current enchantment charge here when it is implemented - if ( Misc::StringUtils::ciEqual(ptr1.getCellRef().mRefID, ptr2.getCellRef().mRefID) - && MWWorld::Class::get(ptr1).getScript(ptr1) == "" // item with a script never stacks - && MWWorld::Class::get(ptr1).getEnchantment(ptr1) == "" // item with enchantment never stacks (we could revisit this later, but for now it makes selecting items in the spell window much easier) - && ptr1.getCellRef().mOwner == ptr2.getCellRef().mOwner - && ptr1.getCellRef().mSoul == ptr2.getCellRef().mSoul - // item that is already partly used up never stacks - && (!MWWorld::Class::get(ptr1).hasItemHealth(ptr1) || ptr1.getCellRef().mCharge == -1 - || MWWorld::Class::get(ptr1).getItemMaxHealth(ptr1) == ptr1.getCellRef().mCharge) - && (!MWWorld::Class::get(ptr2).hasItemHealth(ptr2) || ptr2.getCellRef().mCharge == -1 - || MWWorld::Class::get(ptr2).getItemMaxHealth(ptr2) == ptr2.getCellRef().mCharge)) - return true; + const MWWorld::Class& cls1 = MWWorld::Class::get(stack); + const MWWorld::Class& cls2 = MWWorld::Class::get(item); - return false; + /// \todo add current enchantment charge here when it is implemented + return stack != item // an item never stacks onto itself + && Misc::StringUtils::ciEqual(stack.getCellRef().mRefID, item.getCellRef().mRefID) + && stack.getCellRef().mOwner == item.getCellRef().mOwner + && stack.getCellRef().mSoul == item.getCellRef().mSoul + + // item with a script never stacks + && cls1.getScript(stack) == "" + && cls2.getScript(item) == "" + + // item with enchantment never stacks (we could revisit this later, + // but for now it makes selecting items in the spell window much easier) + && cls1.getEnchantment(stack) == "" + && cls2.getEnchantment(item) == "" + + // item that is already partly used up never stacks + && (!cls1.hasItemHealth(stack) || stack.getCellRef().mCharge == -1 + || cls1.getItemMaxHealth(stack) == stack.getCellRef().mCharge) + && (!cls2.hasItemHealth(item) || item.getCellRef().mCharge == -1 + || cls2.getItemMaxHealth(item) == item.getCellRef().mCharge); } MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr, const Ptr& actorPtr) @@ -140,22 +149,20 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr) || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_025") || Misc::StringUtils::ciEqual(ptr.getCellRef().mRefID, "gold_100")) { - MWWorld::ManualRef ref(esmStore, "Gold_001"); - int count = MWWorld::Class::get(ptr).getValue(ptr) * ptr.getRefData().getCount(); - ref.getPtr().getRefData().setCount(count); for (MWWorld::ContainerStoreIterator iter (begin(type)); iter!=end(); ++iter) { if (Misc::StringUtils::ciEqual((*iter).get()->mRef.mRefID, "gold_001")) { - (*iter).getRefData().setCount( (*iter).getRefData().getCount() + count); + iter->getRefData().setCount(iter->getRefData().getCount() + count); flagAsModified(); return iter; } } - return addImpl(ref.getPtr()); + MWWorld::ManualRef ref(esmStore, "Gold_001", count); + return addNewStack(ref.getPtr()); } // determine whether to stack or not @@ -171,10 +178,10 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImp (const Ptr& ptr) } } // if we got here, this means no stacking - return addImpl(ptr); + return addNewStack(ptr); } -MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImpl (const Ptr& ptr) +MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addNewStack (const Ptr& ptr) { ContainerStoreIterator it = begin(); @@ -198,6 +205,40 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::addImpl (const Ptr& ptr return it; } +int MWWorld::ContainerStore::remove(const std::string& itemId, int count, const Ptr& actor) +{ + int toRemove = count; + + for (ContainerStoreIterator iter(begin()); iter != end() && toRemove > 0; ++iter) + if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, itemId)) + toRemove -= remove(*iter, toRemove, actor); + + // number of removed items + return count - toRemove; +} + +int MWWorld::ContainerStore::remove(const Ptr& item, int count, const Ptr& actor) +{ + assert(this == item.getContainerStore()); + + int toRemove = count; + RefData& itemRef = item.getRefData(); + + if (itemRef.getCount() <= toRemove) + { + toRemove -= itemRef.getCount(); + itemRef.setCount(0); + } + else + { + itemRef.setCount(itemRef.getCount() - toRemove); + toRemove = 0; + } + + // number of removed items + return count - toRemove; +} + void MWWorld::ContainerStore::fill (const ESM::InventoryList& items, const std::string& owner, const MWWorld::ESMStore& store) { for (std::vector::const_iterator iter (items.mList.begin()); iter!=items.mList.end(); @@ -216,7 +257,7 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: try { - ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id); + ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), id, count); if (ref.getPtr().getTypeName()==typeid (ESM::ItemLevList).name()) { @@ -266,7 +307,6 @@ void MWWorld::ContainerStore::addInitialItem (const std::string& id, const std:: } else { - ref.getPtr().getRefData().setCount (count); ref.getPtr().getCellRef().mOwner = owner; addImp (ref.getPtr()); } diff --git a/apps/openmw/mwworld/containerstore.hpp b/apps/openmw/mwworld/containerstore.hpp index 9a11f1603..2cdefd2f4 100644 --- a/apps/openmw/mwworld/containerstore.hpp +++ b/apps/openmw/mwworld/containerstore.hpp @@ -75,14 +75,25 @@ namespace MWWorld /// /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item. + int remove(const std::string& itemId, int count, const Ptr& actor); + ///< Remove \a count item(s) designated by \a itemId from this container. + /// + /// @return the number of items actually removed + + virtual int remove(const Ptr& item, int count, const Ptr& actor); + ///< Remove \a count item(s) designated by \a item from this inventory. + /// + /// @return the number of items actually removed + protected: - ContainerStoreIterator addImpl (const Ptr& ptr); - ///< Add the item to this container (no stacking) + ContainerStoreIterator addNewStack (const Ptr& ptr); + ///< Add the item to this container (do not try to stack it onto existing items) public: - virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); + virtual bool stacks (const Ptr& stack, const Ptr& item); ///< @return true if the two specified objects can stack with each other + /// @note stack is the item that is already in this container void fill (const ESM::InventoryList& items, const std::string& owner, const MWWorld::ESMStore& store); ///< Insert items into *this. diff --git a/apps/openmw/mwworld/contentloader.hpp b/apps/openmw/mwworld/contentloader.hpp new file mode 100644 index 000000000..46bd7d3f9 --- /dev/null +++ b/apps/openmw/mwworld/contentloader.hpp @@ -0,0 +1,36 @@ +#ifndef CONTENTLOADER_HPP +#define CONTENTLOADER_HPP + +#include +#include +#include + +#include "components/loadinglistener/loadinglistener.hpp" + +namespace MWWorld +{ + +struct ContentLoader +{ + ContentLoader(Loading::Listener& listener) + : mListener(listener) + { + } + + virtual ~ContentLoader() + { + } + + virtual void load(const boost::filesystem::path& filepath, int& index) + { + std::cout << "Loading content file " << filepath.string() << std::endl; + mListener.setLabel(filepath.string()); + } + + protected: + Loading::Listener& mListener; +}; + +} /* namespace MWWorld */ + +#endif /* CONTENTLOADER_HPP */ diff --git a/apps/openmw/mwworld/esmloader.cpp b/apps/openmw/mwworld/esmloader.cpp new file mode 100644 index 000000000..1b8880d37 --- /dev/null +++ b/apps/openmw/mwworld/esmloader.cpp @@ -0,0 +1,31 @@ +#include "esmloader.hpp" +#include "esmstore.hpp" + +#include "components/to_utf8/to_utf8.hpp" + +namespace MWWorld +{ + +EsmLoader::EsmLoader(MWWorld::ESMStore& store, std::vector& readers, + ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener) + : ContentLoader(listener) + , mStore(store) + , mEsm(readers) + , mEncoder(encoder) +{ +} + +void EsmLoader::load(const boost::filesystem::path& filepath, int& index) +{ + ContentLoader::load(filepath.filename(), index); + + ESM::ESMReader lEsm; + lEsm.setEncoder(mEncoder); + lEsm.setIndex(index); + lEsm.setGlobalReaderList(&mEsm); + lEsm.open(filepath.string()); + mEsm[index] = lEsm; + mStore.load(mEsm[index], &mListener); +} + +} /* namespace MWWorld */ diff --git a/apps/openmw/mwworld/esmloader.hpp b/apps/openmw/mwworld/esmloader.hpp new file mode 100644 index 000000000..d799c3f15 --- /dev/null +++ b/apps/openmw/mwworld/esmloader.hpp @@ -0,0 +1,34 @@ +#ifndef ESMLOADER_HPP +#define ESMLOADER_HPP + +#include + +#include "contentloader.hpp" +#include "components/esm/esmreader.hpp" + +namespace ToUTF8 +{ + class Utf8Encoder; +} + +namespace MWWorld +{ + +class ESMStore; + +struct EsmLoader : public ContentLoader +{ + EsmLoader(MWWorld::ESMStore& store, std::vector& readers, + ToUTF8::Utf8Encoder* encoder, Loading::Listener& listener); + + void load(const boost::filesystem::path& filepath, int& index); + + private: + std::vector& mEsm; + MWWorld::ESMStore& mStore; + ToUTF8::Utf8Encoder* mEncoder; +}; + +} /* namespace MWWorld */ + +#endif // ESMLOADER_HPP diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index 7703f2d23..ab8878717 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -36,7 +36,7 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) // all files/readers used by the engine. This will greaty accelerate // refnumber mangling, as required for handling moved references. int index = ~0; - const std::vector &masters = esm.getMasters(); + const std::vector &masters = esm.getGameFiles(); std::vector *allPlugins = esm.getGlobalReaderList(); for (size_t j = 0; j < masters.size(); j++) { ESM::Header::MasterData &mast = const_cast(masters[j]); @@ -100,11 +100,7 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) it->second->load(esm, id); if (n.val==ESM::REC_DIAL) { - // dirty hack, but it is better than non-const search() - // or friends - //dialogue = &mDialogs.mStatic.back(); dialogue = const_cast(mDialogs.find(id)); - assert (dialogue->mId == id); } else { dialogue = 0; } diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 98cb6d347..dc2d0cab1 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -8,6 +8,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwbase/windowmanager.hpp" #include "../mwmechanics/npcstats.hpp" @@ -40,6 +41,7 @@ void MWWorld::InventoryStore::initSlots (TSlots& slots) MWWorld::InventoryStore::InventoryStore() : mMagicEffectsUpToDate (false) , mSelectedEnchantItem(end()) + , mActorModelUpdateEnabled (true) { initSlots (mSlots); } @@ -80,7 +82,7 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::add(const Ptr& itemPtr, return retVal; } -void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& iterator) +void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& iterator, const Ptr& actor) { if (slot<0 || slot>=static_cast (mSlots.size())) throw std::runtime_error ("slot number out of range"); @@ -97,19 +99,8 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite throw std::runtime_error ("invalid slot"); } - // restack item previously in this slot (if required) if (mSlots[slot] != end()) - { - for (MWWorld::ContainerStoreIterator iter (begin()); iter!=end(); ++iter) - { - if (stacks(*iter, *mSlots[slot])) - { - iter->getRefData().setCount( iter->getRefData().getCount() + mSlots[slot]->getRefData().getCount() ); - mSlots[slot]->getRefData().setCount(0); - break; - } - } - } + unequipSlot(slot, actor); // unstack item pointed to by iterator if required if (iterator!=end() && !slots.second && iterator->getRefData().getCount() > 1) // if slots.second is true, item can stay stacked when equipped @@ -117,30 +108,21 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite // add the item again with a count of count-1, then set the count of the original (that will be equipped) to 1 int count = iterator->getRefData().getCount(); iterator->getRefData().setCount(count-1); - addImpl(*iterator); + addNewStack(*iterator); iterator->getRefData().setCount(1); } mSlots[slot] = iterator; flagAsModified(); + + updateActorModel(actor); } void MWWorld::InventoryStore::unequipAll(const MWWorld::Ptr& actor) { for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) - { - MWWorld::ContainerStoreIterator it = getSlot(slot); - if (it != end()) - { - equip(slot, end()); - std::string script = MWWorld::Class::get(*it).getScript(*it); - - // Unset OnPCEquip Variable on item's script, if it has a script with that variable declared - if((actor.getRefData().getHandle() == "player") && (script != "")) - (*it).getRefData().getLocals().setVarByInt(script, "onpcequip", 0); - } - } + unequipSlot(slot, actor); } MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) @@ -169,6 +151,9 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc) TSlots slots; initSlots (slots); + // Disable model update during auto-equip + mActorModelUpdateEnabled = false; + for (ContainerStoreIterator iter (begin()); iter!=end(); ++iter) { Ptr test = *iter; @@ -224,10 +209,10 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc) case 0: continue; case 2: - invStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, invStore.end()); + invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedLeft, npc); break; case 3: - invStore.equip(MWWorld::InventoryStore::Slot_CarriedRight, invStore.end()); + invStore.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, npc); break; } @@ -239,7 +224,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc) // add the item again with a count of count-1, then set the count of the original (that will be equipped) to 1 int count = iter->getRefData().getCount(); iter->getRefData().setCount(count-1); - addImpl(*iter); + addNewStack(*iter); iter->getRefData().setCount(1); } } @@ -257,9 +242,12 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc) changed = true; } + mActorModelUpdateEnabled = true; + if (changed) { mSlots.swap (slots); + updateActorModel(npc); flagAsModified(); } } @@ -297,20 +285,22 @@ void MWWorld::InventoryStore::flagAsModified() mMagicEffectsUpToDate = false; } -bool MWWorld::InventoryStore::stacks(const Ptr& ptr1, const Ptr& ptr2) +bool MWWorld::InventoryStore::stacks(const Ptr& stack, const Ptr& item) { - bool canStack = MWWorld::ContainerStore::stacks(ptr1, ptr2); + bool canStack = MWWorld::ContainerStore::stacks(stack, item); if (!canStack) return false; - // don't stack if the item being checked against is currently equipped. + // don't stack if 'stack' (the item being checked against) is currently equipped. for (TSlots::const_iterator iter (mSlots.begin()); iter!=mSlots.end(); ++iter) { - if (*iter != end() && ptr1 == **iter) - return false; - if (*iter != end() && ptr2 == **iter) - return false; + if (*iter != end() && stack == **iter) + { + bool stackWhenEquipped = MWWorld::Class::get(**iter).getEquipmentSlots(**iter).second; + if (!stackWhenEquipped) + return false; + } } return true; @@ -325,3 +315,105 @@ MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSelectedEnchantItem( { return mSelectedEnchantItem; } + +int MWWorld::InventoryStore::remove(const Ptr& item, int count, const Ptr& actor) +{ + for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) + { + if (mSlots[slot] == end()) + continue; + + if (*mSlots[slot] == item) + { + // restacking is disabled cause it may break removal + unequipSlot(slot, actor, false); + break; + } + } + + int retCount = ContainerStore::remove(item, count, actor); + + // If an armor/clothing item is removed, try to find a replacement, + // but not for the player nor werewolves. + if ((actor.getRefData().getHandle() != "player") + && !(MWWorld::Class::get(actor).getNpcStats(actor).isWerewolf())) + { + std::string type = item.getTypeName(); + if ((type == typeid(ESM::Armor).name()) || (type == typeid(ESM::Clothing).name())) + autoEquip(actor); + } + + return retCount; +} + +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipSlot(int slot, const MWWorld::Ptr& actor, bool restack) +{ + ContainerStoreIterator it = getSlot(slot); + + if (it != end()) + { + ContainerStoreIterator retval = it; + + if (restack) { + // restack item previously in this slot + for (MWWorld::ContainerStoreIterator iter (begin()); iter != end(); ++iter) + { + if (stacks(*iter, *it)) + { + iter->getRefData().setCount(iter->getRefData().getCount() + it->getRefData().getCount()); + it->getRefData().setCount(0); + retval = iter; + break; + } + } + } + + // empty this slot + mSlots[slot] = end(); + + if (actor.getRefData().getHandle() == "player") + { + // Unset OnPCEquip Variable on item's script, if it has a script with that variable declared + const std::string& script = Class::get(*it).getScript(*it); + if (script != "") + (*it).getRefData().getLocals().setVarByInt(script, "onpcequip", 0); + + // Update HUD icon when removing player weapon or selected enchanted item. + // We have to check for both as the weapon could also be the enchanted item. + if (slot == MWWorld::InventoryStore::Slot_CarriedRight) + { + // weapon + MWBase::Environment::get().getWindowManager()->unsetSelectedWeapon(); + } + if ((mSelectedEnchantItem != end()) && (mSelectedEnchantItem == it)) + { + // enchanted item + MWBase::Environment::get().getWindowManager()->unsetSelectedSpell(); + } + } + + updateActorModel(actor); + + return retval; + } + + return it; +} + +MWWorld::ContainerStoreIterator MWWorld::InventoryStore::unequipItem(const MWWorld::Ptr& item, const MWWorld::Ptr& actor) +{ + for (int slot=0; slotupdateAnimParts(actor); +} diff --git a/apps/openmw/mwworld/inventorystore.hpp b/apps/openmw/mwworld/inventorystore.hpp index f0cba0f9f..c4ad51777 100644 --- a/apps/openmw/mwworld/inventorystore.hpp +++ b/apps/openmw/mwworld/inventorystore.hpp @@ -45,6 +45,7 @@ namespace MWWorld mutable MWMechanics::MagicEffects mMagicEffects; mutable bool mMagicEffectsUpToDate; + bool mActorModelUpdateEnabled; typedef std::vector TSlots; @@ -57,6 +58,8 @@ namespace MWWorld void initSlots (TSlots& slots); + void updateActorModel (const Ptr& actor); + public: InventoryStore(); @@ -76,7 +79,7 @@ namespace MWWorld /// /// @return if stacking happened, return iterator to the item that was stacked against, otherwise iterator to the newly inserted item. - void equip (int slot, const ContainerStoreIterator& iterator); + void equip (int slot, const ContainerStoreIterator& iterator, const Ptr& actor); ///< \note \a iterator can be an end-iterator void setSelectedEnchantItem(const ContainerStoreIterator& iterator); @@ -104,10 +107,29 @@ namespace MWWorld ///< \attention This function is internal to the world model and should not be called from /// outside. - virtual bool stacks (const Ptr& ptr1, const Ptr& ptr2); + virtual bool stacks (const Ptr& stack, const Ptr& item); ///< @return true if the two specified objects can stack with each other - /// @note ptr1 is the item that is already in this container + /// @note stack is the item that is already in this container (it may be equipped) + + virtual int remove(const Ptr& item, int count, const Ptr& actor); + ///< Remove \a count item(s) designated by \a item from this inventory. + /// + /// @return the number of items actually removed + ContainerStoreIterator unequipSlot(int slot, const Ptr& actor, bool restack = true); + ///< Unequip \a slot. + /// + /// @return an iterator to the item that was previously in the slot + /// (if \a restack is true, the item can be re-stacked so its count + /// may differ from when it was equipped). + + ContainerStoreIterator unequipItem(const Ptr& item, const Ptr& actor); + ///< Unequip an item identified by its Ptr. An exception is thrown + /// if the item is not currently equipped. + /// + /// @return an iterator to the item that was previously in the slot + /// (it can be re-stacked so its count may be different than when it + /// was equipped). }; } diff --git a/apps/openmw/mwworld/manualref.hpp b/apps/openmw/mwworld/manualref.hpp index 6616165fa..1cdcd8484 100644 --- a/apps/openmw/mwworld/manualref.hpp +++ b/apps/openmw/mwworld/manualref.hpp @@ -37,7 +37,7 @@ namespace MWWorld public: - ManualRef (const MWWorld::ESMStore& store, const std::string& name) + ManualRef (const MWWorld::ESMStore& store, const std::string& name, const int count=1) { // create if (!create (store.get(), name) && @@ -74,6 +74,7 @@ namespace MWWorld cellRef.mTeleport = false; cellRef.mLockLevel = 0; cellRef.mReferenceBlocked = 0; + mPtr.getRefData().setCount(count); } const Ptr& getPtr() const diff --git a/apps/openmw/mwworld/omwloader.cpp b/apps/openmw/mwworld/omwloader.cpp new file mode 100644 index 000000000..8562a4fe0 --- /dev/null +++ b/apps/openmw/mwworld/omwloader.cpp @@ -0,0 +1,17 @@ +#include "omwloader.hpp" + +namespace MWWorld +{ + +OmwLoader::OmwLoader(Loading::Listener& listener) + : ContentLoader(listener) +{ +} + +void OmwLoader::load(const boost::filesystem::path& filepath, int& index) +{ + ContentLoader::load(filepath.filename(), index); +} + +} /* namespace MWWorld */ + diff --git a/apps/openmw/mwworld/omwloader.hpp b/apps/openmw/mwworld/omwloader.hpp new file mode 100644 index 000000000..cb9faa430 --- /dev/null +++ b/apps/openmw/mwworld/omwloader.hpp @@ -0,0 +1,21 @@ +#ifndef OMWLOADER_HPP +#define OMWLOADER_HPP + +#include "contentloader.hpp" + +namespace MWWorld +{ + +/** + * @brief Placeholder for real OpenMW content loader + */ +struct OmwLoader : public ContentLoader +{ + OmwLoader(Loading::Listener& listener); + + void load(const boost::filesystem::path& filepath, int& index); +}; + +} /* namespace MWWorld */ + +#endif /* OMWLOADER_HPP */ diff --git a/apps/openmw/mwworld/refdata.hpp b/apps/openmw/mwworld/refdata.hpp index 642f5412c..d5701efc5 100644 --- a/apps/openmw/mwworld/refdata.hpp +++ b/apps/openmw/mwworld/refdata.hpp @@ -73,6 +73,11 @@ namespace MWWorld void setLocals (const ESM::Script& script); void setCount (int count); + /// Set object count (an object pile is a simple object with a count >1). + /// + /// \warning Do not call setCount() to add or remove objects from a + /// container or an actor's inventory. Call ContainerStore::add() or + /// ContainerStore::remove() instead. MWScript::Locals& getLocals(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a352c0e11..1cda204e5 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1,4 +1,11 @@ #include "worldimp.hpp" +#ifdef _WIN32 +#include +#elif defined HAVE_UNORDERED_MAP +#include +#else +#include +#endif #include @@ -33,6 +40,10 @@ #include "containerstore.hpp" #include "inventorystore.hpp" +#include "contentloader.hpp" +#include "esmloader.hpp" +#include "omwloader.hpp" + using namespace Ogre; namespace @@ -82,6 +93,38 @@ namespace namespace MWWorld { + struct GameContentLoader : public ContentLoader + { + GameContentLoader(Loading::Listener& listener) + : ContentLoader(listener) + { + } + + bool addLoader(const std::string& extension, ContentLoader* loader) + { + return mLoaders.insert(std::make_pair(extension, loader)).second; + } + + void load(const boost::filesystem::path& filepath, int& index) + { + LoadersContainer::iterator it(mLoaders.find(filepath.extension().string())); + if (it != mLoaders.end()) + { + it->second->load(filepath, index); + } + else + { + std::string msg("Cannot load file: "); + msg += filepath.string(); + throw std::runtime_error(msg.c_str()); + } + } + + private: + typedef std::tr1::unordered_map LoadersContainer; + LoadersContainer mLoaders; + }; + Ptr World::getPtrViaHandle (const std::string& handle, Ptr::CellStore& cell) { if (MWWorld::LiveCellRef *ref = @@ -165,7 +208,7 @@ namespace MWWorld World::World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::vector& master, const std::vector& plugins, + const std::vector& contentFiles, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int mActivationDistanceOverride) : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), @@ -183,44 +226,22 @@ namespace MWWorld mWeatherManager = new MWWorld::WeatherManager(mRendering,&mFallback); - int idx = 0; // NOTE: We might need to reserve one more for the running game / save. - mEsm.resize(master.size() + plugins.size()); + mEsm.resize(contentFiles.size()); Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); listener->loadingOn(); - for (std::vector::size_type i = 0; i < master.size(); i++, idx++) - { - boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master[i])); - std::cout << "Loading ESM " << masterPath.string() << "\n"; - listener->setLabel(masterPath.filename().string()); + GameContentLoader gameContentLoader(*listener); + EsmLoader esmLoader(mStore, mEsm, encoder, *listener); + OmwLoader omwLoader(*listener); - // This parses the ESM file - ESM::ESMReader lEsm; - lEsm.setEncoder(encoder); - lEsm.setIndex(idx); - lEsm.setGlobalReaderList(&mEsm); - lEsm.open (masterPath.string()); - mEsm[idx] = lEsm; - mStore.load (mEsm[idx], listener); - } - - for (std::vector::size_type i = 0; i < plugins.size(); i++, idx++) - { - boost::filesystem::path pluginPath (fileCollections.getCollection (".esp").getPath (plugins[i])); + gameContentLoader.addLoader(".esm", &esmLoader); + gameContentLoader.addLoader(".esp", &esmLoader); + gameContentLoader.addLoader(".omwgame", &omwLoader); + gameContentLoader.addLoader(".omwaddon", &omwLoader); - std::cout << "Loading ESP " << pluginPath.string() << "\n"; - listener->setLabel(pluginPath.filename().string()); + loadContentFiles(fileCollections, contentFiles, gameContentLoader); - // This parses the ESP file - ESM::ESMReader lEsm; - lEsm.setEncoder(encoder); - lEsm.setIndex(idx); - lEsm.setGlobalReaderList(&mEsm); - lEsm.open (pluginPath.string()); - mEsm[idx] = lEsm; - mStore.load (mEsm[idx], listener); - } listener->loadingOff(); // insert records that may not be present in all versions of MW @@ -816,12 +837,13 @@ namespace MWWorld void World::deleteObject (const Ptr& ptr) { - if (ptr.getRefData().getCount()>0) + if (ptr.getRefData().getCount() > 0) { - ptr.getRefData().setCount (0); + ptr.getRefData().setCount(0); - if (mWorldScene->getActiveCells().find (ptr.getCell())!=mWorldScene->getActiveCells().end() && - ptr.getRefData().isEnabled()) + if (ptr.isInCell() + && mWorldScene->getActiveCells().find(ptr.getCell()) != mWorldScene->getActiveCells().end() + && ptr.getRefData().isEnabled()) { mWorldScene->removeObjectFromScene (ptr); mLocalScripts.remove (ptr); @@ -1456,7 +1478,7 @@ namespace MWWorld item.getRefData().getLocals().setVarByInt(script, "onpcdrop", 1); } - bool World::placeObject (const Ptr& object, float cursorX, float cursorY) + bool World::placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount) { std::pair result = mPhysics->castRay(cursorX, cursorY); @@ -1481,9 +1503,14 @@ namespace MWWorld pos.rot[0] = 0; pos.rot[1] = 0; + // copy the object and set its count + int origCount = object.getRefData().getCount(); + object.getRefData().setCount(amount); Ptr dropped = copyObjectToCell(object, *cell, pos); + object.getRefData().setCount(origCount); + + // only the player place items in the world, so no need to check actor PCDropped(dropped); - object.getRefData().setCount(0); return true; } @@ -1528,7 +1555,7 @@ namespace MWWorld return dropped; } - void World::dropObjectOnGround (const Ptr& actor, const Ptr& object) + void World::dropObjectOnGround (const Ptr& actor, const Ptr& object, int amount) { MWWorld::Ptr::CellStore* cell = actor.getCell(); @@ -1549,10 +1576,14 @@ namespace MWWorld mPhysics->castRay(orig, dir, len); pos.pos[2] = hit.second.z; + // copy the object and set its count + int origCount = object.getRefData().getCount(); + object.getRefData().setCount(amount); Ptr dropped = copyObjectToCell(object, *cell, pos); + object.getRefData().setCount(origCount); + if(actor == mPlayer->getPlayer()) // Only call if dropped by player PCDropped(dropped); - object.getRefData().setCount(0); } void World::processChangedSettings(const Settings::CategorySettingVector& settings) @@ -1918,7 +1949,6 @@ namespace MWWorld if(werewolf) { ManualRef ref(getStore(), "WerewolfRobe"); - ref.getPtr().getRefData().setCount(1); // Configure item's script variables std::string script = Class::get(ref.getPtr()).getScript(ref.getPtr()); @@ -1930,18 +1960,11 @@ namespace MWWorld // Not sure this is right InventoryStore &inv = Class::get(actor).getInventoryStore(actor); - inv.equip(InventoryStore::Slot_Robe, inv.add(ref.getPtr(), actor)); + inv.equip(InventoryStore::Slot_Robe, inv.add(ref.getPtr(), actor), actor); } else { - ContainerStore &store = Class::get(actor).getContainerStore(actor); - - const std::string item = "WerewolfRobe"; - for(ContainerStoreIterator iter(store.begin());iter != store.end();++iter) - { - if(Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, item)) - iter->getRefData().setCount(0); - } + Class::get(actor).getContainerStore(actor).remove("WerewolfRobe", 1, actor); } if(actor.getRefData().getHandle() == "player") @@ -1985,6 +2008,22 @@ namespace MWWorld return mGodMode; } + void World::loadContentFiles(const Files::Collections& fileCollections, + const std::vector& content, ContentLoader& contentLoader) + { + std::vector::const_iterator it(content.begin()); + std::vector::const_iterator end(content.end()); + for (int idx = 0; it != end; ++it, ++idx) + { + boost::filesystem::path filename(*it); + const Files::MultiDirCollection& col = fileCollections.getCollection(filename.extension().string()); + if (col.doesExist(*it)) + { + contentLoader.load(col.getPath(*it), idx); + } + } + } + void World::castSpell(const Ptr &actor) { MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor); @@ -2122,4 +2161,8 @@ namespace MWWorld // TODO: Launch projectile if there's a Target portion } + void World::updateAnimParts(const Ptr& actor) + { + mRendering->updateAnimParts(actor); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 6d05df6ff..602d8d5e7 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -14,6 +14,8 @@ #include "../mwbase/world.hpp" +#include "contentloader.hpp" + namespace Ogre { class Vector3; @@ -41,6 +43,8 @@ namespace MWRender class Animation; } +struct ContentLoader; + namespace MWWorld { class WeatherManager; @@ -113,6 +117,15 @@ namespace MWWorld void ensureNeededRecords(); + /** + * @brief loadContentFiles - Loads content files (esm,esp,omwgame,omwaddon) + * @param fileCollections- Container which holds content file names and their paths + * @param content - Container which holds content file names + * @param contentLoader - + */ + void loadContentFiles(const Files::Collections& fileCollections, + const std::vector& content, ContentLoader& contentLoader); + int mPlayIntro; bool mTeleportEnabled; @@ -122,7 +135,7 @@ namespace MWWorld World (OEngine::Render::OgreRenderer& renderer, const Files::Collections& fileCollections, - const std::vector& master, const std::vector& plugins, + const std::vector& contentFiles, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, ToUTF8::Utf8Encoder* encoder, const std::map& fallbackMap, int mActivationDistanceOverride); @@ -341,14 +354,19 @@ namespace MWWorld virtual void update (float duration, bool paused); - virtual bool placeObject (const Ptr& object, float cursorX, float cursorY); - ///< place an object into the gameworld at the specified cursor position + virtual bool placeObject (const MWWorld::Ptr& object, float cursorX, float cursorY, int amount); + ///< copy and place an object into the gameworld at the specified cursor position /// @param object /// @param cursor X (relative 0-1) /// @param cursor Y (relative 0-1) + /// @param number of objects to place /// @return true if the object was placed, or false if it was rejected because the position is too far away - virtual void dropObjectOnGround (const Ptr& actor, const Ptr& object); + virtual void dropObjectOnGround (const MWWorld::Ptr& actor, const MWWorld::Ptr& object, int amount); + ///< copy and place an object into the gameworld at the given actor's position + /// @param actor giving the dropped object position + /// @param object + /// @param number of objects to place virtual bool canPlaceObject(float cursorX, float cursorY); ///< @return true if it is possible to place on object at specified cursor location @@ -455,6 +473,8 @@ namespace MWWorld virtual bool toggleGodMode(); virtual void castSpell (const MWWorld::Ptr& actor); + + virtual void updateAnimParts(const MWWorld::Ptr& ptr); }; } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 04423dc6f..59fb084a8 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 @@ -19,7 +19,7 @@ add_component_dir (nif ) add_component_dir (nifogre - ogrenifloader skeleton material mesh + ogrenifloader skeleton material mesh particles ) add_component_dir (nifbullet @@ -48,7 +48,7 @@ add_component_dir (misc add_component_dir (files linuxpath windowspath macospath fixedpath multidircollection collections fileops configurationmanager - filelibrary ogreplugin constrainedfiledatastream lowlevelfile + filelibrary constrainedfiledatastream lowlevelfile ) add_component_dir (compiler @@ -65,30 +65,39 @@ add_component_dir (interpreter add_component_dir (translation translation ) - + add_component_dir (terrain quadtreenode chunk world storage material ) - + add_component_dir (loadinglistener loadinglistener ) +add_component_dir (ogreinit + ogreinit ogreplugin + ) + +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/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp new file mode 100644 index 000000000..5f3575eb4 --- /dev/null +++ b/components/contentselector/model/contentmodel.cpp @@ -0,0 +1,616 @@ +#include "contentmodel.hpp" +#include "esmfile.hpp" + +#include +#include +#include + +#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(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(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(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 000000000..8c8c2124b --- /dev/null +++ b/components/contentselector/model/contentmodel.hpp @@ -0,0 +1,77 @@ +#ifndef CONTENTMODEL_HPP +#define CONTENTMODEL_HPP + +#include +#include + +namespace ContentSelectorModel +{ + class EsmFile; + + typedef QList ContentFileList; + + enum ContentType + { + ContentType_GameFile, + ContentType_Addon + }; + + class ContentModel : public QAbstractTableModel + { + Q_OBJECT + public: + explicit ContentModel(QObject *parent = 0); + + void setEncoding(const QString &encoding); + + 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 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 000000000..a0a09105a --- /dev/null +++ b/components/contentselector/model/esmfile.cpp @@ -0,0 +1,137 @@ +#include "esmfile.hpp" + +#include +#include + +int ContentSelectorModel::EsmFile::sPropertyCount = 7; +QString ContentSelectorModel::EsmFile::sToolTip = QString("Author: %1
\ + Version: %2
\ + Path:
%3
\ +
Description:
%4
\ +
Dependencies: %5
"); + + +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 000000000..ca24b52d1 --- /dev/null +++ b/components/contentselector/model/esmfile.hpp @@ -0,0 +1,86 @@ +#ifndef ESMFILE_HPP +#define ESMFILE_HPP + +#include +#include + +#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 000000000..e1d737c2d --- /dev/null +++ b/components/contentselector/model/modelitem.cpp @@ -0,0 +1,69 @@ +#include "modelitem.hpp" + +ContentSelectorModel::ModelItem::ModelItem(ModelItem *parent) + : mParentItem(parent) +{ +} +/* +ContentSelectorModel::ModelItem::ModelItem(const ModelItem *parent) + // : mParentItem(parent) +{ +} +*/ + +ContentSelectorModel::ModelItem::~ModelItem() +{ + qDeleteAll(mChildItems); +} + + +ContentSelectorModel::ModelItem *ContentSelectorModel::ModelItem::parent() const +{ + return mParentItem; +} + +bool ContentSelectorModel::ModelItem::hasFormat(const QString &mimetype) const +{ + if (mimetype == "application/omwcontent") + return true; + + return QMimeData::hasFormat(mimetype); +} +int ContentSelectorModel::ModelItem::row() const +{ + if (mParentItem) + return 1; + //return mParentItem->childRow(const_cast(this)); + //return mParentItem->mChildItems.indexOf(const_cast(this)); + + return -1; +} + + +int ContentSelectorModel::ModelItem::childCount() const +{ + return mChildItems.count(); +} + +int ContentSelectorModel::ModelItem::childRow(ModelItem *child) const +{ + Q_ASSERT(child); + + return mChildItems.indexOf(child); +} + +ContentSelectorModel::ModelItem *ContentSelectorModel::ModelItem::child(int row) +{ + return mChildItems.value(row); +} + + +void ContentSelectorModel::ModelItem::appendChild(ModelItem *item) +{ + mChildItems.append(item); +} + +void ContentSelectorModel::ModelItem::removeChild(int row) +{ + mChildItems.removeAt(row); +} diff --git a/components/contentselector/model/modelitem.hpp b/components/contentselector/model/modelitem.hpp new file mode 100644 index 000000000..57214b09c --- /dev/null +++ b/components/contentselector/model/modelitem.hpp @@ -0,0 +1,39 @@ +#ifndef MODELITEM_HPP +#define MODELITEM_HPP + +#include +#include + +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 mChildItems; + }; +} + +#endif diff --git a/components/fileorderlist/utils/naturalsort.cpp b/components/contentselector/model/naturalsort.cpp similarity index 100% rename from components/fileorderlist/utils/naturalsort.cpp rename to components/contentselector/model/naturalsort.cpp diff --git a/components/contentselector/model/naturalsort.hpp b/components/contentselector/model/naturalsort.hpp new file mode 100644 index 000000000..8386e4e9f --- /dev/null +++ b/components/contentselector/model/naturalsort.hpp @@ -0,0 +1,11 @@ +#ifndef NATURALSORT_H +#define NATURALSORT_H + +#include + + bool naturalSortLessThanCS( const QString &left, const QString &right ); + bool naturalSortLessThanCI( const QString &left, const QString &right ); + bool naturalSortGreaterThanCS( const QString &left, const QString &right ); + bool naturalSortGreaterThanCI( const QString &left, const QString &right ); + +#endif diff --git a/components/contentselector/view/combobox.cpp b/components/contentselector/view/combobox.cpp new file mode 100644 index 000000000..1d773b62d --- /dev/null +++ b/components/contentselector/view/combobox.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include + +#include "combobox.hpp" + +ContentSelectorView::ComboBox::ComboBox(QWidget *parent) : + QComboBox(parent) +{ + mValidator = new QRegExpValidator(QRegExp("^[a-zA-Z0-9_]*$"), this); // Alpha-numeric + underscore + setValidator(mValidator); + setCompleter(0); + setEnabled (true); + + setInsertPolicy(QComboBox::NoInsert); +} + +void ContentSelectorView::ComboBox::paintEvent(QPaintEvent *) +{ + QStylePainter painter(this); + painter.setPen(palette().color(QPalette::Text)); + + // draw the combobox frame, focusrect and selected etc. + QStyleOptionComboBox opt; + initStyleOption(&opt); + painter.drawComplexControl(QStyle::CC_ComboBox, opt); + + // draw the icon and text + if (!opt.editable && currentIndex() == -1) // <<< we adjust the text displayed when nothing is selected + opt.currentText = mPlaceholderText; + painter.drawControl(QStyle::CE_ComboBoxLabel, opt); +} + +void ContentSelectorView::ComboBox::setPlaceholderText(const QString &text) +{ + mPlaceholderText = text; +} diff --git a/components/contentselector/view/combobox.hpp b/components/contentselector/view/combobox.hpp new file mode 100644 index 000000000..e3888af2c --- /dev/null +++ b/components/contentselector/view/combobox.hpp @@ -0,0 +1,30 @@ +#ifndef COMBOBOX_HPP +#define COMBOBOX_HPP + +#include +#include + +class QString; +class QRegExpValidator; + +namespace ContentSelectorView +{ + class ComboBox : public QComboBox + { + Q_OBJECT + + public: + explicit ComboBox (QWidget *parent = 0); + + void setPlaceholderText(const QString &text); + + private: + QString mPlaceholderText; + + protected: + void paintEvent(QPaintEvent *); + QRegExpValidator *mValidator; + }; +} + +#endif // COMBOBOX_HPP diff --git a/components/contentselector/view/contentselector.cpp b/components/contentselector/view/contentselector.cpp new file mode 100644 index 000000000..e9599de49 --- /dev/null +++ b/components/contentselector/view/contentselector.cpp @@ -0,0 +1,203 @@ +#include "contentselector.hpp" + +#include "../model/esmfile.hpp" + +#include + +#include +#include + +#include +#include +#include +#include + +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(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 000000000..a25eb20ae --- /dev/null +++ b/components/contentselector/view/contentselector.hpp @@ -0,0 +1,68 @@ +#ifndef CONTENTSELECTOR_HPP +#define CONTENTSELECTOR_HPP + +#include + +#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 1440dbd13..cf4951de7 100644 --- a/components/esm/aipackage.cpp +++ b/components/esm/aipackage.cpp @@ -44,9 +44,9 @@ namespace ESM } } - void AIPackageList::save(ESMWriter &esm) + void AIPackageList::save(ESMWriter &esm) const { - typedef std::vector::iterator PackageIter; + typedef std::vector::const_iterator PackageIter; for (PackageIter it = mList.begin(); it != mList.end(); ++it) { switch (it->mType) { case AI_Wander: diff --git a/components/esm/aipackage.hpp b/components/esm/aipackage.hpp index 38499b2dd..b06cb529a 100644 --- a/components/esm/aipackage.hpp +++ b/components/esm/aipackage.hpp @@ -93,7 +93,7 @@ namespace ESM /// it needs to use retSubName() if needed. But, hey, there /// is only one field left (XSCL) and only two records uses AI void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 95cf24d33..e91059b26 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -3,7 +3,7 @@ #include "esmwriter.hpp" -void ESM::CellRef::save(ESMWriter &esm) +void ESM::CellRef::save(ESMWriter &esm) const { esm.writeHNT("FRMR", mRefnum); esm.writeHNCString("NAME", mRefID); diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 31889914c..47cb0b99e 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -83,7 +83,7 @@ namespace ESM // Position and rotation of this object within the cell Position mPos; - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); }; diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index bd86f9ba0..dd7ebfe93 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -36,6 +36,7 @@ struct Position enum RecNameInts { + // format 0 / legacy REC_ACTI = 0x49544341, REC_ALCH = 0x48434c41, REC_APPA = 0x41505041, @@ -80,7 +81,10 @@ enum RecNameInts REC_SPEL = 0x4c455053, REC_SSCR = 0x52435353, REC_STAT = 0x54415453, - REC_WEAP = 0x50414557 + REC_WEAP = 0x50414557, + + // format 1 + REC_FILT = 0x544C4946 }; } diff --git a/components/esm/effectlist.cpp b/components/esm/effectlist.cpp index 88f87d6e2..bc126846b 100644 --- a/components/esm/effectlist.cpp +++ b/components/esm/effectlist.cpp @@ -14,9 +14,9 @@ void EffectList::load(ESMReader &esm) } } -void EffectList::save(ESMWriter &esm) +void EffectList::save(ESMWriter &esm) const { - for (std::vector::iterator it = mList.begin(); it != mList.end(); ++it) { + for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNT("ENAM", *it, 24); } } diff --git a/components/esm/effectlist.hpp b/components/esm/effectlist.hpp index 9f5b87aed..04adcc5cd 100644 --- a/components/esm/effectlist.hpp +++ b/components/esm/effectlist.hpp @@ -35,9 +35,9 @@ namespace ESM std::vector mList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; - + } #endif diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index edc724cd2..3bf194c4e 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -34,7 +34,7 @@ public: float getFVer() const { if(mHeader.mData.version == VER_12) return 1.2; else return 1.3; } const std::string getAuthor() const { return mHeader.mData.author.toString(); } const std::string getDesc() const { return mHeader.mData.desc.toString(); } - const std::vector &getMasters() const { return mHeader.mMaster; } + const std::vector &getGameFiles() const { return mHeader.mMaster; } int getFormat() const; const NAME &retSubName() const { return mCtx.subName; } uint32_t getSubSize() const { return mCtx.leftSub; } diff --git a/components/esm/esmwriter.cpp b/components/esm/esmwriter.cpp index 3ea6bd350..f39aa2b89 100644 --- a/components/esm/esmwriter.cpp +++ b/components/esm/esmwriter.cpp @@ -2,185 +2,188 @@ #include #include -#include - -bool count = true; +#include 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(0); // Size goes here - writeT(0); // Unused header? - writeT(flags); - m_records.push_back(rec); - - assert(m_records.back().size == 0); -} + endRecord("TES3"); + } -void ESMWriter::startSubRecord(const std::string& name) -{ - writeName(name); - RecordData rec; - rec.name = name; - rec.position = m_stream->tellp(); - rec.size = 0; - writeT(0); // Size goes here - m_records.push_back(rec); - - assert(m_records.back().size == 0); -} + void ESMWriter::close() + { + if (!mRecords.empty()) + throw std::runtime_error ("Unclosed record remaining"); + } -void ESMWriter::endRecord(const std::string& name) -{ - RecordData rec = m_records.back(); - assert(rec.name == name); - m_records.pop_back(); + void ESMWriter::startRecord(const std::string& name, uint32_t flags) + { + mRecordCount++; + + writeName(name); + RecordData rec; + rec.name = name; + rec.position = mStream->tellp(); + rec.size = 0; + writeT(0); // Size goes here + writeT(0); // Unused header? + writeT(flags); + mRecords.push_back(rec); + + assert(mRecords.back().size == 0); + } - m_stream->seekp(rec.position); + void ESMWriter::startSubRecord(const std::string& name) + { + writeName(name); + RecordData rec; + rec.name = name; + rec.position = mStream->tellp(); + rec.size = 0; + writeT(0); // Size goes here + mRecords.push_back(rec); + + assert(mRecords.back().size == 0); + } - count = false; - write((char*)&rec.size, sizeof(int)); - count = true; + void ESMWriter::endRecord(const std::string& name) + { + RecordData rec = mRecords.back(); + assert(rec.name == name); + mRecords.pop_back(); - m_stream->seekp(0, std::ios::end); + mStream->seekp(rec.position); -} + mCounting = false; + write (reinterpret_cast (&rec.size), sizeof(int)); + mCounting = true; -void ESMWriter::writeHNString(const std::string& name, const std::string& data) -{ - startSubRecord(name); - writeHString(data); - endRecord(name); -} + mStream->seekp(0, std::ios::end); -void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) -{ - assert(data.size() <= size); - startSubRecord(name); - writeHString(data); + } - if (data.size() < size) + void ESMWriter::writeHNString(const std::string& name, const std::string& data) { - for (size_t i = data.size(); i < size; ++i) - write("\0",1); + startSubRecord(name); + writeHString(data); + endRecord(name); } - endRecord(name); -} - -void ESMWriter::writeHString(const std::string& data) -{ - if (data.size() == 0) - write("\0", 1); - else + void ESMWriter::writeHNString(const std::string& name, const std::string& data, size_t size) { - // Convert to UTF8 and return - std::string ascii = m_encoder->getLegacyEnc(data); + assert(data.size() <= size); + startSubRecord(name); + writeHString(data); + + if (data.size() < size) + { + for (size_t i = data.size(); i < size; ++i) + write("\0",1); + } - write(ascii.c_str(), ascii.size()); + endRecord(name); } -} -void ESMWriter::writeHCString(const std::string& data) -{ - writeHString(data); - if (data.size() > 0 && data[data.size()-1] != '\0') - write("\0", 1); -} + void ESMWriter::writeHString(const std::string& data) + { + if (data.size() == 0) + write("\0", 1); + else + { + // Convert to UTF8 and return + std::string ascii = mEncoder->getLegacyEnc(data); + + write(ascii.c_str(), ascii.size()); + } + } -void ESMWriter::writeName(const std::string& name) -{ - assert((name.size() == 4 && name[3] != '\0')); - write(name.c_str(), name.size()); -} + void ESMWriter::writeHCString(const std::string& data) + { + writeHString(data); + if (data.size() > 0 && data[data.size()-1] != '\0') + write("\0", 1); + } -void ESMWriter::write(const char* data, size_t size) -{ - if (count && !m_records.empty()) + void ESMWriter::writeName(const std::string& name) { - for (std::list::iterator it = m_records.begin(); it != m_records.end(); ++it) - it->size += size; + assert((name.size() == 4 && name[3] != '\0')); + write(name.c_str(), name.size()); } - m_stream->write(data, size); -} + void ESMWriter::write(const char* data, size_t size) + { + if (mCounting && !mRecords.empty()) + { + for (std::list::iterator it = mRecords.begin(); it != mRecords.end(); ++it) + it->size += size; + } -void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) -{ - m_encoder = encoder; -} + mStream->write(data, size); + } + void ESMWriter::setEncoder(ToUTF8::Utf8Encoder* encoder) + { + mEncoder = encoder; + } } diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index be3ae33ab..104f97f90 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 + void writeHNT(const std::string& name, const T& data) + { + startSubRecord(name); + writeT(data); + endRecord(name); + } + + template + void writeHNT(const std::string& name, const T& data, int size) + { + startSubRecord(name); + writeT(data, size); + endRecord(name); + } + + template + void writeT(const T& data) + { + write((char*)&data, sizeof(T)); + } + + template + void writeT(const T& data, size_t size) + { + write((char*)&data, size); + } + + void startRecord(const std::string& name, uint32_t flags = 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 mRecords; + std::ostream* mStream; + std::streampos mHeaderPos; + ToUTF8::Utf8Encoder* mEncoder; + int mRecordCount; + bool mCounting; + + Header mHeader; }; - -public: - int getVersion(); - void setVersion(int ver); - void setEncoder(ToUTF8::Utf8Encoder *encoding); // Write strings as UTF-8? - void setAuthor(const std::string& author); - void setDescription(const std::string& desc); - void setRecordCount (int count); - void setFormat (int format); - - void addMaster(const std::string& name, uint64_t size); - - void save(const std::string& file); - void save(std::ostream& file); - void close(); - - void writeHNString(const std::string& name, const std::string& data); - void writeHNString(const std::string& name, const std::string& data, size_t size); - void writeHNCString(const std::string& name, const std::string& data) - { - startSubRecord(name); - writeHCString(data); - endRecord(name); - } - void writeHNOString(const std::string& name, const std::string& data) - { - if (!data.empty()) - writeHNString(name, data); - } - void writeHNOCString(const std::string& name, const std::string& data) - { - if (!data.empty()) - writeHNCString(name, data); - } - - template - void writeHNT(const std::string& name, const T& data) - { - startSubRecord(name); - writeT(data); - endRecord(name); - } - - template - void writeHNT(const std::string& name, const T& data, int size) - { - startSubRecord(name); - writeT(data, size); - endRecord(name); - } - - template - void writeT(const T& data) - { - write((char*)&data, sizeof(T)); - } - - template - void writeT(const T& data, size_t size) - { - write((char*)&data, size); - } - - void startRecord(const std::string& name, uint32_t flags); - void startSubRecord(const std::string& name); - void endRecord(const std::string& name); - void writeHString(const std::string& data); - void writeHCString(const std::string& data); - void writeName(const std::string& data); - void write(const char* data, size_t size); - -private: - std::list m_records; - std::ostream* m_stream; - std::streampos m_headerPos; - ToUTF8::Utf8Encoder* m_encoder; - int m_recordCount; - - Header mHeader; -}; - } + #endif diff --git a/components/esm/filter.cpp b/components/esm/filter.cpp index 7d4851a5f..a80427bbe 100644 --- a/components/esm/filter.cpp +++ b/components/esm/filter.cpp @@ -3,6 +3,9 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" + +unsigned int ESM::Filter::sRecordId = REC_FILT; void ESM::Filter::load (ESMReader& esm) { @@ -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 0fd564361..bc3dd7bdc 100644 --- a/components/esm/filter.hpp +++ b/components/esm/filter.hpp @@ -10,6 +10,8 @@ namespace ESM struct Filter { + static unsigned int sRecordId; + std::string mId; std::string mDescription; @@ -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 fd022af7e..6ba0df0b3 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 a62990590..88f27de27 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 dbb69c066..f6bfc6a11 100644 --- a/components/esm/loadalch.cpp +++ b/components/esm/loadalch.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Potion::sRecordId = REC_ALCH; + void Potion::load(ESMReader &esm) { mModel = esm.getHNString("MODL"); @@ -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 3ede85342..141765aa8 100644 --- a/components/esm/loadalch.hpp +++ b/components/esm/loadalch.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Potion { + static unsigned int sRecordId; + struct ALDTstruct { float mWeight; @@ -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 4b8d2b763..29ea78acc 100644 --- a/components/esm/loadappa.cpp +++ b/components/esm/loadappa.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Apparatus::sRecordId = REC_APPA; + void Apparatus::load(ESMReader &esm) { // we will not treat duplicated subrecords as errors here @@ -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 ed9d335be..adc8e071f 100644 --- a/components/esm/loadappa.hpp +++ b/components/esm/loadappa.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct Apparatus { + static unsigned int sRecordId; + enum AppaType { MortarPestle = 0, @@ -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 e64c8705d..ec8ff4f20 100644 --- a/components/esm/loadarmo.cpp +++ b/components/esm/loadarmo.cpp @@ -2,6 +2,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -18,9 +19,9 @@ void PartReferenceList::load(ESMReader &esm) } } -void PartReferenceList::save(ESMWriter &esm) +void PartReferenceList::save(ESMWriter &esm) const { - for (std::vector::iterator it = mParts.begin(); it != mParts.end(); ++it) + for (std::vector::const_iterator it = mParts.begin(); it != mParts.end(); ++it) { esm.writeHNT("INDX", it->mPart); esm.writeHNOString("BNAM", it->mMale); @@ -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 eaef42be8..991f4e185 100644 --- a/components/esm/loadarmo.hpp +++ b/components/esm/loadarmo.hpp @@ -56,11 +56,13 @@ struct PartReferenceList std::vector 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 e95a8a860..4015e6c91 100644 --- a/components/esm/loadbody.cpp +++ b/components/esm/loadbody.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int BodyPart::sRecordId = REC_BODY; + void BodyPart::load(ESMReader &esm) { @@ -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 3ad9b1b95..9623caa31 100644 --- a/components/esm/loadbody.hpp +++ b/components/esm/loadbody.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct BodyPart { + static unsigned int sRecordId; + enum MeshPart { MP_Head = 0, @@ -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 3a70ac786..c8b7e9478 100644 --- a/components/esm/loadbook.cpp +++ b/components/esm/loadbook.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Book::sRecordId = REC_BOOK; void Book::load(ESMReader &esm) { @@ -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 68042e246..f96fbd709 100644 --- a/components/esm/loadbook.hpp +++ b/components/esm/loadbook.hpp @@ -14,6 +14,8 @@ class ESMWriter; struct Book { + static unsigned int sRecordId; + struct BKDTstruct { float mWeight; @@ -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 cb500f674..55e1e7f65 100644 --- a/components/esm/loadbsgn.cpp +++ b/components/esm/loadbsgn.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int BirthSign::sRecordId = REC_BSGN; void BirthSign::load(ESMReader &esm) { @@ -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 434ddf68e..9f9435c8f 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 d8d0c1291..c22c1b22b 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -7,9 +7,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Cell::sRecordId = REC_CELL; /// Some overloaded compare operators. bool operator==(const MovedCellRef& ref, int pRefnum) @@ -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 &masters = esm.getMasters(); + const std::vector &masters = esm.getGameFiles(); global = masters[local-1].index + 1; ref.mRefnum |= global << 24; // insert global plugin ID } @@ -276,7 +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 &masters = esm.getMasters(); + const std::vector &masters = esm.getGameFiles(); global = masters[local-1].index + 1; mref.mRefnum |= global << 24; // insert global plugin ID diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index 51288b291..61d586b9d 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -16,7 +16,6 @@ namespace MWWorld namespace ESM { - class ESMReader; class ESMWriter; @@ -55,6 +54,8 @@ typedef std::list CellRefTracker; */ struct Cell { + static unsigned int sRecordId; + enum Flags { Interior = 0x01, // Interior cell @@ -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 bdc461462..33489eec4 100644 --- a/components/esm/loadclas.cpp +++ b/components/esm/loadclas.cpp @@ -4,9 +4,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Class::sRecordId = REC_CLAS; const Class::Specialization Class::sSpecializationIds[3] = { Class::Combat, @@ -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 4f85e6ee8..3e489bb58 100644 --- a/components/esm/loadclas.hpp +++ b/components/esm/loadclas.hpp @@ -17,6 +17,8 @@ class ESMWriter; // class struct Class { + static unsigned int sRecordId; + enum AutoCalc { Weapon = 0x00001, @@ -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 10b00970f..d64564d77 100644 --- a/components/esm/loadclot.cpp +++ b/components/esm/loadclot.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Clothing::sRecordId = REC_CLOT; void Clothing::load(ESMReader &esm) { @@ -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 816d03cb2..50896622a 100644 --- a/components/esm/loadclot.hpp +++ b/components/esm/loadclot.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Clothing { + static unsigned int sRecordId; + enum Type { Pants = 0, @@ -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 853c8bd50..7bdf9f05b 100644 --- a/components/esm/loadcont.cpp +++ b/components/esm/loadcont.cpp @@ -2,6 +2,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -16,14 +17,16 @@ void InventoryList::load(ESMReader &esm) } } -void InventoryList::save(ESMWriter &esm) +void InventoryList::save(ESMWriter &esm) const { - for (std::vector::iterator it = mList.begin(); it != mList.end(); ++it) + for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNT("NPCO", *it, 36); } } + 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 b2bbab73d..2808b67b5 100644 --- a/components/esm/loadcont.hpp +++ b/components/esm/loadcont.hpp @@ -27,11 +27,13 @@ struct InventoryList std::vector 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 86d05b8a5..650de0801 100644 --- a/components/esm/loadcrea.cpp +++ b/components/esm/loadcrea.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Creature::sRecordId = REC_CREA; + void Creature::load(ESMReader &esm) { mPersistent = esm.getRecordFlags() & 0x0400; @@ -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 279e2ea3f..99c4f5225 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -20,6 +20,8 @@ class ESMWriter; struct Creature { + static unsigned int sRecordId; + // Default is 0x48? enum Flags { @@ -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 6904df15a..280739aca 100644 --- a/components/esm/loadcrec.hpp +++ b/components/esm/loadcrec.hpp @@ -17,6 +17,8 @@ class ESMWriter; /// Changes a creature struct LoadCREC { + static unsigned int sRecordId; + std::string mId; void load(ESMReader &esm) @@ -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 fb50d5e9f..fb43ee858 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 61f3f763d..63d78833e 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 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 a4c7b7d58..c56b06337 100644 --- a/components/esm/loaddoor.cpp +++ b/components/esm/loaddoor.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Door::sRecordId = REC_DOOR; void Door::load(ESMReader &esm) { @@ -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 77ffc6489..ee2b7f7ac 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 c4e278368..a1e885f23 100644 --- a/components/esm/loadench.cpp +++ b/components/esm/loadench.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Enchantment::sRecordId = REC_ENCH; void Enchantment::load(ESMReader &esm) { @@ -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 999f93ad9..f6ba8c6ab 100644 --- a/components/esm/loadench.hpp +++ b/components/esm/loadench.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Enchantment { + static unsigned int sRecordId; + enum Type { CastOnce = 0, @@ -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 e2712d462..61fa90263 100644 --- a/components/esm/loadfact.cpp +++ b/components/esm/loadfact.cpp @@ -4,9 +4,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Faction::sRecordId = REC_FACT; + int& Faction::FADTstruct::getSkill (int index, bool ignored) { if (index<0 || index>=6) @@ -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::iterator it = mReactions.begin(); it != mReactions.end(); ++it) + for (std::vector::const_iterator it = mReactions.begin(); it != mReactions.end(); ++it) { esm.writeHNString("ANAM", it->mFaction); esm.writeHNT("INTV", it->mReaction); diff --git a/components/esm/loadfact.hpp b/components/esm/loadfact.hpp index 891b99647..9c257e068 100644 --- a/components/esm/loadfact.hpp +++ b/components/esm/loadfact.hpp @@ -29,6 +29,8 @@ struct RankData struct Faction { + static unsigned int sRecordId; + std::string mId, mName; struct FADTstruct @@ -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 ccb519acd..a78ed1a1b 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 72e16c0ce..51b2e2dc9 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 fe1cc1b04..21d66339a 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 a6e0c2ecb..6b66ac832 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 90f8fcf35..4f248cc65 100644 --- a/components/esm/loadinfo.cpp +++ b/components/esm/loadinfo.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int DialInfo::sRecordId = REC_INFO; void DialInfo::load(ESMReader &esm) { @@ -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::iterator it = mSelects.begin(); it != mSelects.end(); ++it) + for (std::vector::const_iterator it = mSelects.begin(); it != mSelects.end(); ++it) { esm.writeHNString("SCVR", it->mSelectRule); it->mValue.write (esm, Variant::Format_Info); diff --git a/components/esm/loadinfo.hpp b/components/esm/loadinfo.hpp index 2361ed9eb..2589ea7b8 100644 --- a/components/esm/loadinfo.hpp +++ b/components/esm/loadinfo.hpp @@ -20,6 +20,8 @@ class ESMWriter; struct DialInfo { + static unsigned int sRecordId; + enum Gender { Male = 0, @@ -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 7e31a4116..0e0243362 100644 --- a/components/esm/loadingr.cpp +++ b/components/esm/loadingr.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Ingredient::sRecordId = REC_INGR; void Ingredient::load(ESMReader &esm) { @@ -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 5e286535f..85f2d5e7d 100644 --- a/components/esm/loadingr.hpp +++ b/components/esm/loadingr.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct Ingredient { + static unsigned int sRecordId; + struct IRDTstruct { float mWeight; @@ -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 60c475040..ede200d79 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Land::sRecordId = REC_LAND; void Land::LandData::save(ESMWriter &esm) { @@ -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 9c1fd1f5c..5649f9980 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Land { + static unsigned int sRecordId; + Land(); ~Land(); @@ -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 b54a91276..6385b9a71 100644 --- a/components/esm/loadlevlist.cpp +++ b/components/esm/loadlevlist.cpp @@ -2,6 +2,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -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("INDX", mList.size()); - for (std::vector::iterator it = mList.begin(); it != mList.end(); ++it) + for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNCString(mRecName, it->mId); esm.writeHNT("INTV", it->mLevel); @@ -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 7339cac56..9dcc6177a 100644 --- a/components/esm/loadlevlist.hpp +++ b/components/esm/loadlevlist.hpp @@ -51,7 +51,7 @@ struct LeveledListBase std::vector mList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID). @@ -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 89a2b8c65..c02bb46b6 100644 --- a/components/esm/loadligh.cpp +++ b/components/esm/loadligh.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Light::sRecordId = REC_LIGH; void Light::load(ESMReader &esm) { @@ -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 3f0b76d6e..74eb37197 100644 --- a/components/esm/loadligh.hpp +++ b/components/esm/loadligh.hpp @@ -16,6 +16,8 @@ class ESMWriter; struct Light { + static unsigned int sRecordId; + enum Flags { Dynamic = 0x001, @@ -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 03eac52bd..9ffce78a7 100644 --- a/components/esm/loadlock.cpp +++ b/components/esm/loadlock.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Lockpick::sRecordId = REC_LOCK; void Lockpick::load(ESMReader &esm) { @@ -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 953066cb2..c44e2b006 100644 --- a/components/esm/loadlock.hpp +++ b/components/esm/loadlock.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct Lockpick { + static unsigned int sRecordId; + struct Data { float mWeight; @@ -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 e523e9fa7..bd28c8488 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 6e6d987d4..5e84428b2 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 19d535600..1a90f5b09 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::iterator DestIter; + typedef std::vector::const_iterator DestIter; for (DestIter it = mTransport.begin(); it != mTransport.end(); ++it) { esm.writeHNT("DODT", it->mPos, sizeof(it->mPos)); esm.writeHNOCString("DNAM", it->mCellName); diff --git a/components/esm/loadnpc.hpp b/components/esm/loadnpc.hpp index 009bc5ef3..d9e691669 100644 --- a/components/esm/loadnpc.hpp +++ b/components/esm/loadnpc.hpp @@ -20,6 +20,8 @@ class ESMWriter; struct NPC { + static unsigned int sRecordId; + // Services enum Services { @@ -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 79d92397f..c87c2545f 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 882addcb9..3b5330e9f 100644 --- a/components/esm/loadpgrd.cpp +++ b/components/esm/loadpgrd.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Pathgrid::sRecordId = REC_PGRD; void Pathgrid::load(ESMReader &esm) { @@ -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::iterator it = mEdges.begin(); it != mEdges.end(); ++it) + for (std::vector::const_iterator it = mEdges.begin(); it != mEdges.end(); ++it) { esm.writeT(it->mV1); } diff --git a/components/esm/loadpgrd.hpp b/components/esm/loadpgrd.hpp index c3f50fc4d..9ee49552d 100644 --- a/components/esm/loadpgrd.hpp +++ b/components/esm/loadpgrd.hpp @@ -15,6 +15,8 @@ class ESMWriter; */ struct Pathgrid { + static unsigned int sRecordId; + struct DATAstruct { int mX, mY; // Grid location, matches cell for exterior cells @@ -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 729f8404e..caa3d7e0e 100644 --- a/components/esm/loadprob.cpp +++ b/components/esm/loadprob.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Probe::sRecordId = REC_PROB; void Probe::load(ESMReader &esm) { @@ -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 55b896bcd..b89b2ddeb 100644 --- a/components/esm/loadprob.hpp +++ b/components/esm/loadprob.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct Probe { + static unsigned int sRecordId; + struct Data { float mWeight; @@ -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 955424e2b..e50e43a74 100644 --- a/components/esm/loadrace.cpp +++ b/components/esm/loadrace.cpp @@ -2,9 +2,12 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Race::sRecordId = REC_RACE; + int Race::MaleFemale::getValue (bool male) const { return male ? mMale : mFemale; @@ -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 6ecec8ebb..7d5736d9b 100644 --- a/components/esm/loadrace.hpp +++ b/components/esm/loadrace.hpp @@ -17,6 +17,8 @@ class ESMWriter; struct Race { + static unsigned int sRecordId; + struct SkillBonus { int mSkill; // SkillEnum @@ -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 41c7f507a..fa4271e26 100644 --- a/components/esm/loadregn.cpp +++ b/components/esm/loadregn.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Region::sRecordId = REC_REGN; void Region::load(ESMReader &esm) { @@ -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::iterator it = mSoundList.begin(); it != mSoundList.end(); ++it) + for (std::vector::const_iterator it = mSoundList.begin(); it != mSoundList.end(); ++it) { esm.writeHNT("SNAM", *it); } diff --git a/components/esm/loadregn.hpp b/components/esm/loadregn.hpp index f2a3d9a10..1992c951b 100644 --- a/components/esm/loadregn.hpp +++ b/components/esm/loadregn.hpp @@ -18,6 +18,8 @@ class ESMWriter; struct Region { + static unsigned int sRecordId; + #pragma pack(push) #pragma pack(1) struct WEATstruct @@ -47,7 +49,7 @@ struct Region std::vector mSoundList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadrepa.cpp b/components/esm/loadrepa.cpp index ced6daa2e..a7132828d 100644 --- a/components/esm/loadrepa.cpp +++ b/components/esm/loadrepa.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Repair::sRecordId = REC_REPA; void Repair::load(ESMReader &esm) { @@ -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 83812bad9..5b404b0e4 100644 --- a/components/esm/loadrepa.hpp +++ b/components/esm/loadrepa.hpp @@ -11,6 +11,8 @@ class ESMWriter; struct Repair { + static unsigned int sRecordId; + struct Data { float mWeight; @@ -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 2c1b018d9..30460c17a 100644 --- a/components/esm/loadscpt.cpp +++ b/components/esm/loadscpt.cpp @@ -2,6 +2,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -12,6 +13,8 @@ struct SCHD Script::SCHDstruct mData; }; + unsigned int Script::sRecordId = REC_SCPT; + void Script::load(ESMReader &esm) { SCHD data; @@ -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::iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) + for (std::vector::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) varNameString.append(*it); SCHD data; @@ -68,7 +71,7 @@ void Script::save(ESMWriter &esm) if (!mVarNames.empty()) { esm.startSubRecord("SCVR"); - for (std::vector::iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) + for (std::vector::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it) { esm.writeHCString(*it); } diff --git a/components/esm/loadscpt.hpp b/components/esm/loadscpt.hpp index be7e83900..d5200d4c1 100644 --- a/components/esm/loadscpt.hpp +++ b/components/esm/loadscpt.hpp @@ -19,6 +19,8 @@ class ESMWriter; class Script { public: + static unsigned int sRecordId; + struct SCHDstruct { /* Script name. @@ -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 676a835c3..b6724e938 100644 --- a/components/esm/loadskil.cpp +++ b/components/esm/loadskil.cpp @@ -6,6 +6,7 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { @@ -126,6 +127,8 @@ namespace ESM HandToHand }}; + unsigned int Skill::sRecordId = REC_SKIL; + void Skill::load(ESMReader &esm) { esm.getHNT(mIndex, "INDX"); @@ -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 384f87454..1b9db5bcf 100644 --- a/components/esm/loadskil.hpp +++ b/components/esm/loadskil.hpp @@ -19,6 +19,8 @@ class ESMWriter; struct Skill { + static unsigned int sRecordId; + std::string mId; struct SKDTstruct @@ -75,7 +77,7 @@ struct Skill static const boost::array sSkillIds; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; void blank(); ///< Set record to default state (does not touch the ID/index). diff --git a/components/esm/loadsndg.cpp b/components/esm/loadsndg.cpp index 42d524226..1a8ca6335 100644 --- a/components/esm/loadsndg.cpp +++ b/components/esm/loadsndg.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int SoundGenerator::sRecordId = REC_SNDG; void SoundGenerator::load(ESMReader &esm) { @@ -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 a6226c154..5509661c1 100644 --- a/components/esm/loadsndg.hpp +++ b/components/esm/loadsndg.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct SoundGenerator { + static unsigned int sRecordId; + enum Type { LeftFoot = 0, @@ -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 07af2b5e9..49c9eb54e 100644 --- a/components/esm/loadsoun.cpp +++ b/components/esm/loadsoun.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Sound::sRecordId = REC_SOUN; void Sound::load(ESMReader &esm) { @@ -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 f8e38ac09..04a0984fd 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 8149fe4ce..2c98d796d 100644 --- a/components/esm/loadspel.cpp +++ b/components/esm/loadspel.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Spell::sRecordId = REC_SPEL; void Spell::load(ESMReader &esm) { @@ -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 3a620962d..cbf5366c4 100644 --- a/components/esm/loadspel.hpp +++ b/components/esm/loadspel.hpp @@ -13,6 +13,8 @@ class ESMWriter; struct Spell { + static unsigned int sRecordId; + enum SpellType { ST_Spell = 0, // Normal spell, must be cast and costs mana @@ -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 ae50de517..69b04bb23 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 713fe96b5..d09ad883e 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 c9346dafc..a71f22dc2 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 1adb7d05b..d912d1058 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 74d578ba7..87a8d1d57 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 b73a4c31e..5614d295f 100644 --- a/components/esm/loadtes3.hpp +++ b/components/esm/loadtes3.hpp @@ -24,7 +24,7 @@ namespace ESM versions are 1.2 and 1.3. These correspond to: 1.2 = 0x3f99999a and 1.3 = 0x3fa66666 */ - int version; + unsigned int version; int type; // 0=esp, 1=esm, 32=ess (unused) NAME32 author; // Author's name NAME256 desc; // File description diff --git a/components/esm/loadweap.cpp b/components/esm/loadweap.cpp index 253712396..1d0b149df 100644 --- a/components/esm/loadweap.cpp +++ b/components/esm/loadweap.cpp @@ -2,9 +2,11 @@ #include "esmreader.hpp" #include "esmwriter.hpp" +#include "defs.hpp" namespace ESM { + unsigned int Weapon::sRecordId = REC_WEAP; void Weapon::load(ESMReader &esm) { @@ -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 b62179ccb..fde716b91 100644 --- a/components/esm/loadweap.hpp +++ b/components/esm/loadweap.hpp @@ -15,6 +15,8 @@ class ESMWriter; struct Weapon { + static unsigned int sRecordId; + enum Type { ShortBladeOneHand = 0, @@ -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 dd886cf7f..24d3c3d0a 100644 --- a/components/esm/spelllist.cpp +++ b/components/esm/spelllist.cpp @@ -12,9 +12,9 @@ void SpellList::load(ESMReader &esm) } } -void SpellList::save(ESMWriter &esm) +void SpellList::save(ESMWriter &esm) const { - for (std::vector::iterator it = mList.begin(); it != mList.end(); ++it) { + for (std::vector::const_iterator it = mList.begin(); it != mList.end(); ++it) { esm.writeHNString("NPCS", *it, 32); } } diff --git a/components/esm/spelllist.hpp b/components/esm/spelllist.hpp index 52999270a..934bdda7a 100644 --- a/components/esm/spelllist.hpp +++ b/components/esm/spelllist.hpp @@ -17,7 +17,7 @@ namespace ESM std::vector mList; void load(ESMReader &esm); - void save(ESMWriter &esm); + void save(ESMWriter &esm) const; }; } diff --git a/components/esm/variantimp.cpp b/components/esm/variantimp.cpp index 160402aa4..1bacdc077 100644 --- a/components/esm/variantimp.cpp +++ b/components/esm/variantimp.cpp @@ -193,7 +193,7 @@ void ESM::VariantIntegerData::write (ESMWriter& esm, Variant::Format format, Var } else if (format==Variant::Format_Gmst || format==Variant::Format_Info) { - if (type==VT_Int) + if (type!=VT_Int) { std::ostringstream stream; stream diff --git a/components/fileorderlist/model/datafilesmodel.cpp b/components/fileorderlist/model/datafilesmodel.cpp deleted file mode 100644 index 02a6766b0..000000000 --- a/components/fileorderlist/model/datafilesmodel.cpp +++ /dev/null @@ -1,445 +0,0 @@ -#include -#include -#include -#include - -#include - -#include - -#include "esm/esmfile.hpp" - -#include "datafilesmodel.hpp" - -#include - -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("Author: %1
\ - Version: %2
\ -
Description:
%3
\ -
Dependencies: %4
") - .arg(file->author()) - .arg(QString::number(file->version())) - .arg(file->description()) - .arg(file->masters().join(", ")); - - - return tooltip; - - } - 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(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 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::ConstIterator it; - QList::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::ConstIterator it; - QList::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::ConstIterator it; - QList::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::ConstIterator it; - QList::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 0a07a536f..000000000 --- a/components/fileorderlist/model/datafilesmodel.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef DATAFILESMODEL_HPP -#define DATAFILESMODEL_HPP - -#include -#include -#include -#include - - -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 mFiles; - QHash 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 93d83091e..000000000 --- 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 52b3fbd00..000000000 --- a/components/fileorderlist/model/esm/esmfile.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef ESMFILE_HPP -#define ESMFILE_HPP - -#include -#include - -#include "../modelitem.hpp" - -class EsmFile : public ModelItem -{ - Q_OBJECT - Q_PROPERTY(QString filename READ fileName) - -public: - EsmFile(QString fileName = QString(), ModelItem *parent = 0); - - ~EsmFile() - {} - - void setFileName(const QString &fileName); - void setAuthor(const QString &author); - void setSize(const int size); - void setDates(const QDateTime &modified, const QDateTime &accessed); - void setVersion(const float version); - void setPath(const QString &path); - void setMasters(const QStringList &masters); - void setDescription(const QString &description); - - inline QString fileName() const { return mFileName; } - inline QString author() const { return mAuthor; } - inline int size() const { return mSize; } - inline QDateTime modified() const { return mModified; } - inline QDateTime accessed() const { return mAccessed; } - inline float version() const { return mVersion; } - inline QString path() const { return mPath; } - inline QStringList masters() const { return mMasters; } - inline QString description() const { return mDescription; } - - -private: - QString mFileName; - QString mAuthor; - int mSize; - QDateTime mModified; - QDateTime mAccessed; - float mVersion; - QString mPath; - QStringList mMasters; - QString mDescription; - -}; - - -#endif diff --git a/components/fileorderlist/model/modelitem.cpp b/components/fileorderlist/model/modelitem.cpp deleted file mode 100644 index 0ff7e45cb..000000000 --- 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(this)); - //return mParentItem->mChildItems.indexOf(const_cast(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 f4cb4322f..000000000 --- a/components/fileorderlist/model/modelitem.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef MODELITEM_HPP -#define MODELITEM_HPP - -#include -#include - -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 mChildItems; -}; - -#endif diff --git a/components/fileorderlist/model/pluginsproxymodel.cpp b/components/fileorderlist/model/pluginsproxymodel.cpp deleted file mode 100644 index 6be152b55..000000000 --- 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 8fde73236..000000000 --- a/components/fileorderlist/model/pluginsproxymodel.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef PLUGINSPROXYMODEL_HPP -#define PLUGINSPROXYMODEL_HPP - -#include - -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 4d62e1399..000000000 --- a/components/fileorderlist/utils/comboboxlineedit.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include -#include - -#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 ba10731ae..000000000 --- a/components/fileorderlist/utils/comboboxlineedit.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/**************************************************************************** -** -** Copyright (c) 2007 Trolltech ASA -** -** Use, modification and distribution is allowed without limitation, -** warranty, liability or support of any kind. -** -****************************************************************************/ - -#ifndef LINEEDIT_H -#define LINEEDIT_H - -#include - -class QToolButton; - -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 59271547a..000000000 --- a/components/fileorderlist/utils/naturalsort.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef NATURALSORT_H -#define NATURALSORT_H - -#include - -bool naturalSortLessThanCS( const QString &left, const QString &right ); -bool naturalSortLessThanCI( const QString &left, const QString &right ); -bool naturalSortGreaterThanCS( const QString &left, const QString &right ); -bool naturalSortGreaterThanCI( const QString &left, const QString &right ); - -#endif diff --git a/components/fileorderlist/utils/profilescombobox.hpp b/components/fileorderlist/utils/profilescombobox.hpp deleted file mode 100644 index 08ead9a7a..000000000 --- a/components/fileorderlist/utils/profilescombobox.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef PROFILESCOMBOBOX_HPP -#define PROFILESCOMBOBOX_HPP - -#include - -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/libs/openengine/ogre/particles.cpp b/components/nifogre/particles.cpp similarity index 100% rename from libs/openengine/ogre/particles.cpp rename to components/nifogre/particles.cpp diff --git a/libs/openengine/ogre/particles.hpp b/components/nifogre/particles.hpp similarity index 100% rename from libs/openengine/ogre/particles.hpp rename to components/nifogre/particles.hpp diff --git a/components/ogreinit/ogreinit.cpp b/components/ogreinit/ogreinit.cpp new file mode 100644 index 000000000..92a6ed012 --- /dev/null +++ b/components/ogreinit/ogreinit.cpp @@ -0,0 +1,165 @@ +#include "ogreinit.hpp" + +#include + +#include +#include +#include + +#include + +#include "ogreplugin.hpp" + +namespace OgreInit +{ + + OgreInit::OgreInit() + : mRoot(NULL) + #ifdef ENABLE_PLUGIN_CgProgramManager + , mCgPlugin(NULL) + #endif + #ifdef ENABLE_PLUGIN_OctreeSceneManager + , mOctreePlugin(NULL) + #endif + #ifdef ENABLE_PLUGIN_ParticleFX + , mParticleFXPlugin(NULL) + #endif + #ifdef ENABLE_PLUGIN_GL + , mGLPlugin(NULL) + #endif + #ifdef ENABLE_PLUGIN_Direct3D9 + , mD3D9Plugin(NULL) + #endif + {} + + Ogre::Root* OgreInit::init(const std::string &logPath) + { + // Set up logging first + new Ogre::LogManager; + Ogre::Log *log = Ogre::LogManager::getSingleton().createLog(logPath + std::string("Ogre.log")); + + // Disable logging to cout/cerr + log->setDebugOutputEnabled(false); + + mRoot = new Ogre::Root("", "", ""); + + #if defined(ENABLE_PLUGIN_GL) || defined(ENABLE_PLUGIN_Direct3D9) || defined(ENABLE_PLUGIN_CgProgramManager) || defined(ENABLE_PLUGIN_OctreeSceneManager) || defined(ENABLE_PLUGIN_ParticleFX) + loadStaticPlugins(); + #else + loadPlugins(); + #endif + + loadParticleFactories(); + + return mRoot; + } + + OgreInit::~OgreInit() + { + delete mRoot; + + std::vector::iterator ei; + for(ei = mEmitterFactories.begin();ei != mEmitterFactories.end();++ei) + OGRE_DELETE (*ei); + mEmitterFactories.clear(); + + std::vector::iterator ai; + for(ai = mAffectorFactories.begin();ai != mAffectorFactories.end();++ai) + OGRE_DELETE (*ai); + mAffectorFactories.clear(); + + #ifdef ENABLE_PLUGIN_GL + delete mGLPlugin; + mGLPlugin = NULL; + #endif + #ifdef ENABLE_PLUGIN_Direct3D9 + delete mD3D9Plugin; + mD3D9Plugin = NULL; + #endif + #ifdef ENABLE_PLUGIN_CgProgramManager + delete mCgPlugin; + mCgPlugin = NULL; + #endif + #ifdef ENABLE_PLUGIN_OctreeSceneManager + delete mOctreePlugin; + mOctreePlugin = NULL; + #endif + #ifdef ENABLE_PLUGIN_ParticleFX + delete mParticleFXPlugin; + mParticleFXPlugin = NULL; + #endif + } + + void OgreInit::loadStaticPlugins() + { + #ifdef ENABLE_PLUGIN_GL + mGLPlugin = new Ogre::GLPlugin(); + mRoot->installPlugin(mGLPlugin); + #endif + #ifdef ENABLE_PLUGIN_Direct3D9 + mD3D9Plugin = new Ogre::D3D9Plugin(); + mRoot->installPlugin(mD3D9Plugin); + #endif + #ifdef ENABLE_PLUGIN_CgProgramManager + mCgPlugin = new Ogre::CgPlugin(); + mRoot->installPlugin(mCgPlugin); + #endif + #ifdef ENABLE_PLUGIN_OctreeSceneManager + mOctreePlugin = new Ogre::OctreePlugin(); + mRoot->installPlugin(mOctreePlugin); + #endif + #ifdef ENABLE_PLUGIN_ParticleFX + mParticleFXPlugin = new Ogre::ParticleFXPlugin(); + mRoot->installPlugin(mParticleFXPlugin); + #endif + } + + void OgreInit::loadPlugins() + { + std::string pluginDir; + const char* pluginEnv = getenv("OPENMW_OGRE_PLUGIN_DIR"); + if (pluginEnv) + pluginDir = pluginEnv; + else + { + #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 + pluginDir = ".\\"; + #endif + #if OGRE_PLATFORM == OGRE_PLATFORM_APPLE + pluginDir = OGRE_PLUGIN_DIR; + #endif + #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX + pluginDir = OGRE_PLUGIN_DIR_REL; + #endif + } + + boost::filesystem::path absPluginPath = boost::filesystem::absolute(boost::filesystem::path(pluginDir)); + + pluginDir = absPluginPath.string(); + + Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mRoot); + Files::loadOgrePlugin(pluginDir, "RenderSystem_GLES2", *mRoot); + Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mRoot); + Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mRoot); + Files::loadOgrePlugin(pluginDir, "Plugin_CgProgramManager", *mRoot); + Files::loadOgrePlugin(pluginDir, "Plugin_ParticleFX", *mRoot); + } + + void OgreInit::loadParticleFactories() + { + Ogre::ParticleEmitterFactory *emitter; + emitter = OGRE_NEW NifEmitterFactory(); + Ogre::ParticleSystemManager::getSingleton().addEmitterFactory(emitter); + mEmitterFactories.push_back(emitter); + + Ogre::ParticleAffectorFactory *affector; + affector = OGRE_NEW GrowFadeAffectorFactory(); + Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector); + mAffectorFactories.push_back(affector); + + affector = OGRE_NEW GravityAffectorFactory(); + Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector); + mAffectorFactories.push_back(affector); + } + +} diff --git a/components/ogreinit/ogreinit.hpp b/components/ogreinit/ogreinit.hpp new file mode 100644 index 000000000..b6fe4631a --- /dev/null +++ b/components/ogreinit/ogreinit.hpp @@ -0,0 +1,75 @@ +#ifndef OPENMW_COMPONENTS_OGREINIT_H +#define OPENMW_COMPONENTS_OGREINIT_H + +#include +#include + +// Static plugin headers +#ifdef ENABLE_PLUGIN_CgProgramManager +# include "OgreCgPlugin.h" +#endif +#ifdef ENABLE_PLUGIN_OctreeSceneManager +# include "OgreOctreePlugin.h" +#endif +#ifdef ENABLE_PLUGIN_ParticleFX +# include "OgreParticleFXPlugin.h" +#endif +#ifdef ENABLE_PLUGIN_GL +# include "OgreGLPlugin.h" +#endif +#ifdef ENABLE_PLUGIN_Direct3D9 +# include "OgreD3D9Plugin.h" +#endif + +namespace Ogre +{ + class ParticleEmitterFactory; + class ParticleAffectorFactory; + class Root; +} + +namespace OgreInit +{ + /** + * @brief Starts Ogre::Root and loads required plugins and NIF particle factories + */ + class OgreInit + { + public: + OgreInit(); + + Ogre::Root* init(const std::string &logPath // Path to directory where to store log files + ); + + ~OgreInit(); + + private: + std::vector mEmitterFactories; + std::vector mAffectorFactories; + Ogre::Root* mRoot; + + void loadStaticPlugins(); + void loadPlugins(); + void loadParticleFactories(); + + + #ifdef ENABLE_PLUGIN_CgProgramManager + Ogre::CgPlugin* mCgPlugin; + #endif + #ifdef ENABLE_PLUGIN_OctreeSceneManager + Ogre::OctreePlugin* mOctreePlugin; + #endif + #ifdef ENABLE_PLUGIN_ParticleFX + Ogre::ParticleFXPlugin* mParticleFXPlugin; + #endif + #ifdef ENABLE_PLUGIN_GL + Ogre::GLPlugin* mGLPlugin; + #endif + #ifdef ENABLE_PLUGIN_Direct3D9 + Ogre::D3D9Plugin* mD3D9Plugin; + #endif + + }; +} + +#endif diff --git a/components/files/ogreplugin.cpp b/components/ogreinit/ogreplugin.cpp similarity index 100% rename from components/files/ogreplugin.cpp rename to components/ogreinit/ogreplugin.cpp diff --git a/components/files/ogreplugin.hpp b/components/ogreinit/ogreplugin.hpp similarity index 100% rename from components/files/ogreplugin.hpp rename to components/ogreinit/ogreplugin.hpp diff --git a/extern/sdl4ogre/sdlinputwrapper.cpp b/extern/sdl4ogre/sdlinputwrapper.cpp index e7cbd549c..5c0eff1c0 100644 --- a/extern/sdl4ogre/sdlinputwrapper.cpp +++ b/extern/sdl4ogre/sdlinputwrapper.cpp @@ -127,7 +127,7 @@ namespace SFO case SDL_WINDOWEVENT_SIZE_CHANGED: int w,h; SDL_GetWindowSize(mSDLWindow, &w, &h); - // TODO: Fix Ogre to handle this more consistently + // TODO: Fix Ogre to handle this more consistently (fixed in 1.9) #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX mOgreWindow->resize(w, h); #else @@ -138,7 +138,7 @@ namespace SFO break; case SDL_WINDOWEVENT_RESIZED: - // TODO: Fix Ogre to handle this more consistently + // TODO: Fix Ogre to handle this more consistently (fixed in 1.9) #if OGRE_PLATFORM == OGRE_PLATFORM_LINUX mOgreWindow->resize(evt.window.data1, evt.window.data2); #else diff --git a/files/mygui/CMakeLists.txt b/files/mygui/CMakeLists.txt index 3329c01f9..f79f2c3ed 100644 --- a/files/mygui/CMakeLists.txt +++ b/files/mygui/CMakeLists.txt @@ -9,7 +9,6 @@ set(MYGUI_FILES core.skin core.xml EBGaramond-Regular.ttf - Obliviontt.zip openmw_alchemy_window.layout openmw_book.layout openmw_box.skin.xml diff --git a/files/mygui/Obliviontt.zip b/files/mygui/Obliviontt.zip deleted file mode 100644 index af4f809fd..000000000 Binary files a/files/mygui/Obliviontt.zip and /dev/null differ diff --git a/files/opencs/defaultfilters b/files/opencs/defaultfilters new file mode 100644 index 000000000..0ac3c8db4 Binary files /dev/null and b/files/opencs/defaultfilters differ diff --git a/files/ui/contentselector.ui b/files/ui/contentselector.ui new file mode 100644 index 000000000..b9b5ba5a0 --- /dev/null +++ b/files/ui/contentselector.ui @@ -0,0 +1,111 @@ + + + ContentSelector + + + + 0 + 0 + 518 + 436 + + + + + 0 + 0 + + + + Qt::DefaultContextMenu + + + + 0 + + + + + Content + + + + 3 + + + + + false + + + + + + + + 0 + 0 + + + + Qt::DefaultContextMenu + + + true + + + QAbstractItemView::NoEditTriggers + + + true + + + false + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + Qt::ElideLeft + + + false + + + false + + + true + + + false + + + + + + + + + + + ContentSelectorView::ComboBox + QComboBox +

components/contentselector/view/combobox.hpp
+ + + + + diff --git a/files/ui/datafilespage.ui b/files/ui/datafilespage.ui index 041a9576d..eb5ebc61d 100644 --- a/files/ui/datafilespage.ui +++ b/files/ui/datafilespage.ui @@ -7,108 +7,97 @@ 0 0 518 - 304 + 108 + + + 0 + 0 + + + + Qt::DefaultContextMenu + - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Filter: - - - - - - - + - - - - 0 - 0 - + + + Qt::NoFocus - - Qt::Horizontal + + Profile - + false - - + + + 6 + + + 3 + + + 6 + + + 0 + + + 6 + + + + + true + + + + 0 + 0 + + + + Select a profiile + + + + + + + New Profile + + + &New Profile + + + true + + + + + + + Delete Profile + + + Delete Profile + + + Ctrl+D + + + true + + + + - - - - - - Current Profile: - - - - - - - true - - - - 0 - 0 - - - - - - - - New Profile - - - &New Profile - - - true - - - - - - - Delete Profile - - - Delete Profile - - - Ctrl+D - - - true - - - - - @@ -127,6 +116,9 @@ + + false + @@ -140,6 +132,9 @@ + + true + Check Selection @@ -151,15 +146,10 @@ - - LineEdit - QLineEdit -
components/fileorderlist/utils/lineedit.hpp
-
ProfilesComboBox QComboBox -
components/fileorderlist/utils/profilescombobox.hpp
+
apps/launcher/utils/profilescombobox.hpp
diff --git a/files/ui/filedialog.ui b/files/ui/filedialog.ui new file mode 100644 index 000000000..b3af166da --- /dev/null +++ b/files/ui/filedialog.ui @@ -0,0 +1,73 @@ + + + FileDialog + + + + 0 + 0 + 518 + 109 + + + + + 0 + 0 + + + + Qt::DefaultContextMenu + + + + + + + + + Qt::NoFocus + + + Project Name + + + false + + + + 6 + + + 3 + + + 6 + + + 0 + + + 6 + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + + + + + diff --git a/libs/openengine/ogre/renderer.cpp b/libs/openengine/ogre/renderer.cpp index d078e3c61..2a438e1fe 100644 --- a/libs/openengine/ogre/renderer.cpp +++ b/libs/openengine/ogre/renderer.cpp @@ -1,27 +1,17 @@ #include "renderer.hpp" #include "fader.hpp" -#include "particles.hpp" #include -#include "OgreRoot.h" -#include "OgreRenderWindow.h" -#include "OgreLogManager.h" -#include "OgreLog.h" -#include "OgreTextureManager.h" -#include "OgreTexture.h" -#include "OgreHardwarePixelBuffer.h" -#include -#include "OgreParticleAffectorFactory.h" - -#include - -#include +#include +#include +#include +#include +#include #include #include -#include #include using namespace Ogre; @@ -33,74 +23,11 @@ void OgreRenderer::cleanup() delete mFader; mFader = NULL; - delete mRoot; - mRoot = NULL; - // If we don't do this, the desktop resolution is not restored on exit SDL_SetWindowFullscreen(mSDLWindow, 0); SDL_DestroyWindow(mSDLWindow); mSDLWindow = NULL; - - unloadPlugins(); -} - -void OgreRenderer::loadPlugins() -{ - #ifdef ENABLE_PLUGIN_GL - mGLPlugin = new Ogre::GLPlugin(); - mRoot->installPlugin(mGLPlugin); - #endif - #ifdef ENABLE_PLUGIN_Direct3D9 - mD3D9Plugin = new Ogre::D3D9Plugin(); - mRoot->installPlugin(mD3D9Plugin); - #endif - #ifdef ENABLE_PLUGIN_CgProgramManager - mCgPlugin = new Ogre::CgPlugin(); - mRoot->installPlugin(mCgPlugin); - #endif - #ifdef ENABLE_PLUGIN_OctreeSceneManager - mOctreePlugin = new Ogre::OctreePlugin(); - mRoot->installPlugin(mOctreePlugin); - #endif - #ifdef ENABLE_PLUGIN_ParticleFX - mParticleFXPlugin = new Ogre::ParticleFXPlugin(); - mRoot->installPlugin(mParticleFXPlugin); - #endif -} - -void OgreRenderer::unloadPlugins() -{ - std::vector::iterator ei; - for(ei = mEmitterFactories.begin();ei != mEmitterFactories.end();++ei) - OGRE_DELETE (*ei); - mEmitterFactories.clear(); - - std::vector::iterator ai; - for(ai = mAffectorFactories.begin();ai != mAffectorFactories.end();++ai) - OGRE_DELETE (*ai); - mAffectorFactories.clear(); - - #ifdef ENABLE_PLUGIN_GL - delete mGLPlugin; - mGLPlugin = NULL; - #endif - #ifdef ENABLE_PLUGIN_Direct3D9 - delete mD3D9Plugin; - mD3D9Plugin = NULL; - #endif - #ifdef ENABLE_PLUGIN_CgProgramManager - delete mCgPlugin; - mCgPlugin = NULL; - #endif - #ifdef ENABLE_PLUGIN_OctreeSceneManager - delete mOctreePlugin; - mOctreePlugin = NULL; - #endif - #ifdef ENABLE_PLUGIN_ParticleFX - delete mParticleFXPlugin; - mParticleFXPlugin = NULL; - #endif } void OgreRenderer::update(float dt) @@ -120,70 +47,10 @@ float OgreRenderer::getFPS() void OgreRenderer::configure(const std::string &logPath, const std::string& renderSystem, - const std::string& rttMode, - bool _logging) + const std::string& rttMode + ) { - // Set up logging first - new LogManager; - Log *log = LogManager::getSingleton().createLog(logPath + std::string("Ogre.log")); - - if(_logging) - // Full log detail - log->setLogDetail(LL_BOREME); - else - // Disable logging - log->setDebugOutputEnabled(false); - - mRoot = new Root("", "", ""); - - #if defined(ENABLE_PLUGIN_GL) || defined(ENABLE_PLUGIN_Direct3D9) || defined(ENABLE_PLUGIN_CgProgramManager) || defined(ENABLE_PLUGIN_OctreeSceneManager) || defined(ENABLE_PLUGIN_ParticleFX) - loadPlugins(); - #endif - - std::string pluginDir; - const char* pluginEnv = getenv("OPENMW_OGRE_PLUGIN_DIR"); - if (pluginEnv) - pluginDir = pluginEnv; - else - { -#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 - pluginDir = ".\\"; -#endif -#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE - pluginDir = OGRE_PLUGIN_DIR; -#endif -#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX - pluginDir = OGRE_PLUGIN_DIR_REL; -#endif - } - - boost::filesystem::path absPluginPath = boost::filesystem::absolute(boost::filesystem::path(pluginDir)); - - pluginDir = absPluginPath.string(); - - Files::loadOgrePlugin(pluginDir, "RenderSystem_GL", *mRoot); - Files::loadOgrePlugin(pluginDir, "RenderSystem_GLES2", *mRoot); - Files::loadOgrePlugin(pluginDir, "RenderSystem_GL3Plus", *mRoot); - Files::loadOgrePlugin(pluginDir, "RenderSystem_Direct3D9", *mRoot); - Files::loadOgrePlugin(pluginDir, "Plugin_CgProgramManager", *mRoot); - Files::loadOgrePlugin(pluginDir, "Plugin_ParticleFX", *mRoot); - - - Ogre::ParticleEmitterFactory *emitter; - emitter = OGRE_NEW NifEmitterFactory(); - Ogre::ParticleSystemManager::getSingleton().addEmitterFactory(emitter); - mEmitterFactories.push_back(emitter); - - - Ogre::ParticleAffectorFactory *affector; - affector = OGRE_NEW GrowFadeAffectorFactory(); - Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector); - mAffectorFactories.push_back(affector); - - affector = OGRE_NEW GravityAffectorFactory(); - Ogre::ParticleSystemManager::getSingleton().addAffectorFactory(affector); - mAffectorFactories.push_back(affector); - + mRoot = mOgreInit.init(logPath); RenderSystem* rs = mRoot->getRenderSystemByName(renderSystem); if (rs == 0) diff --git a/libs/openengine/ogre/renderer.hpp b/libs/openengine/ogre/renderer.hpp index c6a838805..e4af0bf77 100644 --- a/libs/openengine/ogre/renderer.hpp +++ b/libs/openengine/ogre/renderer.hpp @@ -7,25 +7,9 @@ #include -// Static plugin headers -#ifdef ENABLE_PLUGIN_CgProgramManager -# include "OgreCgPlugin.h" -#endif -#ifdef ENABLE_PLUGIN_OctreeSceneManager -# include "OgreOctreePlugin.h" -#endif -#ifdef ENABLE_PLUGIN_ParticleFX -# include "OgreParticleFXPlugin.h" -#endif -#ifdef ENABLE_PLUGIN_GL -# include "OgreGLPlugin.h" -#endif -#ifdef ENABLE_PLUGIN_Direct3D9 -# include "OgreD3D9Plugin.h" -#endif +#include -#include "OgreTexture.h" -#include +#include struct SDL_Window; @@ -72,24 +56,10 @@ namespace OEngine Ogre::SceneManager *mScene; Ogre::Camera *mCamera; Ogre::Viewport *mView; - #ifdef ENABLE_PLUGIN_CgProgramManager - Ogre::CgPlugin* mCgPlugin; - #endif - #ifdef ENABLE_PLUGIN_OctreeSceneManager - Ogre::OctreePlugin* mOctreePlugin; - #endif - #ifdef ENABLE_PLUGIN_ParticleFX - Ogre::ParticleFXPlugin* mParticleFXPlugin; - #endif - #ifdef ENABLE_PLUGIN_GL - Ogre::GLPlugin* mGLPlugin; - #endif - #ifdef ENABLE_PLUGIN_Direct3D9 - Ogre::D3D9Plugin* mD3D9Plugin; - #endif + + OgreInit::OgreInit mOgreInit; + Fader* mFader; - std::vector mEmitterFactories; - std::vector mAffectorFactories; WindowSizeListener* mWindowListener; @@ -102,21 +72,6 @@ namespace OEngine , mCamera(NULL) , mView(NULL) , mWindowListener(NULL) - #ifdef ENABLE_PLUGIN_CgProgramManager - , mCgPlugin(NULL) - #endif - #ifdef ENABLE_PLUGIN_OctreeSceneManager - , mOctreePlugin(NULL) - #endif - #ifdef ENABLE_PLUGIN_ParticleFX - , mParticleFXPlugin(NULL) - #endif - #ifdef ENABLE_PLUGIN_GL - , mGLPlugin(NULL) - #endif - #ifdef ENABLE_PLUGIN_Direct3D9 - , mD3D9Plugin(NULL) - #endif , mFader(NULL) { } @@ -128,8 +83,7 @@ namespace OEngine void configure( const std::string &logPath, // Path to directory where to store log files const std::string &renderSystem, - const std::string &rttMode, - bool _logging); // Enable or disable logging + const std::string &rttMode); // Enable or disable logging /// Create a window with the given title void createWindow(const std::string &title, const WindowSettings& settings); @@ -145,10 +99,6 @@ namespace OEngine /// Kill the renderer. void cleanup(); - void loadPlugins(); - - void unloadPlugins(); - void update(float dt); /// Write a screenshot to file diff --git a/readme.txt b/readme.txt index 7865f8dba..afcfadea3 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