Merge remote-tracking branch 'upstream/master'

actorid
Pieter van der Kloet 14 years ago
commit 0a04008fc9

4
.gitignore vendored

@ -1,4 +1,6 @@
build
*~
Doxygen
prebuilt
prebuilt
apps/openmw/config.hpp
Docs/mainpage.hpp

@ -2,8 +2,23 @@ project(OpenMW)
IF (APPLE)
set(APP_BUNDLE_DIR "${OpenMW_BINARY_DIR}/OpenMW.app")
# using 10.6 sdk
set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.6.sdk")
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
option(USE_AUDIERE "use Audiere 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")
else()
if (APPLE)
# set path inside bundle
set(MORROWIND_DATA_FILES "../data" CACHE PATH "location of Morrowind data files")
set(MORROWIND_RESOURCE_FILES "Contents/Resources/resources" CACHE PATH "location of OpenMW resources 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")
else()
set(MORROWIND_DATA_FILES "data" CACHE PATH "location of Morrowind data 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(BULLET_ROOT "${PREBUILT_DIR}/bullet")
ELSE()
message (STATUS "OpenMW pre-built binaries not found. Using standard locations.")
ENDIF()
@ -365,7 +380,7 @@ endif()
if (APPLE)
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
"${APP_BUNDLE_DIR}/Contents/Info.plist" COPYONLY)
@ -384,9 +399,6 @@ if (APPLE)
configure_file(${OGRE_PLUGIN_DIR}/Plugin_ParticleFX.dylib
"${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)
@ -397,8 +409,9 @@ if (CMAKE_COMPILER_IS_GNUCC)
endif (CMAKE_COMPILER_IS_GNUCC)
# Apple bundling
# TODO REWRITE!
if (APPLE)
set(MISC_FILES
set(MISC_FILES
${APP_BUNDLE_DIR}/Contents/MacOS/openmw.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)
set(PACKAGE_MAINTAINER "${GIT_NAME} <${GIT_EMAIL}>")
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 "0.10.0")
set(VERSION_STRING "${OPENMW_VERSION}")
set(PACKAGE_MAINTAINER "unknown")
endif()
@ -483,6 +495,8 @@ if (BUILD_ESMTOOL)
add_subdirectory( apps/esmtool )
endif()
add_subdirectory( apps/launcher )
if (WIN32)
if (MSVC)
if (USE_DEBUG_CONSOLE)

@ -576,6 +576,7 @@ WARN_LOGFILE =
INPUT = apps \
components \
libs \
Docs
# 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

@ -576,6 +576,7 @@ WARN_LOGFILE =
INPUT = apps \
components \
libs \
Docs
# 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

@ -0,0 +1,5 @@
/// \mainpage
///
/// This is the source documentation for:
///
/// OpenMW @OPENMW_VERSION@

@ -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()

@ -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

@ -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 &currentPath, 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 &currentIndex, 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 &currentIndex, 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 &currentIndex, 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 &currentIndex, 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 &currentIndex, 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 &currentMaster, 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 &currentIndex, 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 &current)
{
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();
}

@ -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 &current);
// 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

@ -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());
}

@ -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

@ -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

@ -0,0 +1 @@
IDI_ICON1 ICON DISCARDABLE "resources/images/openmw.ico"

@ -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());
}

@ -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

@ -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();
}

@ -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 &currentFile, 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();
}

@ -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

@ -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);
}

@ -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

@ -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);
}

@ -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

@ -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;
}

@ -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

@ -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 );
}

@ -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

@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 498 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 793 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

@ -0,0 +1,8 @@
[Icon Theme]
Name=Tango
Comment=Tango Theme
Inherits=default
Directories=16x16
[16x16]
Size=16

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 KiB

