Merge remote-tracking branch 'upstream/master'
4
.gitignore
vendored
|
@ -1,4 +1,6 @@
|
||||||
build
|
build
|
||||||
*~
|
*~
|
||||||
Doxygen
|
Doxygen
|
||||||
prebuilt
|
prebuilt
|
||||||
|
apps/openmw/config.hpp
|
||||||
|
Docs/mainpage.hpp
|
||||||
|
|
|
@ -2,8 +2,23 @@ project(OpenMW)
|
||||||
|
|
||||||
IF (APPLE)
|
IF (APPLE)
|
||||||
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/OpenMW.app")
|
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/OpenMW.app")
|
||||||
|
|
||||||
|
# using 10.6 sdk
|
||||||
|
set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.6.sdk")
|
||||||
ENDIF (APPLE)
|
ENDIF (APPLE)
|
||||||
|
|
||||||
|
# Version
|
||||||
|
|
||||||
|
set (OPENMW_VERSION_MAJOR 0)
|
||||||
|
set (OPENMW_VERSION_MINOR 11)
|
||||||
|
set (OPENMW_VERSION_RELEASE 0)
|
||||||
|
|
||||||
|
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
|
||||||
|
|
||||||
|
# doxygen main page
|
||||||
|
|
||||||
|
configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp")
|
||||||
|
|
||||||
# Sound source selection
|
# Sound source selection
|
||||||
option(USE_AUDIERE "use Audiere for sound" OFF)
|
option(USE_AUDIERE "use Audiere for sound" OFF)
|
||||||
option(USE_FFMPEG "use ffmpeg for sound" OFF)
|
option(USE_FFMPEG "use ffmpeg for sound" OFF)
|
||||||
|
@ -17,9 +32,8 @@ if(DPKG_PROGRAM)
|
||||||
set(MORROWIND_RESOURCE_FILES "/usr/share/games/openmw/resources/" CACHE PATH "location of OpenMW resources files")
|
set(MORROWIND_RESOURCE_FILES "/usr/share/games/openmw/resources/" CACHE PATH "location of OpenMW resources files")
|
||||||
else()
|
else()
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
# set path inside bundle
|
set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files")
|
||||||
set(MORROWIND_DATA_FILES "../data" CACHE PATH "location of Morrowind data files")
|
set(MORROWIND_RESOURCE_FILES "./resources" CACHE PATH "location of OpenMW resources files")
|
||||||
set(MORROWIND_RESOURCE_FILES "Contents/Resources/resources" CACHE PATH "location of OpenMW resources files")
|
|
||||||
else()
|
else()
|
||||||
set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data files")
|
set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data files")
|
||||||
set(MORROWIND_RESOURCE_FILES "resources" CACHE PATH "location of OpenMW resources files")
|
set(MORROWIND_RESOURCE_FILES "resources" CACHE PATH "location of OpenMW resources files")
|
||||||
|
@ -57,6 +71,7 @@ IF(EXISTS "${CMAKE_SOURCE_DIR}/prebuilt/vc100-mt-gd/ogre_1_7_1")
|
||||||
|
|
||||||
set(ENV{OPENALDIR} "${PREBUILT_DIR}/OpenAL 1.1 SDK")
|
set(ENV{OPENALDIR} "${PREBUILT_DIR}/OpenAL 1.1 SDK")
|
||||||
|
|
||||||
|
set(BULLET_ROOT "${PREBUILT_DIR}/bullet")
|
||||||
ELSE()
|
ELSE()
|
||||||
message (STATUS "OpenMW pre-built binaries not found. Using standard locations.")
|
message (STATUS "OpenMW pre-built binaries not found. Using standard locations.")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
@ -365,7 +380,7 @@ endif()
|
||||||
|
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.mac
|
configure_file(${OpenMW_SOURCE_DIR}/files/plugins.cfg.mac
|
||||||
"${APP_BUNDLE_DIR}/Contents/MacOS/plugins.cfg")
|
"${OpenMW_BINARY_DIR}/plugins.cfg")
|
||||||
|
|
||||||
configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist
|
configure_file(${OpenMW_SOURCE_DIR}/files/mac/Info.plist
|
||||||
"${APP_BUNDLE_DIR}/Contents/Info.plist" COPYONLY)
|
"${APP_BUNDLE_DIR}/Contents/Info.plist" COPYONLY)
|
||||||
|
@ -384,9 +399,6 @@ if (APPLE)
|
||||||
configure_file(${OGRE_PLUGIN_DIR}/Plugin_ParticleFX.dylib
|
configure_file(${OGRE_PLUGIN_DIR}/Plugin_ParticleFX.dylib
|
||||||
"${APP_BUNDLE_DIR}/Contents/Plugins/Plugin_ParticleFX.dylib" COPYONLY)
|
"${APP_BUNDLE_DIR}/Contents/Plugins/Plugin_ParticleFX.dylib" COPYONLY)
|
||||||
|
|
||||||
configure_file(${OpenMW_SOURCE_DIR}/files/openmw.cfg
|
|
||||||
"${APP_BUNDLE_DIR}/Contents/MacOS/openmw.cfg")
|
|
||||||
|
|
||||||
endif (APPLE)
|
endif (APPLE)
|
||||||
|
|
||||||
|
|
||||||
|
@ -397,8 +409,9 @@ if (CMAKE_COMPILER_IS_GNUCC)
|
||||||
endif (CMAKE_COMPILER_IS_GNUCC)
|
endif (CMAKE_COMPILER_IS_GNUCC)
|
||||||
|
|
||||||
# Apple bundling
|
# Apple bundling
|
||||||
|
# TODO REWRITE!
|
||||||
if (APPLE)
|
if (APPLE)
|
||||||
set(MISC_FILES
|
set(MISC_FILES
|
||||||
${APP_BUNDLE_DIR}/Contents/MacOS/openmw.cfg
|
${APP_BUNDLE_DIR}/Contents/MacOS/openmw.cfg
|
||||||
${APP_BUNDLE_DIR}/Contents/MacOS/plugins.cfg)
|
${APP_BUNDLE_DIR}/Contents/MacOS/plugins.cfg)
|
||||||
|
|
||||||
|
@ -435,8 +448,7 @@ if(DPKG_PROGRAM)
|
||||||
exec_program("git" ARGS "config --get user.email" OUTPUT_VARIABLE GIT_EMAIL)
|
exec_program("git" ARGS "config --get user.email" OUTPUT_VARIABLE GIT_EMAIL)
|
||||||
set(PACKAGE_MAINTAINER "${GIT_NAME} <${GIT_EMAIL}>")
|
set(PACKAGE_MAINTAINER "${GIT_NAME} <${GIT_EMAIL}>")
|
||||||
else()
|
else()
|
||||||
#FIXME this should probably be read from some file like ${CMAKE_CURRENT_SOURCE_DIR}/VERSION or something that gets updated when changing version
|
set(VERSION_STRING "${OPENMW_VERSION}")
|
||||||
set(VERSION_STRING "0.10.0")
|
|
||||||
set(PACKAGE_MAINTAINER "unknown")
|
set(PACKAGE_MAINTAINER "unknown")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -483,6 +495,8 @@ if (BUILD_ESMTOOL)
|
||||||
add_subdirectory( apps/esmtool )
|
add_subdirectory( apps/esmtool )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory( apps/launcher )
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
if (USE_DEBUG_CONSOLE)
|
if (USE_DEBUG_CONSOLE)
|
||||||
|
|
|
@ -576,6 +576,7 @@ WARN_LOGFILE =
|
||||||
INPUT = apps \
|
INPUT = apps \
|
||||||
components \
|
components \
|
||||||
libs \
|
libs \
|
||||||
|
Docs
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# This tag can be used to specify the character encoding of the source files
|
||||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
||||||
|
|
|
@ -576,6 +576,7 @@ WARN_LOGFILE =
|
||||||
INPUT = apps \
|
INPUT = apps \
|
||||||
components \
|
components \
|
||||||
libs \
|
libs \
|
||||||
|
Docs
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# This tag can be used to specify the character encoding of the source files
|
||||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
|
||||||
|
|
5
Docs/mainpage.hpp.cmake
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/// \mainpage
|
||||||
|
///
|
||||||
|
/// This is the source documentation for:
|
||||||
|
///
|
||||||
|
/// OpenMW @OPENMW_VERSION@
|
79
apps/launcher/CMakeLists.txt
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
set(LAUNCHER
|
||||||
|
datafilespage.cpp
|
||||||
|
graphicspage.cpp
|
||||||
|
lineedit.cpp
|
||||||
|
main.cpp
|
||||||
|
maindialog.cpp
|
||||||
|
naturalsort.cpp
|
||||||
|
playpage.cpp
|
||||||
|
pluginsmodel.cpp
|
||||||
|
pluginsview.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(LAUNCHER_HEADER
|
||||||
|
combobox.hpp
|
||||||
|
datafilespage.hpp
|
||||||
|
graphicspage.hpp
|
||||||
|
lineedit.hpp
|
||||||
|
maindialog.hpp
|
||||||
|
naturalsort.hpp
|
||||||
|
playpage.hpp
|
||||||
|
pluginsmodel.hpp
|
||||||
|
pluginsview.hpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Headers that must be pre-processed
|
||||||
|
set(LAUNCHER_HEADER_MOC
|
||||||
|
combobox.hpp
|
||||||
|
datafilespage.hpp
|
||||||
|
graphicspage.hpp
|
||||||
|
lineedit.hpp
|
||||||
|
maindialog.hpp
|
||||||
|
playpage.hpp
|
||||||
|
pluginsmodel.hpp
|
||||||
|
pluginsview.hpp
|
||||||
|
)
|
||||||
|
|
||||||
|
source_group(launcher FILES ${LAUNCHER} ${LAUNCHER_HEADER} ${LAUNCHER_HEADER_MOC})
|
||||||
|
|
||||||
|
find_package(Qt4 REQUIRED)
|
||||||
|
set(QT_USE_QTGUI 1)
|
||||||
|
|
||||||
|
find_package(PNG REQUIRED)
|
||||||
|
include_directories(${PNG_INCLUDE_DIR})
|
||||||
|
|
||||||
|
QT4_ADD_RESOURCES(RCC_SRCS resources.qrc)
|
||||||
|
QT4_WRAP_CPP(MOC_SRCS ${LAUNCHER_HEADER_MOC})
|
||||||
|
|
||||||
|
include(${QT_USE_FILE})
|
||||||
|
|
||||||
|
# Main executable
|
||||||
|
add_executable(omwlauncher
|
||||||
|
${LAUNCHER}
|
||||||
|
${MISC} ${MISC_HEADER}
|
||||||
|
${FILES} ${FILES_HEADER}
|
||||||
|
${TO_UTF8}
|
||||||
|
${ESM}
|
||||||
|
${RCC_SRCS}
|
||||||
|
${MOC_SRCS}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(omwlauncher
|
||||||
|
${Boost_LIBRARIES}
|
||||||
|
${OGRE_LIBRARIES}
|
||||||
|
${QT_LIBRARIES}
|
||||||
|
${PNG_LIBRARY}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss
|
||||||
|
"${APP_BUNDLE_DIR}/../launcher.qss")
|
||||||
|
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss
|
||||||
|
"${APP_BUNDLE_DIR}/../launcher.cfg")
|
||||||
|
else()
|
||||||
|
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.qss
|
||||||
|
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.qss")
|
||||||
|
|
||||||
|
configure_file(${CMAKE_SOURCE_DIR}/files/launcher.cfg
|
||||||
|
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/launcher.cfg")
|
||||||
|
endif()
|
28
apps/launcher/combobox.hpp
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef COMBOBOX_H
|
||||||
|
#define COMBOBOX_H
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
|
|
||||||
|
class ComboBox : public QComboBox
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private:
|
||||||
|
QString oldText;
|
||||||
|
public:
|
||||||
|
ComboBox(QWidget *parent=0) : QComboBox(parent), oldText()
|
||||||
|
{
|
||||||
|
connect(this,SIGNAL(editTextChanged(const QString&)), this,
|
||||||
|
SLOT(textChangedSlot(const QString&)));
|
||||||
|
connect(this,SIGNAL(currentIndexChanged(const QString&)), this,
|
||||||
|
SLOT(textChangedSlot(const QString&)));
|
||||||
|
}
|
||||||
|
private slots:
|
||||||
|
void textChangedSlot(const QString &newText)
|
||||||
|
{
|
||||||
|
emit textChanged(oldText, newText);
|
||||||
|
oldText = newText;
|
||||||
|
}
|
||||||
|
signals:
|
||||||
|
void textChanged(const QString &oldText, const QString &newText);
|
||||||
|
};
|
||||||
|
#endif
|
979
apps/launcher/datafilespage.cpp
Normal file
|
@ -0,0 +1,979 @@
|
||||||
|
#include <QtGui>
|
||||||
|
|
||||||
|
#include <components/esm/esm_reader.hpp>
|
||||||
|
#include <components/files/path.hpp>
|
||||||
|
#include <components/files/collections.hpp>
|
||||||
|
#include <components/files/multidircollection.hpp>
|
||||||
|
|
||||||
|
#include "datafilespage.hpp"
|
||||||
|
#include "lineedit.hpp"
|
||||||
|
#include "naturalsort.hpp"
|
||||||
|
#include "pluginsmodel.hpp"
|
||||||
|
#include "pluginsview.hpp"
|
||||||
|
|
||||||
|
using namespace ESM;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
//sort QModelIndexList ascending
|
||||||
|
bool rowGreaterThan(const QModelIndex &index1, const QModelIndex &index2)
|
||||||
|
{
|
||||||
|
return index1.row() >= index2.row();
|
||||||
|
}
|
||||||
|
|
||||||
|
//sort QModelIndexList descending
|
||||||
|
bool rowSmallerThan(const QModelIndex &index1, const QModelIndex &index2)
|
||||||
|
{
|
||||||
|
return index1.row() <= index2.row();
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFilesPage::DataFilesPage(QWidget *parent) : QWidget(parent)
|
||||||
|
{
|
||||||
|
mDataFilesModel = new QStandardItemModel(); // Contains all plugins with masters
|
||||||
|
mPluginsModel = new PluginsModel(); // Contains selectable plugins
|
||||||
|
|
||||||
|
mPluginsProxyModel = new QSortFilterProxyModel();
|
||||||
|
mPluginsProxyModel->setDynamicSortFilter(true);
|
||||||
|
mPluginsProxyModel->setSourceModel(mPluginsModel);
|
||||||
|
|
||||||
|
QLabel *filterLabel = new QLabel(tr("&Filter:"), this);
|
||||||
|
LineEdit *filterLineEdit = new LineEdit(this);
|
||||||
|
filterLabel->setBuddy(filterLineEdit);
|
||||||
|
|
||||||
|
QToolBar *filterToolBar = new QToolBar(this);
|
||||||
|
filterToolBar->setMovable(false);
|
||||||
|
|
||||||
|
// Create a container widget and a layout to get the spacer to work
|
||||||
|
QWidget *filterWidget = new QWidget(this);
|
||||||
|
QHBoxLayout *filterLayout = new QHBoxLayout(filterWidget);
|
||||||
|
QSpacerItem *hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
|
|
||||||
|
filterLayout->addItem(hSpacer1);
|
||||||
|
filterLayout->addWidget(filterLabel);
|
||||||
|
filterLayout->addWidget(filterLineEdit);
|
||||||
|
|
||||||
|
filterToolBar->addWidget(filterWidget);
|
||||||
|
|
||||||
|
mMastersWidget = new QTableWidget(this); // Contains the available masters
|
||||||
|
mMastersWidget->setObjectName("MastersWidget");
|
||||||
|
mMastersWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
mMastersWidget->setSelectionMode(QAbstractItemView::MultiSelection);
|
||||||
|
mMastersWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
mMastersWidget->setAlternatingRowColors(true);
|
||||||
|
mMastersWidget->horizontalHeader()->setStretchLastSection(true);
|
||||||
|
mMastersWidget->horizontalHeader()->hide();
|
||||||
|
mMastersWidget->verticalHeader()->hide();
|
||||||
|
mMastersWidget->insertColumn(0);
|
||||||
|
|
||||||
|
mPluginsTable = new PluginsView(this);
|
||||||
|
mPluginsTable->setModel(mPluginsProxyModel);
|
||||||
|
mPluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
|
||||||
|
|
||||||
|
mPluginsTable->horizontalHeader()->setStretchLastSection(true);
|
||||||
|
mPluginsTable->horizontalHeader()->hide();
|
||||||
|
|
||||||
|
// Set the row height to the size of the checkboxes
|
||||||
|
QCheckBox checkBox;
|
||||||
|
unsigned int height = checkBox.sizeHint().height() + 4;
|
||||||
|
|
||||||
|
mPluginsTable->verticalHeader()->setDefaultSectionSize(height);
|
||||||
|
|
||||||
|
// Add both tables to a splitter
|
||||||
|
QSplitter *splitter = new QSplitter(this);
|
||||||
|
splitter->setOrientation(Qt::Horizontal);
|
||||||
|
|
||||||
|
splitter->addWidget(mMastersWidget);
|
||||||
|
splitter->addWidget(mPluginsTable);
|
||||||
|
|
||||||
|
// Adjust the default widget widths inside the splitter
|
||||||
|
QList<int> sizeList;
|
||||||
|
sizeList << 100 << 300;
|
||||||
|
splitter->setSizes(sizeList);
|
||||||
|
|
||||||
|
// Bottom part with profile options
|
||||||
|
QLabel *profileLabel = new QLabel(tr("Current Profile: "), this);
|
||||||
|
|
||||||
|
mProfilesComboBox = new ComboBox(this);
|
||||||
|
mProfilesComboBox->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum));
|
||||||
|
mProfilesComboBox->setInsertPolicy(QComboBox::InsertAtBottom);
|
||||||
|
|
||||||
|
mProfileToolBar = new QToolBar(this);
|
||||||
|
mProfileToolBar->setMovable(false);
|
||||||
|
mProfileToolBar->setIconSize(QSize(16, 16));
|
||||||
|
|
||||||
|
mProfileToolBar->addWidget(profileLabel);
|
||||||
|
mProfileToolBar->addWidget(mProfilesComboBox);
|
||||||
|
|
||||||
|
QVBoxLayout *pageLayout = new QVBoxLayout(this);
|
||||||
|
|
||||||
|
pageLayout->addWidget(filterToolBar);
|
||||||
|
pageLayout->addWidget(splitter);
|
||||||
|
pageLayout->addWidget(mProfileToolBar);
|
||||||
|
|
||||||
|
connect(mMastersWidget->selectionModel(),
|
||||||
|
SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
|
||||||
|
this, SLOT(masterSelectionChanged(const QItemSelection&, const QItemSelection&)));
|
||||||
|
|
||||||
|
connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(const QString)));
|
||||||
|
|
||||||
|
connect(mPluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
|
||||||
|
connect(mPluginsTable, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&)));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
setupConfig();
|
||||||
|
createActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::setupDataFiles(const QStringList &paths, bool strict)
|
||||||
|
{
|
||||||
|
// Put the paths in a boost::filesystem vector to use with Files::Collections
|
||||||
|
std::vector<boost::filesystem::path> dataDirs;
|
||||||
|
|
||||||
|
foreach (const QString ¤tPath, paths) {
|
||||||
|
dataDirs.push_back(boost::filesystem::path(currentPath.toStdString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a file collection for the dataDirs
|
||||||
|
Files::Collections mFileCollections(dataDirs, strict);
|
||||||
|
|
||||||
|
// First we add all the master files
|
||||||
|
const Files::MultiDirCollection &esm = mFileCollections.getCollection(".esm");
|
||||||
|
unsigned int i = 0; // Row number
|
||||||
|
|
||||||
|
for (Files::MultiDirCollection::TIter iter(esm.begin()); iter!=esm.end(); ++iter)
|
||||||
|
{
|
||||||
|
std::string filename = boost::filesystem::path (iter->second.filename()).string();
|
||||||
|
QString currentMaster = QString::fromStdString(filename);
|
||||||
|
|
||||||
|
const QList<QTableWidgetItem*> itemList = mMastersWidget->findItems(currentMaster, Qt::MatchExactly);
|
||||||
|
|
||||||
|
if (itemList.isEmpty()) // Master is not yet in the widget
|
||||||
|
{
|
||||||
|
mMastersWidget->insertRow(i);
|
||||||
|
QTableWidgetItem *item = new QTableWidgetItem(currentMaster);
|
||||||
|
mMastersWidget->setItem(i, 0, item);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now on to the plugins
|
||||||
|
const Files::MultiDirCollection &esp = mFileCollections.getCollection(".esp");
|
||||||
|
|
||||||
|
for (Files::MultiDirCollection::TIter iter(esp.begin()); iter!=esp.end(); ++iter)
|
||||||
|
{
|
||||||
|
ESMReader fileReader;
|
||||||
|
QStringList availableMasters; // Will contain all found masters
|
||||||
|
|
||||||
|
fileReader.setEncoding("win1252");
|
||||||
|
fileReader.open(iter->second.string());
|
||||||
|
|
||||||
|
// First we fill the availableMasters and the mMastersWidget
|
||||||
|
ESMReader::MasterList mlist = fileReader.getMasters();
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < mlist.size(); ++i) {
|
||||||
|
const QString currentMaster = QString::fromStdString(mlist[i].name);
|
||||||
|
availableMasters.append(currentMaster);
|
||||||
|
|
||||||
|
const QList<QTableWidgetItem*> itemList = mMastersWidget->findItems(currentMaster, Qt::MatchExactly);
|
||||||
|
|
||||||
|
if (itemList.isEmpty()) // Master is not yet in the widget
|
||||||
|
{
|
||||||
|
// TODO: Show warning, missing master
|
||||||
|
mMastersWidget->insertRow(i);
|
||||||
|
QTableWidgetItem *item = new QTableWidgetItem(currentMaster);
|
||||||
|
mMastersWidget->setItem(i, 0, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
availableMasters.sort(); // Sort the masters alphabetically
|
||||||
|
|
||||||
|
// Now we put the current plugin in the mDataFilesModel under its masters
|
||||||
|
QStandardItem *parent = new QStandardItem(availableMasters.join(","));
|
||||||
|
|
||||||
|
std::string filename = boost::filesystem::path (iter->second.filename()).string();
|
||||||
|
QStandardItem *child = new QStandardItem(QString::fromStdString(std::string(filename)));
|
||||||
|
|
||||||
|
const QList<QStandardItem*> masterList = mDataFilesModel->findItems(availableMasters.join(","));
|
||||||
|
|
||||||
|
if (masterList.isEmpty()) { // Masters node not yet in the mDataFilesModel
|
||||||
|
parent->appendRow(child);
|
||||||
|
mDataFilesModel->appendRow(parent);
|
||||||
|
} else {
|
||||||
|
// Masters node exists, append current plugin
|
||||||
|
foreach (QStandardItem *currentItem, masterList) {
|
||||||
|
currentItem->appendRow(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::setupConfig()
|
||||||
|
{
|
||||||
|
QString config = "./launcher.cfg";
|
||||||
|
QFile file(config);
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
config = QString::fromStdString(Files::getPath(Files::Path_ConfigUser,
|
||||||
|
"openmw", "launcher.cfg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
file.setFileName(config); // Just for displaying information
|
||||||
|
|
||||||
|
// Open our config file
|
||||||
|
mLauncherConfig = new QSettings(config, QSettings::IniFormat);
|
||||||
|
mLauncherConfig->sync();
|
||||||
|
|
||||||
|
|
||||||
|
// Make sure we have no groups open
|
||||||
|
while (!mLauncherConfig->group().isEmpty()) {
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
mLauncherConfig->beginGroup("Profiles");
|
||||||
|
QStringList profiles = mLauncherConfig->childGroups();
|
||||||
|
|
||||||
|
if (profiles.isEmpty()) {
|
||||||
|
// Add a default profile
|
||||||
|
profiles.append("Default");
|
||||||
|
}
|
||||||
|
|
||||||
|
mProfilesComboBox->addItems(profiles);
|
||||||
|
|
||||||
|
QString currentProfile = mLauncherConfig->value("CurrentProfile").toString();
|
||||||
|
|
||||||
|
if (currentProfile.isEmpty()) {
|
||||||
|
// No current profile selected
|
||||||
|
currentProfile = "Default";
|
||||||
|
}
|
||||||
|
mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(currentProfile));
|
||||||
|
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
|
||||||
|
// Now we connect the combobox to do something if the profile changes
|
||||||
|
// This prevents strange behaviour while reading and appending the profiles
|
||||||
|
connect(mProfilesComboBox, SIGNAL(textChanged(const QString&, const QString&)), this, SLOT(profileChanged(const QString&, const QString&)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::createActions()
|
||||||
|
{
|
||||||
|
// Refresh the plugins
|
||||||
|
QAction *refreshAction = new QAction(tr("Refresh"), this);
|
||||||
|
refreshAction->setShortcut(QKeySequence(tr("F5")));
|
||||||
|
connect(refreshAction, SIGNAL(triggered()), this, SLOT(refresh()));
|
||||||
|
|
||||||
|
// Profile actions
|
||||||
|
mNewProfileAction = new QAction(QIcon::fromTheme("document-new"), tr("&New Profile"), this);
|
||||||
|
mNewProfileAction->setToolTip(tr("New Profile"));
|
||||||
|
mNewProfileAction->setShortcut(QKeySequence(tr("Ctrl+N")));
|
||||||
|
connect(mNewProfileAction, SIGNAL(triggered()), this, SLOT(newProfile()));
|
||||||
|
|
||||||
|
|
||||||
|
mCopyProfileAction = new QAction(QIcon::fromTheme("edit-copy"), tr("&Copy Profile"), this);
|
||||||
|
mCopyProfileAction->setToolTip(tr("Copy Profile"));
|
||||||
|
mCopyProfileAction->setShortcut(QKeySequence(tr("Ctrl+C")));
|
||||||
|
connect(mCopyProfileAction, SIGNAL(triggered()), this, SLOT(copyProfile()));
|
||||||
|
|
||||||
|
mDeleteProfileAction = new QAction(QIcon::fromTheme("edit-delete"), tr("Delete Profile"), this);
|
||||||
|
mDeleteProfileAction->setToolTip(tr("Delete Profile"));
|
||||||
|
mDeleteProfileAction->setShortcut(QKeySequence(tr("Delete")));
|
||||||
|
connect(mDeleteProfileAction, SIGNAL(triggered()), this, SLOT(deleteProfile()));
|
||||||
|
|
||||||
|
// Add the newly created actions to the toolbar
|
||||||
|
mProfileToolBar->addSeparator();
|
||||||
|
mProfileToolBar->addAction(mNewProfileAction);
|
||||||
|
mProfileToolBar->addAction(mCopyProfileAction);
|
||||||
|
mProfileToolBar->addAction(mDeleteProfileAction);
|
||||||
|
|
||||||
|
// Context menu actions
|
||||||
|
mMoveUpAction = new QAction(QIcon::fromTheme("go-up"), tr("Move &Up"), this);
|
||||||
|
mMoveUpAction->setShortcut(QKeySequence(tr("Ctrl+U")));
|
||||||
|
connect(mMoveUpAction, SIGNAL(triggered()), this, SLOT(moveUp()));
|
||||||
|
|
||||||
|
mMoveDownAction = new QAction(QIcon::fromTheme("go-down"), tr("&Move Down"), this);
|
||||||
|
mMoveDownAction->setShortcut(QKeySequence(tr("Ctrl+M")));
|
||||||
|
connect(mMoveDownAction, SIGNAL(triggered()), this, SLOT(moveDown()));
|
||||||
|
|
||||||
|
mMoveTopAction = new QAction(QIcon::fromTheme("go-top"), tr("Move to Top"), this);
|
||||||
|
mMoveTopAction->setShortcut(QKeySequence(tr("Ctrl+Shift+U")));
|
||||||
|
connect(mMoveTopAction, SIGNAL(triggered()), this, SLOT(moveTop()));
|
||||||
|
|
||||||
|
mMoveBottomAction = new QAction(QIcon::fromTheme("go-bottom"), tr("Move to Bottom"), this);
|
||||||
|
mMoveBottomAction->setShortcut(QKeySequence(tr("Ctrl+Shift+M")));
|
||||||
|
connect(mMoveBottomAction, SIGNAL(triggered()), this, SLOT(moveBottom()));
|
||||||
|
|
||||||
|
mCheckAction = new QAction(tr("Check selected"), this);
|
||||||
|
connect(mCheckAction, SIGNAL(triggered()), this, SLOT(check()));
|
||||||
|
|
||||||
|
mUncheckAction = new QAction(tr("Uncheck selected"), this);
|
||||||
|
connect(mUncheckAction, SIGNAL(triggered()), this, SLOT(uncheck()));
|
||||||
|
|
||||||
|
// Makes shortcuts work even if the context menu is hidden
|
||||||
|
this->addAction(refreshAction);
|
||||||
|
this->addAction(mMoveUpAction);
|
||||||
|
this->addAction(mMoveDownAction);
|
||||||
|
this->addAction(mMoveTopAction);
|
||||||
|
this->addAction(mMoveBottomAction);
|
||||||
|
|
||||||
|
// Context menu for the plugins table
|
||||||
|
mContextMenu = new QMenu(this);
|
||||||
|
|
||||||
|
mContextMenu->addAction(mMoveUpAction);
|
||||||
|
mContextMenu->addAction(mMoveDownAction);
|
||||||
|
mContextMenu->addSeparator();
|
||||||
|
mContextMenu->addAction(mMoveTopAction);
|
||||||
|
mContextMenu->addAction(mMoveBottomAction);
|
||||||
|
mContextMenu->addSeparator();
|
||||||
|
mContextMenu->addAction(mCheckAction);
|
||||||
|
mContextMenu->addAction(mUncheckAction);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::newProfile()
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
QString text = QInputDialog::getText(this, tr("New Profile"),
|
||||||
|
tr("Profile Name:"), QLineEdit::Normal,
|
||||||
|
tr("New Profile"), &ok);
|
||||||
|
if (ok && !text.isEmpty()) {
|
||||||
|
if (mProfilesComboBox->findText(text) != -1) {
|
||||||
|
QMessageBox::warning(this, tr("Profile already exists"),
|
||||||
|
tr("the profile <b>%0</b> already exists.").arg(text),
|
||||||
|
QMessageBox::Ok);
|
||||||
|
} else {
|
||||||
|
// Add the new profile to the combobox
|
||||||
|
mProfilesComboBox->addItem(text);
|
||||||
|
mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(text));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::copyProfile()
|
||||||
|
{
|
||||||
|
QString profile = mProfilesComboBox->currentText();
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
QString text = QInputDialog::getText(this, tr("Copy Profile"),
|
||||||
|
tr("Profile Name:"), QLineEdit::Normal,
|
||||||
|
tr("%0 Copy").arg(profile), &ok);
|
||||||
|
if (ok && !text.isEmpty()) {
|
||||||
|
if (mProfilesComboBox->findText(text) != -1) {
|
||||||
|
QMessageBox::warning(this, tr("Profile already exists"),
|
||||||
|
tr("the profile <b>%0</b> already exists.").arg(text),
|
||||||
|
QMessageBox::Ok);
|
||||||
|
} else {
|
||||||
|
// Add the new profile to the combobox
|
||||||
|
mProfilesComboBox->addItem(text);
|
||||||
|
|
||||||
|
// First write the current profile as the new profile
|
||||||
|
writeConfig(text);
|
||||||
|
mProfilesComboBox->setCurrentIndex(mProfilesComboBox->findText(text));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::deleteProfile()
|
||||||
|
{
|
||||||
|
QString profile = mProfilesComboBox->currentText();
|
||||||
|
|
||||||
|
|
||||||
|
if (profile.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox deleteMessageBox(this);
|
||||||
|
deleteMessageBox.setWindowTitle(tr("Delete Profile"));
|
||||||
|
deleteMessageBox.setText(tr("Are you sure you want to delete <b>%0</b>?").arg(profile));
|
||||||
|
deleteMessageBox.setIcon(QMessageBox::Warning);
|
||||||
|
QAbstractButton *deleteButton =
|
||||||
|
deleteMessageBox.addButton(tr("Delete"), QMessageBox::ActionRole);
|
||||||
|
|
||||||
|
deleteMessageBox.addButton(QMessageBox::Cancel);
|
||||||
|
|
||||||
|
deleteMessageBox.exec();
|
||||||
|
|
||||||
|
if (deleteMessageBox.clickedButton() == deleteButton) {
|
||||||
|
// Make sure we have no groups open
|
||||||
|
while (!mLauncherConfig->group().isEmpty()) {
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
mLauncherConfig->beginGroup("Profiles");
|
||||||
|
|
||||||
|
// Open the profile-name subgroup
|
||||||
|
mLauncherConfig->beginGroup(profile);
|
||||||
|
mLauncherConfig->remove(""); // Clear the subgroup
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
|
||||||
|
// Remove the profile from the combobox
|
||||||
|
mProfilesComboBox->removeItem(mProfilesComboBox->findText(profile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::moveUp()
|
||||||
|
{
|
||||||
|
// Shift the selected plugins up one row
|
||||||
|
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
//sort selection ascending because selectedIndexes returns an unsorted list
|
||||||
|
qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan);
|
||||||
|
|
||||||
|
QModelIndex firstIndex = mPluginsProxyModel->mapToSource(selectedIndexes.first());
|
||||||
|
|
||||||
|
// Check if the first selected plugin is the top one
|
||||||
|
if (firstIndex.row() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (const QModelIndex ¤tIndex, selectedIndexes) {
|
||||||
|
const QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(currentIndex);
|
||||||
|
int currentRow = sourceModelIndex.row();
|
||||||
|
|
||||||
|
if (sourceModelIndex.isValid() && currentRow > 0) {
|
||||||
|
mPluginsModel->insertRow((currentRow - 1), mPluginsModel->takeRow(currentRow));
|
||||||
|
|
||||||
|
const QModelIndex targetIndex = mPluginsModel->index((currentRow - 1), 0, QModelIndex());
|
||||||
|
|
||||||
|
mPluginsTable->selectionModel()->select(targetIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
|
||||||
|
scrollToSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::moveDown()
|
||||||
|
{
|
||||||
|
// Shift the selected plugins down one row
|
||||||
|
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
//sort selection descending because selectedIndexes returns an unsorted list
|
||||||
|
qSort(selectedIndexes.begin(), selectedIndexes.end(), rowGreaterThan);
|
||||||
|
|
||||||
|
const QModelIndex lastIndex = mPluginsProxyModel->mapToSource(selectedIndexes.first());
|
||||||
|
|
||||||
|
// Check if last selected plugin is bottom one
|
||||||
|
if ((lastIndex.row() + 1) == mPluginsModel->rowCount()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (const QModelIndex ¤tIndex, selectedIndexes) {
|
||||||
|
const QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(currentIndex);
|
||||||
|
int currentRow = sourceModelIndex.row();
|
||||||
|
|
||||||
|
if (sourceModelIndex.isValid() && (currentRow + 1) < mPluginsModel->rowCount()) {
|
||||||
|
mPluginsModel->insertRow((currentRow + 1), mPluginsModel->takeRow(currentRow));
|
||||||
|
|
||||||
|
const QModelIndex targetIndex = mPluginsModel->index((currentRow + 1), 0, QModelIndex());
|
||||||
|
|
||||||
|
mPluginsTable->selectionModel()->select(targetIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
|
||||||
|
scrollToSelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::moveTop()
|
||||||
|
{
|
||||||
|
// Move the selection to the top of the table
|
||||||
|
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
//sort selection ascending because selectedIndexes returns an unsorted list
|
||||||
|
qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan);
|
||||||
|
|
||||||
|
QModelIndex firstIndex = mPluginsProxyModel->mapToSource(selectedIndexes.first());
|
||||||
|
|
||||||
|
// Check if the first selected plugin is the top one
|
||||||
|
if (firstIndex.row() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i < selectedIndexes.count(); ++i) {
|
||||||
|
|
||||||
|
const QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(selectedIndexes.at(i));
|
||||||
|
|
||||||
|
int currentRow = sourceModelIndex.row();
|
||||||
|
|
||||||
|
if (sourceModelIndex.isValid() && currentRow > 0) {
|
||||||
|
|
||||||
|
mPluginsModel->insertRow(i, mPluginsModel->takeRow(currentRow));
|
||||||
|
mPluginsTable->selectionModel()->select(mPluginsModel->index(i, 0, QModelIndex()), QItemSelectionModel::Select | QItemSelectionModel::Rows);
|
||||||
|
mPluginsTable->scrollToTop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::moveBottom()
|
||||||
|
{
|
||||||
|
// Move the selection to the bottom of the table
|
||||||
|
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
//sort selection descending because selectedIndexes returns an unsorted list
|
||||||
|
qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan);
|
||||||
|
|
||||||
|
const QModelIndex lastIndex = mPluginsProxyModel->mapToSource(selectedIndexes.last());
|
||||||
|
|
||||||
|
// Check if last selected plugin is bottom one
|
||||||
|
if ((lastIndex.row() + 1) == mPluginsModel->rowCount()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i < selectedIndexes.count(); ++i) {
|
||||||
|
|
||||||
|
const QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(selectedIndexes.at(i));
|
||||||
|
|
||||||
|
// Subtract iterations because takeRow shifts the rows below the taken row up
|
||||||
|
int currentRow = sourceModelIndex.row() - i;
|
||||||
|
|
||||||
|
if (sourceModelIndex.isValid() && (currentRow + 1) < mPluginsModel->rowCount()) {
|
||||||
|
mPluginsModel->appendRow(mPluginsModel->takeRow(currentRow));
|
||||||
|
|
||||||
|
// Rowcount starts with 1, row numbers start with 0
|
||||||
|
const QModelIndex lastRow = mPluginsModel->index((mPluginsModel->rowCount() -1), 0, QModelIndex());
|
||||||
|
|
||||||
|
mPluginsTable->selectionModel()->select(lastRow, QItemSelectionModel::Select | QItemSelectionModel::Rows);
|
||||||
|
mPluginsTable->scrollToBottom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::check()
|
||||||
|
{
|
||||||
|
// Check the current selection
|
||||||
|
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
//sort selection ascending because selectedIndexes returns an unsorted list
|
||||||
|
qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan);
|
||||||
|
|
||||||
|
foreach (const QModelIndex ¤tIndex, selectedIndexes) {
|
||||||
|
QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(currentIndex);
|
||||||
|
|
||||||
|
if (sourceModelIndex.isValid()) {
|
||||||
|
mPluginsModel->setData(sourceModelIndex, Qt::Checked, Qt::CheckStateRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::uncheck()
|
||||||
|
{
|
||||||
|
// Uncheck the current selection
|
||||||
|
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
//sort selection ascending because selectedIndexes returns an unsorted list
|
||||||
|
qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan);
|
||||||
|
|
||||||
|
foreach (const QModelIndex ¤tIndex, selectedIndexes) {
|
||||||
|
QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(currentIndex);
|
||||||
|
|
||||||
|
if (sourceModelIndex.isValid()) {
|
||||||
|
mPluginsModel->setData(sourceModelIndex, Qt::Unchecked, Qt::CheckStateRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::refresh()
|
||||||
|
{
|
||||||
|
// Refresh the plugins table
|
||||||
|
|
||||||
|
writeConfig();
|
||||||
|
readConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::scrollToSelection()
|
||||||
|
{
|
||||||
|
// Scroll to the selected plugins
|
||||||
|
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the selected indexes visible by determining the middle index
|
||||||
|
QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
qSort(selectedIndexes.begin(), selectedIndexes.end(), rowSmallerThan);
|
||||||
|
|
||||||
|
// The selected rows including non-selected inside selection
|
||||||
|
unsigned int selectedRows = selectedIndexes.last().row() - selectedIndexes.first().row();
|
||||||
|
|
||||||
|
// Determine the row which is roughly in the middle of the selection
|
||||||
|
unsigned int middleRow = selectedIndexes.first().row() + (int)(selectedRows / 2) + 1;
|
||||||
|
|
||||||
|
|
||||||
|
const QModelIndex middleIndex = mPluginsProxyModel->mapFromSource(mPluginsModel->index(middleRow, 0, QModelIndex()));
|
||||||
|
|
||||||
|
// Make sure the whole selection is visible
|
||||||
|
mPluginsTable->scrollTo(selectedIndexes.first());
|
||||||
|
mPluginsTable->scrollTo(selectedIndexes.last());
|
||||||
|
mPluginsTable->scrollTo(middleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::showContextMenu(const QPoint &point)
|
||||||
|
{
|
||||||
|
|
||||||
|
QPoint globalPos = mPluginsTable->mapToGlobal(point);
|
||||||
|
|
||||||
|
QModelIndexList selectedIndexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
// Show the check/uncheck actions depending on the state of the selected items
|
||||||
|
mUncheckAction->setEnabled(false);
|
||||||
|
mCheckAction->setEnabled(false);
|
||||||
|
|
||||||
|
foreach (const QModelIndex ¤tIndex, selectedIndexes) {
|
||||||
|
if (currentIndex.isValid()) {
|
||||||
|
|
||||||
|
const QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(currentIndex);
|
||||||
|
|
||||||
|
if (!sourceIndex.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStandardItem *currentItem = mPluginsModel->itemFromIndex(sourceIndex);
|
||||||
|
|
||||||
|
if (currentItem->checkState() == Qt::Checked) {
|
||||||
|
mUncheckAction->setEnabled(true);
|
||||||
|
} else {
|
||||||
|
mCheckAction->setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure these are enabled because they might still be disabled
|
||||||
|
mMoveUpAction->setEnabled(true);
|
||||||
|
mMoveTopAction->setEnabled(true);
|
||||||
|
mMoveDownAction->setEnabled(true);
|
||||||
|
mMoveBottomAction->setEnabled(true);
|
||||||
|
|
||||||
|
QModelIndex firstIndex = mPluginsProxyModel->mapToSource(selectedIndexes.first());
|
||||||
|
QModelIndex lastIndex = mPluginsProxyModel->mapToSource(selectedIndexes.last());
|
||||||
|
|
||||||
|
// Check if selected first item is top row in model
|
||||||
|
if (firstIndex.row() == 0) {
|
||||||
|
mMoveUpAction->setEnabled(false);
|
||||||
|
mMoveTopAction->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if last row is bottom row in model
|
||||||
|
if ((lastIndex.row() + 1) == mPluginsModel->rowCount()) {
|
||||||
|
mMoveDownAction->setEnabled(false);
|
||||||
|
mMoveBottomAction->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show menu
|
||||||
|
mContextMenu->exec(globalPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::masterSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
|
||||||
|
{
|
||||||
|
if (mMastersWidget->selectionModel()->hasSelection()) {
|
||||||
|
|
||||||
|
QStringList masters;
|
||||||
|
QString masterstr;
|
||||||
|
|
||||||
|
// Create a QStringList containing all the masters
|
||||||
|
const QStringList masterList = selectedMasters();
|
||||||
|
|
||||||
|
foreach (const QString ¤tMaster, masterList) {
|
||||||
|
masters.append(currentMaster);
|
||||||
|
}
|
||||||
|
|
||||||
|
masters.sort();
|
||||||
|
masterstr = masters.join(","); // Make a comma-separated QString
|
||||||
|
|
||||||
|
// Iterate over all masters in the datafilesmodel to see if they are selected
|
||||||
|
for (int r=0; r<mDataFilesModel->rowCount(); ++r) {
|
||||||
|
QModelIndex currentIndex = mDataFilesModel->index(r, 0);
|
||||||
|
QString master = currentIndex.data().toString();
|
||||||
|
|
||||||
|
if (currentIndex.isValid()) {
|
||||||
|
// See if the current master is in the string with selected masters
|
||||||
|
if (masterstr.contains(master))
|
||||||
|
{
|
||||||
|
// Append the plugins from the current master to pluginsmodel
|
||||||
|
addPlugins(currentIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// See what plugins to remove
|
||||||
|
QModelIndexList deselectedIndexes = deselected.indexes();
|
||||||
|
|
||||||
|
if (!deselectedIndexes.isEmpty()) {
|
||||||
|
foreach (const QModelIndex ¤tIndex, deselectedIndexes) {
|
||||||
|
|
||||||
|
QString master = currentIndex.data().toString();
|
||||||
|
master.prepend("*");
|
||||||
|
master.append("*");
|
||||||
|
const QList<QStandardItem *> itemList = mDataFilesModel->findItems(master, Qt::MatchWildcard);
|
||||||
|
|
||||||
|
foreach (const QStandardItem *currentItem, itemList) {
|
||||||
|
QModelIndex index = currentItem->index();
|
||||||
|
removePlugins(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::addPlugins(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
// Find the plugins in the datafilesmodel and append them to the pluginsmodel
|
||||||
|
if (!index.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int r=0; r<mDataFilesModel->rowCount(index); ++r ) {
|
||||||
|
QModelIndex childIndex = index.child(r, 0);
|
||||||
|
|
||||||
|
if (childIndex.isValid()) {
|
||||||
|
// Now we see if the pluginsmodel already contains this plugin
|
||||||
|
const QString childIndexData = QVariant(mDataFilesModel->data(childIndex)).toString();
|
||||||
|
|
||||||
|
const QList<QStandardItem *> itemList = mPluginsModel->findItems(childIndexData);
|
||||||
|
|
||||||
|
if (itemList.isEmpty())
|
||||||
|
{
|
||||||
|
// Plugin not yet in the pluginsmodel, add it
|
||||||
|
QStandardItem *item = new QStandardItem(childIndexData);
|
||||||
|
item->setFlags(item->flags() & ~(Qt::ItemIsDropEnabled));
|
||||||
|
item->setCheckable(true);
|
||||||
|
|
||||||
|
mPluginsModel->appendRow(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::removePlugins(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!index.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int r=0; r<mDataFilesModel->rowCount(index); ++r) {
|
||||||
|
QModelIndex childIndex = index.child(r, 0);
|
||||||
|
|
||||||
|
const QList<QStandardItem *> itemList = mPluginsModel->findItems(QVariant(childIndex.data()).toString());
|
||||||
|
|
||||||
|
if (!itemList.isEmpty()) {
|
||||||
|
foreach (const QStandardItem *currentItem, itemList) {
|
||||||
|
mPluginsModel->removeRow(currentItem->row());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::setCheckState(QModelIndex index)
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QModelIndex sourceModelIndex = mPluginsProxyModel->mapToSource(index);
|
||||||
|
|
||||||
|
if (mPluginsModel->data(sourceModelIndex, Qt::CheckStateRole) == Qt::Checked) {
|
||||||
|
// Selected row is checked, uncheck it
|
||||||
|
mPluginsModel->setData(sourceModelIndex, Qt::Unchecked, Qt::CheckStateRole);
|
||||||
|
} else {
|
||||||
|
mPluginsModel->setData(sourceModelIndex, Qt::Checked, Qt::CheckStateRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStringList DataFilesPage::selectedMasters()
|
||||||
|
{
|
||||||
|
QStringList masters;
|
||||||
|
const QList<QTableWidgetItem *> selectedMasters = mMastersWidget->selectedItems();
|
||||||
|
|
||||||
|
foreach (const QTableWidgetItem *item, selectedMasters) {
|
||||||
|
masters.append(item->data(Qt::DisplayRole).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return masters;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStringList DataFilesPage::checkedPlugins()
|
||||||
|
{
|
||||||
|
QStringList checkedItems;
|
||||||
|
|
||||||
|
for (int r=0; r<mPluginsModel->rowCount(); ++r ) {
|
||||||
|
QModelIndex index = mPluginsModel->index(r, 0);
|
||||||
|
|
||||||
|
if (index.isValid()) {
|
||||||
|
// See if the current item is checked
|
||||||
|
if (mPluginsModel->data(index, Qt::CheckStateRole) == Qt::Checked) {
|
||||||
|
checkedItems.append(index.data().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checkedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::uncheckPlugins()
|
||||||
|
{
|
||||||
|
for (int r=0; r<mPluginsModel->rowCount(); ++r ) {
|
||||||
|
QModelIndex index = mPluginsModel->index(r, 0);
|
||||||
|
|
||||||
|
if (index.isValid()) {
|
||||||
|
// See if the current item is checked
|
||||||
|
if (mPluginsModel->data(index, Qt::CheckStateRole) == Qt::Checked) {
|
||||||
|
mPluginsModel->setData(index, Qt::Unchecked, Qt::CheckStateRole);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::filterChanged(const QString filter)
|
||||||
|
{
|
||||||
|
QRegExp regExp(filter, Qt::CaseInsensitive, QRegExp::FixedString);
|
||||||
|
mPluginsProxyModel->setFilterRegExp(regExp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::profileChanged(const QString &previous, const QString ¤t)
|
||||||
|
{
|
||||||
|
if (!previous.isEmpty()) {
|
||||||
|
writeConfig(previous);
|
||||||
|
mLauncherConfig->sync();
|
||||||
|
}
|
||||||
|
|
||||||
|
uncheckPlugins();
|
||||||
|
// Deselect the masters
|
||||||
|
mMastersWidget->selectionModel()->clearSelection();
|
||||||
|
readConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::readConfig()
|
||||||
|
{
|
||||||
|
QString profile = mProfilesComboBox->currentText();
|
||||||
|
|
||||||
|
// Make sure we have no groups open
|
||||||
|
while (!mLauncherConfig->group().isEmpty()) {
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
mLauncherConfig->beginGroup("Profiles");
|
||||||
|
mLauncherConfig->beginGroup(profile);
|
||||||
|
|
||||||
|
QStringList childKeys = mLauncherConfig->childKeys();
|
||||||
|
QStringList plugins;
|
||||||
|
|
||||||
|
// Sort the child keys numerical instead of alphabetically
|
||||||
|
// i.e. Plugin1, Plugin2 instead of Plugin1, Plugin10
|
||||||
|
qSort(childKeys.begin(), childKeys.end(), naturalSortLessThanCI);
|
||||||
|
|
||||||
|
foreach (const QString &key, childKeys) {
|
||||||
|
const QString keyValue = mLauncherConfig->value(key).toString();
|
||||||
|
|
||||||
|
if (key.startsWith("Plugin")) {
|
||||||
|
plugins.append(keyValue);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key.startsWith("Master")) {
|
||||||
|
const QList<QTableWidgetItem*> masterList = mMastersWidget->findItems(keyValue, Qt::MatchFixedString);
|
||||||
|
|
||||||
|
if (!masterList.isEmpty()) {
|
||||||
|
foreach (QTableWidgetItem *currentMaster, masterList) {
|
||||||
|
mMastersWidget->selectionModel()->select(mMastersWidget->model()->index(currentMaster->row(), 0), QItemSelectionModel::Select);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over the plugins to set their checkstate and position
|
||||||
|
for (int i = 0; i < plugins.size(); ++i) {
|
||||||
|
const QString plugin = plugins.at(i);
|
||||||
|
|
||||||
|
const QList<QStandardItem *> pluginList = mPluginsModel->findItems(plugin);
|
||||||
|
|
||||||
|
if (!pluginList.isEmpty())
|
||||||
|
{
|
||||||
|
foreach (const QStandardItem *currentPlugin, pluginList) {
|
||||||
|
mPluginsModel->setData(currentPlugin->index(), Qt::Checked, Qt::CheckStateRole);
|
||||||
|
|
||||||
|
// Move the plugin to the position specified in the config file
|
||||||
|
mPluginsModel->insertRow(i, mPluginsModel->takeRow(currentPlugin->row()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesPage::writeConfig(QString profile)
|
||||||
|
{
|
||||||
|
if (profile.isEmpty()) {
|
||||||
|
profile = mProfilesComboBox->currentText();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we have no groups open
|
||||||
|
while (!mLauncherConfig->group().isEmpty()) {
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
mLauncherConfig->beginGroup("Profiles");
|
||||||
|
mLauncherConfig->setValue("CurrentProfile", profile);
|
||||||
|
|
||||||
|
// Open the profile-name subgroup
|
||||||
|
mLauncherConfig->beginGroup(profile);
|
||||||
|
mLauncherConfig->remove(""); // Clear the subgroup
|
||||||
|
|
||||||
|
// First write the masters to the config
|
||||||
|
const QStringList masterList = selectedMasters();
|
||||||
|
|
||||||
|
// We don't use foreach because we need i
|
||||||
|
for (int i = 0; i < masterList.size(); ++i) {
|
||||||
|
const QString master = masterList.at(i);
|
||||||
|
mLauncherConfig->setValue(QString("Master%0").arg(i), master);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now write all checked plugins
|
||||||
|
const QStringList plugins = checkedPlugins();
|
||||||
|
|
||||||
|
for (int i = 0; i < plugins.size(); ++i)
|
||||||
|
{
|
||||||
|
mLauncherConfig->setValue(QString("Plugin%1").arg(i), plugins.at(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
mLauncherConfig->endGroup();
|
||||||
|
|
||||||
|
}
|
93
apps/launcher/datafilespage.hpp
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
#ifndef DATAFILESPAGE_H
|
||||||
|
#define DATAFILESPAGE_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QModelIndex>
|
||||||
|
#include "combobox.hpp"
|
||||||
|
|
||||||
|
class QTableWidget;
|
||||||
|
class QStandardItemModel;
|
||||||
|
class QItemSelection;
|
||||||
|
class QItemSelectionModel;
|
||||||
|
class QSortFilterProxyModel;
|
||||||
|
class QStringListModel;
|
||||||
|
class QSettings;
|
||||||
|
class QAction;
|
||||||
|
class QToolBar;
|
||||||
|
class QMenu;
|
||||||
|
class PluginsModel;
|
||||||
|
class PluginsView;
|
||||||
|
class ComboBox;
|
||||||
|
|
||||||
|
class DataFilesPage : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
DataFilesPage(QWidget *parent = 0);
|
||||||
|
|
||||||
|
ComboBox *mProfilesComboBox;
|
||||||
|
QSettings *mLauncherConfig;
|
||||||
|
|
||||||
|
const QStringList checkedPlugins();
|
||||||
|
const QStringList selectedMasters();
|
||||||
|
void setupConfig();
|
||||||
|
void readConfig();
|
||||||
|
void writeConfig(QString profile = QString());
|
||||||
|
|
||||||
|
void setupDataFiles(const QStringList &paths, bool strict);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void masterSelectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
|
||||||
|
void setCheckState(QModelIndex index);
|
||||||
|
|
||||||
|
void filterChanged(const QString filter);
|
||||||
|
void showContextMenu(const QPoint &point);
|
||||||
|
void profileChanged(const QString &previous, const QString ¤t);
|
||||||
|
|
||||||
|
// Action slots
|
||||||
|
void newProfile();
|
||||||
|
void copyProfile();
|
||||||
|
void deleteProfile();
|
||||||
|
void moveUp();
|
||||||
|
void moveDown();
|
||||||
|
void moveTop();
|
||||||
|
void moveBottom();
|
||||||
|
void check();
|
||||||
|
void uncheck();
|
||||||
|
void refresh();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTableWidget *mMastersWidget;
|
||||||
|
PluginsView *mPluginsTable;
|
||||||
|
|
||||||
|
QStandardItemModel *mDataFilesModel;
|
||||||
|
PluginsModel *mPluginsModel;
|
||||||
|
|
||||||
|
QSortFilterProxyModel *mPluginsProxyModel;
|
||||||
|
QItemSelectionModel *mPluginsSelectModel;
|
||||||
|
|
||||||
|
QToolBar *mProfileToolBar;
|
||||||
|
QMenu *mContextMenu;
|
||||||
|
|
||||||
|
QAction *mNewProfileAction;
|
||||||
|
QAction *mCopyProfileAction;
|
||||||
|
QAction *mDeleteProfileAction;
|
||||||
|
|
||||||
|
QAction *mMoveUpAction;
|
||||||
|
QAction *mMoveDownAction;
|
||||||
|
QAction *mMoveTopAction;
|
||||||
|
QAction *mMoveBottomAction;
|
||||||
|
QAction *mCheckAction;
|
||||||
|
QAction *mUncheckAction;
|
||||||
|
|
||||||
|
void addPlugins(const QModelIndex &index);
|
||||||
|
void removePlugins(const QModelIndex &index);
|
||||||
|
void uncheckPlugins();
|
||||||
|
void createActions();
|
||||||
|
|
||||||
|
void scrollToSelection();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
484
apps/launcher/graphicspage.cpp
Normal file
|
@ -0,0 +1,484 @@
|
||||||
|
#include <QtGui>
|
||||||
|
|
||||||
|
#include <components/files/path.hpp>
|
||||||
|
|
||||||
|
#include "graphicspage.hpp"
|
||||||
|
|
||||||
|
GraphicsPage::GraphicsPage(QWidget *parent) : QWidget(parent)
|
||||||
|
{
|
||||||
|
QGroupBox *rendererGroup = new QGroupBox(tr("Renderer"), this);
|
||||||
|
|
||||||
|
QLabel *rendererLabel = new QLabel(tr("Rendering Subsystem:"), rendererGroup);
|
||||||
|
mRendererComboBox = new QComboBox(rendererGroup);
|
||||||
|
|
||||||
|
// Layout for the combobox and label
|
||||||
|
QGridLayout *renderSystemLayout = new QGridLayout();
|
||||||
|
renderSystemLayout->addWidget(rendererLabel, 0, 0, 1, 1);
|
||||||
|
renderSystemLayout->addWidget(mRendererComboBox, 0, 1, 1, 1);
|
||||||
|
|
||||||
|
mRendererStackedWidget = new QStackedWidget(rendererGroup);
|
||||||
|
|
||||||
|
QVBoxLayout *rendererGroupLayout = new QVBoxLayout(rendererGroup);
|
||||||
|
|
||||||
|
rendererGroupLayout->addLayout(renderSystemLayout);
|
||||||
|
rendererGroupLayout->addWidget(mRendererStackedWidget);
|
||||||
|
|
||||||
|
// Display
|
||||||
|
QGroupBox *displayGroup = new QGroupBox(tr("Display"), this);
|
||||||
|
|
||||||
|
mDisplayStackedWidget = new QStackedWidget(displayGroup);
|
||||||
|
|
||||||
|
QVBoxLayout *displayGroupLayout = new QVBoxLayout(displayGroup);
|
||||||
|
QSpacerItem *vSpacer3 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||||
|
|
||||||
|
displayGroupLayout->addWidget(mDisplayStackedWidget);
|
||||||
|
displayGroupLayout->addItem(vSpacer3);
|
||||||
|
|
||||||
|
// Layout for the whole page
|
||||||
|
QVBoxLayout *pageLayout = new QVBoxLayout(this);
|
||||||
|
|
||||||
|
pageLayout->addWidget(rendererGroup);
|
||||||
|
pageLayout->addWidget(displayGroup);
|
||||||
|
|
||||||
|
connect(mRendererComboBox, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(rendererChanged(const QString&)));
|
||||||
|
|
||||||
|
createPages();
|
||||||
|
setupConfig();
|
||||||
|
setupOgre();
|
||||||
|
|
||||||
|
readConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsPage::createPages()
|
||||||
|
{
|
||||||
|
// OpenGL rendering settings
|
||||||
|
QWidget *mOGLRendererPage = new QWidget();
|
||||||
|
|
||||||
|
QLabel *OGLRTTLabel = new QLabel(tr("Preferred RTT Mode:"), mOGLRendererPage);
|
||||||
|
mOGLRTTComboBox = new QComboBox(mOGLRendererPage);
|
||||||
|
|
||||||
|
QLabel *OGLAntiAliasingLabel = new QLabel(tr("Antialiasing:"), mOGLRendererPage);
|
||||||
|
mOGLAntiAliasingComboBox = new QComboBox(mOGLRendererPage);
|
||||||
|
|
||||||
|
QGridLayout *OGLRendererLayout = new QGridLayout(mOGLRendererPage);
|
||||||
|
QSpacerItem *vSpacer1 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||||
|
|
||||||
|
OGLRendererLayout->addWidget(OGLRTTLabel, 0, 0, 1, 1);
|
||||||
|
OGLRendererLayout->addWidget(mOGLRTTComboBox, 0, 1, 1, 1);
|
||||||
|
OGLRendererLayout->addWidget(OGLAntiAliasingLabel, 1, 0, 1, 1);
|
||||||
|
OGLRendererLayout->addWidget(mOGLAntiAliasingComboBox, 1, 1, 1, 1);
|
||||||
|
OGLRendererLayout->addItem(vSpacer1, 2, 1, 1, 1);
|
||||||
|
|
||||||
|
// OpenGL display settings
|
||||||
|
QWidget *mOGLDisplayPage = new QWidget();
|
||||||
|
|
||||||
|
QLabel *OGLResolutionLabel = new QLabel(tr("Resolution:"), mOGLDisplayPage);
|
||||||
|
mOGLResolutionComboBox = new QComboBox(mOGLDisplayPage);
|
||||||
|
|
||||||
|
QLabel *OGLFrequencyLabel = new QLabel(tr("Display Frequency:"), mOGLDisplayPage);
|
||||||
|
mOGLFrequencyComboBox = new QComboBox(mOGLDisplayPage);
|
||||||
|
|
||||||
|
mOGLVSyncCheckBox = new QCheckBox(tr("Vertical Sync"), mOGLDisplayPage);
|
||||||
|
mOGLFullScreenCheckBox = new QCheckBox(tr("Full Screen"), mOGLDisplayPage);
|
||||||
|
|
||||||
|
QGridLayout *OGLDisplayLayout = new QGridLayout(mOGLDisplayPage);
|
||||||
|
QSpacerItem *vSpacer2 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||||
|
|
||||||
|
OGLDisplayLayout->addWidget(OGLResolutionLabel, 0, 0, 1, 1);
|
||||||
|
OGLDisplayLayout->addWidget(mOGLResolutionComboBox, 0, 1, 1, 1);
|
||||||
|
OGLDisplayLayout->addWidget(OGLFrequencyLabel, 1, 0, 1, 1);
|
||||||
|
OGLDisplayLayout->addWidget(mOGLFrequencyComboBox, 1, 1, 1, 1);
|
||||||
|
|
||||||
|
OGLDisplayLayout->addItem(vSpacer2, 2, 1, 1, 1);
|
||||||
|
OGLDisplayLayout->addWidget(mOGLVSyncCheckBox, 3, 0, 1, 1);
|
||||||
|
OGLDisplayLayout->addWidget(mOGLFullScreenCheckBox, 6, 0, 1, 1);
|
||||||
|
|
||||||
|
// Direct3D rendering settings
|
||||||
|
QWidget *mD3DRendererPage = new QWidget();
|
||||||
|
|
||||||
|
QLabel *D3DRenderDeviceLabel = new QLabel(tr("Rendering Device:"), mD3DRendererPage);
|
||||||
|
mD3DRenderDeviceComboBox = new QComboBox(mD3DRendererPage);
|
||||||
|
|
||||||
|
QLabel *D3DAntiAliasingLabel = new QLabel(tr("Antialiasing:"), mD3DRendererPage);
|
||||||
|
mD3DAntiAliasingComboBox = new QComboBox(mD3DRendererPage);
|
||||||
|
|
||||||
|
QLabel *D3DFloatingPointLabel = new QLabel(tr("Floating-point Mode:"), mD3DRendererPage);
|
||||||
|
mD3DFloatingPointComboBox = new QComboBox(mD3DRendererPage);
|
||||||
|
|
||||||
|
mD3DNvPerfCheckBox = new QCheckBox(tr("Allow NVPerfHUD"), mD3DRendererPage);
|
||||||
|
|
||||||
|
QGridLayout *D3DRendererLayout = new QGridLayout(mD3DRendererPage);
|
||||||
|
QSpacerItem *vSpacer3 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||||
|
QSpacerItem *vSpacer4 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||||
|
|
||||||
|
D3DRendererLayout->addWidget(D3DRenderDeviceLabel, 0, 0, 1, 1);
|
||||||
|
D3DRendererLayout->addWidget(mD3DRenderDeviceComboBox, 0, 1, 1, 1);
|
||||||
|
D3DRendererLayout->addWidget(D3DAntiAliasingLabel, 1, 0, 1, 1);
|
||||||
|
D3DRendererLayout->addWidget(mD3DAntiAliasingComboBox, 1, 1, 1, 1);
|
||||||
|
D3DRendererLayout->addWidget(D3DFloatingPointLabel, 2, 0, 1, 1);
|
||||||
|
D3DRendererLayout->addWidget(mD3DFloatingPointComboBox, 2, 1, 1, 1);
|
||||||
|
D3DRendererLayout->addItem(vSpacer3, 3, 1, 1, 1);
|
||||||
|
D3DRendererLayout->addWidget(mD3DNvPerfCheckBox, 4, 0, 1, 1);
|
||||||
|
D3DRendererLayout->addItem(vSpacer4, 5, 1, 1, 1);
|
||||||
|
|
||||||
|
// Direct3D display settings
|
||||||
|
QWidget *mD3DDisplayPage = new QWidget();
|
||||||
|
|
||||||
|
QLabel *D3DResolutionLabel = new QLabel(tr("Resolution:"), mD3DDisplayPage);
|
||||||
|
mD3DResolutionComboBox = new QComboBox(mD3DDisplayPage);
|
||||||
|
|
||||||
|
mD3DVSyncCheckBox = new QCheckBox(tr("Vertical Sync"), mD3DDisplayPage);
|
||||||
|
mD3DFullScreenCheckBox = new QCheckBox(tr("Full Screen"), mD3DDisplayPage);
|
||||||
|
|
||||||
|
QGridLayout *mD3DDisplayLayout = new QGridLayout(mD3DDisplayPage);
|
||||||
|
QSpacerItem *vSpacer5 = new QSpacerItem(20, 10, QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||||
|
|
||||||
|
mD3DDisplayLayout->addWidget(D3DResolutionLabel, 0, 0, 1, 1);
|
||||||
|
mD3DDisplayLayout->addWidget(mD3DResolutionComboBox, 0, 1, 1, 1);
|
||||||
|
mD3DDisplayLayout->addItem(vSpacer5, 1, 1, 1, 1);
|
||||||
|
mD3DDisplayLayout->addWidget(mD3DVSyncCheckBox, 2, 0, 1, 1);
|
||||||
|
mD3DDisplayLayout->addWidget(mD3DFullScreenCheckBox, 5, 0, 1, 1);
|
||||||
|
|
||||||
|
// Add the created pages
|
||||||
|
mRendererStackedWidget->addWidget(mOGLRendererPage);
|
||||||
|
mRendererStackedWidget->addWidget(mD3DRendererPage);
|
||||||
|
|
||||||
|
mDisplayStackedWidget->addWidget(mOGLDisplayPage);
|
||||||
|
mDisplayStackedWidget->addWidget(mD3DDisplayPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsPage::setupConfig()
|
||||||
|
{
|
||||||
|
QString ogreCfg = "./ogre.cfg";
|
||||||
|
|
||||||
|
QFile file(ogreCfg);
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
ogreCfg = QString::fromStdString(Files::getPath(Files::Path_ConfigUser,
|
||||||
|
"openmw", "ogre.cfg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
mOgreConfig = new QSettings(ogreCfg, QSettings::IniFormat);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsPage::setupOgre()
|
||||||
|
{
|
||||||
|
QString pluginCfg = "./plugins.cfg";
|
||||||
|
QFile file(pluginCfg);
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
pluginCfg = QString::fromStdString(Files::getPath(Files::Path_ConfigUser,
|
||||||
|
"openmw", "plugins.cfg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reopen the file from user directory
|
||||||
|
file.setFileName(pluginCfg);
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
// There's no plugins.cfg in the user directory, use global directory
|
||||||
|
pluginCfg = QString::fromStdString(Files::getPath(Files::Path_ConfigGlobal,
|
||||||
|
"openmw", "plugins.cfg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a log manager so we can surpress debug text to stdout/stderr
|
||||||
|
Ogre::LogManager* logMgr = OGRE_NEW Ogre::LogManager;
|
||||||
|
logMgr->createLog("launcherOgre.log", true, false, false);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
mOgre = new Ogre::Root(pluginCfg.toStdString());
|
||||||
|
}
|
||||||
|
catch(Ogre::Exception &ex)
|
||||||
|
{
|
||||||
|
QString ogreError = QString::fromStdString(ex.getFullDescription().c_str());
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error creating Ogre::Root");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Failed to create the Ogre::Root object</b><br><br> \
|
||||||
|
Make sure the plugins.cfg is present and valid.<br><br> \
|
||||||
|
Press \"Show Details...\" for more information.<br>"));
|
||||||
|
msgBox.setDetailedText(ogreError);
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
qCritical("Error creating Ogre::Root, the error reported was:\n %s", qPrintable(ogreError));
|
||||||
|
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the available renderers and put them in the combobox
|
||||||
|
const Ogre::RenderSystemList &renderers = mOgre->getAvailableRenderers();
|
||||||
|
|
||||||
|
for (Ogre::RenderSystemList::const_iterator r = renderers.begin(); r != renderers.end(); ++r) {
|
||||||
|
mSelectedRenderSystem = *r;
|
||||||
|
mRendererComboBox->addItem((*r)->getName().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
int index = mRendererComboBox->findText(mOgreConfig->value("Render System").toString());
|
||||||
|
|
||||||
|
if ( index != -1) {
|
||||||
|
mRendererComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create separate rendersystems
|
||||||
|
QString openGLName = mRendererComboBox->itemText(mRendererComboBox->findText(QString("OpenGL"), Qt::MatchStartsWith));
|
||||||
|
QString direct3DName = mRendererComboBox->itemText(mRendererComboBox->findText(QString("Direct3D"), Qt::MatchStartsWith));
|
||||||
|
|
||||||
|
mOpenGLRenderSystem = mOgre->getRenderSystemByName(openGLName.toStdString());
|
||||||
|
mDirect3DRenderSystem = mOgre->getRenderSystemByName(direct3DName.toStdString());
|
||||||
|
|
||||||
|
if (!mOpenGLRenderSystem && !mDirect3DRenderSystem) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error creating renderer");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not select a valid render system</b><br><br> \
|
||||||
|
Please make sure the plugins.cfg file exists and contains a valid rendering plugin.<br>"));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
std::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now fill the GUI elements
|
||||||
|
// OpenGL
|
||||||
|
if (mOpenGLRenderSystem) {
|
||||||
|
mOGLRTTComboBox->addItems(getAvailableOptions(QString("RTT Preferred Mode"), mOpenGLRenderSystem));
|
||||||
|
mOGLAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mOpenGLRenderSystem));
|
||||||
|
|
||||||
|
QStringList videoModes = getAvailableOptions(QString("Video Mode"), mOpenGLRenderSystem);
|
||||||
|
// Remove extraneous spaces
|
||||||
|
videoModes.replaceInStrings(QRegExp("\\s{2,}"), QString(" "));
|
||||||
|
videoModes.replaceInStrings(QRegExp("^\\s"), QString());
|
||||||
|
|
||||||
|
mOGLResolutionComboBox->addItems(videoModes);
|
||||||
|
mOGLFrequencyComboBox->addItems(getAvailableOptions(QString("Display Frequency"), mOpenGLRenderSystem));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direct3D
|
||||||
|
if (mDirect3DRenderSystem) {
|
||||||
|
mD3DRenderDeviceComboBox->addItems(getAvailableOptions(QString("Rendering Device"), mDirect3DRenderSystem));
|
||||||
|
mD3DAntiAliasingComboBox->addItems(getAvailableOptions(QString("FSAA"), mDirect3DRenderSystem));
|
||||||
|
mD3DFloatingPointComboBox->addItems(getAvailableOptions(QString("Floating-point mode"), mDirect3DRenderSystem));
|
||||||
|
|
||||||
|
QStringList videoModes = getAvailableOptions(QString("Video Mode"), mDirect3DRenderSystem);
|
||||||
|
// Remove extraneous spaces
|
||||||
|
videoModes.replaceInStrings(QRegExp("\\s{2,}"), QString(" "));
|
||||||
|
videoModes.replaceInStrings(QRegExp("^\\s"), QString());
|
||||||
|
mD3DResolutionComboBox->addItems(videoModes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsPage::readConfig()
|
||||||
|
{
|
||||||
|
// Read the config file settings
|
||||||
|
if (mOpenGLRenderSystem) {
|
||||||
|
|
||||||
|
int index = mOGLRTTComboBox->findText(getConfigValue("RTT Preferred Mode", mOpenGLRenderSystem));
|
||||||
|
if ( index != -1) {
|
||||||
|
mOGLRTTComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = mOGLAntiAliasingComboBox->findText(getConfigValue("FSAA", mOpenGLRenderSystem));
|
||||||
|
if ( index != -1){
|
||||||
|
mOGLAntiAliasingComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = mOGLResolutionComboBox->findText(getConfigValue("Video Mode", mOpenGLRenderSystem));
|
||||||
|
if ( index != -1) {
|
||||||
|
mOGLResolutionComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = mOGLFrequencyComboBox->findText(getConfigValue("Display Frequency", mOpenGLRenderSystem));
|
||||||
|
if ( index != -1) {
|
||||||
|
mOGLFrequencyComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we do the same for the checkboxes
|
||||||
|
if (getConfigValue("VSync", mOpenGLRenderSystem) == QLatin1String("Yes")) {
|
||||||
|
mOGLVSyncCheckBox->setCheckState(Qt::Checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getConfigValue("Full Screen", mOpenGLRenderSystem) == QLatin1String("Yes")) {
|
||||||
|
mOGLFullScreenCheckBox->setCheckState(Qt::Checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDirect3DRenderSystem) {
|
||||||
|
|
||||||
|
int index = mD3DRenderDeviceComboBox->findText(getConfigValue("Rendering Device", mDirect3DRenderSystem));
|
||||||
|
if ( index != -1) {
|
||||||
|
mD3DRenderDeviceComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = mD3DAntiAliasingComboBox->findText(getConfigValue("FSAA", mDirect3DRenderSystem));
|
||||||
|
if ( index != -1) {
|
||||||
|
mD3DAntiAliasingComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = mD3DFloatingPointComboBox->findText(getConfigValue("Floating-point mode", mDirect3DRenderSystem));
|
||||||
|
if ( index != -1) {
|
||||||
|
mD3DFloatingPointComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = mD3DResolutionComboBox->findText(getConfigValue("Video Mode", mDirect3DRenderSystem));
|
||||||
|
if ( index != -1) {
|
||||||
|
mD3DResolutionComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getConfigValue("Allow NVPerfHUD", mDirect3DRenderSystem) == QLatin1String("Yes")) {
|
||||||
|
mD3DNvPerfCheckBox->setCheckState(Qt::Checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getConfigValue("VSync", mDirect3DRenderSystem) == QLatin1String("Yes")) {
|
||||||
|
mD3DVSyncCheckBox->setCheckState(Qt::Checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getConfigValue("Full Screen", mDirect3DRenderSystem) == QLatin1String("Yes")) {
|
||||||
|
mD3DFullScreenCheckBox->setCheckState(Qt::Checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsPage::writeConfig()
|
||||||
|
{
|
||||||
|
// Write the config file settings
|
||||||
|
|
||||||
|
// Custom write method: We cannot use QSettings because it does not accept spaces
|
||||||
|
QFile file(mOgreConfig->fileName());
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
|
||||||
|
// File could not be opened,
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error opening Ogre configuration file");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not open %0</b><br><br> \
|
||||||
|
Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
|
||||||
|
msgBox.exec();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream out(&file);
|
||||||
|
|
||||||
|
out << "Render System=" << mSelectedRenderSystem->getName().c_str() << endl << endl;
|
||||||
|
|
||||||
|
if (mDirect3DRenderSystem) {
|
||||||
|
QString direct3DName = mDirect3DRenderSystem->getName().c_str();
|
||||||
|
direct3DName.prepend("[");
|
||||||
|
direct3DName.append("]");
|
||||||
|
out << direct3DName << endl;
|
||||||
|
|
||||||
|
if (mD3DNvPerfCheckBox->checkState() == Qt::Checked) {
|
||||||
|
out << "Allow NVPerfHUD=Yes" << endl;
|
||||||
|
} else {
|
||||||
|
out << "Allow NVPerfHUD=No" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
out << "FSAA=" << mD3DAntiAliasingComboBox->currentText() << endl;
|
||||||
|
out << "Floating-point mode=" << mD3DFloatingPointComboBox->currentText() << endl;
|
||||||
|
|
||||||
|
if (mD3DFullScreenCheckBox->checkState() == Qt::Checked) {
|
||||||
|
out << "Full Screen=Yes" << endl;
|
||||||
|
} else {
|
||||||
|
out << "Full Screen=No" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
out << "Rendering Device=" << mD3DRenderDeviceComboBox->currentText() << endl;
|
||||||
|
out << "Resource Creation Policy=Create on all devices" << endl;
|
||||||
|
|
||||||
|
if (mD3DVSyncCheckBox->checkState() == Qt::Checked) {
|
||||||
|
out << "VSync=Yes" << endl;
|
||||||
|
} else {
|
||||||
|
out << "VSync=No" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
out << "VSync Interval=1" << endl;
|
||||||
|
out << "Video Mode=" << mD3DResolutionComboBox->currentText() << endl;
|
||||||
|
out << "sRGB Gamma Conversion=No" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
out << endl;
|
||||||
|
|
||||||
|
if (mOpenGLRenderSystem) {
|
||||||
|
QString openGLName = mOpenGLRenderSystem->getName().c_str();
|
||||||
|
openGLName.prepend("[");
|
||||||
|
openGLName.append("]");
|
||||||
|
out << openGLName << endl;
|
||||||
|
|
||||||
|
out << "Colour Depth=32" << endl;
|
||||||
|
out << "Display Frequency=" << mOGLFrequencyComboBox->currentText() << endl;
|
||||||
|
out << "FSAA=" << mOGLAntiAliasingComboBox->currentText() << endl;
|
||||||
|
|
||||||
|
if (mOGLFullScreenCheckBox->checkState() == Qt::Checked) {
|
||||||
|
out << "Full Screen=Yes" << endl;
|
||||||
|
} else {
|
||||||
|
out << "Full Screen=No" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
out << "RTT Preferred Mode=" << mOGLRTTComboBox->currentText() << endl;
|
||||||
|
|
||||||
|
if (mOGLVSyncCheckBox->checkState() == Qt::Checked) {
|
||||||
|
out << "VSync=Yes" << endl;
|
||||||
|
} else {
|
||||||
|
out << "VSync=No" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
out << "VSync Interval=1" << endl;
|
||||||
|
out << "Video Mode=" << mOGLResolutionComboBox->currentText() << endl;
|
||||||
|
out << "sRGB Gamma Conversion=No" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
QString GraphicsPage::getConfigValue(const QString &key, Ogre::RenderSystem *renderer)
|
||||||
|
{
|
||||||
|
QString result;
|
||||||
|
|
||||||
|
mOgreConfig->beginGroup(renderer->getName().c_str());
|
||||||
|
result = mOgreConfig->value(key).toString();
|
||||||
|
mOgreConfig->endGroup();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList GraphicsPage::getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer)
|
||||||
|
{
|
||||||
|
QStringList result;
|
||||||
|
|
||||||
|
uint row = 0;
|
||||||
|
Ogre::ConfigOptionMap options = renderer->getConfigOptions();
|
||||||
|
|
||||||
|
for (Ogre::ConfigOptionMap::iterator i = options.begin (); i != options.end (); i++, row++)
|
||||||
|
{
|
||||||
|
Ogre::StringVector::iterator opt_it;
|
||||||
|
uint idx = 0;
|
||||||
|
for (opt_it = i->second.possibleValues.begin ();
|
||||||
|
opt_it != i->second.possibleValues.end (); opt_it++, idx++)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (strcmp (key.toStdString().c_str(), i->first.c_str()) == 0)
|
||||||
|
result << (*opt_it).c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GraphicsPage::rendererChanged(const QString &renderer)
|
||||||
|
{
|
||||||
|
if (renderer.contains("Direct3D")) {
|
||||||
|
mRendererStackedWidget->setCurrentIndex(1);
|
||||||
|
mDisplayStackedWidget->setCurrentIndex(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renderer.contains("OpenGL")) {
|
||||||
|
mRendererStackedWidget->setCurrentIndex(0);
|
||||||
|
mDisplayStackedWidget->setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
mSelectedRenderSystem = mOgre->getRenderSystemByName(renderer.toStdString());
|
||||||
|
}
|
69
apps/launcher/graphicspage.hpp
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
#ifndef GRAPHICSPAGE_H
|
||||||
|
#define GRAPHICSPAGE_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include <OgreRoot.h>
|
||||||
|
#include <OgreRenderSystem.h>
|
||||||
|
#include <OgreConfigFile.h>
|
||||||
|
#include <OgreConfigDialog.h>
|
||||||
|
|
||||||
|
class QComboBox;
|
||||||
|
class QCheckBox;
|
||||||
|
class QStackedWidget;
|
||||||
|
class QSettings;
|
||||||
|
|
||||||
|
class GraphicsPage : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
GraphicsPage(QWidget *parent = 0);
|
||||||
|
|
||||||
|
QSettings *mOgreConfig;
|
||||||
|
|
||||||
|
void writeConfig();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void rendererChanged(const QString &renderer);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ogre::Root *mOgre;
|
||||||
|
Ogre::RenderSystem *mSelectedRenderSystem;
|
||||||
|
Ogre::RenderSystem *mOpenGLRenderSystem;
|
||||||
|
Ogre::RenderSystem *mDirect3DRenderSystem;
|
||||||
|
|
||||||
|
QComboBox *mRendererComboBox;
|
||||||
|
|
||||||
|
QStackedWidget *mRendererStackedWidget;
|
||||||
|
QStackedWidget *mDisplayStackedWidget;
|
||||||
|
|
||||||
|
// OpenGL
|
||||||
|
QComboBox *mOGLRTTComboBox;
|
||||||
|
QComboBox *mOGLAntiAliasingComboBox;
|
||||||
|
QComboBox *mOGLResolutionComboBox;
|
||||||
|
QComboBox *mOGLFrequencyComboBox;
|
||||||
|
|
||||||
|
QCheckBox *mOGLVSyncCheckBox;
|
||||||
|
QCheckBox *mOGLFullScreenCheckBox;
|
||||||
|
|
||||||
|
// Direct3D
|
||||||
|
QComboBox *mD3DRenderDeviceComboBox;
|
||||||
|
QComboBox *mD3DAntiAliasingComboBox;
|
||||||
|
QComboBox *mD3DFloatingPointComboBox;
|
||||||
|
QComboBox *mD3DResolutionComboBox;
|
||||||
|
|
||||||
|
QCheckBox *mD3DNvPerfCheckBox;
|
||||||
|
QCheckBox *mD3DVSyncCheckBox;
|
||||||
|
QCheckBox *mD3DFullScreenCheckBox;
|
||||||
|
|
||||||
|
QString getConfigValue(const QString &key, Ogre::RenderSystem *renderer);
|
||||||
|
QStringList getAvailableOptions(const QString &key, Ogre::RenderSystem *renderer);
|
||||||
|
|
||||||
|
void createPages();
|
||||||
|
void setupConfig();
|
||||||
|
void setupOgre();
|
||||||
|
void readConfig();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
30
apps/launcher/launcher.pro
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
######################################################################
|
||||||
|
# Automatically generated by qmake (2.01a) Fri Jun 24 21:14:15 2011
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
TEMPLATE = app
|
||||||
|
TARGET =
|
||||||
|
DEPENDPATH += .
|
||||||
|
INCLUDEPATH += .
|
||||||
|
|
||||||
|
# Input
|
||||||
|
HEADERS += combobox.hpp \
|
||||||
|
datafilespage.hpp \
|
||||||
|
graphicspage.hpp \
|
||||||
|
lineedit.hpp \
|
||||||
|
maindialog.hpp \
|
||||||
|
naturalsort.hpp \
|
||||||
|
playpage.hpp \
|
||||||
|
pluginsmodel.hpp \
|
||||||
|
pluginsview.hpp
|
||||||
|
SOURCES += datafilespage.cpp \
|
||||||
|
graphicspage.cpp \
|
||||||
|
lineedit.cpp \
|
||||||
|
main.cpp \
|
||||||
|
maindialog.cpp \
|
||||||
|
naturalsort.cpp \
|
||||||
|
playpage.cpp \
|
||||||
|
pluginsmodel.cpp \
|
||||||
|
pluginsview.cpp
|
||||||
|
RESOURCES += resources.qrc
|
||||||
|
win32:RC_FILE = launcher.rc
|
1
apps/launcher/launcher.rc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
IDI_ICON1 ICON DISCARDABLE "resources/images/openmw.ico"
|
46
apps/launcher/lineedit.cpp
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (c) 2007 Trolltech ASA <info@trolltech.com>
|
||||||
|
**
|
||||||
|
** Use, modification and distribution is allowed without limitation,
|
||||||
|
** warranty, liability or support of any kind.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "lineedit.hpp"
|
||||||
|
#include <QToolButton>
|
||||||
|
#include <QStyle>
|
||||||
|
|
||||||
|
LineEdit::LineEdit(QWidget *parent)
|
||||||
|
: QLineEdit(parent)
|
||||||
|
{
|
||||||
|
clearButton = new QToolButton(this);
|
||||||
|
QPixmap pixmap(":images/clear.png");
|
||||||
|
clearButton->setIcon(QIcon(pixmap));
|
||||||
|
clearButton->setIconSize(pixmap.size());
|
||||||
|
clearButton->setCursor(Qt::ArrowCursor);
|
||||||
|
clearButton->setStyleSheet("QToolButton { border: none; padding: 0px; }");
|
||||||
|
clearButton->hide();
|
||||||
|
connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
|
||||||
|
connect(this, SIGNAL(textChanged(const QString&)), this, SLOT(updateCloseButton(const QString&)));
|
||||||
|
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
|
||||||
|
setStyleSheet(QString("QLineEdit { padding-right: %1px; } ").arg(clearButton->sizeHint().width() + frameWidth + 1));
|
||||||
|
QSize msz = minimumSizeHint();
|
||||||
|
setMinimumSize(qMax(msz.width(), clearButton->sizeHint().height() + frameWidth * 2 + 2),
|
||||||
|
qMax(msz.height(), clearButton->sizeHint().height() + frameWidth * 2 + 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LineEdit::resizeEvent(QResizeEvent *)
|
||||||
|
{
|
||||||
|
QSize sz = clearButton->sizeHint();
|
||||||
|
int frameWidth = style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
|
||||||
|
clearButton->move(rect().right() - frameWidth - sz.width(),
|
||||||
|
(rect().bottom() + 1 - sz.height())/2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LineEdit::updateCloseButton(const QString& text)
|
||||||
|
{
|
||||||
|
clearButton->setVisible(!text.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
35
apps/launcher/lineedit.hpp
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (c) 2007 Trolltech ASA <info@trolltech.com>
|
||||||
|
**
|
||||||
|
** Use, modification and distribution is allowed without limitation,
|
||||||
|
** warranty, liability or support of any kind.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef LINEEDIT_H
|
||||||
|
#define LINEEDIT_H
|
||||||
|
|
||||||
|
#include <QLineEdit>
|
||||||
|
|
||||||
|
class QToolButton;
|
||||||
|
|
||||||
|
class LineEdit : public QLineEdit
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
LineEdit(QWidget *parent = 0);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void resizeEvent(QResizeEvent *);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void updateCloseButton(const QString &text);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QToolButton *clearButton;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // LIENEDIT_H
|
||||||
|
|
35
apps/launcher/main.cpp
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
|
||||||
|
#include "maindialog.hpp"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
|
// Now we make sure the current dir is set to application path
|
||||||
|
QDir dir(QCoreApplication::applicationDirPath());
|
||||||
|
|
||||||
|
#if defined(Q_OS_MAC)
|
||||||
|
if (dir.dirName() == "MacOS") {
|
||||||
|
dir.cdUp();
|
||||||
|
dir.cdUp();
|
||||||
|
dir.cdUp();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QDir::setCurrent(dir.absolutePath());
|
||||||
|
|
||||||
|
// Load the stylesheet
|
||||||
|
QFile file("./launcher.qss");
|
||||||
|
|
||||||
|
file.open(QFile::ReadOnly);
|
||||||
|
QString styleSheet = QLatin1String(file.readAll());
|
||||||
|
app.setStyleSheet(styleSheet);
|
||||||
|
|
||||||
|
MainDialog dialog;
|
||||||
|
return dialog.exec();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
355
apps/launcher/maindialog.cpp
Normal file
|
@ -0,0 +1,355 @@
|
||||||
|
#include <QtGui>
|
||||||
|
|
||||||
|
#include <components/files/path.hpp>
|
||||||
|
|
||||||
|
#include "maindialog.hpp"
|
||||||
|
#include "playpage.hpp"
|
||||||
|
#include "graphicspage.hpp"
|
||||||
|
#include "datafilespage.hpp"
|
||||||
|
|
||||||
|
MainDialog::MainDialog()
|
||||||
|
{
|
||||||
|
mIconWidget = new QListWidget;
|
||||||
|
mIconWidget->setObjectName("IconWidget");
|
||||||
|
mIconWidget->setViewMode(QListView::IconMode);
|
||||||
|
mIconWidget->setWrapping(false);
|
||||||
|
mIconWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // Just to be sure
|
||||||
|
mIconWidget->setIconSize(QSize(48, 48));
|
||||||
|
mIconWidget->setMovement(QListView::Static);
|
||||||
|
|
||||||
|
mIconWidget->setMinimumWidth(400);
|
||||||
|
mIconWidget->setFixedHeight(80);
|
||||||
|
mIconWidget->setSpacing(4);
|
||||||
|
mIconWidget->setCurrentRow(0);
|
||||||
|
mIconWidget->setFlow(QListView::LeftToRight);
|
||||||
|
|
||||||
|
QGroupBox *groupBox = new QGroupBox(this);
|
||||||
|
QVBoxLayout *groupLayout = new QVBoxLayout(groupBox);
|
||||||
|
|
||||||
|
mPagesWidget = new QStackedWidget(groupBox);
|
||||||
|
groupLayout->addWidget(mPagesWidget);
|
||||||
|
|
||||||
|
QPushButton *playButton = new QPushButton(tr("Play"));
|
||||||
|
|
||||||
|
QDialogButtonBox *buttonBox = new QDialogButtonBox(this);
|
||||||
|
buttonBox->setStandardButtons(QDialogButtonBox::Close);
|
||||||
|
buttonBox->addButton(playButton, QDialogButtonBox::AcceptRole);
|
||||||
|
|
||||||
|
QVBoxLayout *dialogLayout = new QVBoxLayout(this);
|
||||||
|
dialogLayout->addWidget(mIconWidget);
|
||||||
|
dialogLayout->addWidget(groupBox);
|
||||||
|
dialogLayout->addWidget(buttonBox);
|
||||||
|
|
||||||
|
|
||||||
|
setWindowTitle(tr("OpenMW Launcher"));
|
||||||
|
setWindowIcon(QIcon(":/images/openmw.png"));
|
||||||
|
// Remove what's this? button
|
||||||
|
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
|
setMinimumSize(QSize(575, 575));
|
||||||
|
|
||||||
|
connect(buttonBox, SIGNAL(rejected()), this, SLOT(close()));
|
||||||
|
connect(buttonBox, SIGNAL(accepted()), this, SLOT(play()));
|
||||||
|
|
||||||
|
setupConfig();
|
||||||
|
createIcons();
|
||||||
|
createPages();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::createIcons()
|
||||||
|
{
|
||||||
|
if (!QIcon::hasThemeIcon("document-new")) {
|
||||||
|
QIcon::setThemeName("tango");
|
||||||
|
}
|
||||||
|
|
||||||
|
// We create a fallback icon because the default fallback doesn't work
|
||||||
|
QIcon graphicsIcon = QIcon(":/icons/tango/video-display.png");
|
||||||
|
|
||||||
|
QListWidgetItem *playButton = new QListWidgetItem(mIconWidget);
|
||||||
|
playButton->setIcon(QIcon(":/images/openmw.png"));
|
||||||
|
playButton->setText(tr("Play"));
|
||||||
|
playButton->setTextAlignment(Qt::AlignCenter);
|
||||||
|
playButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
|
QListWidgetItem *graphicsButton = new QListWidgetItem(mIconWidget);
|
||||||
|
graphicsButton->setIcon(QIcon::fromTheme("video-display", graphicsIcon));
|
||||||
|
graphicsButton->setText(tr("Graphics"));
|
||||||
|
graphicsButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom | Qt::AlignAbsolute);
|
||||||
|
graphicsButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
|
QListWidgetItem *dataFilesButton = new QListWidgetItem(mIconWidget);
|
||||||
|
dataFilesButton->setIcon(QIcon(":/images/openmw-plugin.png"));
|
||||||
|
dataFilesButton->setText(tr("Data Files"));
|
||||||
|
dataFilesButton->setTextAlignment(Qt::AlignHCenter | Qt::AlignBottom);
|
||||||
|
dataFilesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
|
||||||
|
|
||||||
|
connect(mIconWidget,
|
||||||
|
SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
|
||||||
|
this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::createPages()
|
||||||
|
{
|
||||||
|
// Various pages
|
||||||
|
mPlayPage = new PlayPage(this);
|
||||||
|
mGraphicsPage = new GraphicsPage(this);
|
||||||
|
mDataFilesPage = new DataFilesPage(this);
|
||||||
|
|
||||||
|
// First we retrieve all data= keys from the config
|
||||||
|
// We can't use QSettings directly because it
|
||||||
|
// does not support multiple keys with the same name
|
||||||
|
QFile file(mGameConfig->fileName());
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error opening OpenMW configuration file");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not open %0</b><br><br> \
|
||||||
|
Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
QApplication::exit(); // File cannot be opened or created
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream in(&file);
|
||||||
|
|
||||||
|
QStringList dataDirs;
|
||||||
|
|
||||||
|
// Add each data= value
|
||||||
|
while (!in.atEnd()) {
|
||||||
|
QString line = in.readLine();
|
||||||
|
|
||||||
|
if (line.startsWith("data=")) {
|
||||||
|
dataDirs.append(line.remove("data="));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the data-local= key
|
||||||
|
QString dataLocal = mGameConfig->value("data-local").toString();
|
||||||
|
if (!dataLocal.isEmpty()) {
|
||||||
|
dataDirs.append(dataLocal);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dataDirs.isEmpty()) {
|
||||||
|
// Now pass the datadirs on to the DataFilesPage
|
||||||
|
mDataFilesPage->setupDataFiles(dataDirs, mGameConfig->value("fs-strict").toBool());
|
||||||
|
} else {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error reading OpenMW configuration file");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not read the location of the data files</b><br><br> \
|
||||||
|
Please make sure OpenMW is correctly configured and try again.<br>"));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
QApplication::exit(); // No data or data-local entries in openmw.cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the combobox of the play page to imitate the comobox on the datafilespage
|
||||||
|
mPlayPage->mProfilesComboBox->setModel(mDataFilesPage->mProfilesComboBox->model());
|
||||||
|
mPlayPage->mProfilesComboBox->setCurrentIndex(mDataFilesPage->mProfilesComboBox->currentIndex());
|
||||||
|
|
||||||
|
// Add the pages to the stacked widget
|
||||||
|
mPagesWidget->addWidget(mPlayPage);
|
||||||
|
mPagesWidget->addWidget(mGraphicsPage);
|
||||||
|
mPagesWidget->addWidget(mDataFilesPage);
|
||||||
|
|
||||||
|
// Select the first page
|
||||||
|
mIconWidget->setCurrentItem(mIconWidget->item(0), QItemSelectionModel::Select);
|
||||||
|
|
||||||
|
connect(mPlayPage->mPlayButton, SIGNAL(clicked()), this, SLOT(play()));
|
||||||
|
|
||||||
|
connect(mPlayPage->mProfilesComboBox,
|
||||||
|
SIGNAL(currentIndexChanged(int)),
|
||||||
|
this, SLOT(profileChanged(int)));
|
||||||
|
|
||||||
|
connect(mDataFilesPage->mProfilesComboBox,
|
||||||
|
SIGNAL(currentIndexChanged(int)),
|
||||||
|
this, SLOT(profileChanged(int)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::profileChanged(int index)
|
||||||
|
{
|
||||||
|
// Just to be sure, should always have a selection
|
||||||
|
if (!mIconWidget->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString currentPage = mIconWidget->currentItem()->data(Qt::DisplayRole).toString();
|
||||||
|
if (currentPage == QLatin1String("Play")) {
|
||||||
|
mDataFilesPage->mProfilesComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentPage == QLatin1String("Data Files")) {
|
||||||
|
mPlayPage->mProfilesComboBox->setCurrentIndex(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::changePage(QListWidgetItem *current, QListWidgetItem *previous)
|
||||||
|
{
|
||||||
|
if (!current)
|
||||||
|
current = previous;
|
||||||
|
|
||||||
|
mPagesWidget->setCurrentIndex(mIconWidget->row(current));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::closeEvent(QCloseEvent *event)
|
||||||
|
{
|
||||||
|
// Now write all config files
|
||||||
|
writeConfig();
|
||||||
|
event->accept();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::play()
|
||||||
|
{
|
||||||
|
// First do a write of all the configs, just to be sure
|
||||||
|
writeConfig();
|
||||||
|
|
||||||
|
#ifdef Q_WS_WIN
|
||||||
|
QString game = "./openmw.exe";
|
||||||
|
QFile file(game);
|
||||||
|
#elif defined(Q_WS_MAC)
|
||||||
|
QDir dir(QCoreApplication::applicationDirPath());
|
||||||
|
QString game = dir.absoluteFilePath("openmw");
|
||||||
|
QFile file(game);
|
||||||
|
#else
|
||||||
|
QString game = "./openmw";
|
||||||
|
QFile file(game);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QProcess process;
|
||||||
|
QFileInfo info(file);
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error starting OpenMW");
|
||||||
|
msgBox.setIcon(QMessageBox::Warning);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not find OpenMW</b><br><br> \
|
||||||
|
The OpenMW application is not found.<br> \
|
||||||
|
Please make sure OpenMW is installed correctly and try again.<br>"));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info.isExecutable()) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error starting OpenMW");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not start OpenMW</b><br><br> \
|
||||||
|
The OpenMW application is not executable.<br> \
|
||||||
|
Please make sure you have the right permissions and try again.<br>"));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the game
|
||||||
|
if (!process.startDetached(game)) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error starting OpenMW");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not start OpenMW</b><br><br> \
|
||||||
|
An error occurred while starting OpenMW.<br><br> \
|
||||||
|
Press \"Show Details...\" for more information.<br>"));
|
||||||
|
msgBox.setDetailedText(process.errorString());
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::setupConfig()
|
||||||
|
{
|
||||||
|
// First we read the OpenMW config
|
||||||
|
QString config = "./openmw.cfg";
|
||||||
|
QFile file(config);
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
config = QString::fromStdString(Files::getPath(Files::Path_ConfigUser,
|
||||||
|
"openmw", "openmw.cfg"));
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// Open our config file
|
||||||
|
mGameConfig = new QSettings(config, QSettings::IniFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainDialog::writeConfig()
|
||||||
|
{
|
||||||
|
// Write the profiles
|
||||||
|
mDataFilesPage->writeConfig();
|
||||||
|
mDataFilesPage->mLauncherConfig->sync();
|
||||||
|
|
||||||
|
// Write the graphics settings
|
||||||
|
mGraphicsPage->writeConfig();
|
||||||
|
mGraphicsPage->mOgreConfig->sync();
|
||||||
|
|
||||||
|
QStringList dataFiles = mDataFilesPage->selectedMasters();
|
||||||
|
dataFiles.append(mDataFilesPage->checkedPlugins());
|
||||||
|
|
||||||
|
// Open the config as a QFile
|
||||||
|
QFile file(mGameConfig->fileName());
|
||||||
|
|
||||||
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
// File cannot be opened or created
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error opening OpenMW configuration file");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not open %0</b><br><br> \
|
||||||
|
Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextStream in(&file);
|
||||||
|
QByteArray buffer;
|
||||||
|
|
||||||
|
// Remove all previous master/plugin entries from config
|
||||||
|
while (!in.atEnd()) {
|
||||||
|
QString line = in.readLine();
|
||||||
|
if (!line.contains("master") && !line.contains("plugin")) {
|
||||||
|
buffer += line += "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
// Now we write back the other config entries
|
||||||
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
|
||||||
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Error writing OpenMW configuration file");
|
||||||
|
msgBox.setIcon(QMessageBox::Critical);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Ok);
|
||||||
|
msgBox.setText(tr("<br><b>Could not write to %0</b><br><br> \
|
||||||
|
Please make sure you have the right permissions and try again.<br>").arg(file.fileName()));
|
||||||
|
msgBox.exec();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.write(buffer);
|
||||||
|
QTextStream out(&file);
|
||||||
|
|
||||||
|
// Write the list of game files to the config
|
||||||
|
foreach (const QString ¤tFile, dataFiles) {
|
||||||
|
|
||||||
|
if (currentFile.endsWith(QString(".esm"), Qt::CaseInsensitive)) {
|
||||||
|
out << "master=" << currentFile << endl;
|
||||||
|
} else if (currentFile.endsWith(QString(".esp"), Qt::CaseInsensitive)) {
|
||||||
|
out << "plugin=" << currentFile << endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
}
|
46
apps/launcher/maindialog.hpp
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#ifndef MAINDIALOG_H
|
||||||
|
#define MAINDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
class QListWidget;
|
||||||
|
class QListWidgetItem;
|
||||||
|
class QStackedWidget;
|
||||||
|
class QStringListModel;
|
||||||
|
class QSettings;
|
||||||
|
|
||||||
|
class PlayPage;
|
||||||
|
class GraphicsPage;
|
||||||
|
class DataFilesPage;
|
||||||
|
|
||||||
|
class MainDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MainDialog();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void changePage(QListWidgetItem *current, QListWidgetItem *previous);
|
||||||
|
void play();
|
||||||
|
void profileChanged(int index);
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createIcons();
|
||||||
|
void createPages();
|
||||||
|
void setupConfig();
|
||||||
|
void writeConfig();
|
||||||
|
void closeEvent(QCloseEvent *event);
|
||||||
|
|
||||||
|
QListWidget *mIconWidget;
|
||||||
|
QStackedWidget *mPagesWidget;
|
||||||
|
|
||||||
|
PlayPage *mPlayPage;
|
||||||
|
GraphicsPage *mGraphicsPage;
|
||||||
|
DataFilesPage *mDataFilesPage;
|
||||||
|
|
||||||
|
QSettings *mGameConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
95
apps/launcher/naturalsort.cpp
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* This file contains code found in the QtGui module of the Qt Toolkit.
|
||||||
|
* See Qt's qfilesystemmodel source files for more information
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "naturalsort.hpp"
|
||||||
|
|
||||||
|
static inline QChar getNextChar(const QString &s, int location)
|
||||||
|
{
|
||||||
|
return (location < s.length()) ? s.at(location) : QChar();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Natural number sort, skips spaces.
|
||||||
|
*
|
||||||
|
* Examples:
|
||||||
|
* 1, 2, 10, 55, 100
|
||||||
|
* 01.jpg, 2.jpg, 10.jpg
|
||||||
|
*
|
||||||
|
* Note on the algorithm:
|
||||||
|
* Only as many characters as necessary are looked at and at most they all
|
||||||
|
* are looked at once.
|
||||||
|
*
|
||||||
|
* Slower then QString::compare() (of course)
|
||||||
|
*/
|
||||||
|
int naturalCompare(const QString &s1, const QString &s2, Qt::CaseSensitivity cs)
|
||||||
|
{
|
||||||
|
for (int l1 = 0, l2 = 0; l1 <= s1.count() && l2 <= s2.count(); ++l1, ++l2) {
|
||||||
|
// skip spaces, tabs and 0's
|
||||||
|
QChar c1 = getNextChar(s1, l1);
|
||||||
|
while (c1.isSpace())
|
||||||
|
c1 = getNextChar(s1, ++l1);
|
||||||
|
QChar c2 = getNextChar(s2, l2);
|
||||||
|
while (c2.isSpace())
|
||||||
|
c2 = getNextChar(s2, ++l2);
|
||||||
|
|
||||||
|
if (c1.isDigit() && c2.isDigit()) {
|
||||||
|
while (c1.digitValue() == 0)
|
||||||
|
c1 = getNextChar(s1, ++l1);
|
||||||
|
while (c2.digitValue() == 0)
|
||||||
|
c2 = getNextChar(s2, ++l2);
|
||||||
|
|
||||||
|
int lookAheadLocation1 = l1;
|
||||||
|
int lookAheadLocation2 = l2;
|
||||||
|
int currentReturnValue = 0;
|
||||||
|
// find the last digit, setting currentReturnValue as we go if it isn't equal
|
||||||
|
for (
|
||||||
|
QChar lookAhead1 = c1, lookAhead2 = c2;
|
||||||
|
(lookAheadLocation1 <= s1.length() && lookAheadLocation2 <= s2.length());
|
||||||
|
lookAhead1 = getNextChar(s1, ++lookAheadLocation1),
|
||||||
|
lookAhead2 = getNextChar(s2, ++lookAheadLocation2)
|
||||||
|
) {
|
||||||
|
bool is1ADigit = !lookAhead1.isNull() && lookAhead1.isDigit();
|
||||||
|
bool is2ADigit = !lookAhead2.isNull() && lookAhead2.isDigit();
|
||||||
|
if (!is1ADigit && !is2ADigit)
|
||||||
|
break;
|
||||||
|
if (!is1ADigit)
|
||||||
|
return -1;
|
||||||
|
if (!is2ADigit)
|
||||||
|
return 1;
|
||||||
|
if (currentReturnValue == 0) {
|
||||||
|
if (lookAhead1 < lookAhead2) {
|
||||||
|
currentReturnValue = -1;
|
||||||
|
} else if (lookAhead1 > lookAhead2) {
|
||||||
|
currentReturnValue = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (currentReturnValue != 0)
|
||||||
|
return currentReturnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cs == Qt::CaseInsensitive) {
|
||||||
|
if (!c1.isLower()) c1 = c1.toLower();
|
||||||
|
if (!c2.isLower()) c2 = c2.toLower();
|
||||||
|
}
|
||||||
|
int r = QString::localeAwareCompare(c1, c2);
|
||||||
|
if (r < 0)
|
||||||
|
return -1;
|
||||||
|
if (r > 0)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// The two strings are the same (02 == 2) so fall back to the normal sort
|
||||||
|
return QString::compare(s1, s2, cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool naturalSortLessThanCS( const QString &left, const QString &right )
|
||||||
|
{
|
||||||
|
return (naturalCompare( left, right, Qt::CaseSensitive ) < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool naturalSortLessThanCI( const QString &left, const QString &right )
|
||||||
|
{
|
||||||
|
return (naturalCompare( left, right, Qt::CaseInsensitive ) < 0);
|
||||||
|
}
|
9
apps/launcher/naturalsort.hpp
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef NATURALSORT_H
|
||||||
|
#define NATURALSORT_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
bool naturalSortLessThanCS( const QString &left, const QString &right );
|
||||||
|
bool naturalSortLessThanCI( const QString &left, const QString &right );
|
||||||
|
|
||||||
|
#endif
|
43
apps/launcher/playpage.cpp
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#include <QtGui>
|
||||||
|
|
||||||
|
#include "playpage.hpp"
|
||||||
|
|
||||||
|
PlayPage::PlayPage(QWidget *parent) : QWidget(parent)
|
||||||
|
{
|
||||||
|
QWidget *playWidget = new QWidget(this);
|
||||||
|
playWidget->setObjectName("PlayGroup");
|
||||||
|
playWidget->setFixedSize(QSize(425, 375));
|
||||||
|
|
||||||
|
mPlayButton = new QPushButton(tr("Play"), playWidget);
|
||||||
|
mPlayButton->setObjectName("PlayButton");
|
||||||
|
mPlayButton->setMinimumSize(QSize(200, 50));
|
||||||
|
|
||||||
|
QLabel *profileLabel = new QLabel(tr("Current Profile:"), playWidget);
|
||||||
|
profileLabel->setObjectName("ProfileLabel");
|
||||||
|
|
||||||
|
QPlastiqueStyle *style = new QPlastiqueStyle;
|
||||||
|
mProfilesComboBox = new QComboBox(playWidget);
|
||||||
|
mProfilesComboBox->setObjectName("ProfilesComboBox");
|
||||||
|
mProfilesComboBox->setStyle(style);
|
||||||
|
|
||||||
|
QGridLayout *playLayout = new QGridLayout(playWidget);
|
||||||
|
|
||||||
|
QSpacerItem *hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
|
QSpacerItem *hSpacer2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
|
|
||||||
|
QSpacerItem *vSpacer1 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||||
|
QSpacerItem *vSpacer2 = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||||||
|
|
||||||
|
playLayout->addWidget(mPlayButton, 1, 1, 1, 1);
|
||||||
|
playLayout->addWidget(profileLabel, 2, 1, 1, 1);
|
||||||
|
playLayout->addWidget(mProfilesComboBox, 3, 1, 1, 1);
|
||||||
|
playLayout->addItem(hSpacer1, 2, 0, 1, 1);
|
||||||
|
playLayout->addItem(hSpacer2, 2, 2, 1, 1);
|
||||||
|
playLayout->addItem(vSpacer1, 0, 1, 1, 1);
|
||||||
|
playLayout->addItem(vSpacer2, 4, 1, 1, 1);
|
||||||
|
|
||||||
|
QHBoxLayout *pageLayout = new QHBoxLayout(this);
|
||||||
|
|
||||||
|
pageLayout->addWidget(playWidget);
|
||||||
|
|
||||||
|
}
|
21
apps/launcher/playpage.hpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef PLAYPAGE_H
|
||||||
|
#define PLAYPAGE_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
class QComboBox;
|
||||||
|
class QPushButton;
|
||||||
|
|
||||||
|
class PlayPage : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PlayPage(QWidget *parent = 0);
|
||||||
|
|
||||||
|
QComboBox *mProfilesComboBox;
|
||||||
|
QPushButton *mPlayButton;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
149
apps/launcher/pluginsmodel.cpp
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
#include <QMimeData>
|
||||||
|
#include <QBitArray>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "pluginsmodel.hpp"
|
||||||
|
|
||||||
|
PluginsModel::PluginsModel(QObject *parent) : QStandardItemModel(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void decodeDataRecursive(QDataStream &stream, QStandardItem *item)
|
||||||
|
{
|
||||||
|
int colCount, childCount;
|
||||||
|
stream >> *item;
|
||||||
|
stream >> colCount >> childCount;
|
||||||
|
item->setColumnCount(colCount);
|
||||||
|
|
||||||
|
int childPos = childCount;
|
||||||
|
|
||||||
|
while(childPos > 0) {
|
||||||
|
childPos--;
|
||||||
|
QStandardItem *child = new QStandardItem();
|
||||||
|
decodeDataRecursive(stream, child);
|
||||||
|
item->setChild( childPos / colCount, childPos % colCount, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PluginsModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
|
||||||
|
int row, int column, const QModelIndex &parent)
|
||||||
|
{
|
||||||
|
// Code largely based on QStandardItemModel::dropMimeData
|
||||||
|
|
||||||
|
// check if the action is supported
|
||||||
|
if (!data || !(action == Qt::CopyAction || action == Qt::MoveAction))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// check if the format is supported
|
||||||
|
QString format = QLatin1String("application/x-qstandarditemmodeldatalist");
|
||||||
|
if (!data->hasFormat(format))
|
||||||
|
return QAbstractItemModel::dropMimeData(data, action, row, column, parent);
|
||||||
|
|
||||||
|
if (row > rowCount(parent))
|
||||||
|
row = rowCount(parent);
|
||||||
|
if (row == -1)
|
||||||
|
row = rowCount(parent);
|
||||||
|
if (column == -1)
|
||||||
|
column = 0;
|
||||||
|
|
||||||
|
// decode and insert
|
||||||
|
QByteArray encoded = data->data(format);
|
||||||
|
QDataStream stream(&encoded, QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
|
||||||
|
//code based on QAbstractItemModel::decodeData
|
||||||
|
// adapted to work with QStandardItem
|
||||||
|
int top = std::numeric_limits<int>::max();
|
||||||
|
int left = std::numeric_limits<int>::max();
|
||||||
|
int bottom = 0;
|
||||||
|
int right = 0;
|
||||||
|
QVector<int> rows, columns;
|
||||||
|
QVector<QStandardItem *> items;
|
||||||
|
|
||||||
|
while (!stream.atEnd()) {
|
||||||
|
int r, c;
|
||||||
|
QStandardItem *item = new QStandardItem();
|
||||||
|
stream >> r >> c;
|
||||||
|
decodeDataRecursive(stream, item);
|
||||||
|
|
||||||
|
rows.append(r);
|
||||||
|
columns.append(c);
|
||||||
|
items.append(item);
|
||||||
|
top = qMin(r, top);
|
||||||
|
left = qMin(c, left);
|
||||||
|
bottom = qMax(r, bottom);
|
||||||
|
right = qMax(c, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert the dragged items into the table, use a bit array to avoid overwriting items,
|
||||||
|
// since items from different tables can have the same row and column
|
||||||
|
int dragRowCount = 0;
|
||||||
|
int dragColumnCount = right - left + 1;
|
||||||
|
|
||||||
|
// Compute the number of continuous rows upon insertion and modify the rows to match
|
||||||
|
QVector<int> rowsToInsert(bottom + 1);
|
||||||
|
for (int i = 0; i < rows.count(); ++i)
|
||||||
|
rowsToInsert[rows.at(i)] = 1;
|
||||||
|
for (int i = 0; i < rowsToInsert.count(); ++i) {
|
||||||
|
if (rowsToInsert[i] == 1){
|
||||||
|
rowsToInsert[i] = dragRowCount;
|
||||||
|
++dragRowCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < rows.count(); ++i)
|
||||||
|
rows[i] = top + rowsToInsert[rows[i]];
|
||||||
|
|
||||||
|
QBitArray isWrittenTo(dragRowCount * dragColumnCount);
|
||||||
|
|
||||||
|
// make space in the table for the dropped data
|
||||||
|
int colCount = columnCount(parent);
|
||||||
|
if (colCount < dragColumnCount + column) {
|
||||||
|
insertColumns(colCount, dragColumnCount + column - colCount, parent);
|
||||||
|
colCount = columnCount(parent);
|
||||||
|
}
|
||||||
|
insertRows(row, dragRowCount, parent);
|
||||||
|
|
||||||
|
row = qMax(0, row);
|
||||||
|
column = qMax(0, column);
|
||||||
|
|
||||||
|
QStandardItem *parentItem = itemFromIndex (parent);
|
||||||
|
if (!parentItem)
|
||||||
|
parentItem = invisibleRootItem();
|
||||||
|
|
||||||
|
QVector<QPersistentModelIndex> newIndexes(items.size());
|
||||||
|
// set the data in the table
|
||||||
|
for (int j = 0; j < items.size(); ++j) {
|
||||||
|
int relativeRow = rows.at(j) - top;
|
||||||
|
int relativeColumn = columns.at(j) - left;
|
||||||
|
int destinationRow = relativeRow + row;
|
||||||
|
int destinationColumn = relativeColumn + column;
|
||||||
|
int flat = (relativeRow * dragColumnCount) + relativeColumn;
|
||||||
|
// if the item was already written to, or we just can't fit it in the table, create a new row
|
||||||
|
if (destinationColumn >= colCount || isWrittenTo.testBit(flat)) {
|
||||||
|
destinationColumn = qBound(column, destinationColumn, colCount - 1);
|
||||||
|
destinationRow = row + dragRowCount;
|
||||||
|
insertRows(row + dragRowCount, 1, parent);
|
||||||
|
flat = (dragRowCount * dragColumnCount) + relativeColumn;
|
||||||
|
isWrittenTo.resize(++dragRowCount * dragColumnCount);
|
||||||
|
}
|
||||||
|
if (!isWrittenTo.testBit(flat)) {
|
||||||
|
newIndexes[j] = index(destinationRow, destinationColumn, parentItem->index());
|
||||||
|
isWrittenTo.setBit(flat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int k = 0; k < newIndexes.size(); k++) {
|
||||||
|
if (newIndexes.at(k).isValid()) {
|
||||||
|
parentItem->setChild(newIndexes.at(k).row(), newIndexes.at(k).column(), items.at(k));
|
||||||
|
} else {
|
||||||
|
delete items.at(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The important part, tell the view what is dropped
|
||||||
|
emit indexesDropped(newIndexes);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
21
apps/launcher/pluginsmodel.hpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef PLUGINSMODEL_H
|
||||||
|
#define PLUGINSMODEL_H
|
||||||
|
|
||||||
|
#include <QStandardItemModel>
|
||||||
|
|
||||||
|
class PluginsModel : public QStandardItemModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PluginsModel(QObject *parent = 0);
|
||||||
|
~PluginsModel() {};
|
||||||
|
|
||||||
|
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void indexesDropped(QVector<QPersistentModelIndex> indexes);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
42
apps/launcher/pluginsview.cpp
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
|
#include "pluginsview.hpp"
|
||||||
|
|
||||||
|
PluginsView::PluginsView(QWidget *parent) : QTableView(parent)
|
||||||
|
{
|
||||||
|
setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||||
|
setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
setAlternatingRowColors(true);
|
||||||
|
setDragEnabled(true);
|
||||||
|
setDragDropMode(QAbstractItemView::InternalMove);
|
||||||
|
setDropIndicatorShown(true);
|
||||||
|
setDragDropOverwriteMode(false);
|
||||||
|
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginsView::startDrag(Qt::DropActions supportedActions)
|
||||||
|
{
|
||||||
|
selectionModel()->select( selectionModel()->selection(),
|
||||||
|
QItemSelectionModel::Select | QItemSelectionModel::Rows );
|
||||||
|
QAbstractItemView::startDrag( supportedActions );
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginsView::setModel(QSortFilterProxyModel *model)
|
||||||
|
{
|
||||||
|
QTableView::setModel(model);
|
||||||
|
|
||||||
|
qRegisterMetaType< QVector<QPersistentModelIndex> >();
|
||||||
|
|
||||||
|
connect(model->sourceModel(), SIGNAL(indexesDropped(QVector<QPersistentModelIndex>)),
|
||||||
|
this, SLOT(selectIndexes(QVector<QPersistentModelIndex>)), Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PluginsView::selectIndexes( QVector<QPersistentModelIndex> aIndexes )
|
||||||
|
{
|
||||||
|
selectionModel()->clearSelection();
|
||||||
|
foreach( QPersistentModelIndex pIndex, aIndexes )
|
||||||
|
selectionModel()->select( pIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows );
|
||||||
|
}
|
29
apps/launcher/pluginsview.hpp
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
#ifndef PLUGINSVIEW_H
|
||||||
|
#define PLUGINSVIEW_H
|
||||||
|
|
||||||
|
#include <QTableView>
|
||||||
|
|
||||||
|
#include "pluginsmodel.hpp"
|
||||||
|
|
||||||
|
class QSortFilterProxyModel;
|
||||||
|
|
||||||
|
class PluginsView : public QTableView
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
PluginsView(QWidget *parent = 0);
|
||||||
|
|
||||||
|
PluginsModel* model() const
|
||||||
|
{ return qobject_cast<PluginsModel*>(QAbstractItemView::model()); }
|
||||||
|
|
||||||
|
void startDrag(Qt::DropActions supportedActions);
|
||||||
|
void setModel(QSortFilterProxyModel *model);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void selectIndexes(QVector<QPersistentModelIndex> aIndexes);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QVector<QPersistentModelIndex>);
|
||||||
|
|
||||||
|
#endif
|
21
apps/launcher/resources.qrc
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<!DOCTYPE RCC><RCC version="1.0">
|
||||||
|
<qresource prefix="/images">
|
||||||
|
<file alias="clear.png">resources/images/clear.png</file>
|
||||||
|
<file alias="down.png">resources/images/down.png</file>
|
||||||
|
<file alias="openmw.png">resources/images/openmw.png</file>
|
||||||
|
<file alias="openmw-plugin.png">resources/images/openmw-plugin.png</file>
|
||||||
|
<file alias="openmw-header.png">resources/images/openmw-header.png</file>
|
||||||
|
<file alias="playpage-background.png">resources/images/playpage-background.png</file>
|
||||||
|
</qresource>
|
||||||
|
<qresource prefix="icons/tango">
|
||||||
|
<file alias="index.theme">resources/icons/tango/index.theme</file>
|
||||||
|
<file alias="video-display.png">resources/icons/tango/video-display.png</file>
|
||||||
|
<file alias="16x16/document-new.png">resources/icons/tango/document-new.png</file>
|
||||||
|
<file alias="16x16/edit-copy.png">resources/icons/tango/edit-copy.png</file>
|
||||||
|
<file alias="16x16/edit-delete.png">resources/icons/tango/edit-delete.png</file>
|
||||||
|
<file alias="16x16/go-bottom.png">resources/icons/tango/go-bottom.png</file>
|
||||||
|
<file alias="16x16/go-down.png">resources/icons/tango/go-down.png</file>
|
||||||
|
<file alias="16x16/go-top.png">resources/icons/tango/go-top.png</file>
|
||||||
|
<file alias="16x16/go-up.png">resources/icons/tango/go-up.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
BIN
apps/launcher/resources/icons/tango/document-new.png
Normal file
After Width: | Height: | Size: 477 B |
BIN
apps/launcher/resources/icons/tango/edit-copy.png
Normal file
After Width: | Height: | Size: 498 B |
BIN
apps/launcher/resources/icons/tango/edit-delete.png
Normal file
After Width: | Height: | Size: 793 B |
BIN
apps/launcher/resources/icons/tango/go-bottom.png
Normal file
After Width: | Height: | Size: 663 B |
BIN
apps/launcher/resources/icons/tango/go-down.png
Normal file
After Width: | Height: | Size: 683 B |
BIN
apps/launcher/resources/icons/tango/go-top.png
Normal file
After Width: | Height: | Size: 636 B |
BIN
apps/launcher/resources/icons/tango/go-up.png
Normal file
After Width: | Height: | Size: 652 B |
8
apps/launcher/resources/icons/tango/index.theme
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
[Icon Theme]
|
||||||
|
Name=Tango
|
||||||
|
Comment=Tango Theme
|
||||||
|
Inherits=default
|
||||||
|
Directories=16x16
|
||||||
|
|
||||||
|
[16x16]
|
||||||
|
Size=16
|
BIN
apps/launcher/resources/icons/tango/video-display.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
apps/launcher/resources/images/clear.png
Normal file
After Width: | Height: | Size: 644 B |
BIN
apps/launcher/resources/images/down.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
apps/launcher/resources/images/openmw-header.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
apps/launcher/resources/images/openmw-plugin.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
apps/launcher/resources/images/openmw.ico
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
apps/launcher/resources/images/openmw.png
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
apps/launcher/resources/images/playpage-background.png
Normal file
After Width: | Height: | Size: 231 KiB |
|
@ -1,5 +1,9 @@
|
||||||
project(OpenMW)
|
project(OpenMW)
|
||||||
|
|
||||||
|
# config file
|
||||||
|
|
||||||
|
configure_file ("${OpenMW_SOURCE_DIR}/config.hpp.cmake" "${OpenMW_SOURCE_DIR}/config.hpp")
|
||||||
|
|
||||||
# local files
|
# local files
|
||||||
|
|
||||||
set(GAME
|
set(GAME
|
||||||
|
@ -7,7 +11,8 @@ set(GAME
|
||||||
engine.cpp
|
engine.cpp
|
||||||
)
|
)
|
||||||
set(GAME_HEADER
|
set(GAME_HEADER
|
||||||
engine.hpp)
|
engine.hpp
|
||||||
|
config.hpp)
|
||||||
source_group(game FILES ${GAME} ${GAME_HEADER})
|
source_group(game FILES ${GAME} ${GAME_HEADER})
|
||||||
|
|
||||||
set(GAMEREND
|
set(GAMEREND
|
||||||
|
@ -50,6 +55,7 @@ set(GAMEGUI_HEADER
|
||||||
mwgui/dialogue_history.hpp
|
mwgui/dialogue_history.hpp
|
||||||
mwgui/window_base.hpp
|
mwgui/window_base.hpp
|
||||||
mwgui/stats_window.hpp
|
mwgui/stats_window.hpp
|
||||||
|
mwgui/messagebox.hpp
|
||||||
)
|
)
|
||||||
set(GAMEGUI
|
set(GAMEGUI
|
||||||
mwgui/window_manager.cpp
|
mwgui/window_manager.cpp
|
||||||
|
@ -65,6 +71,7 @@ set(GAMEGUI
|
||||||
mwgui/dialogue_history.cpp
|
mwgui/dialogue_history.cpp
|
||||||
mwgui/window_base.cpp
|
mwgui/window_base.cpp
|
||||||
mwgui/stats_window.cpp
|
mwgui/stats_window.cpp
|
||||||
|
mwgui/messagebox.cpp
|
||||||
)
|
)
|
||||||
source_group(apps\\openmw\\mwgui FILES ${GAMEGUI_HEADER} ${GAMEGUI})
|
source_group(apps\\openmw\\mwgui FILES ${GAMEGUI_HEADER} ${GAMEGUI})
|
||||||
|
|
||||||
|
|
9
apps/openmw/config.hpp.cmake
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
|
#define OPENMW_VERSION_MAJOR @OPENMW_VERSION_MAJOR@
|
||||||
|
#define OPENMW_VERSION_MINOR @OPENMW_VERSION_MINOR@
|
||||||
|
#define OPENMW_VERSION_RELEASE @OPENMW_VERSION_RELEASE@
|
||||||
|
#define OPENMW_VERSION "@OPENMW_VERSION@"
|
||||||
|
|
||||||
|
#endif
|
|
@ -7,6 +7,7 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <OgreVector3.h>
|
#include <OgreVector3.h>
|
||||||
|
#include <Ogre.h>
|
||||||
|
|
||||||
#include "components/esm/records.hpp"
|
#include "components/esm/records.hpp"
|
||||||
#include <components/esm_store/cell_store.hpp>
|
#include <components/esm_store/cell_store.hpp>
|
||||||
|
@ -44,10 +45,6 @@
|
||||||
|
|
||||||
#include <OgreRoot.h>
|
#include <OgreRoot.h>
|
||||||
|
|
||||||
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
|
|
||||||
#include <OSX/macUtils.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <MyGUI_WidgetManager.h>
|
#include <MyGUI_WidgetManager.h>
|
||||||
#include "mwgui/class.hpp"
|
#include "mwgui/class.hpp"
|
||||||
|
|
||||||
|
@ -76,7 +73,7 @@ void OMW::Engine::executeLocalScripts()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt)
|
bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
|
||||||
{
|
{
|
||||||
if(mShowFPS)
|
if(mShowFPS)
|
||||||
{
|
{
|
||||||
|
@ -92,6 +89,8 @@ bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt)
|
||||||
std::string effect;
|
std::string effect;
|
||||||
|
|
||||||
MWWorld::Ptr::CellStore *current = mEnvironment.mWorld->getPlayer().getPlayer().getCell();
|
MWWorld::Ptr::CellStore *current = mEnvironment.mWorld->getPlayer().getPlayer().getCell();
|
||||||
|
|
||||||
|
|
||||||
//If the region has changed
|
//If the region has changed
|
||||||
if(!(current->cell->data.flags & current->cell->Interior) && timer.elapsed() >= 10){
|
if(!(current->cell->data.flags & current->cell->Interior) && timer.elapsed() >= 10){
|
||||||
timer.restart();
|
timer.restart();
|
||||||
|
@ -153,6 +152,9 @@ bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt)
|
||||||
{
|
{
|
||||||
mEnvironment.mFrameDuration = evt.timeSinceLastFrame;
|
mEnvironment.mFrameDuration = evt.timeSinceLastFrame;
|
||||||
|
|
||||||
|
//
|
||||||
|
mEnvironment.mWindowManager->onFrame(mEnvironment.mFrameDuration);
|
||||||
|
|
||||||
// global scripts
|
// global scripts
|
||||||
mEnvironment.mGlobalScripts->run (mEnvironment);
|
mEnvironment.mGlobalScripts->run (mEnvironment);
|
||||||
|
|
||||||
|
@ -246,8 +248,12 @@ void OMW::Engine::loadBSA()
|
||||||
for (Files::MultiDirCollection::TIter iter (bsa.begin()); iter!=bsa.end(); ++iter)
|
for (Files::MultiDirCollection::TIter iter (bsa.begin()); iter!=bsa.end(); ++iter)
|
||||||
{
|
{
|
||||||
std::cout << "Adding " << iter->second.string() << std::endl;
|
std::cout << "Adding " << iter->second.string() << std::endl;
|
||||||
addBSA (iter->second.string());
|
Bsa::addBSA (iter->second.string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string m = mDataDir.string();
|
||||||
|
std::cout << "Data dir" << m << "\n";
|
||||||
|
Bsa::addDir(m, mFSStrict);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add resources directory
|
// add resources directory
|
||||||
|
@ -338,15 +344,12 @@ void OMW::Engine::go()
|
||||||
ogreCfg.insert(0, cfgUserDir);
|
ogreCfg.insert(0, cfgUserDir);
|
||||||
|
|
||||||
//A local plugins.cfg will be used if it exist, otherwise look in the default path
|
//A local plugins.cfg will be used if it exist, otherwise look in the default path
|
||||||
if(!isFile(plugCfg.c_str()))
|
if(!Misc::isFile(plugCfg.c_str()))
|
||||||
{
|
{
|
||||||
plugCfg.insert(0, cfgDir);
|
plugCfg.insert(0, cfgDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
mOgre.configure(!isFile(ogreCfg.c_str()), cfgUserDir, plugCfg, false);
|
mOgre.configure(!Misc::isFile(ogreCfg.c_str()), cfgUserDir, plugCfg, false);
|
||||||
|
|
||||||
addResourcesDirectory (mDataDir / "Meshes");
|
|
||||||
addResourcesDirectory (mDataDir / "Textures");
|
|
||||||
|
|
||||||
// This has to be added BEFORE MyGUI is initialized, as it needs
|
// This has to be added BEFORE MyGUI is initialized, as it needs
|
||||||
// to find core.xml here.
|
// to find core.xml here.
|
||||||
|
@ -358,12 +361,12 @@ void OMW::Engine::go()
|
||||||
loadBSA();
|
loadBSA();
|
||||||
|
|
||||||
// Create physics. shapeLoader is deleted by the physic engine
|
// Create physics. shapeLoader is deleted by the physic engine
|
||||||
ManualBulletShapeLoader* shapeLoader = new ManualBulletShapeLoader();
|
NifBullet::ManualBulletShapeLoader* shapeLoader = new NifBullet::ManualBulletShapeLoader();
|
||||||
mPhysicEngine = new OEngine::Physic::PhysicEngine(shapeLoader);
|
mPhysicEngine = new OEngine::Physic::PhysicEngine(shapeLoader);
|
||||||
|
|
||||||
// Create the world
|
// Create the world
|
||||||
mEnvironment.mWorld = new MWWorld::World (mOgre, mPhysicEngine, mFileCollections, mMaster,
|
mEnvironment.mWorld = new MWWorld::World (mOgre, mPhysicEngine, mFileCollections, mMaster,
|
||||||
mResDir, mNewGame, mEnvironment);
|
mResDir, mNewGame, mEnvironment, mEncoding);
|
||||||
|
|
||||||
// Set up the GUI system
|
// Set up the GUI system
|
||||||
mGuiManager = new OEngine::GUI::MyGUIManager(mOgre.getWindow(), mOgre.getScene(), false, cfgDir);
|
mGuiManager = new OEngine::GUI::MyGUIManager(mOgre.getWindow(), mOgre.getScene(), false, cfgDir);
|
||||||
|
@ -384,7 +387,7 @@ void OMW::Engine::go()
|
||||||
mOgre.getCamera(),
|
mOgre.getCamera(),
|
||||||
mEnvironment.mWorld->getStore(),
|
mEnvironment.mWorld->getStore(),
|
||||||
(mDataDir),
|
(mDataDir),
|
||||||
mUseSound);
|
mUseSound, mFSStrict);
|
||||||
|
|
||||||
// Create script system
|
// Create script system
|
||||||
mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full,
|
mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full,
|
||||||
|
@ -512,3 +515,8 @@ void OMW::Engine::setCompileAll (bool all)
|
||||||
{
|
{
|
||||||
mCompileAll = all;
|
mCompileAll = all;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OMW::Engine::setEncoding(const std::string& encoding)
|
||||||
|
{
|
||||||
|
mEncoding = encoding;
|
||||||
|
}
|
|
@ -56,6 +56,7 @@ namespace OMW
|
||||||
|
|
||||||
class Engine : private Ogre::FrameListener
|
class Engine : private Ogre::FrameListener
|
||||||
{
|
{
|
||||||
|
std::string mEncoding;
|
||||||
boost::filesystem::path mDataDir;
|
boost::filesystem::path mDataDir;
|
||||||
boost::filesystem::path mResDir;
|
boost::filesystem::path mResDir;
|
||||||
OEngine::Render::OgreRenderer mOgre;
|
OEngine::Render::OgreRenderer mOgre;
|
||||||
|
@ -100,7 +101,7 @@ namespace OMW
|
||||||
|
|
||||||
void executeLocalScripts();
|
void executeLocalScripts();
|
||||||
|
|
||||||
virtual bool frameStarted(const Ogre::FrameEvent& evt);
|
virtual bool frameRenderingQueued (const Ogre::FrameEvent& evt);
|
||||||
|
|
||||||
/// Process pending commands
|
/// Process pending commands
|
||||||
|
|
||||||
|
@ -157,6 +158,9 @@ namespace OMW
|
||||||
|
|
||||||
/// Compile all scripts (excludign dialogue scripts) at startup?
|
/// Compile all scripts (excludign dialogue scripts) at startup?
|
||||||
void setCompileAll (bool all);
|
void setCompileAll (bool all);
|
||||||
|
|
||||||
|
/// Font encoding
|
||||||
|
void setEncoding(const std::string& encoding);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,13 @@
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// for Ogre::macBundlePath
|
||||||
|
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
|
||||||
|
#include <OSX/macUtils.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "config.hpp"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
/// Parse command line options and openmw.cfg file (if one exists). Results are directly
|
/// Parse command line options and openmw.cfg file (if one exists). Results are directly
|
||||||
|
@ -41,7 +48,8 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
|
||||||
"Syntax: openmw <options>\nAllowed options");
|
"Syntax: openmw <options>\nAllowed options");
|
||||||
|
|
||||||
desc.add_options()
|
desc.add_options()
|
||||||
("help", "print help message")
|
("help", "print help message and quit")
|
||||||
|
("version", "print version information and quit")
|
||||||
("data", bpo::value<std::vector<std::string> >()
|
("data", bpo::value<std::vector<std::string> >()
|
||||||
->default_value (std::vector<std::string>(), "data")
|
->default_value (std::vector<std::string>(), "data")
|
||||||
->multitoken(),
|
->multitoken(),
|
||||||
|
@ -77,6 +85,13 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
|
||||||
( "fs-strict", boost::program_options::value<bool>()->
|
( "fs-strict", boost::program_options::value<bool>()->
|
||||||
implicit_value (true)->default_value (false),
|
implicit_value (true)->default_value (false),
|
||||||
"strict file system handling (no case folding)")
|
"strict file system handling (no case folding)")
|
||||||
|
|
||||||
|
( "encoding", boost::program_options::value<std::string>()->
|
||||||
|
default_value("win1252"),
|
||||||
|
"Character encoding used in OpenMW game messages:\n"
|
||||||
|
"\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n"
|
||||||
|
"\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n"
|
||||||
|
"\n\twin1252 - Western European (Latin) alphabet, used by default")
|
||||||
;
|
;
|
||||||
|
|
||||||
bpo::variables_map variables;
|
bpo::variables_map variables;
|
||||||
|
@ -84,7 +99,7 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
|
||||||
//If there is an openmw.cfg in the current path use that as global config
|
//If there is an openmw.cfg in the current path use that as global config
|
||||||
//Otherwise try getPath
|
//Otherwise try getPath
|
||||||
std::string cfgFile = "openmw.cfg";
|
std::string cfgFile = "openmw.cfg";
|
||||||
if(!isFile(cfgFile.c_str()))
|
if(!Misc::isFile(cfgFile.c_str()))
|
||||||
{
|
{
|
||||||
cfgFile = Files::getPath (Files::Path_ConfigGlobal, "openmw", "openmw.cfg");
|
cfgFile = Files::getPath (Files::Path_ConfigGlobal, "openmw", "openmw.cfg");
|
||||||
}
|
}
|
||||||
|
@ -105,10 +120,39 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
|
||||||
if (globalConfigFile.is_open())
|
if (globalConfigFile.is_open())
|
||||||
bpo::store ( bpo::parse_config_file (globalConfigFile, desc), variables);
|
bpo::store ( bpo::parse_config_file (globalConfigFile, desc), variables);
|
||||||
|
|
||||||
|
bool run = true;
|
||||||
|
|
||||||
if (variables.count ("help"))
|
if (variables.count ("help"))
|
||||||
{
|
{
|
||||||
std::cout << desc << std::endl;
|
std::cout << desc << std::endl;
|
||||||
|
run = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variables.count ("version"))
|
||||||
|
{
|
||||||
|
std::cout << "OpenMW version " << OPENMW_VERSION << std::endl;
|
||||||
|
run = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!run)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// Font encoding settings
|
||||||
|
std::string encoding(variables["encoding"].as<std::string>());
|
||||||
|
if (encoding == "win1250")
|
||||||
|
{
|
||||||
|
std::cout << "Using Central and Eastern European font encoding." << std::endl;
|
||||||
|
engine.setEncoding(encoding);
|
||||||
|
}
|
||||||
|
else if (encoding == "win1251")
|
||||||
|
{
|
||||||
|
std::cout << "Using Cyrillic font encoding." << std::endl;
|
||||||
|
engine.setEncoding(encoding);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "Using default (English) font encoding." << std::endl;
|
||||||
|
engine.setEncoding("win1252");
|
||||||
}
|
}
|
||||||
|
|
||||||
// directory settings
|
// directory settings
|
||||||
|
@ -177,7 +221,7 @@ int main(int argc, char**argv)
|
||||||
{
|
{
|
||||||
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
|
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
|
||||||
// set current dir to bundle path
|
// set current dir to bundle path
|
||||||
boost::filesystem::path bundlePath = boost::filesystem::path(Ogre::macBundlePath());
|
boost::filesystem::path bundlePath = boost::filesystem::path(Ogre::macBundlePath()).parent_path();
|
||||||
boost::filesystem::current_path(bundlePath);
|
boost::filesystem::current_path(bundlePath);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ namespace MWClass
|
||||||
ItemLevList::registerSelf();
|
ItemLevList::registerSelf();
|
||||||
Light::registerSelf();
|
Light::registerSelf();
|
||||||
Lockpick::registerSelf();
|
Lockpick::registerSelf();
|
||||||
Misc::registerSelf();
|
Miscellaneous::registerSelf();
|
||||||
Probe::registerSelf();
|
Probe::registerSelf();
|
||||||
Repair::registerSelf();
|
Repair::registerSelf();
|
||||||
Static::registerSelf();
|
Static::registerSelf();
|
||||||
|
|
|
@ -34,7 +34,7 @@ namespace MWClass
|
||||||
if (!model.empty())
|
if (!model.empty())
|
||||||
{
|
{
|
||||||
MWRender::Rendering rendering (cellRender, ref->ref);
|
MWRender::Rendering rendering (cellRender, ref->ref);
|
||||||
cellRender.insertMesh ("meshes\\" + model);
|
cellRender.insertMesh("meshes\\" + model);
|
||||||
cellRender.insertActorPhysics();
|
cellRender.insertActorPhysics();
|
||||||
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
|
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,11 @@
|
||||||
|
|
||||||
namespace MWClass
|
namespace MWClass
|
||||||
{
|
{
|
||||||
void Misc::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
void Miscellaneous::insertObj (const MWWorld::Ptr& ptr, MWRender::CellRenderImp& cellRender,
|
||||||
MWWorld::Environment& environment) const
|
MWWorld::Environment& environment) const
|
||||||
{
|
{
|
||||||
ESMS::LiveCellRef<ESM::Misc, MWWorld::RefData> *ref =
|
ESMS::LiveCellRef<ESM::Miscellaneous, MWWorld::RefData> *ref =
|
||||||
ptr.get<ESM::Misc>();
|
ptr.get<ESM::Miscellaneous>();
|
||||||
|
|
||||||
assert (ref->base != NULL);
|
assert (ref->base != NULL);
|
||||||
const std::string &model = ref->base->model;
|
const std::string &model = ref->base->model;
|
||||||
|
@ -31,39 +31,39 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Misc::getName (const MWWorld::Ptr& ptr) const
|
std::string Miscellaneous::getName (const MWWorld::Ptr& ptr) const
|
||||||
{
|
{
|
||||||
ESMS::LiveCellRef<ESM::Misc, MWWorld::RefData> *ref =
|
ESMS::LiveCellRef<ESM::Miscellaneous, MWWorld::RefData> *ref =
|
||||||
ptr.get<ESM::Misc>();
|
ptr.get<ESM::Miscellaneous>();
|
||||||
|
|
||||||
return ref->base->name;
|
return ref->base->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::shared_ptr<MWWorld::Action> Misc::activate (const MWWorld::Ptr& ptr,
|
boost::shared_ptr<MWWorld::Action> Miscellaneous::activate (const MWWorld::Ptr& ptr,
|
||||||
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
const MWWorld::Ptr& actor, const MWWorld::Environment& environment) const
|
||||||
{
|
{
|
||||||
return boost::shared_ptr<MWWorld::Action> (
|
return boost::shared_ptr<MWWorld::Action> (
|
||||||
new MWWorld::ActionTake (ptr));
|
new MWWorld::ActionTake (ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Misc::insertIntoContainer (const MWWorld::Ptr& ptr,
|
void Miscellaneous::insertIntoContainer (const MWWorld::Ptr& ptr,
|
||||||
MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const
|
MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const
|
||||||
{
|
{
|
||||||
insertIntoContainerStore (ptr, containerStore.miscItems);
|
insertIntoContainerStore (ptr, containerStore.miscItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Misc::getScript (const MWWorld::Ptr& ptr) const
|
std::string Miscellaneous::getScript (const MWWorld::Ptr& ptr) const
|
||||||
{
|
{
|
||||||
ESMS::LiveCellRef<ESM::Misc, MWWorld::RefData> *ref =
|
ESMS::LiveCellRef<ESM::Miscellaneous, MWWorld::RefData> *ref =
|
||||||
ptr.get<ESM::Misc>();
|
ptr.get<ESM::Miscellaneous>();
|
||||||
|
|
||||||
return ref->base->script;
|
return ref->base->script;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Misc::registerSelf()
|
void Miscellaneous::registerSelf()
|
||||||
{
|
{
|
||||||
boost::shared_ptr<Class> instance (new Misc);
|
boost::shared_ptr<Class> instance (new Miscellaneous);
|
||||||
|
|
||||||
registerClass (typeid (ESM::Misc).name(), instance);
|
registerClass (typeid (ESM::Miscellaneous).name(), instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
namespace MWClass
|
namespace MWClass
|
||||||
{
|
{
|
||||||
class Misc : public MWWorld::Class
|
class Miscellaneous : public MWWorld::Class
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
@ -95,8 +95,7 @@ namespace MWClass
|
||||||
upperright[uppernumbers++] = npcName + "chest";
|
upperright[uppernumbers++] = npcName + "chest";
|
||||||
neckandup[neckNumbers++] = npcName + "chest";
|
neckandup[neckNumbers++] = npcName + "chest";
|
||||||
}
|
}
|
||||||
//std::cout << "GETTING NPC PART";
|
|
||||||
//Orgre::SceneNode test = cellRender.getNpcPart();
|
|
||||||
|
|
||||||
const ESM::BodyPart *upperleg = environment.mWorld->getStore().bodyParts.search (bodyRaceID + "upper leg");
|
const ESM::BodyPart *upperleg = environment.mWorld->getStore().bodyParts.search (bodyRaceID + "upper leg");
|
||||||
const ESM::BodyPart *groin = environment.mWorld->getStore().bodyParts.search (bodyRaceID + "groin");
|
const ESM::BodyPart *groin = environment.mWorld->getStore().bodyParts.search (bodyRaceID + "groin");
|
||||||
|
@ -113,8 +112,6 @@ namespace MWClass
|
||||||
const ESM::BodyPart *hands = environment.mWorld->getStore().bodyParts.search (bodyRaceID + "hands.1st");
|
const ESM::BodyPart *hands = environment.mWorld->getStore().bodyParts.search (bodyRaceID + "hands.1st");
|
||||||
|
|
||||||
|
|
||||||
//std::cout << "RACE" << bodyRaceID << "\n";
|
|
||||||
|
|
||||||
Ogre::Vector3 pos2 = Ogre::Vector3( 0, .5, 75);
|
Ogre::Vector3 pos2 = Ogre::Vector3( 0, .5, 75);
|
||||||
|
|
||||||
if (groin){
|
if (groin){
|
||||||
|
@ -124,10 +121,9 @@ namespace MWClass
|
||||||
}
|
}
|
||||||
if (tail) {
|
if (tail) {
|
||||||
cellRender.insertMesh("tail\\" + tail->model, Ogre::Vector3(0 , 0, -76), axis, kOgrePi, npcName + "tail", addresses, numbers, "tail");
|
cellRender.insertMesh("tail\\" + tail->model, Ogre::Vector3(0 , 0, -76), axis, kOgrePi, npcName + "tail", addresses, numbers, "tail");
|
||||||
//std::cout << "TAIL\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//addresses[1] = npcName + "groin";
|
|
||||||
if(upperleg){
|
if(upperleg){
|
||||||
cellRender.insertMesh ("meshes\\" + upperleg->model, Ogre::Vector3( 6, 0, -16), axis, kOgrePi, npcName + "upper leg", addresses, numbers); //-18
|
cellRender.insertMesh ("meshes\\" + upperleg->model, Ogre::Vector3( 6, 0, -16), axis, kOgrePi, npcName + "upper leg", addresses, numbers); //-18
|
||||||
cellRender.insertMesh ("meshes\\" + upperleg->model, Ogre::Vector3( -6, 0, -16), axis, Ogre::Radian(0), npcName + "upper leg2", addresses2, numbers);
|
cellRender.insertMesh ("meshes\\" + upperleg->model, Ogre::Vector3( -6, 0, -16), axis, Ogre::Radian(0), npcName + "upper leg2", addresses2, numbers);
|
||||||
|
@ -218,9 +214,6 @@ namespace MWClass
|
||||||
|
|
||||||
if(hand)
|
if(hand)
|
||||||
{
|
{
|
||||||
//std::cout << "WE FOUND A HAND\n";
|
|
||||||
//-50, 0, -120
|
|
||||||
//std::cout << "WE FOUND HANDS\n";
|
|
||||||
std::string pass;
|
std::string pass;
|
||||||
if(hand->model.compare("b\\B_N_Dark Elf_F_Hands.1st.NIF")==0 && bodyRaceID.compare("b_n_dark elf_m_") == 0)
|
if(hand->model.compare("b\\B_N_Dark Elf_F_Hands.1st.NIF")==0 && bodyRaceID.compare("b_n_dark elf_m_") == 0)
|
||||||
pass = "b\\B_N_Dark Elf_M_Hands.1st.NIF";
|
pass = "b\\B_N_Dark Elf_M_Hands.1st.NIF";
|
||||||
|
@ -230,7 +223,6 @@ namespace MWClass
|
||||||
cellRender.insertMesh("meshes\\" + pass, Ogre::Vector3(42, 1, -110), Ogre::Vector3(0, 0,0), kOgrePi, npcName + "hand2", upperright, uppernumbers, false); //0, 100, -100 0,0,120
|
cellRender.insertMesh("meshes\\" + pass, Ogre::Vector3(42, 1, -110), Ogre::Vector3(0, 0,0), kOgrePi, npcName + "hand2", upperright, uppernumbers, false); //0, 100, -100 0,0,120
|
||||||
upperleft[uppernumbers] = npcName + "hand";
|
upperleft[uppernumbers] = npcName + "hand";
|
||||||
upperright[uppernumbers++] = npcName + "hand2";
|
upperright[uppernumbers++] = npcName + "hand2";
|
||||||
//cellRender.rotateMesh(Ogre::Vector3(0, 0,0), kOgrePi, upperleft, uppernumbers);
|
|
||||||
cellRender.scaleMesh(Ogre::Vector3(1, -1, 1), upperleft, uppernumbers);
|
cellRender.scaleMesh(Ogre::Vector3(1, -1, 1), upperleft, uppernumbers);
|
||||||
cellRender.scaleMesh(Ogre::Vector3(1, -1, 1), upperright, uppernumbers);
|
cellRender.scaleMesh(Ogre::Vector3(1, -1, 1), upperright, uppernumbers);
|
||||||
}
|
}
|
||||||
|
@ -244,7 +236,6 @@ namespace MWClass
|
||||||
else
|
else
|
||||||
pass =hands->model; //-50, 0, -120
|
pass =hands->model; //-50, 0, -120
|
||||||
cellRender.insertMesh("meshes\\" + pass, Ogre::Vector3(42, 1,-110), Ogre::Vector3(0, 0, 0), kOgrePi, npcName + "hand", upperleft, uppernumbers, false); //0, 100, -100 42, 0, -110
|
cellRender.insertMesh("meshes\\" + pass, Ogre::Vector3(42, 1,-110), Ogre::Vector3(0, 0, 0), kOgrePi, npcName + "hand", upperleft, uppernumbers, false); //0, 100, -100 42, 0, -110
|
||||||
//cellRender.insertMesh("meshes\\" + hands->model, Ogre::Vector3(42, 0,110), Ogre::Vector3(1, 0, 0), kOgrePi, npcName + "hand", upperleft, uppernumbers, false); //0, 100, -100 42, 0, -110
|
|
||||||
cellRender.insertMesh("meshes\\" + pass, Ogre::Vector3(42, 1, -110), Ogre::Vector3(0, 0, 0), kOgrePi, npcName + "hand2", upperright, uppernumbers, false); //0, 100, -100 0,0,120
|
cellRender.insertMesh("meshes\\" + pass, Ogre::Vector3(42, 1, -110), Ogre::Vector3(0, 0, 0), kOgrePi, npcName + "hand2", upperright, uppernumbers, false); //0, 100, -100 0,0,120
|
||||||
upperleft[uppernumbers] = npcName + "hand";
|
upperleft[uppernumbers] = npcName + "hand";
|
||||||
upperright[uppernumbers++] = npcName + "hand2";
|
upperright[uppernumbers++] = npcName + "hand2";
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace MWClass
|
||||||
const std::string &model = ref->base->model;
|
const std::string &model = ref->base->model;
|
||||||
if (!model.empty())
|
if (!model.empty())
|
||||||
{
|
{
|
||||||
MWRender::Rendering rendering (cellRender, ref->ref);
|
MWRender::Rendering rendering (cellRender, ref->ref, true);
|
||||||
cellRender.insertMesh ("meshes\\" + model);
|
cellRender.insertMesh ("meshes\\" + model);
|
||||||
cellRender.insertObjectPhysics();
|
cellRender.insertObjectPhysics();
|
||||||
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
|
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
|
||||||
|
|
|
@ -171,6 +171,22 @@ namespace MWGui
|
||||||
MyGUI::KeyCode key,
|
MyGUI::KeyCode key,
|
||||||
MyGUI::Char _char)
|
MyGUI::Char _char)
|
||||||
{
|
{
|
||||||
|
if( key == MyGUI::KeyCode::Tab)
|
||||||
|
{
|
||||||
|
std::vector<std::string> matches;
|
||||||
|
listNames();
|
||||||
|
command->setCaption(complete( command->getCaption(), matches ));
|
||||||
|
#if 0
|
||||||
|
int i = 0;
|
||||||
|
for(std::vector<std::string>::iterator it=matches.begin(); it < matches.end(); it++,i++ )
|
||||||
|
{
|
||||||
|
printOK( *it );
|
||||||
|
if( i == 50 )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
if(command_history.empty()) return;
|
if(command_history.empty()) return;
|
||||||
|
|
||||||
// Traverse history with up and down arrows
|
// Traverse history with up and down arrows
|
||||||
|
@ -223,11 +239,11 @@ namespace MWGui
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ConsoleInterpreterContext interpreterContext (*this, mEnvironment, MWWorld::Ptr());
|
ConsoleInterpreterContext interpreterContext (*this, mEnvironment, MWWorld::Ptr());
|
||||||
Interpreter::Interpreter interpreter (interpreterContext);
|
Interpreter::Interpreter interpreter;
|
||||||
MWScript::installOpcodes (interpreter);
|
MWScript::installOpcodes (interpreter);
|
||||||
std::vector<Interpreter::Type_Code> code;
|
std::vector<Interpreter::Type_Code> code;
|
||||||
output.getCode (code);
|
output.getCode (code);
|
||||||
interpreter.run (&code[0], code.size());
|
interpreter.run (&code[0], code.size(), interpreterContext);
|
||||||
}
|
}
|
||||||
catch (const std::exception& error)
|
catch (const std::exception& error)
|
||||||
{
|
{
|
||||||
|
@ -237,4 +253,119 @@ namespace MWGui
|
||||||
|
|
||||||
command->setCaption("");
|
command->setCaption("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Console::complete( std::string input, std::vector<std::string> &matches )
|
||||||
|
{
|
||||||
|
using namespace std;
|
||||||
|
string output=input;
|
||||||
|
string tmp=input;
|
||||||
|
bool has_front_quote = false;
|
||||||
|
|
||||||
|
/* Does the input string contain things that don't have to be completed? If yes erase them. */
|
||||||
|
/* Are there quotation marks? */
|
||||||
|
if( tmp.find('"') != string::npos ) {
|
||||||
|
int numquotes=0;
|
||||||
|
for(string::iterator it=tmp.begin(); it < tmp.end(); it++) {
|
||||||
|
if( *it == '"' )
|
||||||
|
numquotes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Is it terminated?*/
|
||||||
|
if( numquotes % 2 ) {
|
||||||
|
tmp.erase( 0, tmp.rfind('"')+1 );
|
||||||
|
has_front_quote = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
size_t pos;
|
||||||
|
if( ( ((pos = tmp.rfind(' ')) != string::npos ) ) && ( pos > tmp.rfind('"') ) ) {
|
||||||
|
tmp.erase( 0, tmp.rfind(' ')+1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tmp.clear();
|
||||||
|
}
|
||||||
|
has_front_quote = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* No quotation marks. Are there spaces?*/
|
||||||
|
else {
|
||||||
|
size_t rpos;
|
||||||
|
if( (rpos=tmp.rfind(' ')) != string::npos ) {
|
||||||
|
if( rpos == 0 ) {
|
||||||
|
tmp.clear();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tmp.erase(0, rpos+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Erase the input from the output string so we can easily append the completed form later. */
|
||||||
|
output.erase(output.end()-tmp.length(), output.end());
|
||||||
|
|
||||||
|
/* Is there still something in the input string? If not just display all commands and return the unchanged input. */
|
||||||
|
if( tmp.length() == 0 ) {
|
||||||
|
matches=mNames;
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Iterate through the vector. */
|
||||||
|
for(vector<string>::iterator it=mNames.begin(); it < mNames.end();it++) {
|
||||||
|
bool string_different=false;
|
||||||
|
|
||||||
|
/* Is the string shorter than the input string? If yes skip it. */
|
||||||
|
if( (*it).length() < tmp.length() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Is the beginning of the string different from the input string? If yes skip it. */
|
||||||
|
for( string::iterator iter=tmp.begin(), iter2=(*it).begin(); iter < tmp.end();iter++, iter2++) {
|
||||||
|
if( tolower(*iter) != tolower(*iter2) ) {
|
||||||
|
string_different=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( string_different )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* The beginning of the string matches the input string, save it for the next test. */
|
||||||
|
matches.push_back(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* There are no matches. Return the unchanged input. */
|
||||||
|
if( matches.empty() )
|
||||||
|
{
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only one match. We're done. */
|
||||||
|
if( matches.size() == 1 ) {
|
||||||
|
/* Adding quotation marks when the input string started with a quotation mark or has spaces in it*/
|
||||||
|
if( ( matches.front().find(' ') != string::npos ) ) {
|
||||||
|
if( !has_front_quote )
|
||||||
|
output.append(string("\""));
|
||||||
|
return output.append(matches.front() + string("\" "));
|
||||||
|
}
|
||||||
|
else if( has_front_quote ) {
|
||||||
|
return output.append(matches.front() + string("\" "));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return output.append(matches.front() + string(" "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if all matching strings match further than input. If yes complete to this match. */
|
||||||
|
int i = tmp.length();
|
||||||
|
|
||||||
|
for(string::iterator iter=matches.front().begin()+tmp.length(); iter < matches.front().end(); iter++, i++) {
|
||||||
|
for(vector<string>::iterator it=matches.begin(); it < matches.end();it++) {
|
||||||
|
if( tolower((*it)[i]) != tolower(*iter) ) {
|
||||||
|
/* Append the longest match to the end of the output string*/
|
||||||
|
output.append(matches.front().substr( 0, i));
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* All keywords match with the shortest. Append it to the output string and return it. */
|
||||||
|
return output.append(matches.front());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,8 @@ namespace MWGui
|
||||||
MyGUI::Char _char);
|
MyGUI::Char _char);
|
||||||
|
|
||||||
void acceptCommand(MyGUI::EditPtr _sender);
|
void acceptCommand(MyGUI::EditPtr _sender);
|
||||||
|
|
||||||
|
std::string complete( std::string input, std::vector<std::string> &matches );
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
394
apps/openmw/mwgui/messagebox.cpp
Normal file
|
@ -0,0 +1,394 @@
|
||||||
|
#include "messagebox.hpp"
|
||||||
|
|
||||||
|
using namespace MWGui;
|
||||||
|
|
||||||
|
MessageBoxManager::MessageBoxManager (WindowManager *windowManager)
|
||||||
|
{
|
||||||
|
mWindowManager = windowManager;
|
||||||
|
// defines
|
||||||
|
mMessageBoxSpeed = 0.1;
|
||||||
|
mInterMessageBoxe = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBoxManager::onFrame (float frameDuration)
|
||||||
|
{
|
||||||
|
std::vector<MessageBoxManagerTimer>::iterator it;
|
||||||
|
for(it = mTimers.begin(); it != mTimers.end();)
|
||||||
|
{
|
||||||
|
it->current += frameDuration;
|
||||||
|
if(it->current >= it->max)
|
||||||
|
{
|
||||||
|
it->messageBox->mMarkedToDelete = true;
|
||||||
|
|
||||||
|
if(*mMessageBoxes.begin() == it->messageBox) // if this box is the last one
|
||||||
|
{
|
||||||
|
// collect all with mMarkedToDelete and delete them.
|
||||||
|
// and place the other messageboxes on the right position
|
||||||
|
int height = 0;
|
||||||
|
std::vector<MessageBox*>::iterator it2 = mMessageBoxes.begin();
|
||||||
|
while(it2 != mMessageBoxes.end())
|
||||||
|
{
|
||||||
|
if((*it2)->mMarkedToDelete)
|
||||||
|
{
|
||||||
|
delete (*it2);
|
||||||
|
it2 = mMessageBoxes.erase(it2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(*it2)->update(height);
|
||||||
|
height += (*it2)->getHeight();
|
||||||
|
it2++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it = mTimers.erase(it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mInterMessageBoxe != NULL && mInterMessageBoxe->mMarkedToDelete) {
|
||||||
|
delete mInterMessageBoxe;
|
||||||
|
mInterMessageBoxe = NULL;
|
||||||
|
mWindowManager->setNextMode(GM_Game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBoxManager::createMessageBox (const std::string& message)
|
||||||
|
{
|
||||||
|
std::cout << "MessageBox: " << message << std::endl;
|
||||||
|
|
||||||
|
MessageBox *box = new MessageBox(*this, message);
|
||||||
|
|
||||||
|
removeMessageBox(message.length()*mMessageBoxSpeed, box);
|
||||||
|
|
||||||
|
mMessageBoxes.push_back(box);
|
||||||
|
std::vector<MessageBox*>::iterator it;
|
||||||
|
|
||||||
|
if(mMessageBoxes.size() > 3) {
|
||||||
|
delete *mMessageBoxes.begin();
|
||||||
|
mMessageBoxes.erase(mMessageBoxes.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
int height = 0;
|
||||||
|
for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it)
|
||||||
|
{
|
||||||
|
(*it)->update(height);
|
||||||
|
height += (*it)->getHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MessageBoxManager::createInteractiveMessageBox (const std::string& message, const std::vector<std::string>& buttons)
|
||||||
|
{
|
||||||
|
if(mInterMessageBoxe != NULL) {
|
||||||
|
std::cout << "there is a MessageBox already" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::cout << "interactive MessageBox: " << message << " - ";
|
||||||
|
std::copy (buttons.begin(), buttons.end(), std::ostream_iterator<std::string> (std::cout, ", "));
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
mInterMessageBoxe = new InteractiveMessageBox(*this, message, buttons);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MessageBoxManager::isInteractiveMessageBox ()
|
||||||
|
{
|
||||||
|
return mInterMessageBoxe != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBoxManager::removeMessageBox (float time, MessageBox *msgbox)
|
||||||
|
{
|
||||||
|
MessageBoxManagerTimer timer;
|
||||||
|
timer.current = 0;
|
||||||
|
timer.max = time;
|
||||||
|
timer.messageBox = msgbox;
|
||||||
|
|
||||||
|
mTimers.insert(mTimers.end(), timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MessageBoxManager::removeMessageBox (MessageBox *msgbox)
|
||||||
|
{
|
||||||
|
std::vector<MessageBox*>::iterator it;
|
||||||
|
for(it = mMessageBoxes.begin(); it != mMessageBoxes.end(); ++it)
|
||||||
|
{
|
||||||
|
if((*it) == msgbox)
|
||||||
|
{
|
||||||
|
delete (*it);
|
||||||
|
mMessageBoxes.erase(it);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBoxManager::setMessageBoxSpeed (int speed)
|
||||||
|
{
|
||||||
|
mMessageBoxSpeed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int MessageBoxManager::readPressedButton ()
|
||||||
|
{
|
||||||
|
if(mInterMessageBoxe != NULL)
|
||||||
|
{
|
||||||
|
return mInterMessageBoxe->readPressedButton();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MessageBox::MessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message)
|
||||||
|
: Layout("openmw_messagebox_layout.xml")
|
||||||
|
, mMessageBoxManager(parMessageBoxManager)
|
||||||
|
, cMessage(message)
|
||||||
|
{
|
||||||
|
// defines
|
||||||
|
mFixedWidth = 300;
|
||||||
|
mBottomPadding = 20;
|
||||||
|
mNextBoxPadding = 20;
|
||||||
|
mMarkedToDelete = false;
|
||||||
|
|
||||||
|
getWidget(mMessageWidget, "message");
|
||||||
|
|
||||||
|
mMessageWidget->setOverflowToTheLeft(true);
|
||||||
|
mMessageWidget->addText(cMessage);
|
||||||
|
|
||||||
|
MyGUI::IntSize size;
|
||||||
|
size.width = mFixedWidth;
|
||||||
|
size.height = 100; // dummy
|
||||||
|
|
||||||
|
MyGUI::IntCoord coord;
|
||||||
|
coord.left = 10; // dummy
|
||||||
|
coord.top = 10; // dummy
|
||||||
|
|
||||||
|
mMessageWidget->setSize(size);
|
||||||
|
|
||||||
|
MyGUI::IntSize textSize = mMessageWidget->_getTextSize();
|
||||||
|
size.height = mHeight = textSize.height + 20; // this is the padding between the text and the box
|
||||||
|
|
||||||
|
mMainWidget->setSize(size);
|
||||||
|
size.width -= 15; // this is to center the text (see messagebox_layout.xml, Widget type="Edit" position="-2 -3 0 0")
|
||||||
|
mMessageWidget->setSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MessageBox::update (int height)
|
||||||
|
{
|
||||||
|
MyGUI::IntSize gameWindowSize = mMessageBoxManager.mWindowManager->getGui()->getViewSize();
|
||||||
|
MyGUI::IntCoord coord;
|
||||||
|
coord.left = (gameWindowSize.width - mFixedWidth)/2;
|
||||||
|
coord.top = (gameWindowSize.height - mHeight - height - mBottomPadding);
|
||||||
|
|
||||||
|
MyGUI::IntSize size;
|
||||||
|
size.width = mFixedWidth;
|
||||||
|
size.height = mHeight;
|
||||||
|
|
||||||
|
mMainWidget->setCoord(coord);
|
||||||
|
mMainWidget->setSize(size);
|
||||||
|
mMainWidget->setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
int MessageBox::getHeight ()
|
||||||
|
{
|
||||||
|
return mHeight+mNextBoxPadding; // 20 is the padding between this and the next MessageBox
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
InteractiveMessageBox::InteractiveMessageBox(MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector<std::string>& buttons)
|
||||||
|
: Layout("openmw_interactive_messagebox_layout.xml")
|
||||||
|
, mMessageBoxManager(parMessageBoxManager)
|
||||||
|
, mButtonPressed(-1)
|
||||||
|
{
|
||||||
|
int fixedWidth = 500;
|
||||||
|
int textPadding = 10; // padding between text-widget and main-widget
|
||||||
|
int textButtonPadding = 20; // padding between the text-widget und the button-widget
|
||||||
|
int buttonLeftPadding = 10; // padding between the buttons if horizontal
|
||||||
|
int buttonTopPadding = 5; // ^-- if vertical
|
||||||
|
int buttonPadding = 5; // padding between button label and button itself
|
||||||
|
int buttonMainPadding = 10; // padding between buttons and bottom of the main widget
|
||||||
|
|
||||||
|
mMarkedToDelete = false;
|
||||||
|
|
||||||
|
|
||||||
|
getWidget(mMessageWidget, "message");
|
||||||
|
getWidget(mButtonsWidget, "buttons");
|
||||||
|
|
||||||
|
mMessageWidget->setOverflowToTheLeft(true);
|
||||||
|
mMessageWidget->addText(message);
|
||||||
|
|
||||||
|
MyGUI::IntSize textSize = mMessageWidget->_getTextSize();
|
||||||
|
|
||||||
|
MyGUI::IntSize gameWindowSize = mMessageBoxManager.mWindowManager->getGui()->getViewSize();
|
||||||
|
|
||||||
|
int biggestButtonWidth = 0;
|
||||||
|
int buttonWidth = 0;
|
||||||
|
int buttonsWidth = 0;
|
||||||
|
int buttonHeight = 0;
|
||||||
|
MyGUI::IntCoord dummyCoord(0, 0, 0, 0);
|
||||||
|
|
||||||
|
std::vector<std::string>::const_iterator it;
|
||||||
|
for(it = buttons.begin(); it != buttons.end(); ++it)
|
||||||
|
{
|
||||||
|
MyGUI::ButtonPtr button = mButtonsWidget->createWidget<MyGUI::Button>(
|
||||||
|
MyGUI::WidgetStyle::Child,
|
||||||
|
std::string("MW_Button"),
|
||||||
|
dummyCoord,
|
||||||
|
MyGUI::Align::Default);
|
||||||
|
button->setCaption(*it);
|
||||||
|
|
||||||
|
button->eventMouseButtonClick = MyGUI::newDelegate(this, &InteractiveMessageBox::mousePressed);
|
||||||
|
|
||||||
|
mButtons.push_back(button);
|
||||||
|
|
||||||
|
buttonWidth = button->_getTextSize().width + 2*buttonPadding + buttonLeftPadding;
|
||||||
|
buttonsWidth += buttonWidth;
|
||||||
|
buttonHeight = button->_getTextSize().height + 2*buttonPadding + buttonTopPadding;
|
||||||
|
|
||||||
|
if(buttonWidth > biggestButtonWidth)
|
||||||
|
{
|
||||||
|
biggestButtonWidth = buttonWidth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buttonsWidth += buttonLeftPadding;
|
||||||
|
|
||||||
|
MyGUI::IntSize mainWidgetSize;
|
||||||
|
if(buttonsWidth < fixedWidth)
|
||||||
|
{
|
||||||
|
// on one line
|
||||||
|
std::cout << "on one line" << std::endl;
|
||||||
|
|
||||||
|
if(textSize.width + 2*textPadding < buttonsWidth)
|
||||||
|
{
|
||||||
|
std::cout << "width = buttonsWidth" << std::endl;
|
||||||
|
mainWidgetSize.width = buttonsWidth;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mainWidgetSize.width = textSize.width + 3*textPadding;
|
||||||
|
}
|
||||||
|
mainWidgetSize.height = textSize.height + textButtonPadding + buttonHeight + buttonMainPadding;
|
||||||
|
|
||||||
|
MyGUI::IntCoord absCoord;
|
||||||
|
absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2;
|
||||||
|
absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2;
|
||||||
|
|
||||||
|
std::cout << "width " << mainWidgetSize.width << " height " << mainWidgetSize.height << std::endl;
|
||||||
|
std::cout << "left " << absCoord.left << " top " << absCoord.top << std::endl;
|
||||||
|
|
||||||
|
mMainWidget->setCoord(absCoord);
|
||||||
|
mMainWidget->setSize(mainWidgetSize);
|
||||||
|
|
||||||
|
|
||||||
|
MyGUI::IntCoord messageWidgetCoord;
|
||||||
|
messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2;
|
||||||
|
messageWidgetCoord.top = textPadding;
|
||||||
|
mMessageWidget->setCoord(messageWidgetCoord);
|
||||||
|
|
||||||
|
mMessageWidget->setSize(textSize);
|
||||||
|
|
||||||
|
MyGUI::IntCoord buttonCord;
|
||||||
|
MyGUI::IntSize buttonSize(0, buttonHeight);
|
||||||
|
int left = (mainWidgetSize.width - buttonsWidth)/2 + buttonPadding;
|
||||||
|
|
||||||
|
std::vector<MyGUI::ButtonPtr>::const_iterator button;
|
||||||
|
for(button = mButtons.begin(); button != mButtons.end(); ++button)
|
||||||
|
{
|
||||||
|
buttonCord.left = left;
|
||||||
|
buttonCord.top = textSize.height + textButtonPadding;
|
||||||
|
|
||||||
|
buttonSize.width = (*button)->_getTextSize().width + 2*buttonPadding;
|
||||||
|
buttonSize.height = (*button)->_getTextSize().height + 2*buttonPadding;
|
||||||
|
|
||||||
|
(*button)->setCoord(buttonCord);
|
||||||
|
(*button)->setSize(buttonSize);
|
||||||
|
|
||||||
|
left += buttonSize.width + buttonLeftPadding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// among each other
|
||||||
|
|
||||||
|
if(biggestButtonWidth > textSize.width) {
|
||||||
|
mainWidgetSize.width = biggestButtonWidth + buttonTopPadding;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mainWidgetSize.width = textSize.width + 3*textPadding;
|
||||||
|
}
|
||||||
|
mainWidgetSize.height = textSize.height + 2*textPadding + textButtonPadding + buttonHeight * buttons.size() + buttonMainPadding;
|
||||||
|
|
||||||
|
std::cout << "biggestButtonWidth " << biggestButtonWidth << " textSize.width " << textSize.width << std::endl;
|
||||||
|
std::cout << "width " << mainWidgetSize.width << " height " << mainWidgetSize.height << std::endl;
|
||||||
|
mMainWidget->setSize(mainWidgetSize);
|
||||||
|
|
||||||
|
MyGUI::IntCoord absCoord;
|
||||||
|
absCoord.left = (gameWindowSize.width - mainWidgetSize.width)/2;
|
||||||
|
absCoord.top = (gameWindowSize.height - mainWidgetSize.height)/2;
|
||||||
|
|
||||||
|
mMainWidget->setCoord(absCoord);
|
||||||
|
mMainWidget->setSize(mainWidgetSize);
|
||||||
|
|
||||||
|
|
||||||
|
MyGUI::IntCoord messageWidgetCoord;
|
||||||
|
messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2;
|
||||||
|
messageWidgetCoord.top = textPadding;
|
||||||
|
mMessageWidget->setCoord(messageWidgetCoord);
|
||||||
|
|
||||||
|
mMessageWidget->setSize(textSize);
|
||||||
|
|
||||||
|
MyGUI::IntCoord buttonCord;
|
||||||
|
MyGUI::IntSize buttonSize(0, buttonHeight);
|
||||||
|
|
||||||
|
int top = textButtonPadding + buttonTopPadding + textSize.height;
|
||||||
|
|
||||||
|
std::vector<MyGUI::ButtonPtr>::const_iterator button;
|
||||||
|
for(button = mButtons.begin(); button != mButtons.end(); ++button)
|
||||||
|
{
|
||||||
|
buttonSize.width = (*button)->_getTextSize().width + buttonPadding*2;
|
||||||
|
buttonSize.height = (*button)->_getTextSize().height + buttonPadding*2;
|
||||||
|
|
||||||
|
buttonCord.top = top;
|
||||||
|
buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2 - 5; // FIXME: -5 is not so nice :/
|
||||||
|
|
||||||
|
(*button)->setCoord(buttonCord);
|
||||||
|
(*button)->setSize(buttonSize);
|
||||||
|
|
||||||
|
top += buttonSize.height + 2*buttonTopPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InteractiveMessageBox::mousePressed (MyGUI::Widget* pressed)
|
||||||
|
{
|
||||||
|
mMarkedToDelete = true;
|
||||||
|
int index = 0;
|
||||||
|
std::vector<MyGUI::ButtonPtr>::const_iterator button;
|
||||||
|
for(button = mButtons.begin(); button != mButtons.end(); ++button)
|
||||||
|
{
|
||||||
|
if(*button == pressed)
|
||||||
|
{
|
||||||
|
mButtonPressed = index;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
std::cout << "Cant be possible :/" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int InteractiveMessageBox::readPressedButton ()
|
||||||
|
{
|
||||||
|
int pressed = mButtonPressed;
|
||||||
|
mButtonPressed = -1;
|
||||||
|
return pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
89
apps/openmw/mwgui/messagebox.hpp
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
#ifndef MWGUI_MESSAGE_BOX_H
|
||||||
|
#define MWGUI_MESSAGE_BOX_H
|
||||||
|
|
||||||
|
#include <openengine/gui/layout.hpp>
|
||||||
|
#include <MyGUI.h>
|
||||||
|
|
||||||
|
#include "window_base.hpp"
|
||||||
|
#include "window_manager.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
namespace MWGui
|
||||||
|
{
|
||||||
|
|
||||||
|
class InteractiveMessageBox;
|
||||||
|
class MessageBoxManager;
|
||||||
|
class MessageBox;
|
||||||
|
|
||||||
|
struct MessageBoxManagerTimer {
|
||||||
|
float current;
|
||||||
|
float max;
|
||||||
|
MessageBox *messageBox;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MessageBoxManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MessageBoxManager (WindowManager* windowManager);
|
||||||
|
void onFrame (float frameDuration);
|
||||||
|
void createMessageBox (const std::string& message);
|
||||||
|
bool createInteractiveMessageBox (const std::string& message, const std::vector<std::string>& buttons);
|
||||||
|
bool isInteractiveMessageBox ();
|
||||||
|
|
||||||
|
void removeMessageBox (float time, MessageBox *msgbox);
|
||||||
|
bool removeMessageBox (MessageBox *msgbox);
|
||||||
|
void setMessageBoxSpeed (int speed);
|
||||||
|
|
||||||
|
int readPressedButton ();
|
||||||
|
|
||||||
|
WindowManager *mWindowManager;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<MessageBox*> mMessageBoxes;
|
||||||
|
InteractiveMessageBox* mInterMessageBoxe;
|
||||||
|
std::vector<MessageBoxManagerTimer> mTimers;
|
||||||
|
float mMessageBoxSpeed;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MessageBox : public OEngine::GUI::Layout
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message);
|
||||||
|
void setMessage (const std::string& message);
|
||||||
|
int getHeight ();
|
||||||
|
void update (int height);
|
||||||
|
|
||||||
|
bool mMarkedToDelete;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
MessageBoxManager& mMessageBoxManager;
|
||||||
|
int mHeight;
|
||||||
|
const std::string& cMessage;
|
||||||
|
MyGUI::EditPtr mMessageWidget;
|
||||||
|
int mFixedWidth;
|
||||||
|
int mBottomPadding;
|
||||||
|
int mNextBoxPadding;
|
||||||
|
};
|
||||||
|
|
||||||
|
class InteractiveMessageBox : public OEngine::GUI::Layout
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector<std::string>& buttons);
|
||||||
|
void mousePressed (MyGUI::Widget* _widget);
|
||||||
|
int readPressedButton ();
|
||||||
|
|
||||||
|
bool mMarkedToDelete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
MessageBoxManager& mMessageBoxManager;
|
||||||
|
MyGUI::EditPtr mMessageWidget;
|
||||||
|
MyGUI::WidgetPtr mButtonsWidget;
|
||||||
|
std::vector<MyGUI::ButtonPtr> mButtons;
|
||||||
|
|
||||||
|
int mTextButtonPadding;
|
||||||
|
int mButtonPressed;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -26,7 +26,10 @@ namespace MWGui
|
||||||
GM_ClassGenerate,
|
GM_ClassGenerate,
|
||||||
GM_ClassPick,
|
GM_ClassPick,
|
||||||
GM_ClassCreate,
|
GM_ClassCreate,
|
||||||
GM_Review
|
GM_Review,
|
||||||
|
|
||||||
|
// interactive MessageBox
|
||||||
|
GM_InterMessageBox
|
||||||
};
|
};
|
||||||
|
|
||||||
// Windows shown in inventory mode
|
// Windows shown in inventory mode
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "dialogue.hpp"
|
#include "dialogue.hpp"
|
||||||
#include "dialogue_history.hpp"
|
#include "dialogue_history.hpp"
|
||||||
#include "stats_window.hpp"
|
#include "stats_window.hpp"
|
||||||
|
#include "messagebox.hpp"
|
||||||
|
|
||||||
#include "../mwmechanics/mechanicsmanager.hpp"
|
#include "../mwmechanics/mechanicsmanager.hpp"
|
||||||
#include "../mwinput/inputmanager.hpp"
|
#include "../mwinput/inputmanager.hpp"
|
||||||
|
@ -60,6 +61,7 @@ WindowManager::WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment
|
||||||
inventory = new InventoryWindow ();
|
inventory = new InventoryWindow ();
|
||||||
#endif
|
#endif
|
||||||
console = new Console(w,h, environment, extensions);
|
console = new Console(w,h, environment, extensions);
|
||||||
|
mMessageBoxManager = new MessageBoxManager(this);
|
||||||
|
|
||||||
// The HUD is always on
|
// The HUD is always on
|
||||||
hud->setVisible(true);
|
hud->setVisible(true);
|
||||||
|
@ -82,6 +84,7 @@ WindowManager::WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment
|
||||||
WindowManager::~WindowManager()
|
WindowManager::~WindowManager()
|
||||||
{
|
{
|
||||||
delete console;
|
delete console;
|
||||||
|
delete mMessageBoxManager;
|
||||||
delete hud;
|
delete hud;
|
||||||
delete map;
|
delete map;
|
||||||
delete menu;
|
delete menu;
|
||||||
|
@ -326,6 +329,14 @@ void WindowManager::updateVisible()
|
||||||
dialogueWindow->open();
|
dialogueWindow->open();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(mode == GM_InterMessageBox)
|
||||||
|
{
|
||||||
|
if(!mMessageBoxManager->isInteractiveMessageBox()) {
|
||||||
|
setGuiMode(GM_Game);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Unsupported mode, switch back to game
|
// Unsupported mode, switch back to game
|
||||||
|
@ -446,14 +457,20 @@ void WindowManager::removeDialog(OEngine::GUI::Layout*dialog)
|
||||||
|
|
||||||
void WindowManager::messageBox (const std::string& message, const std::vector<std::string>& buttons)
|
void WindowManager::messageBox (const std::string& message, const std::vector<std::string>& buttons)
|
||||||
{
|
{
|
||||||
std::cout << "message box: " << message << std::endl;
|
if (buttons.empty())
|
||||||
|
|
||||||
if (!buttons.empty())
|
|
||||||
{
|
{
|
||||||
std::cout << "buttons: ";
|
mMessageBoxManager->createMessageBox(message);
|
||||||
std::copy (buttons.begin(), buttons.end(), std::ostream_iterator<std::string> (std::cout, ", "));
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mMessageBoxManager->createInteractiveMessageBox(message, buttons);
|
||||||
|
setGuiMode(GM_InterMessageBox);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int WindowManager::readPressedButton ()
|
||||||
|
{
|
||||||
|
return mMessageBoxManager->readPressedButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &WindowManager::getGameSettingString(const std::string &id, const std::string &default_)
|
const std::string &WindowManager::getGameSettingString(const std::string &id, const std::string &default_)
|
||||||
|
@ -555,6 +572,11 @@ void WindowManager::onClassChoice(int _index)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WindowManager::onFrame (float frameDuration)
|
||||||
|
{
|
||||||
|
mMessageBoxManager->onFrame(frameDuration);
|
||||||
|
}
|
||||||
|
|
||||||
namespace MWGui
|
namespace MWGui
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ namespace MWGui
|
||||||
class CreateClassDialog;
|
class CreateClassDialog;
|
||||||
class BirthDialog;
|
class BirthDialog;
|
||||||
class ReviewDialog;
|
class ReviewDialog;
|
||||||
|
class MessageBoxManager;
|
||||||
|
|
||||||
struct ClassPoint
|
struct ClassPoint
|
||||||
{
|
{
|
||||||
|
@ -84,6 +85,7 @@ namespace MWGui
|
||||||
MapWindow *map;
|
MapWindow *map;
|
||||||
MainMenu *menu;
|
MainMenu *menu;
|
||||||
StatsWindow *stats;
|
StatsWindow *stats;
|
||||||
|
MessageBoxManager *mMessageBoxManager;
|
||||||
#if 0
|
#if 0
|
||||||
InventoryWindow *inventory;
|
InventoryWindow *inventory;
|
||||||
#endif
|
#endif
|
||||||
|
@ -245,8 +247,13 @@ namespace MWGui
|
||||||
|
|
||||||
void removeDialog(OEngine::GUI::Layout* dialog);
|
void removeDialog(OEngine::GUI::Layout* dialog);
|
||||||
///< Hides dialog and schedules dialog to be deleted.
|
///< Hides dialog and schedules dialog to be deleted.
|
||||||
|
|
||||||
void messageBox (const std::string& message, const std::vector<std::string>& buttons);
|
void messageBox (const std::string& message, const std::vector<std::string>& buttons);
|
||||||
|
|
||||||
|
int readPressedButton ();
|
||||||
|
///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)
|
||||||
|
|
||||||
|
void onFrame (float frameDuration);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches a GMST string from the store, if there is no setting with the given
|
* Fetches a GMST string from the store, if there is no setting with the given
|
||||||
|
|
|
@ -258,7 +258,7 @@ namespace MWInput
|
||||||
}
|
}
|
||||||
|
|
||||||
//NOTE: Used to check for movement keys
|
//NOTE: Used to check for movement keys
|
||||||
bool frameStarted(const Ogre::FrameEvent &evt)
|
bool frameRenderingQueued (const Ogre::FrameEvent &evt)
|
||||||
{
|
{
|
||||||
// Tell OIS to handle all input events
|
// Tell OIS to handle all input events
|
||||||
input.capture();
|
input.capture();
|
||||||
|
|
|
@ -35,7 +35,7 @@ namespace MWRender
|
||||||
virtual ~CellRenderImp() {}
|
virtual ~CellRenderImp() {}
|
||||||
|
|
||||||
/// start inserting a new reference.
|
/// start inserting a new reference.
|
||||||
virtual void insertBegin (ESM::CellRef &ref) = 0;
|
virtual void insertBegin (ESM::CellRef &ref, bool static_ = false) = 0;
|
||||||
|
|
||||||
virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements) = 0;
|
virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements) = 0;
|
||||||
/// insert a mesh related to the most recent insertBegin call.
|
/// insert a mesh related to the most recent insertBegin call.
|
||||||
|
@ -71,10 +71,10 @@ namespace MWRender
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Rendering (CellRenderImp& cellRender, ESM::CellRef &ref)
|
Rendering (CellRenderImp& cellRender, ESM::CellRef &ref, bool static_ = false)
|
||||||
: mCellRender (cellRender), mEnd (false)
|
: mCellRender (cellRender), mEnd (false)
|
||||||
{
|
{
|
||||||
mCellRender.insertBegin (ref);
|
mCellRender.insertBegin (ref, static_);
|
||||||
}
|
}
|
||||||
|
|
||||||
~Rendering()
|
~Rendering()
|
||||||
|
|
|
@ -31,12 +31,24 @@ float ExteriorCellRender::lightQuadraticRadiusMult = 1;
|
||||||
|
|
||||||
bool ExteriorCellRender::lightOutQuadInLin = false;
|
bool ExteriorCellRender::lightOutQuadInLin = false;
|
||||||
|
|
||||||
// start inserting a new reference.
|
int ExteriorCellRender::uniqueID = 0;
|
||||||
|
|
||||||
void ExteriorCellRender::insertBegin (ESM::CellRef &ref)
|
ExteriorCellRender::ExteriorCellRender(ESMS::CellStore<MWWorld::RefData> &_cell, MWWorld::Environment& environment,
|
||||||
|
MWScene &_scene)
|
||||||
|
: mCell(_cell), mEnvironment (environment), mScene(_scene), mBase(NULL), mInsert(NULL), mAmbientMode (0)
|
||||||
|
{
|
||||||
|
uniqueID = uniqueID +1;
|
||||||
|
sg = mScene.getMgr()->createStaticGeometry( "sg" + Ogre::StringConverter::toString(uniqueID));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void ExteriorCellRender::insertBegin (ESM::CellRef &ref, bool static_)
|
||||||
{
|
{
|
||||||
assert (!mInsert);
|
assert (!mInsert);
|
||||||
|
|
||||||
|
isStatic = static_;
|
||||||
|
|
||||||
// Create and place scene node for this object
|
// Create and place scene node for this object
|
||||||
mInsert = mBase->createChildSceneNode();
|
mInsert = mBase->createChildSceneNode();
|
||||||
|
|
||||||
|
@ -59,7 +71,7 @@ void ExteriorCellRender::insertBegin (ESM::CellRef &ref)
|
||||||
// Rotates first around z, then y, then x
|
// Rotates first around z, then y, then x
|
||||||
mInsert->setOrientation(xr*yr*zr);
|
mInsert->setOrientation(xr*yr*zr);
|
||||||
|
|
||||||
mInsertMesh.clear();
|
mInsertMesh.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -112,7 +124,7 @@ void ExteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec,
|
||||||
}
|
}
|
||||||
|
|
||||||
mNpcPart = parent->createChildSceneNode(sceneNodeName);
|
mNpcPart = parent->createChildSceneNode(sceneNodeName);
|
||||||
MeshPtr good2 = NIFLoader::load(mesh);
|
MeshPtr good2 = NifOgre::NIFLoader::load(mesh);
|
||||||
|
|
||||||
MovableObject *ent = mScene.getMgr()->createEntity(mesh);
|
MovableObject *ent = mScene.getMgr()->createEntity(mesh);
|
||||||
|
|
||||||
|
@ -201,12 +213,21 @@ void ExteriorCellRender::insertMesh(const std::string &mesh)
|
||||||
{
|
{
|
||||||
assert (mInsert);
|
assert (mInsert);
|
||||||
|
|
||||||
NIFLoader::load(mesh);
|
NifOgre::NIFLoader::load(mesh);
|
||||||
MovableObject *ent = mScene.getMgr()->createEntity(mesh);
|
Entity *ent = mScene.getMgr()->createEntity(mesh);
|
||||||
mInsert->attachObject(ent);
|
|
||||||
|
|
||||||
if (mInsertMesh.empty())
|
if(!isStatic)
|
||||||
mInsertMesh = mesh;
|
{
|
||||||
|
mInsert->attachObject(ent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sg->addEntity(ent,mInsert->_getDerivedPosition(),mInsert->_getDerivedOrientation(),mInsert->_getDerivedScale());
|
||||||
|
sg->setRegionDimensions(Ogre::Vector3(100000,10000,100000));
|
||||||
|
mScene.getMgr()->destroyEntity(ent);
|
||||||
|
}
|
||||||
|
if (mInsertMesh.empty())
|
||||||
|
mInsertMesh = mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExteriorCellRender::insertObjectPhysics()
|
void ExteriorCellRender::insertObjectPhysics()
|
||||||
|
@ -333,6 +354,8 @@ void ExteriorCellRender::show()
|
||||||
configureFog();
|
configureFog();
|
||||||
|
|
||||||
insertCell(mCell, mEnvironment);
|
insertCell(mCell, mEnvironment);
|
||||||
|
|
||||||
|
sg->build();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExteriorCellRender::hide()
|
void ExteriorCellRender::hide()
|
||||||
|
@ -341,15 +364,49 @@ void ExteriorCellRender::hide()
|
||||||
mBase->setVisible(false);
|
mBase->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExteriorCellRender::destroyAllAttachedMovableObjects(Ogre::SceneNode* i_pSceneNode)
|
||||||
|
{
|
||||||
|
if ( !i_pSceneNode )
|
||||||
|
{
|
||||||
|
assert( false );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy all the attached objects
|
||||||
|
SceneNode::ObjectIterator itObject = i_pSceneNode->getAttachedObjectIterator();
|
||||||
|
|
||||||
|
while ( itObject.hasMoreElements() )
|
||||||
|
{
|
||||||
|
MovableObject* pObject = static_cast<MovableObject*>(itObject.getNext());
|
||||||
|
i_pSceneNode->getCreator()->destroyMovableObject( pObject );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recurse to child SceneNodes
|
||||||
|
SceneNode::ChildNodeIterator itChild = i_pSceneNode->getChildIterator();
|
||||||
|
|
||||||
|
while ( itChild.hasMoreElements() )
|
||||||
|
{
|
||||||
|
SceneNode* pChildNode = static_cast<SceneNode*>(itChild.getNext());
|
||||||
|
destroyAllAttachedMovableObjects( pChildNode );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ExteriorCellRender::destroy()
|
void ExteriorCellRender::destroy()
|
||||||
{
|
{
|
||||||
if(mBase)
|
if(mBase)
|
||||||
{
|
{
|
||||||
|
destroyAllAttachedMovableObjects(mBase);
|
||||||
mBase->removeAndDestroyAllChildren();
|
mBase->removeAndDestroyAllChildren();
|
||||||
mScene.getMgr()->destroySceneNode(mBase);
|
mScene.getMgr()->destroySceneNode(mBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
mBase = NULL;
|
mBase = 0;
|
||||||
|
|
||||||
|
if (sg)
|
||||||
|
{
|
||||||
|
mScene.getMgr()->destroyStaticGeometry (sg);
|
||||||
|
sg = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch through lighting modes.
|
// Switch through lighting modes.
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "OgreColourValue.h"
|
#include "OgreColourValue.h"
|
||||||
#include <OgreMath.h>
|
#include <OgreMath.h>
|
||||||
|
#include <Ogre.h>
|
||||||
|
|
||||||
namespace Ogre
|
namespace Ogre
|
||||||
{
|
{
|
||||||
|
@ -57,19 +58,24 @@ namespace MWRender
|
||||||
std::string mInsertMesh;
|
std::string mInsertMesh;
|
||||||
Ogre::SceneNode *mNpcPart;
|
Ogre::SceneNode *mNpcPart;
|
||||||
|
|
||||||
|
//the static geometry
|
||||||
|
Ogre::StaticGeometry *sg;
|
||||||
|
bool isStatic;
|
||||||
|
|
||||||
// 0 normal, 1 more bright, 2 max
|
// 0 normal, 1 more bright, 2 max
|
||||||
int mAmbientMode;
|
int mAmbientMode;
|
||||||
|
|
||||||
Ogre::ColourValue mAmbientColor;
|
Ogre::ColourValue mAmbientColor;
|
||||||
|
|
||||||
/// start inserting a new reference.
|
/// start inserting a new reference.
|
||||||
virtual void insertBegin (ESM::CellRef &ref);
|
virtual void insertBegin (ESM::CellRef &ref, bool static_ = false);
|
||||||
|
|
||||||
/// insert a mesh related to the most recent insertBegin call.
|
/// insert a mesh related to the most recent insertBegin call.
|
||||||
virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements);
|
virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements);
|
||||||
virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements, bool translateFirst);
|
virtual void insertMesh(const std::string &mesh, Ogre::Vector3 vec, Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName, std::string sceneParent[], int elements, bool translateFirst);
|
||||||
|
|
||||||
virtual void insertMesh(const std::string &mesh);
|
virtual void insertMesh(const std::string &mesh);
|
||||||
|
|
||||||
virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements);
|
virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements);
|
||||||
virtual void scaleMesh(Ogre::Vector3 axis, std::string sceneNodeName[], int elements);
|
virtual void scaleMesh(Ogre::Vector3 axis, std::string sceneNodeName[], int elements);
|
||||||
|
|
||||||
|
@ -95,8 +101,7 @@ namespace MWRender
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ExteriorCellRender(ESMS::CellStore<MWWorld::RefData> &_cell, MWWorld::Environment& environment,
|
ExteriorCellRender(ESMS::CellStore<MWWorld::RefData> &_cell, MWWorld::Environment& environment,
|
||||||
MWScene &_scene)
|
MWScene &_scene);
|
||||||
: mCell(_cell), mEnvironment (environment), mScene(_scene), mBase(NULL), mInsert(NULL), mAmbientMode (0) {}
|
|
||||||
|
|
||||||
virtual ~ExteriorCellRender() { destroy(); }
|
virtual ~ExteriorCellRender() { destroy(); }
|
||||||
|
|
||||||
|
@ -121,6 +126,10 @@ namespace MWRender
|
||||||
|
|
||||||
/// Remove the reference with the given handle permanently from the scene.
|
/// Remove the reference with the given handle permanently from the scene.
|
||||||
virtual void deleteObject (const std::string& handle);
|
virtual void deleteObject (const std::string& handle);
|
||||||
|
|
||||||
|
void destroyAllAttachedMovableObjects(Ogre::SceneNode* i_pSceneNode);
|
||||||
|
|
||||||
|
static int uniqueID;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ bool InteriorCellRender::lightOutQuadInLin = false;
|
||||||
|
|
||||||
// start inserting a new reference.
|
// start inserting a new reference.
|
||||||
|
|
||||||
void InteriorCellRender::insertBegin (ESM::CellRef &ref)
|
void InteriorCellRender::insertBegin (ESM::CellRef &ref, bool static_)
|
||||||
{
|
{
|
||||||
assert (!insert);
|
assert (!insert);
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ void InteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec,
|
||||||
npcPart = parent->createChildSceneNode(sceneNodeName);
|
npcPart = parent->createChildSceneNode(sceneNodeName);
|
||||||
//npcPart->showBoundingBox(true);
|
//npcPart->showBoundingBox(true);
|
||||||
|
|
||||||
MeshPtr good2 = NIFLoader::load(mesh);
|
MeshPtr good2 = NifOgre::NIFLoader::load(mesh);
|
||||||
|
|
||||||
MovableObject *ent = scene.getMgr()->createEntity(mesh);
|
MovableObject *ent = scene.getMgr()->createEntity(mesh);
|
||||||
//ent->extr
|
//ent->extr
|
||||||
|
@ -182,11 +182,11 @@ void InteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec,
|
||||||
|
|
||||||
void InteriorCellRender::insertMesh(const std::string &mesh)
|
void InteriorCellRender::insertMesh(const std::string &mesh)
|
||||||
{
|
{
|
||||||
assert (insert);
|
assert (insert);
|
||||||
|
|
||||||
NIFLoader::load(mesh);
|
NifOgre::NIFLoader::load(mesh);
|
||||||
MovableObject *ent = scene.getMgr()->createEntity(mesh);
|
MovableObject *ent = scene.getMgr()->createEntity(mesh);
|
||||||
insert->attachObject(ent);
|
insert->attachObject(ent);
|
||||||
|
|
||||||
if (mInsertMesh.empty())
|
if (mInsertMesh.empty())
|
||||||
mInsertMesh = mesh;
|
mInsertMesh = mesh;
|
||||||
|
|
|
@ -63,7 +63,7 @@ namespace MWRender
|
||||||
Ogre::ColourValue ambientColor;
|
Ogre::ColourValue ambientColor;
|
||||||
|
|
||||||
/// start inserting a new reference.
|
/// start inserting a new reference.
|
||||||
virtual void insertBegin (ESM::CellRef &ref);
|
virtual void insertBegin (ESM::CellRef &ref, bool static_ = false);
|
||||||
virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements);
|
virtual void rotateMesh(Ogre::Vector3 axis, Ogre::Radian angle, std::string sceneNodeName[], int elements);
|
||||||
virtual void scaleMesh(Ogre::Vector3 axis, std::string sceneNodeName[], int elements);
|
virtual void scaleMesh(Ogre::Vector3 axis, std::string sceneNodeName[], int elements);
|
||||||
/// insert a mesh related to the most recent insertBegin call.
|
/// insert a mesh related to the most recent insertBegin call.
|
||||||
|
|
|
@ -108,4 +108,5 @@ op 0x2000133: Journal
|
||||||
op 0x2000134: SetJournalIndex
|
op 0x2000134: SetJournalIndex
|
||||||
op 0x2000135: GetJournalIndex
|
op 0x2000135: GetJournalIndex
|
||||||
op 0x2000136: GetPCCell
|
op 0x2000136: GetPCCell
|
||||||
opcodes 0x2000137-0x3ffffff unused
|
op 0x2000137: GetButtonPressed
|
||||||
|
opcodes 0x2000138-0x3ffffff unused
|
||||||
|
|
|
@ -15,42 +15,57 @@
|
||||||
namespace MWScript
|
namespace MWScript
|
||||||
{
|
{
|
||||||
namespace Gui
|
namespace Gui
|
||||||
{
|
{
|
||||||
class OpEnableWindow : public Interpreter::Opcode0
|
class OpEnableWindow : public Interpreter::Opcode0
|
||||||
{
|
{
|
||||||
MWGui::GuiWindow mWindow;
|
MWGui::GuiWindow mWindow;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
OpEnableWindow (MWGui::GuiWindow window) : mWindow (window) {}
|
OpEnableWindow (MWGui::GuiWindow window) : mWindow (window) {}
|
||||||
|
|
||||||
virtual void execute (Interpreter::Runtime& runtime)
|
virtual void execute (Interpreter::Runtime& runtime)
|
||||||
{
|
{
|
||||||
InterpreterContext& context =
|
InterpreterContext& context =
|
||||||
static_cast<InterpreterContext&> (runtime.getContext());
|
static_cast<InterpreterContext&> (runtime.getContext());
|
||||||
|
|
||||||
context.getWindowManager().allow (mWindow);
|
context.getWindowManager().allow (mWindow);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class OpShowDialogue : public Interpreter::Opcode0
|
class OpShowDialogue : public Interpreter::Opcode0
|
||||||
{
|
{
|
||||||
MWGui::GuiMode mDialogue;
|
MWGui::GuiMode mDialogue;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
OpShowDialogue (MWGui::GuiMode dialogue)
|
OpShowDialogue (MWGui::GuiMode dialogue)
|
||||||
: mDialogue (dialogue)
|
: mDialogue (dialogue)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual void execute (Interpreter::Runtime& runtime)
|
virtual void execute (Interpreter::Runtime& runtime)
|
||||||
{
|
{
|
||||||
InterpreterContext& context =
|
InterpreterContext& context =
|
||||||
static_cast<InterpreterContext&> (runtime.getContext());
|
static_cast<InterpreterContext&> (runtime.getContext());
|
||||||
|
|
||||||
context.getInputManager().setGuiMode(mDialogue);
|
context.getInputManager().setGuiMode(mDialogue);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class OpGetButtonPressed : public Interpreter::Opcode0
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual void execute (Interpreter::Runtime& runtime)
|
||||||
|
{
|
||||||
|
InterpreterContext& context =
|
||||||
|
static_cast<InterpreterContext&> (runtime.getContext());
|
||||||
|
|
||||||
|
MWWorld::Ptr ptr = context.getReference();
|
||||||
|
|
||||||
|
runtime.push (context.getWindowManager().readPressedButton());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const int opcodeEnableBirthMenu = 0x200000e;
|
const int opcodeEnableBirthMenu = 0x200000e;
|
||||||
const int opcodeEnableClassMenu = 0x200000f;
|
const int opcodeEnableClassMenu = 0x200000f;
|
||||||
|
@ -63,7 +78,8 @@ namespace MWScript
|
||||||
const int opcodeEnableStatsMenu = 0x2000016;
|
const int opcodeEnableStatsMenu = 0x2000016;
|
||||||
const int opcodeEnableRest = 0x2000017;
|
const int opcodeEnableRest = 0x2000017;
|
||||||
const int opcodeShowRestMenu = 0x2000018;
|
const int opcodeShowRestMenu = 0x2000018;
|
||||||
|
const int opcodeGetButtonPressed = 0x2000137;
|
||||||
|
|
||||||
void registerExtensions (Compiler::Extensions& extensions)
|
void registerExtensions (Compiler::Extensions& extensions)
|
||||||
{
|
{
|
||||||
extensions.registerInstruction ("enablebirthmenu", "", opcodeEnableBirthMenu);
|
extensions.registerInstruction ("enablebirthmenu", "", opcodeEnableBirthMenu);
|
||||||
|
@ -80,10 +96,12 @@ namespace MWScript
|
||||||
|
|
||||||
extensions.registerInstruction ("enablerestmenu", "", opcodeEnableRest);
|
extensions.registerInstruction ("enablerestmenu", "", opcodeEnableRest);
|
||||||
extensions.registerInstruction ("enablelevelupmenu", "", opcodeEnableRest);
|
extensions.registerInstruction ("enablelevelupmenu", "", opcodeEnableRest);
|
||||||
|
|
||||||
extensions.registerInstruction ("showrestmenu", "", opcodeShowRestMenu);
|
extensions.registerInstruction ("showrestmenu", "", opcodeShowRestMenu);
|
||||||
|
|
||||||
|
extensions.registerFunction ("getbuttonpressed", 'l', "", opcodeGetButtonPressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void installOpcodes (Interpreter::Interpreter& interpreter)
|
void installOpcodes (Interpreter::Interpreter& interpreter)
|
||||||
{
|
{
|
||||||
interpreter.installSegment5 (opcodeEnableBirthMenu,
|
interpreter.installSegment5 (opcodeEnableBirthMenu,
|
||||||
|
@ -115,6 +133,8 @@ namespace MWScript
|
||||||
|
|
||||||
interpreter.installSegment5 (opcodeShowRestMenu,
|
interpreter.installSegment5 (opcodeShowRestMenu,
|
||||||
new OpShowDialogue (MWGui::GM_Rest));
|
new OpShowDialogue (MWGui::GM_Rest));
|
||||||
|
|
||||||
|
interpreter.installSegment5 (opcodeGetButtonPressed, new OpGetButtonPressed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
#include <components/compiler/scanner.hpp>
|
#include <components/compiler/scanner.hpp>
|
||||||
#include <components/compiler/context.hpp>
|
#include <components/compiler/context.hpp>
|
||||||
|
|
||||||
#include <components/interpreter/interpreter.hpp>
|
|
||||||
|
|
||||||
#include "extensions.hpp"
|
#include "extensions.hpp"
|
||||||
|
|
||||||
namespace MWScript
|
namespace MWScript
|
||||||
|
@ -21,7 +19,8 @@ namespace MWScript
|
||||||
ScriptManager::ScriptManager (const ESMS::ESMStore& store, bool verbose,
|
ScriptManager::ScriptManager (const ESMS::ESMStore& store, bool verbose,
|
||||||
Compiler::Context& compilerContext)
|
Compiler::Context& compilerContext)
|
||||||
: mErrorHandler (std::cerr), mStore (store), mVerbose (verbose),
|
: mErrorHandler (std::cerr), mStore (store), mVerbose (verbose),
|
||||||
mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext)
|
mCompilerContext (compilerContext), mParser (mErrorHandler, mCompilerContext),
|
||||||
|
mOpcodesInstalled (false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool ScriptManager::compile (const std::string& name)
|
bool ScriptManager::compile (const std::string& name)
|
||||||
|
@ -99,9 +98,13 @@ namespace MWScript
|
||||||
if (!iter->second.empty())
|
if (!iter->second.empty())
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Interpreter::Interpreter interpreter (interpreterContext);
|
if (!mOpcodesInstalled)
|
||||||
installOpcodes (interpreter);
|
{
|
||||||
interpreter.run (&iter->second[0], iter->second.size());
|
installOpcodes (mInterpreter);
|
||||||
|
mOpcodesInstalled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mInterpreter.run (&iter->second[0], iter->second.size(), interpreterContext);
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <components/compiler/streamerrorhandler.hpp>
|
#include <components/compiler/streamerrorhandler.hpp>
|
||||||
#include <components/compiler/fileparser.hpp>
|
#include <components/compiler/fileparser.hpp>
|
||||||
|
|
||||||
|
#include <components/interpreter/interpreter.hpp>
|
||||||
#include <components/interpreter/types.hpp>
|
#include <components/interpreter/types.hpp>
|
||||||
|
|
||||||
namespace ESMS
|
namespace ESMS
|
||||||
|
@ -35,6 +36,8 @@ namespace MWScript
|
||||||
bool mVerbose;
|
bool mVerbose;
|
||||||
Compiler::Context& mCompilerContext;
|
Compiler::Context& mCompilerContext;
|
||||||
Compiler::FileParser mParser;
|
Compiler::FileParser mParser;
|
||||||
|
Interpreter::Interpreter mInterpreter;
|
||||||
|
bool mOpcodesInstalled;
|
||||||
|
|
||||||
std::map<std::string, std::vector<Interpreter::Type_Code> > mScripts;
|
std::map<std::string, std::vector<Interpreter::Type_Code> > mScripts;
|
||||||
|
|
||||||
|
|
|
@ -84,20 +84,25 @@ namespace MWSound
|
||||||
// finding. It takes DOS paths (any case, \\ slashes or / slashes)
|
// finding. It takes DOS paths (any case, \\ slashes or / slashes)
|
||||||
// relative to the sound dir, and translates them into full paths
|
// relative to the sound dir, and translates them into full paths
|
||||||
// of existing files in the filesystem, if they exist.
|
// of existing files in the filesystem, if they exist.
|
||||||
|
bool FSstrict;
|
||||||
FileFinder::FileFinder files;
|
FileFinder::FileFinder files;
|
||||||
|
FileFinder::FileFinderStrict strict;
|
||||||
|
FileFinder::FileFinder musicpath;
|
||||||
|
FileFinder::FileFinderStrict musicpathStrict;
|
||||||
|
|
||||||
SoundImpl(Ogre::Root *root, Ogre::Camera *camera,
|
SoundImpl(Ogre::Root *root, Ogre::Camera *camera,
|
||||||
const ESMS::ESMStore &str,
|
const ESMS::ESMStore &str,
|
||||||
const std::string &soundDir)
|
const std::string &soundDir, const std::string &musicDir, bool fsstrict)
|
||||||
: mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY)))
|
: mgr(new OEManager(SoundFactoryPtr(new SOUND_FACTORY)))
|
||||||
, updater(mgr)
|
, updater(mgr)
|
||||||
, cameraTracker(mgr)
|
, cameraTracker(mgr)
|
||||||
, store(str)
|
, store(str)
|
||||||
, files(soundDir)
|
, files(soundDir), strict(soundDir)
|
||||||
|
,musicpath(musicDir), musicpathStrict(musicDir)
|
||||||
{
|
{
|
||||||
|
FSstrict = fsstrict;
|
||||||
cout << "Sound output: " << SOUND_OUT << endl;
|
cout << "Sound output: " << SOUND_OUT << endl;
|
||||||
cout << "Sound decoder: " << SOUND_IN << endl;
|
cout << "Sound decoder: " << SOUND_IN << endl;
|
||||||
|
|
||||||
// Attach the camera to the camera tracker
|
// Attach the camera to the camera tracker
|
||||||
cameraTracker.followCamera(camera);
|
cameraTracker.followCamera(camera);
|
||||||
|
|
||||||
|
@ -111,6 +116,8 @@ namespace MWSound
|
||||||
cameraTracker.unfollowCamera();
|
cameraTracker.unfollowCamera();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static std::string toMp3(std::string str)
|
static std::string toMp3(std::string str)
|
||||||
{
|
{
|
||||||
std::string::size_type i = str.rfind('.');
|
std::string::size_type i = str.rfind('.');
|
||||||
|
@ -122,28 +129,76 @@ namespace MWSound
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasFile(const std::string &str)
|
bool hasFile(const std::string &str, bool music = false)
|
||||||
{
|
{
|
||||||
if(files.has(str)) return true;
|
if(FSstrict == false)
|
||||||
// Not found? Try with .mp3
|
{
|
||||||
return files.has(toMp3(str));
|
if(music)
|
||||||
|
{
|
||||||
|
if(musicpath.has(str)) return true;
|
||||||
|
|
||||||
|
// Not found? Try with .mp3
|
||||||
|
return musicpath.has(toMp3(str));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(files.has(str)) return true;
|
||||||
|
return files.has(toMp3(str));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(music)
|
||||||
|
{
|
||||||
|
if(musicpathStrict.has(str)) return true;
|
||||||
|
|
||||||
|
// Not found? Try with .mp3
|
||||||
|
return musicpathStrict.has(toMp3(str));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(strict.has(str)) return true;
|
||||||
|
return strict.has(toMp3(str));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert a Morrowind sound path (eg. Fx\funny.wav) to full path
|
// Convert a Morrowind sound path (eg. Fx\funny.wav) to full path
|
||||||
// with proper slash conversion (eg. datadir/Sound/Fx/funny.wav)
|
// with proper slash conversion (eg. datadir/Sound/Fx/funny.wav)
|
||||||
std::string convertPath(const std::string &str)
|
std::string convertPath(const std::string &str, bool music = false)
|
||||||
{
|
{
|
||||||
// Search and return
|
if(FSstrict == false)
|
||||||
if(files.has(str))
|
{
|
||||||
return files.lookup(str);
|
// Search and return
|
||||||
|
if(music && musicpath.has(str))
|
||||||
|
return musicpath.lookup(str);
|
||||||
|
else if(files.has(str))
|
||||||
|
return files.lookup(str);
|
||||||
|
|
||||||
// Try mp3 if the wav wasn't found
|
// Try mp3 if the wav wasn't found
|
||||||
std::string mp3 = toMp3(str);
|
std::string mp3 = toMp3(str);
|
||||||
if(files.has(mp3))
|
if(music && musicpath.has(mp3))
|
||||||
return files.lookup(mp3);
|
return musicpath.lookup(mp3);
|
||||||
|
else if(files.has(mp3))
|
||||||
|
return files.lookup(mp3);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(music && musicpathStrict.has(str))
|
||||||
|
return musicpathStrict.lookup(str);
|
||||||
|
else if(strict.has(str))
|
||||||
|
return strict.lookup(str);
|
||||||
|
|
||||||
// Give up
|
// Try mp3 if the wav wasn't found
|
||||||
return "";
|
std::string mp3 = toMp3(str);
|
||||||
|
if(music && musicpathStrict.has(mp3))
|
||||||
|
return musicpathStrict.lookup(mp3);
|
||||||
|
else if(strict.has(str))
|
||||||
|
return strict.lookup(mp3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give up
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert a soundId to file name, and modify the volume
|
// Convert a soundId to file name, and modify the volume
|
||||||
|
@ -304,15 +359,31 @@ namespace MWSound
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void SoundManager::streamMusicFull (const std::string& filename)
|
||||||
|
{
|
||||||
|
if(!mData) return;
|
||||||
|
|
||||||
|
// Play the sound and tell it to stream, if possible. TODO:
|
||||||
|
// Store the reference, the jukebox will need to check status,
|
||||||
|
// control volume etc.
|
||||||
|
if (mData->music)
|
||||||
|
mData->music->stop();
|
||||||
|
mData->music = mData->mgr->load(filename);
|
||||||
|
mData->music->setStreaming(true);
|
||||||
|
mData->music->setVolume(0.4);
|
||||||
|
mData->music->play();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera,
|
SoundManager::SoundManager(Ogre::Root *root, Ogre::Camera *camera,
|
||||||
const ESMS::ESMStore &store,
|
const ESMS::ESMStore &store,
|
||||||
boost::filesystem::path dataDir,
|
boost::filesystem::path dataDir,
|
||||||
bool useSound)
|
bool useSound, bool fsstrict)
|
||||||
: mData(NULL)
|
: mData(NULL), fsStrict (fsstrict)
|
||||||
{
|
{
|
||||||
MP3Lookup(dataDir / "Music/Explore/");
|
MP3Lookup(dataDir / "Music/Explore/");
|
||||||
if(useSound)
|
if(useSound)
|
||||||
mData = new SoundImpl(root, camera, store, (dataDir / "Sound").string());
|
mData = new SoundImpl(root, camera, store, (dataDir / "Sound").string(), (dataDir / "Music").string(), fsstrict);
|
||||||
}
|
}
|
||||||
|
|
||||||
SoundManager::~SoundManager()
|
SoundManager::~SoundManager()
|
||||||
|
@ -321,6 +392,16 @@ namespace MWSound
|
||||||
delete mData;
|
delete mData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SoundManager::streamMusic(const std::string& filename)
|
||||||
|
{
|
||||||
|
if(mData->hasFile(filename, true))
|
||||||
|
{
|
||||||
|
std::string fullpath = mData->convertPath(filename, true);
|
||||||
|
streamMusicFull(fullpath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SoundManager::MP3Lookup(boost::filesystem::path dir)
|
void SoundManager::MP3Lookup(boost::filesystem::path dir)
|
||||||
{
|
{
|
||||||
boost::filesystem::directory_iterator dir_iter(dir), dir_end;
|
boost::filesystem::directory_iterator dir_iter(dir), dir_end;
|
||||||
|
@ -353,7 +434,7 @@ namespace MWSound
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::cout << "Playing " << music << "\n";
|
std::cout << "Playing " << music << "\n";
|
||||||
streamMusic(music);
|
streamMusicFull(music);
|
||||||
}
|
}
|
||||||
catch(std::exception &e)
|
catch(std::exception &e)
|
||||||
{
|
{
|
||||||
|
@ -397,21 +478,6 @@ namespace MWSound
|
||||||
return !mData->isPlaying(ptr, "_say_sound");
|
return !mData->isPlaying(ptr, "_say_sound");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundManager::streamMusic (const std::string& filename)
|
|
||||||
{
|
|
||||||
if(!mData) return;
|
|
||||||
|
|
||||||
// Play the sound and tell it to stream, if possible. TODO:
|
|
||||||
// Store the reference, the jukebox will need to check status,
|
|
||||||
// control volume etc.
|
|
||||||
if (mData->music)
|
|
||||||
mData->music->stop();
|
|
||||||
mData->music = mData->mgr->load(filename);
|
|
||||||
mData->music->setStreaming(true);
|
|
||||||
mData->music->setVolume(0.4);
|
|
||||||
mData->music->play();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoundManager::playSound (const std::string& soundId, float volume, float pitch)
|
void SoundManager::playSound (const std::string& soundId, float volume, float pitch)
|
||||||
{
|
{
|
||||||
|
|
|
@ -27,19 +27,28 @@ namespace MWSound
|
||||||
// Hide implementation details - engine.cpp is compiling
|
// Hide implementation details - engine.cpp is compiling
|
||||||
// enough as it is.
|
// enough as it is.
|
||||||
struct SoundImpl;
|
struct SoundImpl;
|
||||||
|
|
||||||
SoundImpl *mData;
|
SoundImpl *mData;
|
||||||
std::vector<boost::filesystem::path> files;
|
std::vector<boost::filesystem::path> files;
|
||||||
|
bool fsStrict;
|
||||||
|
|
||||||
|
void streamMusicFull (const std::string& filename);
|
||||||
|
///< Play a soundifle
|
||||||
|
/// \param absolute filename
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
SoundManager(Ogre::Root*, Ogre::Camera*, const ESMS::ESMStore &store,
|
SoundManager(Ogre::Root*, Ogre::Camera*, const ESMS::ESMStore &store,
|
||||||
boost::filesystem::path dataDir, bool useSound);
|
boost::filesystem::path dataDir, bool useSound, bool fsstrict);
|
||||||
~SoundManager();
|
~SoundManager();
|
||||||
|
|
||||||
|
void streamMusic(const std::string& filename);
|
||||||
|
///< Play a soundifle
|
||||||
|
/// \param filename name of a sound file in "Music/" in the data directory.
|
||||||
|
|
||||||
void startRandomTitle();
|
void startRandomTitle();
|
||||||
void MP3Lookup(boost::filesystem::path dir);
|
void MP3Lookup(boost::filesystem::path dir);
|
||||||
//struct SoundImpl;
|
|
||||||
bool isMusicPlaying();
|
bool isMusicPlaying();
|
||||||
|
|
||||||
SoundImpl getMData();
|
SoundImpl getMData();
|
||||||
|
@ -51,9 +60,7 @@ namespace MWSound
|
||||||
bool sayDone (MWWorld::Ptr reference) const;
|
bool sayDone (MWWorld::Ptr reference) const;
|
||||||
///< Is actor not speaking?
|
///< Is actor not speaking?
|
||||||
|
|
||||||
void streamMusic (const std::string& filename);
|
|
||||||
///< Play a soundifle
|
|
||||||
/// \param filename name of a sound file in "Music/" in the data directory.
|
|
||||||
|
|
||||||
void playSound (const std::string& soundId, float volume, float pitch);
|
void playSound (const std::string& soundId, float volume, float pitch);
|
||||||
///< Play a sound, independently of 3D-position
|
///< Play a sound, independently of 3D-position
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace MWWorld
|
||||||
ESMS::CellRefList<ESM::Ingredient, D> ingreds;
|
ESMS::CellRefList<ESM::Ingredient, D> ingreds;
|
||||||
ESMS::CellRefList<ESM::Light, D> lights;
|
ESMS::CellRefList<ESM::Light, D> lights;
|
||||||
ESMS::CellRefList<ESM::Tool, D> lockpicks;
|
ESMS::CellRefList<ESM::Tool, D> lockpicks;
|
||||||
ESMS::CellRefList<ESM::Misc, D> miscItems;
|
ESMS::CellRefList<ESM::Miscellaneous, D> miscItems;
|
||||||
ESMS::CellRefList<ESM::Probe, D> probes;
|
ESMS::CellRefList<ESM::Probe, D> probes;
|
||||||
ESMS::CellRefList<ESM::Repair, D> repairs;
|
ESMS::CellRefList<ESM::Repair, D> repairs;
|
||||||
ESMS::CellRefList<ESM::Weapon, D> weapons;
|
ESMS::CellRefList<ESM::Weapon, D> weapons;
|
||||||
|
|
|
@ -135,7 +135,7 @@ namespace MWWorld
|
||||||
if (ESMS::LiveCellRef<ESM::Tool, RefData> *ref = cell.lockpicks.find (name))
|
if (ESMS::LiveCellRef<ESM::Tool, RefData> *ref = cell.lockpicks.find (name))
|
||||||
return Ptr (ref, &cell);
|
return Ptr (ref, &cell);
|
||||||
|
|
||||||
if (ESMS::LiveCellRef<ESM::Misc, RefData> *ref = cell.miscItems.find (name))
|
if (ESMS::LiveCellRef<ESM::Miscellaneous, RefData> *ref = cell.miscItems.find (name))
|
||||||
return Ptr (ref, &cell);
|
return Ptr (ref, &cell);
|
||||||
|
|
||||||
if (ESMS::LiveCellRef<ESM::NPC, RefData> *ref = cell.npcs.find (name))
|
if (ESMS::LiveCellRef<ESM::NPC, RefData> *ref = cell.npcs.find (name))
|
||||||
|
@ -198,7 +198,7 @@ namespace MWWorld
|
||||||
if (ESMS::LiveCellRef<ESM::Tool, RefData> *ref = searchViaHandle (handle, cell.lockpicks))
|
if (ESMS::LiveCellRef<ESM::Tool, RefData> *ref = searchViaHandle (handle, cell.lockpicks))
|
||||||
return Ptr (ref, &cell);
|
return Ptr (ref, &cell);
|
||||||
|
|
||||||
if (ESMS::LiveCellRef<ESM::Misc, RefData> *ref = searchViaHandle (handle, cell.miscItems))
|
if (ESMS::LiveCellRef<ESM::Miscellaneous, RefData> *ref = searchViaHandle (handle, cell.miscItems))
|
||||||
return Ptr (ref, &cell);
|
return Ptr (ref, &cell);
|
||||||
|
|
||||||
if (ESMS::LiveCellRef<ESM::NPC, RefData> *ref = searchViaHandle (handle, cell.npcs))
|
if (ESMS::LiveCellRef<ESM::NPC, RefData> *ref = searchViaHandle (handle, cell.npcs))
|
||||||
|
@ -284,7 +284,6 @@ namespace MWWorld
|
||||||
|
|
||||||
removeScripts (iter->first);
|
removeScripts (iter->first);
|
||||||
mEnvironment.mMechanicsManager->dropActors (iter->first);
|
mEnvironment.mMechanicsManager->dropActors (iter->first);
|
||||||
iter->second->destroy();
|
|
||||||
mEnvironment.mSoundManager->stopSound (iter->first);
|
mEnvironment.mSoundManager->stopSound (iter->first);
|
||||||
delete iter->second;
|
delete iter->second;
|
||||||
mActiveCells.erase (iter);
|
mActiveCells.erase (iter);
|
||||||
|
@ -410,9 +409,9 @@ namespace MWWorld
|
||||||
World::World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng,
|
World::World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng,
|
||||||
const Files::Collections& fileCollections,
|
const Files::Collections& fileCollections,
|
||||||
const std::string& master, const boost::filesystem::path& resDir,
|
const std::string& master, const boost::filesystem::path& resDir,
|
||||||
bool newGame, Environment& environment)
|
bool newGame, Environment& environment, const std::string& encoding)
|
||||||
: mSkyManager (0), mScene (renderer,physEng), mPlayer (0), mCurrentCell (0), mGlobalVariables (0),
|
: mSkyManager (0), mScene (renderer,physEng), mPlayer (0), mCurrentCell (0), mGlobalVariables (0),
|
||||||
mSky (false), mCellChanged (false), mEnvironment (environment)
|
mSky (false), mCellChanged (false), mEnvironment (environment), mNextDynamicRecord (0)
|
||||||
{
|
{
|
||||||
mPhysEngine = physEng;
|
mPhysEngine = physEng;
|
||||||
|
|
||||||
|
@ -421,6 +420,7 @@ namespace MWWorld
|
||||||
std::cout << "Loading ESM " << masterPath.string() << "\n";
|
std::cout << "Loading ESM " << masterPath.string() << "\n";
|
||||||
|
|
||||||
// This parses the ESM file and loads a sample cell
|
// This parses the ESM file and loads a sample cell
|
||||||
|
mEsm.setEncoding(encoding);
|
||||||
mEsm.open (masterPath.string());
|
mEsm.open (masterPath.string());
|
||||||
mStore.load (mEsm);
|
mStore.load (mEsm);
|
||||||
|
|
||||||
|
@ -871,4 +871,36 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
return mScene.toggleRenderMode (mode);
|
return mScene.toggleRenderMode (mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<std::string, const ESM::Potion *> World::createRecord (const ESM::Potion& record)
|
||||||
|
{
|
||||||
|
/// \todo Rewrite the ESMStore so that a dynamic 2nd ESMStore can be attached to it.
|
||||||
|
/// This function should then insert the record into the 2nd store (the code for this
|
||||||
|
/// should also be moved to the ESMStore class). It might be a good idea to review
|
||||||
|
/// the STL-container usage of the ESMStore before the rewrite.
|
||||||
|
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << "$dynamic" << mNextDynamicRecord++;
|
||||||
|
|
||||||
|
const ESM::Potion *created =
|
||||||
|
&mStore.potions.list.insert (std::make_pair (stream.str(), record)).first->second;
|
||||||
|
|
||||||
|
mStore.all.insert (std::make_pair (stream.str(), ESM::REC_ALCH));
|
||||||
|
|
||||||
|
return std::make_pair (stream.str(), created);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string, const ESM::Class *> World::createRecord (const ESM::Class& record)
|
||||||
|
{
|
||||||
|
/// \todo See function above.
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << "$dynamic" << mNextDynamicRecord++;
|
||||||
|
|
||||||
|
const ESM::Class *created =
|
||||||
|
&mStore.classes.list.insert (std::make_pair (stream.str(), record)).first->second;
|
||||||
|
|
||||||
|
mStore.all.insert (std::make_pair (stream.str(), ESM::REC_CLAS));
|
||||||
|
|
||||||
|
return std::make_pair (stream.str(), created);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,6 +79,7 @@ namespace MWWorld
|
||||||
bool mSky;
|
bool mSky;
|
||||||
bool mCellChanged;
|
bool mCellChanged;
|
||||||
Environment& mEnvironment;
|
Environment& mEnvironment;
|
||||||
|
int mNextDynamicRecord;
|
||||||
|
|
||||||
OEngine::Physic::PhysicEngine* mPhysEngine;
|
OEngine::Physic::PhysicEngine* mPhysEngine;
|
||||||
|
|
||||||
|
@ -115,7 +116,7 @@ namespace MWWorld
|
||||||
World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng,
|
World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng,
|
||||||
const Files::Collections& fileCollections,
|
const Files::Collections& fileCollections,
|
||||||
const std::string& master, const boost::filesystem::path& resDir, bool newGame,
|
const std::string& master, const boost::filesystem::path& resDir, bool newGame,
|
||||||
Environment& environment);
|
Environment& environment, const std::string& encoding);
|
||||||
|
|
||||||
~World();
|
~World();
|
||||||
|
|
||||||
|
@ -202,6 +203,14 @@ namespace MWWorld
|
||||||
bool toggleRenderMode (RenderMode mode);
|
bool toggleRenderMode (RenderMode mode);
|
||||||
///< Toggle a render mode.
|
///< Toggle a render mode.
|
||||||
///< \return Resulting mode
|
///< \return Resulting mode
|
||||||
|
|
||||||
|
std::pair<std::string, const ESM::Potion *> createRecord (const ESM::Potion& record);
|
||||||
|
///< Create a new recrod (of type potion) in the ESM store.
|
||||||
|
/// \return ID, pointer to created record
|
||||||
|
|
||||||
|
std::pair<std::string, const ESM::Class *> createRecord (const ESM::Class& record);
|
||||||
|
///< Create a new recrod (of type class) in the ESM store.
|
||||||
|
/// \return ID, pointer to created record
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,16 +23,200 @@
|
||||||
|
|
||||||
#include "bsa_archive.hpp"
|
#include "bsa_archive.hpp"
|
||||||
|
|
||||||
|
#include <OgreFileSystem.h>
|
||||||
#include <OgreArchive.h>
|
#include <OgreArchive.h>
|
||||||
#include <OgreArchiveFactory.h>
|
#include <OgreArchiveFactory.h>
|
||||||
#include <OgreArchiveManager.h>
|
#include <OgreArchiveManager.h>
|
||||||
#include "bsa_file.hpp"
|
#include "bsa_file.hpp"
|
||||||
#include <libs/mangle/stream/clients/ogre_datastream.hpp>
|
#include <libs/mangle/stream/clients/ogre_datastream.hpp>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
using namespace Ogre;
|
using namespace Ogre;
|
||||||
using namespace Mangle::Stream;
|
using namespace Mangle::Stream;
|
||||||
|
using namespace Bsa;
|
||||||
|
|
||||||
|
struct ciLessBoost : std::binary_function<std::string, std::string, bool>
|
||||||
|
{
|
||||||
|
bool operator() (const std::string & s1, const std::string & s2) const {
|
||||||
|
//case insensitive version of is_less
|
||||||
|
return lexicographical_compare(s1, s2, boost::algorithm::is_iless());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool fsstrict = false;
|
||||||
|
|
||||||
/// An OGRE Archive wrapping a BSAFile archive
|
/// An OGRE Archive wrapping a BSAFile archive
|
||||||
|
class DirArchive: public Ogre::FileSystemArchive
|
||||||
|
{
|
||||||
|
|
||||||
|
boost::filesystem::path currentdir;
|
||||||
|
std::map<std::string, std::vector<std::string>, ciLessBoost> m;
|
||||||
|
unsigned int cutoff;
|
||||||
|
|
||||||
|
bool comparePortion(std::string file1, std::string file2, int start, int size) const
|
||||||
|
{
|
||||||
|
for(int i = start; i < start+size; i++)
|
||||||
|
{
|
||||||
|
char one = file1.at(i);
|
||||||
|
char two = file2.at(i);
|
||||||
|
if(tolower(one) != tolower(two) )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DirArchive(const String& name)
|
||||||
|
: FileSystemArchive(name, "Dir"), currentdir (name)
|
||||||
|
{
|
||||||
|
mType = "Dir";
|
||||||
|
std::string s = name;
|
||||||
|
cutoff = s.size() + 1;
|
||||||
|
if(fsstrict == false)
|
||||||
|
populateMap(currentdir);
|
||||||
|
|
||||||
|
}
|
||||||
|
void populateMap(boost::filesystem::path d){
|
||||||
|
//need to cut off first
|
||||||
|
boost::filesystem::directory_iterator dir_iter(d), dir_end;
|
||||||
|
std::vector<std::string> filesind;
|
||||||
|
boost::filesystem::path f;
|
||||||
|
for(;dir_iter != dir_end; dir_iter++)
|
||||||
|
{
|
||||||
|
if(boost::filesystem::is_directory(*dir_iter))
|
||||||
|
populateMap(*dir_iter);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
f = *dir_iter;
|
||||||
|
std::string s = f.string();
|
||||||
|
|
||||||
|
std::string small;
|
||||||
|
if(cutoff < s.size())
|
||||||
|
small = s.substr(cutoff, s.size() - cutoff);
|
||||||
|
else
|
||||||
|
small = s.substr(cutoff - 1, s.size() - cutoff);
|
||||||
|
|
||||||
|
filesind.push_back(small);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::string small;
|
||||||
|
std::string original = d.string();
|
||||||
|
if(cutoff < original.size())
|
||||||
|
small = original.substr(cutoff, original.size() - cutoff);
|
||||||
|
else
|
||||||
|
small = original.substr(cutoff - 1, original.size() - cutoff);
|
||||||
|
m[small] = filesind;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isCaseSensitive() const { return fsstrict; }
|
||||||
|
|
||||||
|
// The archive is loaded in the constructor, and never unloaded.
|
||||||
|
void load() {}
|
||||||
|
void unload() {}
|
||||||
|
|
||||||
|
bool exists(const String& filename) {
|
||||||
|
std::string copy = filename;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < filename.size(); i++)
|
||||||
|
{
|
||||||
|
if(copy.at(i) == '\\' ){
|
||||||
|
copy.replace(i, 1, "/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(copy.at(0) == '\\' || copy.at(0) == '/')
|
||||||
|
{
|
||||||
|
copy.erase(0, 1);
|
||||||
|
}
|
||||||
|
if(fsstrict == true)
|
||||||
|
{
|
||||||
|
//std::cout << "fsstrict " << copy << "\n";
|
||||||
|
return FileSystemArchive::exists(copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int last = copy.size() - 1;
|
||||||
|
int i = last;
|
||||||
|
|
||||||
|
for (;last >= 0; i--)
|
||||||
|
{
|
||||||
|
if(copy.at(i) == '/' || copy.at(i) == '\\')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string folder = copy.substr(0, i); //folder with no slash
|
||||||
|
|
||||||
|
std::vector<std::string>& current = m[folder];
|
||||||
|
|
||||||
|
for(std::vector<std::string>::iterator iter = current.begin(); iter != current.end(); iter++)
|
||||||
|
{
|
||||||
|
if(comparePortion(*iter, copy, i + 1, copy.size() - i -1) == true){
|
||||||
|
return FileSystemArchive::exists(*iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataStreamPtr open(const String& filename, bool readonly = true) const
|
||||||
|
{
|
||||||
|
std::map<std::string, std::vector<std::string>, ciLessBoost> mlocal = m;
|
||||||
|
std::string copy = filename;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < filename.size(); i++)
|
||||||
|
{
|
||||||
|
if(copy.at(i) == '\\' ){
|
||||||
|
copy.replace(i, 1, "/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(copy.at(0) == '\\' || copy.at(0) == '/')
|
||||||
|
{
|
||||||
|
copy.erase(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fsstrict == true)
|
||||||
|
{
|
||||||
|
return FileSystemArchive::open(copy, readonly);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int last = copy.size() - 1;
|
||||||
|
int i = last;
|
||||||
|
|
||||||
|
for (;last >= 0; i--)
|
||||||
|
{
|
||||||
|
if(copy.at(i) == '/' || copy.at(i) == '\\')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string folder = copy.substr(0, i); //folder with no slash
|
||||||
|
std::vector<std::string> current = mlocal[folder];
|
||||||
|
|
||||||
|
for(std::vector<std::string>::iterator iter = current.begin(); iter != current.end(); iter++)
|
||||||
|
{
|
||||||
|
if(comparePortion(*iter, copy, i + 1, copy.size() - i -1) == true){
|
||||||
|
return FileSystemArchive::open(*iter, readonly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataStreamPtr p;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class BSAArchive : public Archive
|
class BSAArchive : public Archive
|
||||||
{
|
{
|
||||||
BSAFile arc;
|
BSAFile arc;
|
||||||
|
@ -145,7 +329,27 @@ public:
|
||||||
void destroyInstance( Archive* arch) { delete arch; }
|
void destroyInstance( Archive* arch) { delete arch; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class DirArchiveFactory : public FileSystemArchiveFactory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const String& getType() const
|
||||||
|
{
|
||||||
|
static String name = "Dir";
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
Archive *createInstance( const String& name )
|
||||||
|
{
|
||||||
|
return new DirArchive(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroyInstance( Archive* arch) { delete arch; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
static bool init = false;
|
static bool init = false;
|
||||||
|
static bool init2 = false;
|
||||||
|
|
||||||
static void insertBSAFactory()
|
static void insertBSAFactory()
|
||||||
{
|
{
|
||||||
if(!init)
|
if(!init)
|
||||||
|
@ -155,6 +359,20 @@ static void insertBSAFactory()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void insertDirFactory()
|
||||||
|
{
|
||||||
|
if(!init2)
|
||||||
|
{
|
||||||
|
ArchiveManager::getSingleton().addArchiveFactory( new DirArchiveFactory );
|
||||||
|
init2 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Bsa
|
||||||
|
{
|
||||||
|
|
||||||
// The function below is the only publicly exposed part of this file
|
// The function below is the only publicly exposed part of this file
|
||||||
|
|
||||||
void addBSA(const std::string& name, const std::string& group)
|
void addBSA(const std::string& name, const std::string& group)
|
||||||
|
@ -163,3 +381,14 @@ void addBSA(const std::string& name, const std::string& group)
|
||||||
ResourceGroupManager::getSingleton().
|
ResourceGroupManager::getSingleton().
|
||||||
addResourceLocation(name, "BSA", group);
|
addResourceLocation(name, "BSA", group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addDir(const std::string& name, const bool& fs, const std::string& group)
|
||||||
|
{
|
||||||
|
fsstrict = fs;
|
||||||
|
insertDirFactory();
|
||||||
|
|
||||||
|
ResourceGroupManager::getSingleton().
|
||||||
|
addResourceLocation(name, "Dir", group);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -22,12 +22,21 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#ifndef _BSA_ARCHIVE_H_
|
#ifndef BSA_BSA_ARCHIVE_H
|
||||||
#define _BSA_ARCHIVE_H_
|
#define BSA_BSA_ARCHIVE_H
|
||||||
|
|
||||||
|
namespace Bsa
|
||||||
|
{
|
||||||
|
|
||||||
/// Add the given BSA file as an input archive in the Ogre resource
|
/// Add the given BSA file as an input archive in the Ogre resource
|
||||||
/// system.
|
/// system.
|
||||||
void addBSA(const std::string& file, const std::string& group="General");
|
void addBSA(const std::string& file, const std::string& group="General");
|
||||||
|
void addDir(const std::string& file, const bool& fs, const std::string& group="General");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace Mangle::Stream;
|
using namespace Mangle::Stream;
|
||||||
|
using namespace Bsa;
|
||||||
|
|
||||||
/// Error handling
|
/// Error handling
|
||||||
void BSAFile::fail(const string &msg)
|
void BSAFile::fail(const string &msg)
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _BSA_FILE_H_
|
#ifndef BSA_BSA_FILE_H
|
||||||
#define _BSA_FILE_H_
|
#define BSA_BSA_FILE_H
|
||||||
|
|
||||||
#include <libs/mangle/stream/stream.hpp>
|
#include <libs/mangle/stream/stream.hpp>
|
||||||
#include <libs/platform/stdint.h>
|
#include <libs/platform/stdint.h>
|
||||||
|
@ -31,6 +31,9 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
namespace Bsa
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This class is used to read "Bethesda Archive Files", or BSAs.
|
This class is used to read "Bethesda Archive Files", or BSAs.
|
||||||
*/
|
*/
|
||||||
|
@ -131,4 +134,6 @@ class BSAFile
|
||||||
{ return files; }
|
{ return files; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace Bsa;
|
||||||
|
|
||||||
BSAFile bsa;
|
BSAFile bsa;
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace Mangle::Stream;
|
using namespace Mangle::Stream;
|
||||||
|
using namespace Bsa;
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,7 +19,7 @@ int main()
|
||||||
Root *root = new Root("","","");
|
Root *root = new Root("","","");
|
||||||
|
|
||||||
// Add the BSA
|
// Add the BSA
|
||||||
addBSA("../../data/Morrowind.bsa");
|
Bsa::addBSA("../../data/Morrowind.bsa");
|
||||||
|
|
||||||
// Pick a sample file
|
// Pick a sample file
|
||||||
String tex = "textures\\tx_natural_cavern_wall13.dds";
|
String tex = "textures\\tx_natural_cavern_wall13.dds";
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
|
||||||
|
using namespace Misc;
|
||||||
|
|
||||||
ESM_Context ESMReader::getContext()
|
ESM_Context ESMReader::getContext()
|
||||||
{
|
{
|
||||||
// Update the file position before returning
|
// Update the file position before returning
|
||||||
|
@ -324,7 +326,7 @@ std::string ESMReader::getString(int size)
|
||||||
mEsm->read(ptr, size);
|
mEsm->read(ptr, size);
|
||||||
|
|
||||||
// Convert to UTF8 and return
|
// Convert to UTF8 and return
|
||||||
return ToUTF8::getUtf8(ToUTF8::WINDOWS_1252);
|
return ToUTF8::getUtf8(mEncoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESMReader::fail(const std::string &msg)
|
void ESMReader::fail(const std::string &msg)
|
||||||
|
@ -342,4 +344,21 @@ void ESMReader::fail(const std::string &msg)
|
||||||
throw std::runtime_error(ss.str());
|
throw std::runtime_error(ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ESMReader::setEncoding(const std::string& encoding)
|
||||||
|
{
|
||||||
|
if (encoding == "win1250")
|
||||||
|
{
|
||||||
|
mEncoding = ToUTF8::WINDOWS_1250;
|
||||||
|
}
|
||||||
|
else if (encoding == "win1251")
|
||||||
|
{
|
||||||
|
mEncoding = ToUTF8::WINDOWS_1251;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Default Latin encoding
|
||||||
|
mEncoding = ToUTF8::WINDOWS_1252;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -350,6 +350,9 @@ public:
|
||||||
/// Used for error handling
|
/// Used for error handling
|
||||||
void fail(const std::string &msg);
|
void fail(const std::string &msg);
|
||||||
|
|
||||||
|
/// Sets font encoding for ESM strings
|
||||||
|
void setEncoding(const std::string& encoding);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Mangle::Stream::StreamPtr mEsm;
|
Mangle::Stream::StreamPtr mEsm;
|
||||||
|
|
||||||
|
@ -360,6 +363,7 @@ private:
|
||||||
|
|
||||||
SaveData mSaveData;
|
SaveData mSaveData;
|
||||||
MasterList mMasters;
|
MasterList mMasters;
|
||||||
|
ToUTF8::FromType mEncoding;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
|
||||||
void Misc::load(ESMReader &esm)
|
void Miscellaneous::load(ESMReader &esm)
|
||||||
{
|
{
|
||||||
model = esm.getHNString("MODL");
|
model = esm.getHNString("MODL");
|
||||||
name = esm.getHNOString("FNAM");
|
name = esm.getHNOString("FNAM");
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace ESM
|
||||||
* carried, bought and sold. It also includes keys.
|
* carried, bought and sold. It also includes keys.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct Misc
|
struct Miscellaneous
|
||||||
{
|
{
|
||||||
struct MCDTstruct
|
struct MCDTstruct
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace ESMS
|
namespace ESMS
|
||||||
{
|
{
|
||||||
|
@ -99,7 +100,7 @@ namespace ESMS
|
||||||
CellRefList<ItemLevList, D> itemLists;
|
CellRefList<ItemLevList, D> itemLists;
|
||||||
CellRefList<ESM::Light, D> lights;
|
CellRefList<ESM::Light, D> lights;
|
||||||
CellRefList<Tool, D> lockpicks;
|
CellRefList<Tool, D> lockpicks;
|
||||||
CellRefList<Misc, D> miscItems;
|
CellRefList<Miscellaneous, D> miscItems;
|
||||||
CellRefList<NPC, D> npcs;
|
CellRefList<NPC, D> npcs;
|
||||||
CellRefList<Probe, D> probes;
|
CellRefList<Probe, D> probes;
|
||||||
CellRefList<Repair, D> repairs;
|
CellRefList<Repair, D> repairs;
|
||||||
|
@ -187,7 +188,14 @@ namespace ESMS
|
||||||
// Get each reference in turn
|
// Get each reference in turn
|
||||||
while(cell->getNextRef(esm, ref))
|
while(cell->getNextRef(esm, ref))
|
||||||
{
|
{
|
||||||
int rec = store.find(ref.refID);
|
std::string lowerCase;
|
||||||
|
|
||||||
|
std::transform (ref.refID.begin(), ref.refID.end(), std::back_inserter (lowerCase),
|
||||||
|
(int(*)(int)) std::tolower);
|
||||||
|
|
||||||
|
int rec = store.find(ref.refID);
|
||||||
|
|
||||||
|
ref.refID = lowerCase;
|
||||||
|
|
||||||
/* We can optimize this further by storing the pointer to the
|
/* We can optimize this further by storing the pointer to the
|
||||||
record itself in store.all, so that we don't need to look it
|
record itself in store.all, so that we don't need to look it
|
||||||
|
|
|
@ -85,7 +85,13 @@ void ESMStore::load(ESMReader &esm)
|
||||||
dialogue = 0;
|
dialogue = 0;
|
||||||
|
|
||||||
// Insert the reference into the global lookup
|
// Insert the reference into the global lookup
|
||||||
if(!id.empty())
|
if(!id.empty() &&
|
||||||
|
(n.val==REC_ACTI || n.val==REC_ALCH || n.val==REC_APPA || n.val==REC_ARMO ||
|
||||||
|
n.val==REC_BOOK || n.val==REC_CLOT || n.val==REC_CONT || n.val==REC_CREA ||
|
||||||
|
n.val==REC_DOOR || n.val==REC_INGR || n.val==REC_LEVC || n.val==REC_LEVI ||
|
||||||
|
n.val==REC_LIGH || n.val==REC_LOCK || n.val==REC_MISC || n.val==REC_NPC_ ||
|
||||||
|
n.val==REC_PROB || n.val==REC_REPA || n.val==REC_STAT || n.val==REC_WEAP)
|
||||||
|
)
|
||||||
all[id] = n.val;
|
all[id] = n.val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace ESMS
|
||||||
RecListT<ItemLevList> itemLists;
|
RecListT<ItemLevList> itemLists;
|
||||||
RecListT<Light> lights;
|
RecListT<Light> lights;
|
||||||
RecListT<Tool> lockpicks;
|
RecListT<Tool> lockpicks;
|
||||||
RecListT<Misc> miscItems;
|
RecListT<Miscellaneous> miscItems;
|
||||||
RecListWithIDT<NPC> npcs;
|
RecListWithIDT<NPC> npcs;
|
||||||
RecListT<LoadNPCC> npcChange;
|
RecListT<LoadNPCC> npcChange;
|
||||||
RecListT<Probe> probes;
|
RecListT<Probe> probes;
|
||||||
|
|
|
@ -20,9 +20,9 @@ class FileFinderT
|
||||||
|
|
||||||
void add(const boost::filesystem::path &pth)
|
void add(const boost::filesystem::path &pth)
|
||||||
{
|
{
|
||||||
std::string file = pth.string();
|
std::string file = pth.string();
|
||||||
std::string key = file.substr(cut);
|
std::string key = file.substr(cut);
|
||||||
owner->table[key] = file;
|
owner->table[key] = file;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -50,17 +50,18 @@ public:
|
||||||
|
|
||||||
bool has(const std::string& file) const
|
bool has(const std::string& file) const
|
||||||
{
|
{
|
||||||
return table.find(file) != table.end();
|
return table.find(file) != table.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the full path from a relative path.
|
// Find the full path from a relative path.
|
||||||
const std::string &lookup(const std::string& file) const
|
const std::string &lookup(const std::string& file) const
|
||||||
{
|
{
|
||||||
return table.find(file)->second;
|
return table.find(file)->second;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// The default is to use path_less for equality checks
|
// The default is to use path_less for equality checks
|
||||||
typedef FileFinderT<path_less> FileFinder;
|
typedef FileFinderT<path_less> FileFinder;
|
||||||
|
typedef FileFinderT<path_slash> FileFinderStrict;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -44,6 +44,41 @@ struct path_less
|
||||||
return compareString(a.c_str(), b.c_str()) < 0;
|
return compareString(a.c_str(), b.c_str()) < 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct path_slash
|
||||||
|
{
|
||||||
|
int compareChar(char a, char b) const
|
||||||
|
{
|
||||||
|
if(a>b) return 1;
|
||||||
|
else if(a<b) return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int comparePathChar(char a, char b) const
|
||||||
|
{
|
||||||
|
if(a == '\\') a = '/';
|
||||||
|
if(b == '\\') b = '/';
|
||||||
|
return compareChar(a,b);
|
||||||
|
}
|
||||||
|
|
||||||
|
int compareString(const char *a, const char *b) const
|
||||||
|
{
|
||||||
|
while(*a && *b)
|
||||||
|
{
|
||||||
|
int i = comparePathChar(*a,*b);
|
||||||
|
if(i != 0) return i;
|
||||||
|
a++; b++;
|
||||||
|
}
|
||||||
|
// At this point, one or both of the chars is a null terminator.
|
||||||
|
// Normal char comparison will get the correct final result here.
|
||||||
|
return compareChar(*a,*b);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator() (const std::string& a, const std::string& b) const
|
||||||
|
{
|
||||||
|
return compareString(a.c_str(), b.c_str()) < 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <locale>
|
||||||
|
#include <cctype>
|
||||||
|
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
|
@ -21,11 +23,12 @@ namespace Files
|
||||||
return left<right;
|
return left<right;
|
||||||
|
|
||||||
std::size_t min = std::min (left.length(), right.length());
|
std::size_t min = std::min (left.length(), right.length());
|
||||||
|
std::locale loc;
|
||||||
|
|
||||||
for (std::size_t i=0; i<min; ++i)
|
for (std::size_t i=0; i<min; ++i)
|
||||||
{
|
{
|
||||||
char l = std::tolower (left[i]);
|
char l = std::tolower (left[i], loc);
|
||||||
char r = std::tolower (right[i]);
|
char r = std::tolower (right[i], loc);
|
||||||
|
|
||||||
if (l<r)
|
if (l<r)
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <OSX/macUtils.h>
|
#include <OSX/macUtils.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX
|
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE
|
||||||
#include <stdlib.h> //getenv
|
#include <stdlib.h> //getenv
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -19,7 +19,9 @@ std::string Files::getPath (PathTypeEnum parType, const std::string parApp, cons
|
||||||
if (parType==Path_ConfigGlobal)
|
if (parType==Path_ConfigGlobal)
|
||||||
{
|
{
|
||||||
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
|
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
|
||||||
theBasePath = Ogre::macBundlePath() + "/Contents/MacOS/"; //FIXME do we have global/local with OSX?
|
boost::filesystem::path path(Ogre::macBundlePath());
|
||||||
|
path = path.parent_path();
|
||||||
|
theBasePath = path.string() + "/";
|
||||||
#elif OGRE_PLATFORM == OGRE_PLATFORM_LINUX
|
#elif OGRE_PLATFORM == OGRE_PLATFORM_LINUX
|
||||||
theBasePath = "/etc/"+parApp+"/";
|
theBasePath = "/etc/"+parApp+"/";
|
||||||
#else
|
#else
|
||||||
|
@ -29,9 +31,7 @@ std::string Files::getPath (PathTypeEnum parType, const std::string parApp, cons
|
||||||
}
|
}
|
||||||
else if (parType==Path_ConfigUser)
|
else if (parType==Path_ConfigUser)
|
||||||
{
|
{
|
||||||
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
|
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE
|
||||||
theBasePath = Ogre::macBundlePath() + "/Contents/MacOS/"; //FIXME do we have global/local with OSX?
|
|
||||||
#elif OGRE_PLATFORM == OGRE_PLATFORM_LINUX
|
|
||||||
const char* theDir;
|
const char* theDir;
|
||||||
if ((theDir = getenv("OPENMW_HOME")) != NULL)
|
if ((theDir = getenv("OPENMW_HOME")) != NULL)
|
||||||
{
|
{
|
||||||
|
|
|
@ -134,8 +134,7 @@ namespace Interpreter
|
||||||
throw std::runtime_error (error.str());
|
throw std::runtime_error (error.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
Interpreter::Interpreter (Context& context)
|
Interpreter::Interpreter()
|
||||||
: mRuntime (context)
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
Interpreter::~Interpreter()
|
Interpreter::~Interpreter()
|
||||||
|
@ -195,11 +194,11 @@ namespace Interpreter
|
||||||
mSegment5.insert (std::make_pair (code, opcode));
|
mSegment5.insert (std::make_pair (code, opcode));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::run (const Type_Code *code, int codeSize)
|
void Interpreter::run (const Type_Code *code, int codeSize, Context& context)
|
||||||
{
|
{
|
||||||
assert (codeSize>=4);
|
assert (codeSize>=4);
|
||||||
|
|
||||||
mRuntime.configure (code, codeSize);
|
mRuntime.configure (code, codeSize, context);
|
||||||
|
|
||||||
int opcodes = static_cast<int> (code[0]);
|
int opcodes = static_cast<int> (code[0]);
|
||||||
|
|
||||||
|
|
|
@ -21,23 +21,23 @@ namespace Interpreter
|
||||||
std::map<int, Opcode1 *> mSegment3;
|
std::map<int, Opcode1 *> mSegment3;
|
||||||
std::map<int, Opcode2 *> mSegment4;
|
std::map<int, Opcode2 *> mSegment4;
|
||||||
std::map<int, Opcode0 *> mSegment5;
|
std::map<int, Opcode0 *> mSegment5;
|
||||||
|
|
||||||
// not implemented
|
// not implemented
|
||||||
Interpreter (const Interpreter&);
|
Interpreter (const Interpreter&);
|
||||||
Interpreter& operator= (const Interpreter&);
|
Interpreter& operator= (const Interpreter&);
|
||||||
|
|
||||||
void execute (Type_Code code);
|
void execute (Type_Code code);
|
||||||
|
|
||||||
void abortUnknownCode (int segment, int opcode);
|
void abortUnknownCode (int segment, int opcode);
|
||||||
|
|
||||||
void abortUnknownSegment (Type_Code code);
|
void abortUnknownSegment (Type_Code code);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Interpreter (Context& context);
|
Interpreter();
|
||||||
|
|
||||||
~Interpreter();
|
~Interpreter();
|
||||||
|
|
||||||
void installSegment0 (int code, Opcode1 *opcode);
|
void installSegment0 (int code, Opcode1 *opcode);
|
||||||
///< ownership of \a opcode is transferred to *this.
|
///< ownership of \a opcode is transferred to *this.
|
||||||
|
|
||||||
|
@ -55,10 +55,9 @@ namespace Interpreter
|
||||||
|
|
||||||
void installSegment5 (int code, Opcode0 *opcode);
|
void installSegment5 (int code, Opcode0 *opcode);
|
||||||
///< ownership of \a opcode is transferred to *this.
|
///< ownership of \a opcode is transferred to *this.
|
||||||
|
|
||||||
void run (const Type_Code *code, int codeSize);
|
void run (const Type_Code *code, int codeSize, Context& context);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -7,50 +7,51 @@
|
||||||
|
|
||||||
namespace Interpreter
|
namespace Interpreter
|
||||||
{
|
{
|
||||||
Runtime::Runtime (Context& context) : mContext (context), mCode (0), mPC (0) {}
|
Runtime::Runtime() : mContext (0), mCode (0), mPC (0) {}
|
||||||
|
|
||||||
int Runtime::getPC() const
|
int Runtime::getPC() const
|
||||||
{
|
{
|
||||||
return mPC;
|
return mPC;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Runtime::getIntegerLiteral (int index) const
|
int Runtime::getIntegerLiteral (int index) const
|
||||||
{
|
{
|
||||||
assert (index>=0 && index<static_cast<int> (mCode[1]));
|
assert (index>=0 && index<static_cast<int> (mCode[1]));
|
||||||
|
|
||||||
const Type_Code *literalBlock = mCode + 4 + mCode[0];
|
const Type_Code *literalBlock = mCode + 4 + mCode[0];
|
||||||
|
|
||||||
return *reinterpret_cast<const int *> (&literalBlock[index]);
|
return *reinterpret_cast<const int *> (&literalBlock[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
float Runtime::getFloatLiteral (int index) const
|
float Runtime::getFloatLiteral (int index) const
|
||||||
{
|
{
|
||||||
assert (index>=0 && index<static_cast<int> (mCode[2]));
|
assert (index>=0 && index<static_cast<int> (mCode[2]));
|
||||||
|
|
||||||
const Type_Code *literalBlock = mCode + 4 + mCode[0] + mCode[1];
|
const Type_Code *literalBlock = mCode + 4 + mCode[0] + mCode[1];
|
||||||
|
|
||||||
return *reinterpret_cast<const float *> (&literalBlock[index]);
|
return *reinterpret_cast<const float *> (&literalBlock[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Runtime::getStringLiteral (int index) const
|
std::string Runtime::getStringLiteral (int index) const
|
||||||
{
|
{
|
||||||
assert (index>=0 && index<static_cast<int> (mCode[3]));
|
assert (index>=0 && index<static_cast<int> (mCode[3]));
|
||||||
|
|
||||||
const char *literalBlock =
|
const char *literalBlock =
|
||||||
reinterpret_cast<const char *> (mCode + 4 + mCode[0] + mCode[1] + mCode[2]);
|
reinterpret_cast<const char *> (mCode + 4 + mCode[0] + mCode[1] + mCode[2]);
|
||||||
|
|
||||||
for (; index; --index)
|
for (; index; --index)
|
||||||
{
|
{
|
||||||
literalBlock += std::strlen (literalBlock) + 1;
|
literalBlock += std::strlen (literalBlock) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return literalBlock;
|
return literalBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Runtime::configure (const Interpreter::Type_Code *code, int codeSize)
|
void Runtime::configure (const Interpreter::Type_Code *code, int codeSize, Context& context)
|
||||||
{
|
{
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
|
mContext = &context;
|
||||||
mCode = code;
|
mCode = code;
|
||||||
mCodeSize = codeSize;
|
mCodeSize = codeSize;
|
||||||
mPC = 0;
|
mPC = 0;
|
||||||
|
@ -58,54 +59,55 @@ namespace Interpreter
|
||||||
|
|
||||||
void Runtime::clear()
|
void Runtime::clear()
|
||||||
{
|
{
|
||||||
|
mContext = 0;
|
||||||
mCode = 0;
|
mCode = 0;
|
||||||
mCodeSize = 0;
|
mCodeSize = 0;
|
||||||
mStack.clear();
|
mStack.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Runtime::setPC (int PC)
|
void Runtime::setPC (int PC)
|
||||||
{
|
{
|
||||||
mPC = PC;
|
mPC = PC;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Runtime::push (const Data& data)
|
void Runtime::push (const Data& data)
|
||||||
{
|
{
|
||||||
mStack.push_back (data);
|
mStack.push_back (data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Runtime::push (Type_Integer value)
|
void Runtime::push (Type_Integer value)
|
||||||
{
|
{
|
||||||
Data data;
|
Data data;
|
||||||
data.mInteger = value;
|
data.mInteger = value;
|
||||||
push (data);
|
push (data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Runtime::push (Type_Float value)
|
void Runtime::push (Type_Float value)
|
||||||
{
|
{
|
||||||
Data data;
|
Data data;
|
||||||
data.mFloat = value;
|
data.mFloat = value;
|
||||||
push (data);
|
push (data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Runtime::pop()
|
void Runtime::pop()
|
||||||
{
|
{
|
||||||
if (mStack.empty())
|
if (mStack.empty())
|
||||||
throw std::runtime_error ("stack underflow");
|
throw std::runtime_error ("stack underflow");
|
||||||
|
|
||||||
mStack.resize (mStack.size()-1);
|
mStack.resize (mStack.size()-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Data& Runtime::operator[] (int Index)
|
Data& Runtime::operator[] (int Index)
|
||||||
{
|
{
|
||||||
if (Index<0 || Index>=static_cast<int> (mStack.size()))
|
if (Index<0 || Index>=static_cast<int> (mStack.size()))
|
||||||
throw std::runtime_error ("stack index out of range");
|
throw std::runtime_error ("stack index out of range");
|
||||||
|
|
||||||
return mStack[mStack.size()-Index-1];
|
return mStack[mStack.size()-Index-1];
|
||||||
}
|
}
|
||||||
|
|
||||||
Context& Runtime::getContext()
|
Context& Runtime::getContext()
|
||||||
{
|
{
|
||||||
return mContext;
|
assert (mContext);
|
||||||
|
return *mContext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,52 +11,52 @@ namespace Interpreter
|
||||||
class Context;
|
class Context;
|
||||||
|
|
||||||
/// Runtime data and engine interface
|
/// Runtime data and engine interface
|
||||||
|
|
||||||
class Runtime
|
class Runtime
|
||||||
{
|
{
|
||||||
Context& mContext;
|
Context *mContext;
|
||||||
const Type_Code *mCode;
|
const Type_Code *mCode;
|
||||||
int mCodeSize;
|
int mCodeSize;
|
||||||
int mPC;
|
int mPC;
|
||||||
std::vector<Data> mStack;
|
std::vector<Data> mStack;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Runtime (Context& context);
|
Runtime ();
|
||||||
|
|
||||||
int getPC() const;
|
int getPC() const;
|
||||||
///< return program counter.
|
///< return program counter.
|
||||||
|
|
||||||
int getIntegerLiteral (int index) const;
|
int getIntegerLiteral (int index) const;
|
||||||
|
|
||||||
float getFloatLiteral (int index) const;
|
float getFloatLiteral (int index) const;
|
||||||
|
|
||||||
std::string getStringLiteral (int index) const;
|
std::string getStringLiteral (int index) const;
|
||||||
|
|
||||||
void configure (const Type_Code *code, int codeSize);
|
void configure (const Type_Code *code, int codeSize, Context& context);
|
||||||
///< \a context and \a code must exist as least until either configure, clear or
|
///< \a context and \a code must exist as least until either configure, clear or
|
||||||
/// the destructor is called. \a codeSize is given in 32-bit words.
|
/// the destructor is called. \a codeSize is given in 32-bit words.
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
void setPC (int PC);
|
void setPC (int PC);
|
||||||
///< set program counter.
|
///< set program counter.
|
||||||
|
|
||||||
void push (const Data& data);
|
void push (const Data& data);
|
||||||
///< push data on stack
|
///< push data on stack
|
||||||
|
|
||||||
void push (Type_Integer value);
|
void push (Type_Integer value);
|
||||||
///< push integer data on stack.
|
///< push integer data on stack.
|
||||||
|
|
||||||
void push (Type_Float value);
|
void push (Type_Float value);
|
||||||
///< push float data on stack.
|
///< push float data on stack.
|
||||||
|
|
||||||
void pop();
|
void pop();
|
||||||
///< pop stack
|
///< pop stack
|
||||||
|
|
||||||
Data& operator[] (int Index);
|
Data& operator[] (int Index);
|
||||||
///< Access stack member, counted from the top.
|
///< Access stack member, counted from the top.
|
||||||
|
|
||||||
Context& getContext();
|
Context& getContext();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,13 @@
|
||||||
|
|
||||||
#include <OgrePrerequisites.h>
|
#include <OgrePrerequisites.h>
|
||||||
|
|
||||||
|
namespace Misc
|
||||||
|
{
|
||||||
|
|
||||||
bool isFile(const char *name)
|
bool isFile(const char *name)
|
||||||
{
|
{
|
||||||
boost::filesystem::path cfg_file_path(name);
|
boost::filesystem::path cfg_file_path(name);
|
||||||
return boost::filesystem::exists(cfg_file_path);
|
return boost::filesystem::exists(cfg_file_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
#ifndef __FILEOPS_H_
|
#ifndef MISC_FILEOPS_H
|
||||||
#define __FILEOPS_H_
|
#define MISC_FILEOPS_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
namespace Misc
|
||||||
|
{
|
||||||
|
|
||||||
/// Check if a given path is an existing file (not a directory)
|
/// Check if a given path is an existing file (not a directory)
|
||||||
bool isFile(const char *name);
|
bool isFile(const char *name);
|
||||||
|
|
||||||
|
@ -10,4 +13,6 @@ bool isFile(const char *name);
|
||||||
std::string macBundlePath();
|
std::string macBundlePath();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|