@ -1,5 +1,9 @@
project(OpenMW)
# config file
configure_file ("${OpenMW_SOURCE_DIR}/config.hpp.cmake" "${OpenMW_SOURCE_DIR}/config.hpp")
# local files
set(GAME
@ -7,7 +11,8 @@ set(GAME
engine.cpp
)
set(GAME_HEADER
engine.hpp)
engine.hpp
config.hpp)
source_group(game FILES ${GAME} ${GAME_HEADER})
set(GAMEREND
@ -50,6 +55,7 @@ set(GAMEGUI_HEADER
mwgui/dialogue_history.hpp
mwgui/window_base.hpp
mwgui/stats_window.hpp
mwgui/messagebox.hpp
)
set(GAMEGUI
mwgui/window_manager.cpp
@ -65,6 +71,7 @@ set(GAMEGUI
mwgui/dialogue_history.cpp
mwgui/window_base.cpp
mwgui/stats_window.cpp
mwgui/messagebox.cpp
)
source_group(apps\\openmw\\mwgui FILES ${GAMEGUI_HEADER} ${GAMEGUI})

@ -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 <OgreVector3.h>
#include <Ogre.h>
#include "components/esm/records.hpp"
#include <components/esm_store/cell_store.hpp>
@ -44,10 +45,6 @@
#include <OgreRoot.h>
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
#include <OSX/macUtils.h>
#endif
#include <MyGUI_WidgetManager.h>
#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)
{
@ -92,6 +89,8 @@ bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt)
std::string effect;
MWWorld::Ptr::CellStore *current = mEnvironment.mWorld->getPlayer().getPlayer().getCell();
//If the region has changed
if(!(current->cell->data.flags & current->cell->Interior) && timer.elapsed() >= 10){
timer.restart();
@ -153,6 +152,9 @@ bool OMW::Engine::frameStarted(const Ogre::FrameEvent& evt)
{
mEnvironment.mFrameDuration = evt.timeSinceLastFrame;
//
mEnvironment.mWindowManager->onFrame(mEnvironment.mFrameDuration);
// global scripts
mEnvironment.mGlobalScripts->run (mEnvironment);
@ -246,8 +248,12 @@ void OMW::Engine::loadBSA()
for (Files::MultiDirCollection::TIter iter (bsa.begin()); iter!=bsa.end(); ++iter)
{
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
@ -338,15 +344,12 @@ void OMW::Engine::go()
ogreCfg.insert(0, cfgUserDir);
//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);
}
mOgre.configure(!isFile(ogreCfg.c_str()), cfgUserDir, plugCfg, false);
addResourcesDirectory (mDataDir / "Meshes");
addResourcesDirectory (mDataDir / "Textures");
mOgre.configure(!Misc::isFile(ogreCfg.c_str()), cfgUserDir, plugCfg, false);
// This has to be added BEFORE MyGUI is initialized, as it needs
// to find core.xml here.
@ -358,12 +361,12 @@ void OMW::Engine::go()
loadBSA();
// 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);
// Create the world
mEnvironment.mWorld = new MWWorld::World (mOgre, mPhysicEngine, mFileCollections, mMaster,
mResDir, mNewGame, mEnvironment);
mResDir, mNewGame, mEnvironment, mEncoding);
// Set up the GUI system
mGuiManager = new OEngine::GUI::MyGUIManager(mOgre.getWindow(), mOgre.getScene(), false, cfgDir);
@ -384,7 +387,7 @@ void OMW::Engine::go()
mOgre.getCamera(),
mEnvironment.mWorld->getStore(),
(mDataDir),
mUseSound);
mUseSound, mFSStrict);
// Create script system
mScriptContext = new MWScript::CompilerContext (MWScript::CompilerContext::Type_Full,
@ -512,3 +515,8 @@ void OMW::Engine::setCompileAll (bool all)
{
mCompileAll = all;
}
void OMW::Engine::setEncoding(const std::string& encoding)
{
mEncoding = encoding;
}

@ -56,6 +56,7 @@ namespace OMW
class Engine : private Ogre::FrameListener
{
std::string mEncoding;
boost::filesystem::path mDataDir;
boost::filesystem::path mResDir;
OEngine::Render::OgreRenderer mOgre;
@ -100,7 +101,7 @@ namespace OMW
void executeLocalScripts();
virtual bool frameStarted(const Ogre::FrameEvent& evt);
virtual bool frameRenderingQueued (const Ogre::FrameEvent& evt);
/// Process pending commands
@ -157,6 +158,9 @@ namespace OMW
/// Compile all scripts (excludign dialogue scripts) at startup?
void setCompileAll (bool all);
/// Font encoding
void setEncoding(const std::string& encoding);
};
}

@ -26,6 +26,13 @@
#endif
// for Ogre::macBundlePath
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
#include <OSX/macUtils.h>
#endif
#include "config.hpp"
using namespace std;
/// 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");
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> >()
->default_value (std::vector<std::string>(), "data")
->multitoken(),
@ -77,6 +85,13 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
( "fs-strict", boost::program_options::value<bool>()->
implicit_value (true)->default_value (false),
"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;
@ -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
//Otherwise try getPath
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");
}
@ -105,10 +120,39 @@ bool parseOptions (int argc, char**argv, OMW::Engine& engine)
if (globalConfigFile.is_open())
bpo::store ( bpo::parse_config_file (globalConfigFile, desc), variables);
bool run = true;
if (variables.count ("help"))
{
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;
// 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
@ -177,7 +221,7 @@ int main(int argc, char**argv)
{
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
// 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);
#endif

@ -42,7 +42,7 @@ namespace MWClass
ItemLevList::registerSelf();
Light::registerSelf();
Lockpick::registerSelf();
Misc::registerSelf();
Miscellaneous::registerSelf();
Probe::registerSelf();
Repair::registerSelf();
Static::registerSelf();

@ -34,7 +34,7 @@ namespace MWClass
if (!model.empty())
{
MWRender::Rendering rendering (cellRender, ref->ref);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertMesh("meshes\\" + model);
cellRender.insertActorPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));
}

@ -14,11 +14,11 @@
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
{
ESMS::LiveCellRef<ESM::Misc, MWWorld::RefData> *ref =
ptr.get<ESM::Misc>();
ESMS::LiveCellRef<ESM::Miscellaneous, MWWorld::RefData> *ref =
ptr.get<ESM::Miscellaneous>();
assert (ref->base != NULL);
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 =
ptr.get<ESM::Misc>();
ESMS::LiveCellRef<ESM::Miscellaneous, MWWorld::RefData> *ref =
ptr.get<ESM::Miscellaneous>();
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
{
return boost::shared_ptr<MWWorld::Action> (
new MWWorld::ActionTake (ptr));
}
void Misc::insertIntoContainer (const MWWorld::Ptr& ptr,
void Miscellaneous::insertIntoContainer (const MWWorld::Ptr& ptr,
MWWorld::ContainerStore<MWWorld::RefData>& containerStore) const
{
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 =
ptr.get<ESM::Misc>();
ESMS::LiveCellRef<ESM::Miscellaneous, MWWorld::RefData> *ref =
ptr.get<ESM::Miscellaneous>();
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
{
class Misc : public MWWorld::Class
class Miscellaneous : public MWWorld::Class
{
public:

@ -95,8 +95,7 @@ namespace MWClass
upperright[uppernumbers++] = 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 *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");
//std::cout << "RACE" << bodyRaceID << "\n";
Ogre::Vector3 pos2 = Ogre::Vector3( 0, .5, 75);
if (groin){
@ -124,10 +121,9 @@ namespace MWClass
}
if (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){
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);
@ -218,9 +214,6 @@ namespace MWClass
if(hand)
{
//std::cout << "WE FOUND A HAND\n";
//-50, 0, -120
//std::cout << "WE FOUND HANDS\n";
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)
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
upperleft[uppernumbers] = npcName + "hand";
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), upperright, uppernumbers);
}
@ -244,7 +236,6 @@ namespace MWClass
else
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\\" + 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
upperleft[uppernumbers] = npcName + "hand";
upperright[uppernumbers++] = npcName + "hand2";

@ -19,7 +19,7 @@ namespace MWClass
const std::string &model = ref->base->model;
if (!model.empty())
{
MWRender::Rendering rendering (cellRender, ref->ref);
MWRender::Rendering rendering (cellRender, ref->ref, true);
cellRender.insertMesh ("meshes\\" + model);
cellRender.insertObjectPhysics();
ref->mData.setHandle (rendering.end (ref->mData.isEnabled()));

@ -171,6 +171,22 @@ namespace MWGui
MyGUI::KeyCode key,
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;
// Traverse history with up and down arrows
@ -223,11 +239,11 @@ namespace MWGui
try
{
ConsoleInterpreterContext interpreterContext (*this, mEnvironment, MWWorld::Ptr());
Interpreter::Interpreter interpreter (interpreterContext);
Interpreter::Interpreter interpreter;
MWScript::installOpcodes (interpreter);
std::vector<Interpreter::Type_Code> code;
output.getCode (code);
interpreter.run (&code[0], code.size());
interpreter.run (&code[0], code.size(), interpreterContext);
}
catch (const std::exception& error)
{
@ -237,4 +253,119 @@ namespace MWGui
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);
void acceptCommand(MyGUI::EditPtr _sender);
std::string complete( std::string input, std::vector<std::string> &matches );
};
}
#endif

@ -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;
}

@ -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_ClassPick,
GM_ClassCreate,
GM_Review
GM_Review,
// interactive MessageBox
GM_InterMessageBox
};
// Windows shown in inventory mode

@ -8,6 +8,7 @@
#include "dialogue.hpp"
#include "dialogue_history.hpp"
#include "stats_window.hpp"
#include "messagebox.hpp"
#include "../mwmechanics/mechanicsmanager.hpp"
#include "../mwinput/inputmanager.hpp"
@ -60,6 +61,7 @@ WindowManager::WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment
inventory = new InventoryWindow ();
#endif
console = new Console(w,h, environment, extensions);
mMessageBoxManager = new MessageBoxManager(this);
// The HUD is always on
hud->setVisible(true);
@ -82,6 +84,7 @@ WindowManager::WindowManager(MyGUI::Gui *_gui, MWWorld::Environment& environment
WindowManager::~WindowManager()
{
delete console;
delete mMessageBoxManager;
delete hud;
delete map;
delete menu;
@ -326,6 +329,14 @@ void WindowManager::updateVisible()
dialogueWindow->open();
return;
}
if(mode == GM_InterMessageBox)
{
if(!mMessageBoxManager->isInteractiveMessageBox()) {
setGuiMode(GM_Game);
}
return;
}
// Unsupported mode, switch back to game
@ -446,16 +457,22 @@ void WindowManager::removeDialog(OEngine::GUI::Layout*dialog)
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())
{
mMessageBoxManager->createMessageBox(message);
}
else
{
std::cout << "buttons: ";
std::copy (buttons.begin(), buttons.end(), std::ostream_iterator<std::string> (std::cout, ", "));
std::cout << std::endl;
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 ESM::GameSetting *setting = environment.mWorld->getStore().gameSettings.search(id);
@ -555,6 +572,11 @@ void WindowManager::onClassChoice(int _index)
};
}
void WindowManager::onFrame (float frameDuration)
{
mMessageBoxManager->onFrame(frameDuration);
}
namespace MWGui
{

@ -62,6 +62,7 @@ namespace MWGui
class CreateClassDialog;
class BirthDialog;
class ReviewDialog;
class MessageBoxManager;
struct ClassPoint
{
@ -84,6 +85,7 @@ namespace MWGui
MapWindow *map;
MainMenu *menu;
StatsWindow *stats;
MessageBoxManager *mMessageBoxManager;
#if 0
InventoryWindow *inventory;
#endif
@ -245,8 +247,13 @@ namespace MWGui
void removeDialog(OEngine::GUI::Layout* dialog);
///< Hides dialog and schedules dialog to be deleted.
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

@ -258,7 +258,7 @@ namespace MWInput
}
//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
input.capture();

@ -35,7 +35,7 @@ namespace MWRender
virtual ~CellRenderImp() {}
/// 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;
/// insert a mesh related to the most recent insertBegin call.
@ -71,10 +71,10 @@ namespace MWRender
public:
Rendering (CellRenderImp& cellRender, ESM::CellRef &ref)
Rendering (CellRenderImp& cellRender, ESM::CellRef &ref, bool static_ = false)
: mCellRender (cellRender), mEnd (false)
{
mCellRender.insertBegin (ref);
mCellRender.insertBegin (ref, static_);
}
~Rendering()

@ -31,12 +31,24 @@ float ExteriorCellRender::lightQuadraticRadiusMult = 1;
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);
isStatic = static_;
// Create and place scene node for this object
mInsert = mBase->createChildSceneNode();
@ -59,7 +71,7 @@ void ExteriorCellRender::insertBegin (ESM::CellRef &ref)
// Rotates first around z, then y, then x
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);
MeshPtr good2 = NIFLoader::load(mesh);
MeshPtr good2 = NifOgre::NIFLoader::load(mesh);
MovableObject *ent = mScene.getMgr()->createEntity(mesh);
@ -201,12 +213,21 @@ void ExteriorCellRender::insertMesh(const std::string &mesh)
{
assert (mInsert);
NIFLoader::load(mesh);
MovableObject *ent = mScene.getMgr()->createEntity(mesh);
mInsert->attachObject(ent);
NifOgre::NIFLoader::load(mesh);
Entity *ent = mScene.getMgr()->createEntity(mesh);
if (mInsertMesh.empty())
mInsertMesh = mesh;
if(!isStatic)
{
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()
@ -333,6 +354,8 @@ void ExteriorCellRender::show()
configureFog();
insertCell(mCell, mEnvironment);
sg->build();
}
void ExteriorCellRender::hide()
@ -341,15 +364,49 @@ void ExteriorCellRender::hide()
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()
{
if(mBase)
{
destroyAllAttachedMovableObjects(mBase);
mBase->removeAndDestroyAllChildren();
mScene.getMgr()->destroySceneNode(mBase);
}
mBase = NULL;
mBase = 0;
if (sg)
{
mScene.getMgr()->destroyStaticGeometry (sg);
sg = 0;
}
}
// Switch through lighting modes.

@ -6,6 +6,7 @@
#include "OgreColourValue.h"
#include <OgreMath.h>
#include <Ogre.h>
namespace Ogre
{
@ -57,19 +58,24 @@ namespace MWRender
std::string mInsertMesh;
Ogre::SceneNode *mNpcPart;
//the static geometry
Ogre::StaticGeometry *sg;
bool isStatic;
// 0 normal, 1 more bright, 2 max
int mAmbientMode;
Ogre::ColourValue mAmbientColor;
/// 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.
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);
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);
@ -95,8 +101,7 @@ namespace MWRender
public:
ExteriorCellRender(ESMS::CellStore<MWWorld::RefData> &_cell, MWWorld::Environment& environment,
MWScene &_scene)
: mCell(_cell), mEnvironment (environment), mScene(_scene), mBase(NULL), mInsert(NULL), mAmbientMode (0) {}
MWScene &_scene);
virtual ~ExteriorCellRender() { destroy(); }
@ -121,6 +126,10 @@ namespace MWRender
/// Remove the reference with the given handle permanently from the scene.
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.
void InteriorCellRender::insertBegin (ESM::CellRef &ref)
void InteriorCellRender::insertBegin (ESM::CellRef &ref, bool static_)
{
assert (!insert);
@ -106,7 +106,7 @@ void InteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec,
npcPart = parent->createChildSceneNode(sceneNodeName);
//npcPart->showBoundingBox(true);
MeshPtr good2 = NIFLoader::load(mesh);
MeshPtr good2 = NifOgre::NIFLoader::load(mesh);
MovableObject *ent = scene.getMgr()->createEntity(mesh);
//ent->extr
@ -182,11 +182,11 @@ void InteriorCellRender::insertMesh(const std::string &mesh, Ogre::Vector3 vec,
void InteriorCellRender::insertMesh(const std::string &mesh)
{
assert (insert);
assert (insert);
NIFLoader::load(mesh);
MovableObject *ent = scene.getMgr()->createEntity(mesh);
insert->attachObject(ent);
NifOgre::NIFLoader::load(mesh);
MovableObject *ent = scene.getMgr()->createEntity(mesh);
insert->attachObject(ent);
if (mInsertMesh.empty())
mInsertMesh = mesh;

@ -63,7 +63,7 @@ namespace MWRender
Ogre::ColourValue ambientColor;
/// 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 scaleMesh(Ogre::Vector3 axis, std::string sceneNodeName[], int elements);
/// insert a mesh related to the most recent insertBegin call.

@ -108,4 +108,5 @@ op 0x2000133: Journal
op 0x2000134: SetJournalIndex
op 0x2000135: GetJournalIndex
op 0x2000136: GetPCCell
opcodes 0x2000137-0x3ffffff unused
op 0x2000137: GetButtonPressed
opcodes 0x2000138-0x3ffffff unused

@ -15,42 +15,57 @@
namespace MWScript
{
namespace Gui
{
{
class OpEnableWindow : public Interpreter::Opcode0
{
MWGui::GuiWindow mWindow;
public:
OpEnableWindow (MWGui::GuiWindow window) : mWindow (window) {}
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context =
static_cast<InterpreterContext&> (runtime.getContext());
context.getWindowManager().allow (mWindow);
}
};
}
};
class OpShowDialogue : public Interpreter::Opcode0
{
MWGui::GuiMode mDialogue;
public:
OpShowDialogue (MWGui::GuiMode dialogue)
: mDialogue (dialogue)
{}
virtual void execute (Interpreter::Runtime& runtime)
{
InterpreterContext& context =
static_cast<InterpreterContext&> (runtime.getContext());
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 opcodeEnableClassMenu = 0x200000f;
@ -63,7 +78,8 @@ namespace MWScript
const int opcodeEnableStatsMenu = 0x2000016;
const int opcodeEnableRest = 0x2000017;
const int opcodeShowRestMenu = 0x2000018;
const int opcodeGetButtonPressed = 0x2000137;
void registerExtensions (Compiler::Extensions& extensions)
{
extensions.registerInstruction ("enablebirthmenu", "", opcodeEnableBirthMenu);
@ -80,10 +96,12 @@ namespace MWScript
extensions.registerInstruction ("enablerestmenu", "", opcodeEnableRest);
extensions.registerInstruction ("enablelevelupmenu", "", opcodeEnableRest);
extensions.registerInstruction ("showrestmenu", "", opcodeShowRestMenu);
extensions.registerFunction ("getbuttonpressed", 'l', "", opcodeGetButtonPressed);
}
void installOpcodes (Interpreter::Interpreter& interpreter)
{
interpreter.installSegment5 (opcodeEnableBirthMenu,
@ -115,6 +133,8 @@ namespace MWScript
interpreter.installSegment5 (opcodeShowRestMenu,
new OpShowDialogue (MWGui::GM_Rest));
interpreter.installSegment5 (opcodeGetButtonPressed, new OpGetButtonPressed);
}
}
}

@ -12,8 +12,6 @@
#include <components/compiler/scanner.hpp>
#include <components/compiler/context.hpp>
#include <components/interpreter/interpreter.hpp>
#include "extensions.hpp"
namespace MWScript
@ -21,7 +19,8 @@ namespace MWScript
ScriptManager::ScriptManager (const ESMS::ESMStore& store, bool verbose,
Compiler::Context& compilerContext)
: 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)
@ -99,9 +98,13 @@ namespace MWScript
if (!iter->second.empty())
try
{
Interpreter::Interpreter interpreter (interpreterContext);
installOpcodes (interpreter);
interpreter.run (&iter->second[0], iter->second.size());
if (!mOpcodesInstalled)
{
installOpcodes (mInterpreter);
mOpcodesInstalled = true;
}
mInterpreter.run (&iter->second[0], iter->second.size(), interpreterContext);
}
catch (const std::exception& e)
{

@ -8,6 +8,7 @@
#include <components/compiler/streamerrorhandler.hpp>
#include <components/compiler/fileparser.hpp>
#include <components/interpreter/interpreter.hpp>
#include <components/interpreter/types.hpp>
namespace ESMS
@ -35,6 +36,8 @@ namespace MWScript
bool mVerbose;
Compiler::Context& mCompilerContext;
Compiler::FileParser mParser;
Interpreter::Interpreter mInterpreter;
bool mOpcodesInstalled;
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)
// relative to the sound dir, and translates them into full paths
// of existing files in the filesystem, if they exist.
bool FSstrict;
FileFinder::FileFinder files;
FileFinder::FileFinderStrict strict;
FileFinder::FileFinder musicpath;
FileFinder::FileFinderStrict musicpathStrict;
SoundImpl(Ogre::Root *root, Ogre::Camera *camera,
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)))
, updater(mgr)
, cameraTracker(mgr)
, store(str)
, files(soundDir)
, files(soundDir), strict(soundDir)
,musicpath(musicDir), musicpathStrict(musicDir)
{
FSstrict = fsstrict;
cout << "Sound output: " << SOUND_OUT << endl;
cout << "Sound decoder: " << SOUND_IN << endl;
// Attach the camera to the camera tracker
cameraTracker.followCamera(camera);
@ -111,6 +116,8 @@ namespace MWSound
cameraTracker.unfollowCamera();
}
static std::string toMp3(std::string str)
{
std::string::size_type i = str.rfind('.');
@ -122,28 +129,76 @@ namespace MWSound
return str;
}
bool hasFile(const std::string &str)
bool hasFile(const std::string &str, bool music = false)
{
if(files.has(str)) return true;
// Not found? Try with .mp3
return files.has(toMp3(str));
if(FSstrict == false)
{
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
// 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(files.has(str))
return files.lookup(str);
// Try mp3 if the wav wasn't found
std::string mp3 = toMp3(str);
if(files.has(mp3))
return files.lookup(mp3);
if(FSstrict == false)
{
// 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
std::string mp3 = toMp3(str);
if(music && musicpath.has(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);
// Try mp3 if the wav wasn't found
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 "";
// Give up
return "";
}
// 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,
const ESMS::ESMStore &store,
boost::filesystem::path dataDir,
bool useSound)
: mData(NULL)
bool useSound, bool fsstrict)
: mData(NULL), fsStrict (fsstrict)
{
MP3Lookup(dataDir / "Music/Explore/");
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()
@ -321,6 +392,16 @@ namespace MWSound
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)
{
boost::filesystem::directory_iterator dir_iter(dir), dir_end;
@ -353,7 +434,7 @@ namespace MWSound
try
{
std::cout << "Playing " << music << "\n";
streamMusic(music);
streamMusicFull(music);
}
catch(std::exception &e)
{
@ -397,21 +478,6 @@ namespace MWSound
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)
{

@ -27,19 +27,28 @@ namespace MWSound
// Hide implementation details - engine.cpp is compiling
// enough as it is.
struct SoundImpl;
SoundImpl *mData;
std::vector<boost::filesystem::path> files;
bool fsStrict;
void streamMusicFull (const std::string& filename);
///< Play a soundifle
/// \param absolute filename
public:
SoundManager(Ogre::Root*, Ogre::Camera*, const ESMS::ESMStore &store,
boost::filesystem::path dataDir, bool useSound);
boost::filesystem::path dataDir, bool useSound, bool fsstrict);
~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 MP3Lookup(boost::filesystem::path dir);
//struct SoundImpl;
bool isMusicPlaying();
SoundImpl getMData();
@ -51,9 +60,7 @@ namespace MWSound
bool sayDone (MWWorld::Ptr reference) const;
///< 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);
///< Play a sound, independently of 3D-position

@ -16,7 +16,7 @@ namespace MWWorld
ESMS::CellRefList<ESM::Ingredient, D> ingreds;
ESMS::CellRefList<ESM::Light, D> lights;
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::Repair, D> repairs;
ESMS::CellRefList<ESM::Weapon, D> weapons;

@ -135,7 +135,7 @@ namespace MWWorld
if (ESMS::LiveCellRef<ESM::Tool, RefData> *ref = cell.lockpicks.find (name))
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);
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))
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);
if (ESMS::LiveCellRef<ESM::NPC, RefData> *ref = searchViaHandle (handle, cell.npcs))
@ -284,7 +284,6 @@ namespace MWWorld
removeScripts (iter->first);
mEnvironment.mMechanicsManager->dropActors (iter->first);
iter->second->destroy();
mEnvironment.mSoundManager->stopSound (iter->first);
delete iter->second;
mActiveCells.erase (iter);
@ -410,9 +409,9 @@ namespace MWWorld
World::World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng,
const Files::Collections& fileCollections,
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),
mSky (false), mCellChanged (false), mEnvironment (environment)
mSky (false), mCellChanged (false), mEnvironment (environment), mNextDynamicRecord (0)
{
mPhysEngine = physEng;
@ -421,6 +420,7 @@ namespace MWWorld
std::cout << "Loading ESM " << masterPath.string() << "\n";
// This parses the ESM file and loads a sample cell
mEsm.setEncoding(encoding);
mEsm.open (masterPath.string());
mStore.load (mEsm);
@ -871,4 +871,36 @@ namespace MWWorld
{
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 mCellChanged;
Environment& mEnvironment;
int mNextDynamicRecord;
OEngine::Physic::PhysicEngine* mPhysEngine;
@ -115,7 +116,7 @@ namespace MWWorld
World (OEngine::Render::OgreRenderer& renderer, OEngine::Physic::PhysicEngine* physEng,
const Files::Collections& fileCollections,
const std::string& master, const boost::filesystem::path& resDir, bool newGame,
Environment& environment);
Environment& environment, const std::string& encoding);
~World();
@ -202,6 +203,14 @@ namespace MWWorld
bool toggleRenderMode (RenderMode mode);
///< Toggle a render 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 <OgreFileSystem.h>
#include <OgreArchive.h>
#include <OgreArchiveFactory.h>
#include <OgreArchiveManager.h>
#include "bsa_file.hpp"
#include <libs/mangle/stream/clients/ogre_datastream.hpp>
namespace
{
using namespace Ogre;
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
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
{
BSAFile arc;
@ -145,7 +329,27 @@ public:
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 init2 = false;
static void insertBSAFactory()
{
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
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().
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 <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include <algorithm>
#ifndef _BSA_ARCHIVE_H_
#define _BSA_ARCHIVE_H_
#ifndef BSA_BSA_ARCHIVE_H
#define BSA_BSA_ARCHIVE_H
namespace Bsa
{
/// Add the given BSA file as an input archive in the Ogre resource
/// system.
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

@ -32,6 +32,7 @@
using namespace std;
using namespace Mangle::Stream;
using namespace Bsa;
/// Error handling
void BSAFile::fail(const string &msg)

@ -21,8 +21,8 @@
*/
#ifndef _BSA_FILE_H_
#define _BSA_FILE_H_
#ifndef BSA_BSA_FILE_H
#define BSA_BSA_FILE_H
#include <libs/mangle/stream/stream.hpp>
#include <libs/platform/stdint.h>
@ -31,6 +31,9 @@
#include <vector>
#include <map>
namespace Bsa
{
/**
This class is used to read "Bethesda Archive Files", or BSAs.
*/
@ -131,4 +134,6 @@ class BSAFile
{ return files; }
};
}
#endif

@ -11,6 +11,7 @@
#include <iostream>
using namespace std;
using namespace Bsa;
BSAFile bsa;

@ -11,6 +11,7 @@
using namespace std;
using namespace Mangle::Stream;
using namespace Bsa;
int main(int argc, char** argv)
{

@ -19,7 +19,7 @@ int main()
Root *root = new Root("","","");
// Add the BSA
addBSA("../../data/Morrowind.bsa");
Bsa::addBSA("../../data/Morrowind.bsa");
// Pick a sample file
String tex = "textures\\tx_natural_cavern_wall13.dds";

@ -3,6 +3,8 @@
namespace ESM
{
using namespace Misc;
ESM_Context ESMReader::getContext()
{
// Update the file position before returning
@ -324,7 +326,7 @@ std::string ESMReader::getString(int size)
mEsm->read(ptr, size);
// Convert to UTF8 and return
return ToUTF8::getUtf8(ToUTF8::WINDOWS_1252);
return ToUTF8::getUtf8(mEncoding);
}
void ESMReader::fail(const std::string &msg)
@ -342,4 +344,21 @@ void ESMReader::fail(const std::string &msg)
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
void fail(const std::string &msg);
/// Sets font encoding for ESM strings
void setEncoding(const std::string& encoding);
private:
Mangle::Stream::StreamPtr mEsm;
@ -360,6 +363,7 @@ private:
SaveData mSaveData;
MasterList mMasters;
ToUTF8::FromType mEncoding;
};
}
#endif

@ -3,7 +3,7 @@
namespace ESM
{
void Misc::load(ESMReader &esm)
void Miscellaneous::load(ESMReader &esm)
{
model = esm.getHNString("MODL");
name = esm.getHNOString("FNAM");

@ -11,7 +11,7 @@ namespace ESM
* carried, bought and sold. It also includes keys.
*/
struct Misc
struct Miscellaneous
{
struct MCDTstruct
{

@ -17,6 +17,7 @@
#include <iostream>
#include <stdexcept>
#include <algorithm>
namespace ESMS
{
@ -99,7 +100,7 @@ namespace ESMS
CellRefList<ItemLevList, D> itemLists;
CellRefList<ESM::Light, D> lights;
CellRefList<Tool, D> lockpicks;
CellRefList<Misc, D> miscItems;
CellRefList<Miscellaneous, D> miscItems;
CellRefList<NPC, D> npcs;
CellRefList<Probe, D> probes;
CellRefList<Repair, D> repairs;
@ -187,7 +188,14 @@ namespace ESMS
// Get each reference in turn
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
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;
// 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;
}
}

@ -52,7 +52,7 @@ namespace ESMS
RecListT<ItemLevList> itemLists;
RecListT<Light> lights;
RecListT<Tool> lockpicks;
RecListT<Misc> miscItems;
RecListT<Miscellaneous> miscItems;
RecListWithIDT<NPC> npcs;
RecListT<LoadNPCC> npcChange;
RecListT<Probe> probes;

@ -20,9 +20,9 @@ class FileFinderT
void add(const boost::filesystem::path &pth)
{
std::string file = pth.string();
std::string key = file.substr(cut);
owner->table[key] = file;
std::string file = pth.string();
std::string key = file.substr(cut);
owner->table[key] = file;
}
};
@ -50,17 +50,18 @@ public:
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.
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
typedef FileFinderT<path_less> FileFinder;
typedef FileFinderT<path_slash> FileFinderStrict;
}
#endif

@ -44,6 +44,41 @@ struct path_less
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

@ -4,6 +4,8 @@
#include <map>
#include <vector>
#include <string>
#include <locale>
#include <cctype>
#include <boost/filesystem/path.hpp>
@ -21,11 +23,12 @@ namespace Files
return left<right;
std::size_t min = std::min (left.length(), right.length());
std::locale loc;
for (std::size_t i=0; i<min; ++i)
{
char l = std::tolower (left[i]);
char r = std::tolower (right[i]);
char l = std::tolower (left[i], loc);
char r = std::tolower (right[i], loc);
if (l<r)
return true;

@ -9,7 +9,7 @@
#include <OSX/macUtils.h>
#endif
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE
#include <stdlib.h> //getenv
#endif
@ -19,7 +19,9 @@ std::string Files::getPath (PathTypeEnum parType, const std::string parApp, cons
if (parType==Path_ConfigGlobal)
{
#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
theBasePath = "/etc/"+parApp+"/";
#else
@ -29,9 +31,7 @@ std::string Files::getPath (PathTypeEnum parType, const std::string parApp, cons
}
else if (parType==Path_ConfigUser)
{
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
theBasePath = Ogre::macBundlePath() + "/Contents/MacOS/"; //FIXME do we have global/local with OSX?
#elif OGRE_PLATFORM == OGRE_PLATFORM_LINUX
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE
const char* theDir;
if ((theDir = getenv("OPENMW_HOME")) != NULL)
{

@ -134,8 +134,7 @@ namespace Interpreter
throw std::runtime_error (error.str());
}
Interpreter::Interpreter (Context& context)
: mRuntime (context)
Interpreter::Interpreter()
{}
Interpreter::~Interpreter()
@ -195,11 +194,11 @@ namespace Interpreter
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);
mRuntime.configure (code, codeSize);
mRuntime.configure (code, codeSize, context);
int opcodes = static_cast<int> (code[0]);

@ -21,23 +21,23 @@ namespace Interpreter
std::map<int, Opcode1 *> mSegment3;
std::map<int, Opcode2 *> mSegment4;
std::map<int, Opcode0 *> mSegment5;
// not implemented
Interpreter (const Interpreter&);
Interpreter& operator= (const Interpreter&);
void execute (Type_Code code);
void abortUnknownCode (int segment, int opcode);
void abortUnknownSegment (Type_Code code);
public:
Interpreter (Context& context);
Interpreter();
~Interpreter();
void installSegment0 (int code, Opcode1 *opcode);
///< ownership of \a opcode is transferred to *this.
@ -55,10 +55,9 @@ namespace Interpreter
void installSegment5 (int code, Opcode0 *opcode);
///< 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

@ -7,50 +7,51 @@
namespace Interpreter
{
Runtime::Runtime (Context& context) : mContext (context), mCode (0), mPC (0) {}
Runtime::Runtime() : mContext (0), mCode (0), mPC (0) {}
int Runtime::getPC() const
{
return mPC;
}
int Runtime::getIntegerLiteral (int index) const
{
assert (index>=0 && index<static_cast<int> (mCode[1]));
const Type_Code *literalBlock = mCode + 4 + mCode[0];
return *reinterpret_cast<const int *> (&literalBlock[index]);
}
float Runtime::getFloatLiteral (int index) const
{
assert (index>=0 && index<static_cast<int> (mCode[2]));
const Type_Code *literalBlock = mCode + 4 + mCode[0] + mCode[1];
return *reinterpret_cast<const float *> (&literalBlock[index]);
}
std::string Runtime::getStringLiteral (int index) const
{
assert (index>=0 && index<static_cast<int> (mCode[3]));
const char *literalBlock =
reinterpret_cast<const char *> (mCode + 4 + mCode[0] + mCode[1] + mCode[2]);
for (; index; --index)
{
literalBlock += std::strlen (literalBlock) + 1;
}
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();
mContext = &context;
mCode = code;
mCodeSize = codeSize;
mPC = 0;
@ -58,54 +59,55 @@ namespace Interpreter
void Runtime::clear()
{
mContext = 0;
mCode = 0;
mCodeSize = 0;
mStack.clear();
}
void Runtime::setPC (int PC)
{
mPC = PC;
}
}
void Runtime::push (const Data& data)
{
mStack.push_back (data);
}
void Runtime::push (Type_Integer value)
{
Data data;
data.mInteger = value;
push (data);
}
void Runtime::push (Type_Float value)
{
Data data;
data.mFloat = value;
push (data);
}
void Runtime::pop()
{
if (mStack.empty())
throw std::runtime_error ("stack underflow");
mStack.resize (mStack.size()-1);
}
Data& Runtime::operator[] (int Index)
{
if (Index<0 || Index>=static_cast<int> (mStack.size()))
throw std::runtime_error ("stack index out of range");
return mStack[mStack.size()-Index-1];
}
Context& Runtime::getContext()
{
return mContext;
assert (mContext);
return *mContext;
}
}

@ -11,52 +11,52 @@ namespace Interpreter
class Context;
/// Runtime data and engine interface
class Runtime
{
Context& mContext;
Context *mContext;
const Type_Code *mCode;
int mCodeSize;
int mPC;
std::vector<Data> mStack;
public:
Runtime (Context& context);
Runtime ();
int getPC() const;
///< return program counter.
int getIntegerLiteral (int index) const;
float getFloatLiteral (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
/// the destructor is called. \a codeSize is given in 32-bit words.
void clear();
void setPC (int PC);
///< set program counter.
void push (const Data& data);
///< push data on stack
void push (Type_Integer value);
///< push integer data on stack.
void push (Type_Float value);
///< push float data on stack.
void pop();
///< pop stack
Data& operator[] (int Index);
///< Access stack member, counted from the top.
Context& getContext();
};
}

@ -4,9 +4,13 @@
#include <OgrePrerequisites.h>
namespace Misc
{
bool isFile(const char *name)
{
boost::filesystem::path cfg_file_path(name);
return boost::filesystem::exists(cfg_file_path);
}
}

@ -1,8 +1,11 @@
#ifndef __FILEOPS_H_
#define __FILEOPS_H_
#ifndef MISC_FILEOPS_H
#define MISC_FILEOPS_H
#include <string>
namespace Misc
{
/// Check if a given path is an existing file (not a directory)
bool isFile(const char *name);
@ -10,4 +13,6 @@ bool isFile(const char *name);
std::string macBundlePath();
#endif
}
#endif

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

Loading…
Cancel
Save